summaryrefslogtreecommitdiff
path: root/game/client/portal
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/portal
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/portal')
-rw-r--r--game/client/portal/C_PortalGhostRenderable.cpp377
-rw-r--r--game/client/portal/C_PortalGhostRenderable.h128
-rw-r--r--game/client/portal/MaterialProxy_Portal_PickAlphaMask.cpp127
-rw-r--r--game/client/portal/PortalRender.cpp1178
-rw-r--r--game/client/portal/PortalRender.h333
-rw-r--r--game/client/portal/Portal_DynamicMeshRenderingUtils.cpp183
-rw-r--r--game/client/portal/Portal_DynamicMeshRenderingUtils.h32
-rw-r--r--game/client/portal/c_env_lightraill_endpoint.cpp499
-rw-r--r--game/client/portal/c_env_portal_path_track.cpp203
-rw-r--r--game/client/portal/c_func_liquidportal.cpp630
-rw-r--r--game/client/portal/c_func_liquidportal.h131
-rw-r--r--game/client/portal/c_neurotoxin_countdown.cpp56
-rw-r--r--game/client/portal/c_neurotoxin_countdown.h39
-rw-r--r--game/client/portal/c_npc_portal_turret_floor.cpp168
-rw-r--r--game/client/portal/c_npc_rocket_turret.cpp177
-rw-r--r--game/client/portal/c_portal_player.cpp1653
-rw-r--r--game/client/portal/c_portal_player.h222
-rw-r--r--game/client/portal/c_portal_radio.cpp256
-rw-r--r--game/client/portal/c_prop_energy_ball.cpp133
-rw-r--r--game/client/portal/c_prop_portal.cpp952
-rw-r--r--game/client/portal/c_prop_portal.h96
-rw-r--r--game/client/portal/c_prop_portal_stats_display.cpp341
-rw-r--r--game/client/portal/c_prop_portal_stats_display.h87
-rw-r--r--game/client/portal/c_weapon_physcannon.cpp388
-rw-r--r--game/client/portal/c_weapon_portalgun.cpp1112
-rw-r--r--game/client/portal/c_weapon_portalgun.h278
-rw-r--r--game/client/portal/c_weapon_stubs_portal.cpp39
-rw-r--r--game/client/portal/clientmode_portal.cpp227
-rw-r--r--game/client/portal/clientmode_portal.h51
-rw-r--r--game/client/portal/fx_portal.cpp123
-rw-r--r--game/client/portal/hud_quickinfo.cpp383
-rw-r--r--game/client/portal/materialproxy_portalstatic.cpp147
-rw-r--r--game/client/portal/portal_credits.cpp1574
-rw-r--r--game/client/portal/portal_hud_crosshair.cpp150
-rw-r--r--game/client/portal/portal_hud_crosshair.h58
-rw-r--r--game/client/portal/portal_render_targets.cpp229
-rw-r--r--game/client/portal/portal_render_targets.h61
-rw-r--r--game/client/portal/portalrenderable_flatbasic.cpp1304
-rw-r--r--game/client/portal/portalrenderable_flatbasic.h121
-rw-r--r--game/client/portal/vgui_neurotoxin_countdown_screen.cpp186
-rw-r--r--game/client/portal/vgui_portal_stats_display_screen.cpp305
41 files changed, 14737 insertions, 0 deletions
diff --git a/game/client/portal/C_PortalGhostRenderable.cpp b/game/client/portal/C_PortalGhostRenderable.cpp
new file mode 100644
index 0000000..1ff714e
--- /dev/null
+++ b/game/client/portal/C_PortalGhostRenderable.cpp
@@ -0,0 +1,377 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "C_PortalGhostRenderable.h"
+#include "PortalRender.h"
+#include "c_portal_player.h"
+#include "model_types.h"
+
+C_PortalGhostRenderable::C_PortalGhostRenderable( C_Prop_Portal *pOwningPortal, C_BaseEntity *pGhostSource, RenderGroup_t sourceRenderGroup, const VMatrix &matGhostTransform, float *pSharedRenderClipPlane, bool bLocalPlayer )
+: m_pGhostedRenderable( pGhostSource ),
+ m_matGhostTransform( matGhostTransform ),
+ m_pSharedRenderClipPlane( pSharedRenderClipPlane ),
+ m_bLocalPlayer( bLocalPlayer ),
+ m_pOwningPortal( pOwningPortal )
+{
+ m_bSourceIsBaseAnimating = (dynamic_cast<C_BaseAnimating *>(pGhostSource) != NULL);
+
+ cl_entitylist->AddNonNetworkableEntity( GetIClientUnknown() );
+ g_pClientLeafSystem->AddRenderable( this, sourceRenderGroup );
+}
+
+C_PortalGhostRenderable::~C_PortalGhostRenderable( void )
+{
+ m_pGhostedRenderable = NULL;
+ g_pClientLeafSystem->RemoveRenderable( RenderHandle() );
+ cl_entitylist->RemoveEntity( GetIClientUnknown()->GetRefEHandle() );
+
+ DestroyModelInstance();
+}
+
+void C_PortalGhostRenderable::PerFrameUpdate( void )
+{
+ if( m_pGhostedRenderable )
+ {
+ SetModelName( m_pGhostedRenderable->GetModelName() );
+ SetModelIndex( m_pGhostedRenderable->GetModelIndex() );
+ SetEffects( m_pGhostedRenderable->GetEffects() | EF_NOINTERP );
+ m_flAnimTime = m_pGhostedRenderable->m_flAnimTime;
+
+ if( m_bSourceIsBaseAnimating )
+ {
+ C_BaseAnimating *pSource = (C_BaseAnimating *)m_pGhostedRenderable;
+ SetCycle( pSource->GetCycle() );
+ SetSequence( pSource->GetSequence() );
+ m_nBody = pSource->m_nBody;
+ m_nSkin = pSource->m_nSkin;
+ }
+ }
+
+
+ // Set position and angles relative to the object it's ghosting
+ Vector ptNewOrigin = m_matGhostTransform * m_pGhostedRenderable->GetAbsOrigin();
+ QAngle qNewAngles = TransformAnglesToWorldSpace( m_pGhostedRenderable->GetAbsAngles(), m_matGhostTransform.As3x4() );
+
+ SetAbsOrigin( ptNewOrigin );
+ SetAbsAngles( qNewAngles );
+
+ AddEffects( EF_NOINTERP );
+
+ RemoveFromInterpolationList();
+
+ g_pClientLeafSystem->RenderableChanged( RenderHandle() );
+}
+
+Vector const& C_PortalGhostRenderable::GetRenderOrigin( void )
+{
+ if( m_pGhostedRenderable == NULL )
+ return m_ReferencedReturns.vRenderOrigin;
+
+ m_ReferencedReturns.vRenderOrigin = m_matGhostTransform * m_pGhostedRenderable->GetRenderOrigin();
+ return m_ReferencedReturns.vRenderOrigin;
+}
+
+QAngle const& C_PortalGhostRenderable::GetRenderAngles( void )
+{
+ if( m_pGhostedRenderable == NULL )
+ return m_ReferencedReturns.qRenderAngle;
+
+ m_ReferencedReturns.qRenderAngle = TransformAnglesToWorldSpace( m_pGhostedRenderable->GetRenderAngles(), m_matGhostTransform.As3x4() );
+ return m_ReferencedReturns.qRenderAngle;
+}
+
+bool C_PortalGhostRenderable::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ int nModelIndex = 0;
+ CBaseCombatWeapon *pParent = dynamic_cast<CBaseCombatWeapon*>( m_pGhostedRenderable );
+ if ( pParent )
+ {
+ nModelIndex = pParent->GetModelIndex();
+ pParent->SetModelIndex( pParent->GetWorldModelIndex() );
+ }
+
+ if( m_pGhostedRenderable->SetupBones( pBoneToWorldOut, nMaxBones, boneMask, currentTime ) )
+ {
+ if( pBoneToWorldOut )
+ {
+ for( int i = 0; i != nMaxBones; ++i ) //FIXME: nMaxBones is most definitely greater than the actual number of bone transforms actually used, find the subset somehow
+ {
+ pBoneToWorldOut[i] = (m_matGhostTransform * pBoneToWorldOut[i]).As3x4();
+ }
+ }
+ return true;
+ }
+
+ if ( pParent )
+ {
+ pParent->SetModelIndex( nModelIndex );
+ }
+
+ return false;
+}
+
+void C_PortalGhostRenderable::GetRenderBounds( Vector& mins, Vector& maxs )
+{
+ if( m_pGhostedRenderable == NULL )
+ {
+ mins = maxs = vec3_origin;
+ return;
+ }
+
+ m_pGhostedRenderable->GetRenderBounds( mins, maxs );
+}
+
+void C_PortalGhostRenderable::GetRenderBoundsWorldspace( Vector& mins, Vector& maxs )
+{
+ if( m_pGhostedRenderable == NULL )
+ {
+ mins = maxs = vec3_origin;
+ return;
+ }
+
+ m_pGhostedRenderable->GetRenderBoundsWorldspace( mins, maxs );
+ TransformAABB( m_matGhostTransform.As3x4(), mins, maxs, mins, maxs );
+}
+
+void C_PortalGhostRenderable::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
+{
+ m_pGhostedRenderable->GetShadowRenderBounds( mins, maxs, shadowType );
+ TransformAABB( m_matGhostTransform.As3x4(), mins, maxs, mins, maxs );
+}
+
+/*bool C_PortalGhostRenderable::GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ return m_pGhostedRenderable->GetShadowCastDistance( pDist, shadowType );
+}
+
+bool C_PortalGhostRenderable::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ if( m_pGhostedRenderable->GetShadowCastDirection( pDirection, shadowType ) )
+ {
+ if( pDirection )
+ *pDirection = m_matGhostTransform.ApplyRotation( *pDirection );
+
+ return true;
+ }
+ return false;
+}*/
+
+const matrix3x4_t & C_PortalGhostRenderable::RenderableToWorldTransform()
+{
+ if( m_pGhostedRenderable == NULL )
+ return m_ReferencedReturns.matRenderableToWorldTransform;
+
+ ConcatTransforms( m_matGhostTransform.As3x4(), m_pGhostedRenderable->RenderableToWorldTransform(), m_ReferencedReturns.matRenderableToWorldTransform );
+ return m_ReferencedReturns.matRenderableToWorldTransform;
+}
+
+bool C_PortalGhostRenderable::GetAttachment( int number, Vector &origin, QAngle &angles )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ if( m_pGhostedRenderable->GetAttachment( number, origin, angles ) )
+ {
+ origin = m_matGhostTransform * origin;
+ angles = TransformAnglesToWorldSpace( angles, m_matGhostTransform.As3x4() );
+ return true;
+ }
+ return false;
+}
+
+bool C_PortalGhostRenderable::GetAttachment( int number, matrix3x4_t &matrix )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ if( m_pGhostedRenderable->GetAttachment( number, matrix ) )
+ {
+ ConcatTransforms( m_matGhostTransform.As3x4(), matrix, matrix );
+ return true;
+ }
+ return false;
+}
+
+bool C_PortalGhostRenderable::GetAttachment( int number, Vector &origin )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ if( m_pGhostedRenderable->GetAttachment( number, origin ) )
+ {
+ origin = m_matGhostTransform * origin;
+ return true;
+ }
+ return false;
+}
+
+bool C_PortalGhostRenderable::GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ Vector ghostVel;
+ if( m_pGhostedRenderable->GetAttachmentVelocity( number, ghostVel, angleVel ) )
+ {
+ Vector3DMultiply( m_matGhostTransform, ghostVel, originVel );
+ Vector3DMultiply( m_matGhostTransform, *(Vector*)( &angleVel ), *(Vector*)( &angleVel ) );
+ return true;
+ }
+ return false;
+}
+
+
+int C_PortalGhostRenderable::DrawModel( int flags )
+{
+ if( m_bSourceIsBaseAnimating )
+ {
+ if( m_bLocalPlayer )
+ {
+ C_Portal_Player *pPlayer = C_Portal_Player::GetLocalPlayer();
+
+ if ( !pPlayer->IsAlive() )
+ {
+ // Dead player uses a ragdoll to draw, so don't ghost the dead entity
+ return 0;
+ }
+ else if( g_pPortalRender->GetViewRecursionLevel() == 0 )
+ {
+ if( pPlayer->m_bEyePositionIsTransformedByPortal )
+ return 0;
+ }
+ else if( g_pPortalRender->GetViewRecursionLevel() == 1 )
+ {
+ if( !pPlayer->m_bEyePositionIsTransformedByPortal )
+ return 0;
+ }
+ }
+
+ return C_BaseAnimating::DrawModel( flags );
+ }
+ else
+ {
+ DrawBrushModelMode_t mode = DBM_DRAW_ALL;
+ if ( flags & STUDIO_TWOPASS )
+ {
+ mode = ( flags & STUDIO_TRANSPARENCY ) ? DBM_DRAW_TRANSLUCENT_ONLY : DBM_DRAW_OPAQUE_ONLY;
+ }
+
+ render->DrawBrushModelEx( m_pGhostedRenderable,
+ (model_t *)m_pGhostedRenderable->GetModel(),
+ GetRenderOrigin(),
+ GetRenderAngles(),
+ mode );
+
+ return 1;
+ }
+
+ return 0;
+}
+
+ModelInstanceHandle_t C_PortalGhostRenderable::GetModelInstance()
+{
+ if ( m_pGhostedRenderable )
+ return m_pGhostedRenderable->GetModelInstance();
+
+ return BaseClass::GetModelInstance();
+}
+
+
+
+
+bool C_PortalGhostRenderable::IsTransparent( void )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ return m_pGhostedRenderable->IsTransparent();
+}
+
+bool C_PortalGhostRenderable::UsesPowerOfTwoFrameBufferTexture()
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ return m_pGhostedRenderable->UsesPowerOfTwoFrameBufferTexture();
+}
+
+/*const model_t* C_PortalGhostRenderable::GetModel( ) const
+{
+ if( m_pGhostedRenderable == NULL )
+ return NULL;
+
+ return m_pGhostedRenderable->GetModel();
+}
+
+int C_PortalGhostRenderable::GetBody()
+{
+ if( m_pGhostedRenderable == NULL )
+ return 0;
+
+ return m_pGhostedRenderable->GetBody();
+}*/
+
+void C_PortalGhostRenderable::GetColorModulation( float* color )
+{
+ if( m_pGhostedRenderable == NULL )
+ return;
+
+ return m_pGhostedRenderable->GetColorModulation( color );
+}
+
+/*ShadowType_t C_PortalGhostRenderable::ShadowCastType()
+{
+ if( m_pGhostedRenderable == NULL )
+ return SHADOWS_NONE;
+
+ return m_pGhostedRenderable->ShadowCastType();
+}*/
+
+int C_PortalGhostRenderable::LookupAttachment( const char *pAttachmentName )
+{
+ if( m_pGhostedRenderable == NULL )
+ return -1;
+
+
+ return m_pGhostedRenderable->LookupAttachment( pAttachmentName );
+}
+
+/*int C_PortalGhostRenderable::GetSkin()
+{
+ if( m_pGhostedRenderable == NULL )
+ return -1;
+
+
+ return m_pGhostedRenderable->GetSkin();
+}
+
+bool C_PortalGhostRenderable::IsTwoPass( void )
+{
+ if( m_pGhostedRenderable == NULL )
+ return false;
+
+ return m_pGhostedRenderable->IsTwoPass();
+}*/
+
+
+
+
+
+
+
+
+
diff --git a/game/client/portal/C_PortalGhostRenderable.h b/game/client/portal/C_PortalGhostRenderable.h
new file mode 100644
index 0000000..bb00303
--- /dev/null
+++ b/game/client/portal/C_PortalGhostRenderable.h
@@ -0,0 +1,128 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef C_PORTALGHOSTRENDERABLE_H
+#define C_PORTALGHOSTRENDERABLE_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+//#include "iclientrenderable.h"
+#include "c_baseanimating.h"
+
+class C_PortalGhostRenderable : public C_BaseAnimating//IClientRenderable, public IClientUnknown
+{
+public:
+ C_BaseEntity *m_pGhostedRenderable; //the renderable we're transforming and re-rendering
+
+ VMatrix m_matGhostTransform;
+ float *m_pSharedRenderClipPlane; //shared by all portal ghost renderables within the same portal
+ bool m_bLocalPlayer; //special draw rules for the local player
+ bool m_bSourceIsBaseAnimating;
+ C_Prop_Portal *m_pOwningPortal;
+
+ struct
+ {
+ Vector vRenderOrigin;
+ QAngle qRenderAngle;
+ matrix3x4_t matRenderableToWorldTransform;
+ } m_ReferencedReturns; //when returning a reference, it has to actually exist somewhere
+
+ C_PortalGhostRenderable( C_Prop_Portal *pOwningPortal, C_BaseEntity *pGhostSource, RenderGroup_t sourceRenderGroup, const VMatrix &matGhostTransform, float *pSharedRenderClipPlane, bool bLocalPlayer );
+ virtual ~C_PortalGhostRenderable( void );
+
+ void PerFrameUpdate( void ); //called once per frame for misc updating
+
+ // Data accessors
+ virtual Vector const& GetRenderOrigin( void );
+ virtual QAngle const& GetRenderAngles( void );
+ virtual bool ShouldDraw( void ) { return true; }
+
+ // Call this to get the current bone transforms for the model.
+ // currentTime parameter will affect interpolation
+ // nMaxBones specifies how many matrices pBoneToWorldOut can hold. (Should be greater than or
+ // equal to studiohdr_t::numbones. Use MAXSTUDIOBONES to be safe.)
+ virtual bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime );
+
+ // Returns the bounds relative to the origin (render bounds)
+ virtual void GetRenderBounds( Vector& mins, Vector& maxs );
+
+ // returns the bounds as an AABB in worldspace
+ virtual void GetRenderBoundsWorldspace( Vector& mins, Vector& maxs );
+
+ // These normally call through to GetRenderAngles/GetRenderBounds, but some entities custom implement them.
+ virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType );
+
+ // These methods return true if we want a per-renderable shadow cast direction + distance
+ //virtual bool GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const;
+ //virtual bool GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const;
+
+ // Returns the transform from RenderOrigin/RenderAngles to world
+ virtual const matrix3x4_t &RenderableToWorldTransform();
+
+ // Attachments
+ virtual bool GetAttachment( int number, Vector &origin, QAngle &angles );
+ virtual bool GetAttachment( int number, matrix3x4_t &matrix );
+ virtual bool GetAttachment( int number, Vector &origin );
+ virtual bool GetAttachmentVelocity( int number, Vector &originVel, Quaternion &angleVel );
+
+ // Rendering clip plane, should be 4 floats, return value of NULL indicates a disabled render clip plane
+ virtual float *GetRenderClipPlane( void ) { return m_pSharedRenderClipPlane; };
+
+ virtual int DrawModel( int flags );
+
+ // Get the model instance of the ghosted model so that decals will properly draw across portals
+ virtual ModelInstanceHandle_t GetModelInstance();
+
+
+
+ //------------------------------------------
+ //IClientRenderable - Trivial or redirection
+ //------------------------------------------
+ virtual IClientUnknown* GetIClientUnknown() { return this; };
+ virtual bool IsTransparent( void );
+ virtual bool UsesPowerOfTwoFrameBufferTexture();
+ //virtual ClientShadowHandle_t GetShadowHandle() const { return m_hShadowHandle; };
+ //virtual ClientRenderHandle_t& RenderHandle() { return m_hRenderHandle; };
+ //virtual const model_t* GetModel( ) const;
+ //virtual int GetBody();
+ //virtual void ComputeFxBlend( ) { return m_pGhostedRenderable->ComputeFxBlend(); };
+ //virtual int GetFxBlend( void ) { return m_pGhostedRenderable->GetFxBlend(); };
+ virtual void GetColorModulation( float* color );
+ //virtual bool LODTest() { return true; };
+ //virtual void SetupWeights( void ) { NULL; };
+ //virtual void DoAnimationEvents( void ) { NULL; }; //TODO: find out if there's something we should be doing with this
+ //virtual IPVSNotify* GetPVSNotifyInterface() { return NULL; };
+ //virtual bool ShouldReceiveProjectedTextures( int flags ) { return false; };//{ return m_pGhostedRenderable->ShouldReceiveProjectedTextures( flags ); };
+ //virtual bool IsShadowDirty( ) { return m_bDirtyShadow; };
+ //virtual void MarkShadowDirty( bool bDirty ) { m_bDirtyShadow = bDirty; };
+ //virtual IClientRenderable * GetShadowParent() { return NULL; };
+ //virtual IClientRenderable * FirstShadowChild() { return NULL; };
+ //virtual IClientRenderable * NextShadowPeer() { return NULL; };
+ //virtual ShadowType_t ShadowCastType();
+ //virtual void CreateModelInstance() { NULL; };
+ //virtual ModelInstanceHandle_t GetModelInstance() { return m_pGhostedRenderable->GetModelInstance(); }; //TODO: find out if sharing an instance causes bugs
+ virtual int LookupAttachment( const char *pAttachmentName );
+ //virtual int GetSkin();
+ //virtual bool IsTwoPass( void );
+ //virtual void OnThreadedDrawSetup() { NULL; };
+
+ //IHandleEntity
+ //virtual void SetRefEHandle( const CBaseHandle &handle ) { m_RefEHandle = handle; };
+ //virtual const CBaseHandle& GetRefEHandle() const { return m_RefEHandle; };
+
+ //IClientUnknown
+ virtual ICollideable* GetCollideable() { return NULL; };
+ virtual IClientNetworkable* GetClientNetworkable() { return NULL; };
+ virtual IClientRenderable* GetClientRenderable() { return this; };
+ virtual IClientEntity* GetIClientEntity() { return NULL; };
+ virtual C_BaseEntity* GetBaseEntity() { return NULL; };
+ virtual IClientThinkable* GetClientThinkable() { return NULL; };
+};
+
+#endif //#ifndef C_PORTALGHOSTRENDERABLE_H \ No newline at end of file
diff --git a/game/client/portal/MaterialProxy_Portal_PickAlphaMask.cpp b/game/client/portal/MaterialProxy_Portal_PickAlphaMask.cpp
new file mode 100644
index 0000000..2d7ff2f
--- /dev/null
+++ b/game/client/portal/MaterialProxy_Portal_PickAlphaMask.cpp
@@ -0,0 +1,127 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "materialsystem/imaterialproxy.h"
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/itexture.h"
+#include "toolframework_client.h"
+#include "portalrenderable_flatbasic.h"
+#include <KeyValues.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+class CPortalPickAlphaMaskProxy : public IMaterialProxy
+{
+private:
+ IMaterialVar *m_AlphaMaskTextureOutput;
+ IMaterialVar *m_AlphaMaskTextureFrame;
+ ITexture *m_pOpeningTexture;
+ ITexture *m_pIdleTexture;
+
+public:
+ CPortalPickAlphaMaskProxy( void );
+ ~CPortalPickAlphaMaskProxy( void );
+ virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
+ virtual void OnBind( void *pBind );
+ virtual void Release( void ) { delete this; }
+ virtual IMaterial * GetMaterial()
+ {
+ if ( m_AlphaMaskTextureOutput )
+ return m_AlphaMaskTextureOutput->GetOwningMaterial();
+ if ( m_AlphaMaskTextureFrame )
+ return m_AlphaMaskTextureFrame->GetOwningMaterial();
+ return NULL;
+ }
+};
+
+CPortalPickAlphaMaskProxy::CPortalPickAlphaMaskProxy( void )
+: m_pOpeningTexture( NULL ), m_pIdleTexture( NULL )
+{
+
+}
+
+CPortalPickAlphaMaskProxy::~CPortalPickAlphaMaskProxy( void )
+{
+ if( m_pOpeningTexture )
+ {
+ m_pOpeningTexture->DecrementReferenceCount();
+ m_pOpeningTexture = NULL;
+ }
+
+ if( m_pIdleTexture )
+ {
+ m_pIdleTexture->DecrementReferenceCount();
+ m_pIdleTexture = NULL;
+ }
+}
+
+bool CPortalPickAlphaMaskProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
+{
+ char const* pszResultVar = pKeyValues->GetString( "maskTextureVar" );
+ if( !pszResultVar )
+ return false;
+
+ char const* pszFrameVar = pKeyValues->GetString( "maskFrameVar" );
+ if( !pszFrameVar )
+ return false;
+
+ char const* pszOpeningTextureInput = pKeyValues->GetString( "openingTexture" );
+ if( !pszOpeningTextureInput )
+ return false;
+
+ char const* pszIdleTextureInput = pKeyValues->GetString( "idleTexture" );
+ if( !pszIdleTextureInput )
+ return false;
+
+ bool foundVar;
+ m_AlphaMaskTextureOutput = pMaterial->FindVar( pszResultVar, &foundVar, false );
+ if( !foundVar )
+ return false;
+
+ m_AlphaMaskTextureFrame = pMaterial->FindVar( pszFrameVar, &foundVar, false );
+ if( !foundVar )
+ return false;
+
+
+ m_pOpeningTexture = materials->FindTexture( pszOpeningTextureInput, TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_pOpeningTexture->IncrementReferenceCount();
+ m_pIdleTexture = materials->FindTexture( pszIdleTextureInput, TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_pIdleTexture->IncrementReferenceCount();
+ return true;
+}
+
+void CPortalPickAlphaMaskProxy::OnBind( void *pBind )
+{
+ if( pBind == NULL )
+ return;
+
+ IClientRenderable *pRenderable = (IClientRenderable*)( pBind );
+
+ CPortalRenderable_FlatBasic *pFlatBasic = dynamic_cast<CPortalRenderable_FlatBasic*>( pRenderable );
+
+ if ( !pFlatBasic )
+ return;
+
+ if( pFlatBasic->m_fOpenAmount == 1.0f )
+ {
+ m_AlphaMaskTextureOutput->SetTextureValue( m_pIdleTexture );
+ }
+ else
+ {
+ m_AlphaMaskTextureOutput->SetTextureValue( m_pOpeningTexture );
+ m_AlphaMaskTextureFrame->SetIntValue( pFlatBasic->m_fOpenAmount * m_pOpeningTexture->GetNumAnimationFrames() );
+ }
+
+ if ( ToolsEnabled() )
+ {
+ ToolFramework_RecordMaterialParams( GetMaterial() );
+ }
+}
+
+EXPOSE_INTERFACE( CPortalPickAlphaMaskProxy, IMaterialProxy, "PortalPickAlphaMask" IMATERIAL_PROXY_INTERFACE_VERSION ); \ No newline at end of file
diff --git a/game/client/portal/PortalRender.cpp b/game/client/portal/PortalRender.cpp
new file mode 100644
index 0000000..ce4ca46
--- /dev/null
+++ b/game/client/portal/PortalRender.cpp
@@ -0,0 +1,1178 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+
+#include "cbase.h"
+#include "PortalRender.h"
+#include "clienteffectprecachesystem.h"
+#include "view.h"
+#include "c_pixel_visibility.h"
+#include "glow_overlay.h"
+#include "portal_render_targets.h" //depth doubler
+#include "materialsystem/itexture.h"
+#include "toolframework/itoolframework.h"
+#include "tier1/KeyValues.h"
+#include "view_scene.h"
+#include "viewrender.h"
+#include "vprof.h"
+
+CLIENTEFFECT_REGISTER_BEGIN( PrecachePortalDrawingMaterials )
+CLIENTEFFECT_MATERIAL( "shadertest/wireframe" )
+CLIENTEFFECT_MATERIAL( "engine/writez_model" )
+CLIENTEFFECT_MATERIAL( "engine/TranslucentVertexColor" )
+CLIENTEFFECT_REGISTER_END()
+
+#define TEMP_DISABLE_PORTAL_VIS_QUERY
+
+static ConVar r_forcecheapwater( "r_forcecheapwater", "0", FCVAR_CLIENTDLL | FCVAR_CHEAT, "Force all water to be cheap water, will show old renders if enabled after water has been seen" );
+
+
+ConVar r_portal_use_stencils( "r_portal_use_stencils", "1", FCVAR_CLIENTDLL, "Render portal views using stencils (if available)" ); //draw portal views using stencil rendering
+ConVar r_portal_stencil_depth( "r_portal_stencil_depth", "2", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "When using stencil views, this changes how many views within views we see" );
+
+//-----------------------------------------------------------------------------
+//
+// Portal rendering management class
+//
+//-----------------------------------------------------------------------------
+static CPortalRender s_PortalRender;
+CPortalRender* g_pPortalRender = &s_PortalRender;
+
+
+//-------------------------------------------
+//Portal View ID Node helpers
+//-------------------------------------------
+CUtlVector<int> s_iFreedViewIDs; //when a view id node gets freed, it's primary view id gets added here
+
+PortalViewIDNode_t *AllocPortalViewIDNode( int iChildLinkCount )
+{
+ PortalViewIDNode_t *pNode = new PortalViewIDNode_t; //for now we just new/delete
+
+ int iFreedIDsCount = s_iFreedViewIDs.Count();
+ if( iFreedIDsCount != 0 )
+ {
+ pNode->iPrimaryViewID = s_iFreedViewIDs.Tail();
+ s_iFreedViewIDs.FastRemove( iFreedIDsCount - 1 );
+ }
+ else
+ {
+ static int iNewAllocationCounter = VIEW_ID_COUNT;
+ pNode->iPrimaryViewID = iNewAllocationCounter;
+ iNewAllocationCounter += 2; //2 to make room for skybox view ids
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pNode->occlusionQueryHandle = pRenderContext->CreateOcclusionQueryObject();
+#endif
+ pNode->iOcclusionQueryPixelsRendered = -5;
+ pNode->iWindowPixelsAtQueryTime = 0;
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+
+ if( iChildLinkCount != 0 )
+ {
+ pNode->ChildNodes.SetCount( iChildLinkCount );
+ memset( pNode->ChildNodes.Base(), NULL, sizeof( PortalViewIDNode_t * ) * iChildLinkCount );
+ }
+
+ return pNode;
+}
+
+void FreePortalViewIDNode( PortalViewIDNode_t *pNode )
+{
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ if( pNode->ChildNodes[i] != NULL )
+ FreePortalViewIDNode( pNode->ChildNodes[i] );
+ }
+
+ s_iFreedViewIDs.AddToTail( pNode->iPrimaryViewID );
+
+ CMatRenderContextPtr pRenderContext( materials );
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pRenderContext->DestroyOcclusionQueryObject( pNode->occlusionQueryHandle );
+#endif
+
+ delete pNode; //for now we just new/delete
+}
+
+void IncreasePortalViewIDChildLinkCount( PortalViewIDNode_t *pNode )
+{
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ if( pNode->ChildNodes[i] != NULL )
+ IncreasePortalViewIDChildLinkCount( pNode->ChildNodes[i] );
+ }
+ pNode->ChildNodes.AddToTail( NULL );
+}
+
+void RemovePortalViewIDChildLinkIndex( PortalViewIDNode_t *pNode, int iRemoveIndex )
+{
+ Assert( pNode->ChildNodes.Count() > iRemoveIndex );
+
+ if( pNode->ChildNodes[iRemoveIndex] != NULL )
+ {
+ FreePortalViewIDNode( pNode->ChildNodes[iRemoveIndex] );
+ pNode->ChildNodes[iRemoveIndex] = NULL;
+ }
+
+ //I know the current behavior for CUtlVector::FastRemove() is to move the tail into the removed index. But I need that behavior to be true in the future as well so I'm doing it explicitly
+ pNode->ChildNodes[iRemoveIndex] = pNode->ChildNodes.Tail();
+ pNode->ChildNodes.Remove( pNode->ChildNodes.Count() - 1 );
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ if( pNode->ChildNodes[i] )
+ RemovePortalViewIDChildLinkIndex( pNode->ChildNodes[i], iRemoveIndex );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Active Portal class
+//
+//-----------------------------------------------------------------------------
+CPortalRenderable::CPortalRenderable( void ) :
+ m_bIsPlaybackPortal( false )
+{
+ m_matrixThisToLinked.Identity();
+
+ //Portal view ID indexing setup
+ IncreasePortalViewIDChildLinkCount( &s_PortalRender.m_HeadPortalViewIDNode );
+ m_iPortalViewIDNodeIndex = s_PortalRender.m_AllPortals.AddToTail( this );
+}
+
+CPortalRenderable::~CPortalRenderable( void )
+{
+ int iLast = s_PortalRender.m_AllPortals.Count() - 1;
+
+ //update the soon-to-be-transplanted portal's index
+ s_PortalRender.m_AllPortals[iLast]->m_iPortalViewIDNodeIndex = m_iPortalViewIDNodeIndex;
+
+ //I know the current behavior for CUtlVector::FastRemove() is to move the tail into the removed index. But I need that behavior to be true in the future as well so I'm doing it explicitly
+ s_PortalRender.m_AllPortals[m_iPortalViewIDNodeIndex] = s_PortalRender.m_AllPortals.Tail();
+ s_PortalRender.m_AllPortals.Remove( iLast );
+
+ RemovePortalViewIDChildLinkIndex( &s_PortalRender.m_HeadPortalViewIDNode, m_iPortalViewIDNodeIndex ); //does the same transplant operation as above to all portal view id nodes
+}
+
+
+void CPortalRenderable::BeginPortalPixelVisibilityQuery( void )
+{
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ return;
+#endif
+
+ if( g_pPortalRender->ShouldUseStencilsToRenderPortals() ) //this function exists because we require help in texture mode, we need no assistance in stencil mode. Moreover, doing the query twice will probably fubar the results
+ return;
+
+ PortalViewIDNode_t *pCurrentPortalViewNode = g_pPortalRender->m_PortalViewIDNodeChain[g_pPortalRender->m_iViewRecursionLevel]->ChildNodes[m_iPortalViewIDNodeIndex];
+
+ if( pCurrentPortalViewNode )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->BeginOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+
+ int iX, iY, iWidth, iHeight;
+ pRenderContext->GetViewport( iX, iY, iWidth, iHeight );
+
+ pCurrentPortalViewNode->iWindowPixelsAtQueryTime = iWidth * iHeight;
+ }
+}
+
+void CPortalRenderable::EndPortalPixelVisibilityQuery( void )
+{
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ return;
+#endif
+
+ if( g_pPortalRender->ShouldUseStencilsToRenderPortals() ) //this function exists because we require help in texture mode, we need no assistance in stencil mode. Moreover, doing the query twice will probably fubar the results
+ return;
+
+ PortalViewIDNode_t *pCurrentPortalViewNode = g_pPortalRender->m_PortalViewIDNodeChain[g_pPortalRender->m_iViewRecursionLevel]->ChildNodes[m_iPortalViewIDNodeIndex];
+
+ if( pCurrentPortalViewNode )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->EndOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CPortalRender::CPortalRender()
+: m_MaterialsAccess( m_Materials )
+{
+ m_iRemainingPortalViewDepth = 1; //let's portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
+ m_iViewRecursionLevel = 0;
+ m_pRenderingViewForPortal = NULL;
+ m_pRenderingViewExitPortal = NULL;
+
+ m_PortalViewIDNodeChain[0] = &m_HeadPortalViewIDNode;
+}
+
+
+void CPortalRender::LevelInitPreEntity()
+{
+ // refresh materials - not sure if this needs to be done every level
+ m_Materials.m_Wireframe.Init( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_WriteZ_Model.Init( "engine/writez_model", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_TranslucentVertexColor.Init( "engine/TranslucentVertexColor", TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+void CPortalRender::LevelShutdownPreEntity()
+{
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ delete m_RecordedPortals[i].m_pActivePortal;
+ }
+ m_RecordedPortals.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// only use stencils when it's requested, and expensive water won't cause it to freak out
+//-----------------------------------------------------------------------------
+bool CPortalRender::ShouldUseStencilsToRenderPortals( ) const
+{
+ // only use stencils when it's requested, and available
+ return r_portal_use_stencils.GetBool() && ( materials->StencilBufferBits() != 0 );
+}
+
+
+int CPortalRender::ShouldForceCheaperWaterLevel() const
+{
+ if( r_forcecheapwater.GetBool() )
+ return 0;
+
+ if( m_iViewRecursionLevel > 0 )
+ {
+ if( portalrendertargets->GetWaterReflectionTextureForStencilDepth( m_iViewRecursionLevel ) == NULL )
+ return 0;
+
+ PortalViewIDNode_t *pPixelVisNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel - 1]->ChildNodes[m_pRenderingViewForPortal->m_iPortalViewIDNodeIndex];
+
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized >= 0.0f )
+ {
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.005f )
+ return 0;
+
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.02f )
+ return 1;
+
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.05f )
+ return 2;
+ }
+ }
+
+ return 3;
+}
+
+bool CPortalRender::ShouldObeyStencilForClears() const
+{
+ return (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals();
+}
+
+void CPortalRender::WaterRenderingHandler_PreReflection() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( false );
+ }
+}
+
+void CPortalRender::WaterRenderingHandler_PostReflection() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( true );
+ }
+}
+
+void CPortalRender::WaterRenderingHandler_PreRefraction() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( false );
+ }
+}
+
+void CPortalRender::WaterRenderingHandler_PostRefraction() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( true );
+ }
+}
+
+
+void Recursive_UpdatePortalPixelVisibility( PortalViewIDNode_t *pNode, IMatRenderContext *pRenderContext )
+{
+ if( pNode->iWindowPixelsAtQueryTime > 0 )
+ {
+ if( pNode->iOcclusionQueryPixelsRendered < -1 )
+ {
+ //First couple queries. We seem to be getting bogus 0's on the first queries sometimes. ignore the results.
+ ++pNode->iOcclusionQueryPixelsRendered;
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+ }
+ else
+ {
+ pNode->iOcclusionQueryPixelsRendered = pRenderContext->OcclusionQuery_GetNumPixelsRendered( pNode->occlusionQueryHandle );
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = ((float)pNode->iOcclusionQueryPixelsRendered) / ((float)pNode->iWindowPixelsAtQueryTime);
+ }
+ }
+ else
+ {
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+ }
+
+ pNode->iWindowPixelsAtQueryTime = 0;
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
+ if( pChildNode )
+ Recursive_UpdatePortalPixelVisibility( pChildNode, pRenderContext );
+ }
+}
+
+void CPortalRender::UpdatePortalPixelVisibility( void )
+{
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ return;
+#endif
+
+ if( m_iViewRecursionLevel != 0 )
+ return;
+
+ IMatRenderContext *pRenderContext = materials->GetRenderContext();
+ //CMatRenderContextPtr pRenderContext( materials );
+
+ for( int i = m_HeadPortalViewIDNode.ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = m_HeadPortalViewIDNode.ChildNodes[i];
+ if( pChildNode )
+ Recursive_UpdatePortalPixelVisibility( pChildNode, pRenderContext );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Invalidates pixel visibility data for all portals for this next frame.
+//-----------------------------------------------------------------------------
+void Recursive_InvalidatePortalPixelVis( PortalViewIDNode_t *pNode )
+{
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+ pNode->iOcclusionQueryPixelsRendered = -5;
+ pNode->iWindowPixelsAtQueryTime = 0;
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
+ if( pChildNode )
+ Recursive_InvalidatePortalPixelVis( pChildNode );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Preserves pixel visibility data when view id's are getting swapped around
+//-----------------------------------------------------------------------------
+void CPortalRender::EnteredPortal( CPortalRenderable *pEnteredPortal )
+{
+ CPortalRenderable *pExitPortal = pEnteredPortal->GetLinkedPortal();
+ Assert( pExitPortal != NULL );
+
+ if ( pExitPortal == NULL )
+ return;
+
+ int iNodeLinkCount = m_HeadPortalViewIDNode.ChildNodes.Count();
+
+ PortalViewIDNode_t *pNewHead = m_HeadPortalViewIDNode.ChildNodes[pEnteredPortal->m_iPortalViewIDNodeIndex];
+ m_HeadPortalViewIDNode.ChildNodes[pEnteredPortal->m_iPortalViewIDNodeIndex] = NULL;
+
+ //Create a new node that will preserve main's visibility. This new node will be linked to the new head node at the exit portal's index (imagine entering a portal walking backwards)
+ PortalViewIDNode_t *pExitPortalsNewNode = AllocPortalViewIDNode( iNodeLinkCount );
+ {
+ for( int i = 0; i != iNodeLinkCount; ++i )
+ {
+ pExitPortalsNewNode->ChildNodes[i] = m_HeadPortalViewIDNode.ChildNodes[i];
+ m_HeadPortalViewIDNode.ChildNodes[i] = NULL;
+ }
+
+ PixelVisibility_ShiftVisibilityViews( VIEW_MAIN, pExitPortalsNewNode->iPrimaryViewID );
+ PixelVisibility_ShiftVisibilityViews( VIEW_3DSKY, pExitPortalsNewNode->iPrimaryViewID + 1 );
+ }
+
+
+
+ if( pNewHead ) //it's possible we entered a portal we couldn't see through
+ {
+ Assert( pNewHead->ChildNodes.Count() == m_HeadPortalViewIDNode.ChildNodes.Count() );
+ Assert( pNewHead->ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] == NULL ); //seeing out of an exit portal back into itself should be impossible
+
+ for( int i = 0; i != iNodeLinkCount; ++i )
+ {
+ m_HeadPortalViewIDNode.ChildNodes[i] = pNewHead->ChildNodes[i];
+ pNewHead->ChildNodes[i] = NULL; //going to be freeing the node in a minute, don't want to kill transplanted children
+ }
+
+ //Since the primary views will always be 0 and 1, we have to shift results instead of replacing the id's
+ PixelVisibility_ShiftVisibilityViews( pNewHead->iPrimaryViewID, VIEW_MAIN );
+ PixelVisibility_ShiftVisibilityViews( pNewHead->iPrimaryViewID + 1, VIEW_3DSKY );
+
+ FreePortalViewIDNode( pNewHead );
+ }
+
+ Assert( m_HeadPortalViewIDNode.ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] == NULL ); //asserted above in pNewHead code, but call me paranoid
+ m_HeadPortalViewIDNode.ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] = pExitPortalsNewNode;
+
+ //Because pixel visibility is based off of *last* frame's visibility. We can get cases where a certain portal
+ //wasn't visible last frame, but is takes up most of the screen this frame.
+ //Set all portal pixel visibility to unknown visibility.
+ for( int i = m_HeadPortalViewIDNode.ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = m_HeadPortalViewIDNode.ChildNodes[i];
+ if( pChildNode )
+ Recursive_InvalidatePortalPixelVis( pChildNode );
+ }
+}
+
+
+
+
+
+bool CPortalRender::DrawPortalsUsingStencils( CViewRender *pViewRender )
+{
+ VPROF( "CPortalRender::DrawPortalsUsingStencils" );
+
+ if( !ShouldUseStencilsToRenderPortals() )
+ return false;
+
+ int iDrawFlags = pViewRender->GetDrawFlags();
+
+ if ( (iDrawFlags & DF_RENDER_REFLECTION) != 0 )
+ return false;
+
+ if ( ((iDrawFlags & DF_CLIP_Z) != 0) && ((iDrawFlags & DF_CLIP_BELOW) == 0) ) //clipping above the water height
+ return false;
+
+ int iNumRenderablePortals = m_ActivePortals.Count();
+
+ // This loop is necessary because tools can suppress rendering without telling the portal system
+ CUtlVector< CPortalRenderable* > actualActivePortals( 0, iNumRenderablePortals );
+ for ( int i = 0; i < iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pPortalRenderable = m_ActivePortals[i];
+ C_BaseEntity *pPairedEntity = pPortalRenderable->PortalRenderable_GetPairedEntity();
+ bool bIsVisible = (pPairedEntity == NULL) || (pPairedEntity->IsVisible() && pPairedEntity->ShouldDraw()); //either unknown visibility or definitely visible.
+
+ if ( !pPortalRenderable->m_bIsPlaybackPortal )
+ {
+ if ( !bIsVisible )
+ {
+ //can't see through the portal, free up it's view id node for use elsewhere
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] != NULL )
+ {
+ FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] );
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] = NULL;
+ }
+
+ continue;
+ }
+ }
+
+ actualActivePortals.AddToTail( m_ActivePortals[i] );
+ }
+ iNumRenderablePortals = actualActivePortals.Count();
+ if( iNumRenderablePortals == 0 )
+ return false;
+
+ const int iMaxDepth = MIN( r_portal_stencil_depth.GetInt(), MIN( MAX_PORTAL_RECURSIVE_VIEWS, (1 << materials->StencilBufferBits()) ) - 1 );
+
+ if( m_iViewRecursionLevel >= iMaxDepth ) //can't support any more views
+ {
+ m_iRemainingPortalViewDepth = 0; //special case handler for max depth 0 cases
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = actualActivePortals[i];
+ pCurrentPortal->DrawPortal();
+ }
+ return false;
+ }
+
+ m_iRemainingPortalViewDepth = (iMaxDepth - m_iViewRecursionLevel) - 1;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Flush( true ); //to prevent screwing up the last opaque object
+
+ //queued mode makes us pass the barrier of just noticeable difference when using a previous frame's occlusion as a draw skip check
+ bool bIsQueuedMode = (materials->GetThreadMode() == MATERIAL_QUEUED_THREADED);
+
+ const CViewSetup *pViewSetup = pViewRender->GetViewSetup();
+ m_RecursiveViewSetups[m_iViewRecursionLevel] = *pViewSetup;
+
+ CViewSetup ViewBackup;// = *pViewSetup; //backup the view, we'll need to restore it
+ memcpy( &ViewBackup, pViewSetup, sizeof( CViewSetup ) );
+
+ Vector ptCameraOrigin = pViewSetup->origin;
+ Vector vCameraForward;
+ AngleVectors( pViewSetup->angles, &vCameraForward, NULL, NULL );
+
+ int iX, iY, iWidth, iHeight;
+ pRenderContext->GetViewport( iX, iY, iWidth, iHeight );
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ int iScreenPixelCount = iWidth * iHeight;
+#endif
+
+ bool bRebuildDrawListsWhenDone = false;
+
+
+ int iParentLevelStencilReferenceValue = m_iViewRecursionLevel;
+ int iStencilReferenceValue = iParentLevelStencilReferenceValue + 1;
+
+ if( m_iViewRecursionLevel == 0 ) //first entry into the stencil drawing
+ {
+ pRenderContext->SetStencilEnable( true );
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_REPLACE );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilTestMask( 0xFF );
+ pRenderContext->SetStencilWriteMask( 0xFF );
+ pRenderContext->SetStencilReferenceValue( 0 );
+
+ m_RecursiveViewComplexFrustums[0].RemoveAll(); //clear any garbage leftover in the complex frustums from last frame
+ }
+
+ if( m_RecursiveViewComplexFrustums[m_iViewRecursionLevel].Count() == 0 )
+ {
+ //nothing in the complex frustum from the current view, copy the standard frustum in
+ m_RecursiveViewComplexFrustums[m_iViewRecursionLevel].AddMultipleToTail( FRUSTUM_NUMPLANES, pViewRender->GetFrustum() );
+ }
+
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = actualActivePortals[i];
+
+ m_RecursiveViewComplexFrustums[m_iViewRecursionLevel + 1].RemoveAll(); //clear any previously stored complex frustum
+
+ if( (pCurrentPortal->GetLinkedPortal() == NULL) ||
+ (pCurrentPortal == m_pRenderingViewExitPortal) ||
+ (pCurrentPortal->ShouldUpdatePortalView_BasedOnView( *pViewSetup, m_RecursiveViewComplexFrustums[m_iViewRecursionLevel] ) == false) )
+ {
+ //can't see through the portal, free up it's view id node for use elsewhere
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] != NULL )
+ {
+ FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] );
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = NULL;
+ }
+ continue;
+ }
+
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
+
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] == NULL )
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = AllocPortalViewIDNode( m_HeadPortalViewIDNode.ChildNodes.Count() );
+
+ PortalViewIDNode_t *pCurrentPortalViewNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
+
+ // Step 0, Allow for special effects to happen before cutting a hole
+ {
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
+
+ pCurrentPortal->DrawPreStencilMask();
+ }
+
+ //step 1, write out the stencil values (and colors if you want, but really not necessary)
+ {
+ //pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_INCR );
+ //pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ //pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ //pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
+
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pRenderContext->BeginOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+ pCurrentPortalViewNode->iWindowPixelsAtQueryTime = iScreenPixelCount;
+#endif
+
+ pCurrentPortal->DrawStencilMask();
+
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pRenderContext->EndOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+#endif
+ }
+
+ //see if we can skip the heavy lifting due to low visibility
+ if ( bIsQueuedMode || //don't use pixel visibly as a skip check in queued mode, the data is simply too old.
+ pCurrentPortal->ShouldUpdatePortalView_BasedOnPixelVisibility( pCurrentPortalViewNode->fScreenFilledByPortalSurfaceLastFrame_Normalized ) )
+ {
+ //step 2, clear the depth buffer in stencil areas so we can render a new scene to them
+ {
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
+ pRenderContext->ClearBuffersObeyStencil( false, true );
+ }
+
+
+ //step 3, fill in stencil views (remember that in multiple depth situations that any subportals will run through this function again before this section completes, thereby screwing with stencil settings)
+ {
+ bRebuildDrawListsWhenDone = true;
+
+ MaterialFogMode_t fogModeBackup = pRenderContext->GetFogMode();
+ unsigned char fogColorBackup[4];
+ pRenderContext->GetFogColor( fogColorBackup );
+ float fFogStartBackup, fFogEndBackup, fFogZBackup;
+ pRenderContext->GetFogDistances( &fFogStartBackup, &fFogEndBackup, &fFogZBackup );
+ CGlowOverlay::BackupSkyOverlayData( m_iViewRecursionLevel );
+
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
+
+ pCurrentPortal->RenderPortalViewToBackBuffer( pViewRender, *pViewSetup );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = NULL;
+
+ CGlowOverlay::RestoreSkyOverlayData( m_iViewRecursionLevel );
+ memcpy( (void *)pViewSetup, &ViewBackup, sizeof( CViewSetup ) );
+ pViewRender->m_pActiveRenderer->EnableWorldFog();
+
+ pRenderContext->FogMode( fogModeBackup );
+ pRenderContext->FogColor3ubv( fogColorBackup );
+ pRenderContext->FogStart( fFogStartBackup );
+ pRenderContext->FogEnd( fFogEndBackup );
+ pRenderContext->SetFogZ( fFogZBackup );
+
+
+ //do a full reset of what we think the stencil operations are in case the recursive calls got weird
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilTestMask( 0xFF );
+ pRenderContext->SetStencilWriteMask( 0xFF );
+ pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
+ }
+
+ //step 4, patch up the fact that we just made a hole in the wall because it's not *really* a hole at all
+ {
+ pCurrentPortal->DrawPostStencilFixes();
+ }
+ }
+
+ //step 5, restore the stencil mask to the parent level
+ {
+ pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_DECR );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+
+ pRenderContext->PerformFullScreenStencilOperation();
+ }
+ }
+
+ //step 6, go back to non-stencil rendering mode in preparation to resume normal scene rendering
+ if( m_iViewRecursionLevel == 0 )
+ {
+ Assert( m_pRenderingViewForPortal == NULL );
+ Assert( m_pRenderingViewExitPortal == NULL );
+ m_pRenderingViewExitPortal = NULL;
+ m_pRenderingViewForPortal = NULL;
+
+ pRenderContext->SetStencilEnable( false );
+ pRenderContext->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NEVER);
+ pRenderContext->SetStencilPassOperation(STENCILOPERATION_KEEP);
+ pRenderContext->SetStencilFailOperation(STENCILOPERATION_KEEP);
+ pRenderContext->SetStencilZFailOperation(STENCILOPERATION_KEEP);
+ pRenderContext->SetStencilTestMask( 0xFF );
+ pRenderContext->SetStencilWriteMask( 0xFF );
+ pRenderContext->SetStencilReferenceValue( 0 );
+
+ m_RecursiveViewComplexFrustums[0].RemoveAll();
+ }
+ else
+ {
+ pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ }
+
+ if( bRebuildDrawListsWhenDone )
+ {
+ memcpy( (void *)pViewSetup, &ViewBackup, sizeof( CViewSetup ) ); //if we don't restore this, the view is permanently altered (in mid render of an existing scene)
+ }
+
+ pRenderContext->Flush( true ); //just in case
+
+ ++m_iRemainingPortalViewDepth;
+
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = actualActivePortals[i];
+ pCurrentPortal->DrawPortal();
+ }
+
+ return bRebuildDrawListsWhenDone;
+}
+
+
+
+
+
+
+#ifdef _DEBUG
+extern bool g_bRenderingCameraView;
+#endif
+
+void CPortalRender::DrawPortalsToTextures( CViewRender *pViewRender, const CViewSetup &cameraView )
+{
+ if( ShouldUseStencilsToRenderPortals() )
+ return;
+
+ /*if ( (pViewRender->GetDrawFlags() & DF_RENDER_REFLECTION) != 0 )
+ return;*/
+
+ m_iRemainingPortalViewDepth = 1;
+ m_iViewRecursionLevel = 0;
+ m_pRenderingViewForPortal = NULL;
+ m_pRenderingViewExitPortal = NULL;
+
+ m_RecursiveViewSetups[m_iViewRecursionLevel] = cameraView;
+
+ m_RecursiveViewComplexFrustums[0].RemoveAll(); //clear any garbage leftover in the complex frustums from last frame
+ m_RecursiveViewComplexFrustums[0].AddMultipleToTail( FRUSTUM_NUMPLANES, pViewRender->GetFrustum() );
+
+
+#ifdef _DEBUG
+ g_bRenderingCameraView = true;
+#endif
+
+ int iNumRenderablePortals = g_pPortalRender->m_ActivePortals.Count();
+
+ Vector ptCameraOrigin = cameraView.origin;
+
+ //an extraneous push to update the frustum
+ render->Push3DView( cameraView, 0, NULL, pViewRender->GetFrustum() );
+
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = g_pPortalRender->m_ActivePortals[i];
+
+ if( (pCurrentPortal->ShouldUpdatePortalView_BasedOnView( cameraView, m_RecursiveViewComplexFrustums[m_iViewRecursionLevel] ) == false) ||
+ (pCurrentPortal->GetLinkedPortal() == NULL) )
+ {
+ //can't see through the portal, free up it's view id node for use elsewhere
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] != NULL )
+ {
+ FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] );
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = NULL;
+ }
+ continue;
+ }
+
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
+
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] == NULL )
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = AllocPortalViewIDNode( m_HeadPortalViewIDNode.ChildNodes.Count() );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
+
+ pCurrentPortal->RenderPortalViewToTexture( pViewRender, cameraView );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = NULL;
+ }
+
+ render->PopView( pViewRender->GetFrustum() );
+
+ m_iRemainingPortalViewDepth = 1;
+ m_iViewRecursionLevel = 0;
+
+ Assert( m_pRenderingViewForPortal == NULL );
+ Assert( m_pRenderingViewExitPortal == NULL );
+ m_pRenderingViewForPortal = NULL;
+ m_pRenderingViewExitPortal = NULL;
+
+#ifdef _DEBUG
+ g_bRenderingCameraView = false;
+#endif
+}
+
+void CPortalRender::AddPortal( CPortalRenderable *pPortal )
+{
+ for( int i = m_ActivePortals.Count(); --i >= 0; )
+ {
+ if( m_ActivePortals[i] == pPortal )
+ return;
+ }
+
+ m_ActivePortals.AddToTail( pPortal );
+}
+
+void CPortalRender::RemovePortal( CPortalRenderable *pPortal )
+{
+ for( int i = m_ActivePortals.Count(); --i >= 0; )
+ {
+ if( m_ActivePortals[i] == pPortal )
+ {
+ m_ActivePortals.FastRemove( i );
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we currently rendering a portal?
+//-----------------------------------------------------------------------------
+bool CPortalRender::IsRenderingPortal() const
+{
+ return m_pRenderingViewForPortal != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns view recursion level
+//-----------------------------------------------------------------------------
+int CPortalRender::GetViewRecursionLevel() const
+{
+ return m_iViewRecursionLevel;
+}
+
+//-----------------------------------------------------------------------------
+//normalized for how many of the screen's possible pixels it takes up, less than zero indicates a lack of data from last frame
+//-----------------------------------------------------------------------------
+float CPortalRender::GetPixelVisilityForPortalSurface( const CPortalRenderable *pPortal ) const
+{
+ PortalViewIDNode_t *pNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortal->m_iPortalViewIDNodeIndex];
+ if( pNode )
+ return pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized;
+
+ return -1.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to query about the exit portal associated with the currently rendering portal
+//-----------------------------------------------------------------------------
+const Vector &CPortalRender::GetExitPortalFogOrigin() const
+{
+ return m_pRenderingViewExitPortal->GetFogOrigin();
+}
+
+void CPortalRender::ShiftFogForExitPortalView() const
+{
+ if ( m_pRenderingViewExitPortal )
+ {
+ m_pRenderingViewExitPortal->ShiftFogForExitPortalView();
+ }
+}
+
+void CPortalRenderable::ShiftFogForExitPortalView() const
+{
+ CMatRenderContextPtr pRenderContext( materials );
+ float fFogStart, fFogEnd, fFogZ;
+ pRenderContext->GetFogDistances( &fFogStart, &fFogEnd, &fFogZ );
+
+ Vector vFogOrigin = GetFogOrigin();
+ Vector vCameraToExitPortal = vFogOrigin - CurrentViewOrigin();
+ float fDistModifier = vCameraToExitPortal.Dot( CurrentViewForward() );
+
+ fFogStart += fDistModifier;
+ fFogEnd += fDistModifier;
+ //fFogZ += something; //FIXME: find out what the hell to do with this
+
+ pRenderContext->FogStart( fFogStart );
+ pRenderContext->FogEnd( fFogEnd );
+ pRenderContext->SetFogZ( fFogZ );
+}
+
+SkyboxVisibility_t CPortalRender::IsSkyboxVisibleFromExitPortal() const
+{
+ return m_pRenderingViewExitPortal->SkyBoxVisibleFromPortal();
+}
+
+bool CPortalRender::DoesExitPortalViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
+{
+ return m_pRenderingViewExitPortal->DoesExitViewIntersectWaterPlane( waterZ, leafWaterDataID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the remaining number of portals to render within other portals
+// lets portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
+//-----------------------------------------------------------------------------
+int CPortalRender::GetRemainingPortalViewDepth() const
+{
+ return m_iRemainingPortalViewDepth;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the current View IDs
+//-----------------------------------------------------------------------------
+int CPortalRender::GetCurrentViewId() const
+{
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel] != NULL );
+#ifdef _DEBUG
+ for( int i = 0; i != m_iViewRecursionLevel; ++i )
+ {
+ Assert( m_PortalViewIDNodeChain[i]->iPrimaryViewID != m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID );
+ }
+#endif
+
+ return m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID;
+}
+
+int CPortalRender::GetCurrentSkyboxViewId() const
+{
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel] != NULL );
+ return m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID + 1;
+}
+
+
+void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float flY, float w, float h ); //implemented in view_scene.cpp
+
+void CPortalRender::OverlayPortalRenderTargets( float w, float h )
+{
+ OverlayCameraRenderTarget( "engine/debug_portal_1", 0,0, w,h );
+ OverlayCameraRenderTarget( "engine/debug_portal_2", w+10,0, w,h );
+
+ OverlayCameraRenderTarget( "engine/debug_water_reflect_0", 0, h+10, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_reflect_1", w+10, h+10, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_reflect_2", (w+10) * 2, h+10, w,h );
+
+ OverlayCameraRenderTarget( "engine/debug_water_refract_0", 0, (h+10) * 2, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_refract_1", w+10, (h+10) * 2, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_refract_2", (w+10) * 2, (h+10) * 2, w,h );
+}
+
+void CPortalRender::UpdateDepthDoublerTexture( const CViewSetup &viewSetup )
+{
+ bool bShouldUpdate = false;
+
+ for( int i = m_ActivePortals.Count(); --i >= 0; )
+ {
+ CPortalRenderable *pPortal = m_ActivePortals[i];
+
+ if( pPortal->ShouldUpdateDepthDoublerTexture( viewSetup ) )
+ {
+ bShouldUpdate = true;
+ break;
+ }
+ }
+
+ if( bShouldUpdate )
+ {
+ Rect_t srcRect;
+ srcRect.x = viewSetup.x;
+ srcRect.y = viewSetup.y;
+ srcRect.width = viewSetup.width;
+ srcRect.height = viewSetup.height;
+
+ ITexture *pTexture = portalrendertargets->GetDepthDoublerTexture();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->CopyRenderTargetToTextureEx( pTexture, 0, &srcRect, NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a recorded portal
+//-----------------------------------------------------------------------------
+int CPortalRender::FindRecordedPortalIndex( int nPortalId )
+{
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_RecordedPortals[i].m_nPortalId == nPortalId )
+ return i;
+ }
+ return -1;
+}
+
+CPortalRenderable* CPortalRender::FindRecordedPortal( int nPortalId )
+{
+ int nIndex = FindRecordedPortalIndex( nPortalId );
+ return ( nIndex >= 0 ) ? m_RecordedPortals[nIndex].m_pActivePortal : NULL;
+}
+
+CPortalRenderable* CPortalRender::FindRecordedPortal( IClientRenderable *pRenderable )
+{
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_RecordedPortals[i].m_pPlaybackRenderable == pRenderable )
+ return m_RecordedPortals[i].m_pActivePortal;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles a portal update message
+//-----------------------------------------------------------------------------
+void CPortalRender::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
+{
+ // Iterate through all the portal ids of all the portals in the keyvalues message
+ CUtlVector<int> foundIds;
+ for ( KeyValues *pCurr = pKeyValues->GetFirstTrueSubKey(); pCurr; pCurr = pCurr->GetNextTrueSubKey() )
+ {
+ // Create new area portals for those ids that don't exist
+ int nPortalId = pCurr->GetInt( "portalId" );
+ IClientRenderable *pRenderable = (IClientRenderable*)pCurr->GetPtr( "clientRenderable" );
+ int nIndex = FindRecordedPortalIndex( nPortalId );
+ if ( nIndex < 0 )
+ {
+ CPortalRenderable *pPortal = NULL;
+ const char *szType = pCurr->GetString( "portalType", "flatBasic" ); //"flatBasic" being the type commonly found in "Portal" mod
+ //search through registered creation functions for one that makes this type of portal
+ for( int i = m_PortalRenderableCreators.Count(); --i >= 0; )
+ {
+ if( FStrEq( szType, m_PortalRenderableCreators[i].portalType.String() ) )
+ {
+ pPortal = m_PortalRenderableCreators[i].creationFunc();
+ break;
+ }
+ }
+
+ if( pPortal == NULL )
+ {
+ AssertMsg( false, "Unable to find creation function for portal type." );
+ Warning( "CPortalRender::HandlePortalPlaybackMessage() unable to find creation function for portal type: %s\n", szType );
+ }
+ else
+ {
+ pPortal->m_bIsPlaybackPortal = true;
+ int k = m_RecordedPortals.AddToTail( );
+ m_RecordedPortals[k].m_pActivePortal = pPortal;
+ m_RecordedPortals[k].m_nPortalId = nPortalId;
+ m_RecordedPortals[k].m_pPlaybackRenderable = pRenderable;
+ AddPortal( pPortal );
+ }
+ }
+ else
+ {
+ m_RecordedPortals[nIndex].m_pPlaybackRenderable = pRenderable;
+ }
+ foundIds.AddToTail( nPortalId );
+ }
+
+ // Delete portals that didn't appear in the list
+ int nFoundCount = foundIds.Count();
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = nCount; --i >= 0; )
+ {
+ int j;
+ for ( j = 0; j < nFoundCount; ++j )
+ {
+ if ( foundIds[j] == m_RecordedPortals[i].m_nPortalId )
+ break;
+ }
+
+ if ( j == nFoundCount )
+ {
+ RemovePortal( m_RecordedPortals[i].m_pActivePortal );
+ delete m_RecordedPortals[i].m_pActivePortal;
+ m_RecordedPortals.FastRemove(i);
+ }
+ }
+
+ // Iterate through all the portal ids of all the portals in the keyvalues message
+ for ( KeyValues *pCurr = pKeyValues->GetFirstTrueSubKey(); pCurr; pCurr = pCurr->GetNextTrueSubKey() )
+ {
+ // Update the state of the portals based on the recorded info
+ int nPortalId = pCurr->GetInt( "portalId" );
+ CPortalRenderable *pPortal = FindRecordedPortal( nPortalId );
+ Assert( pPortal );
+
+ pPortal->HandlePortalPlaybackMessage( pCurr );
+ }
+
+ // Make the portals update their internal state
+ /*nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_RecordedPortals[i].m_pActivePortal->PortalMoved();
+ m_RecordedPortals[i].m_pActivePortal->ComputeLinkMatrix();
+ }*/
+}
+
+
+void CPortalRender::AddPortalCreationFunc( const char *szPortalType, PortalRenderableCreationFunc creationFunc )
+{
+#ifdef _DEBUG
+ for( int i = m_PortalRenderableCreators.Count(); --i >= 0; )
+ {
+ AssertMsg( FStrEq( m_PortalRenderableCreators[i].portalType.String(), szPortalType ) == false, "Multiple portal renderable creation functions for same type of portal renderable." );
+ }
+#endif
+
+ PortalRenderableCreationFunction_t temp;
+ temp.creationFunc = creationFunc;
+ temp.portalType.Set( szPortalType );
+
+ m_PortalRenderableCreators.AddToTail( temp );
+}
+
+bool Recursive_IsPortalViewID( PortalViewIDNode_t *pNode, view_id_t id )
+{
+ if ( pNode->iPrimaryViewID == id )
+ return true;
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
+ if( pChildNode )
+ {
+ return Recursive_IsPortalViewID( pChildNode, id );
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tests the parameter view ID against ID's used by portal pixel vis queries
+// Input : id - id tested against used portal view ids
+// Output : Returns true if id matches an ID used by a portal, or it's recursive sub portals
+//-----------------------------------------------------------------------------
+bool CPortalRender::IsPortalViewID( view_id_t id )
+{
+ if ( id == m_HeadPortalViewIDNode.iPrimaryViewID )
+ return true;
+
+ for ( int i = 0; i < MAX_PORTAL_RECURSIVE_VIEWS; ++i )
+ {
+ PortalViewIDNode_t* pNode = m_PortalViewIDNodeChain[i];
+ if ( pNode )
+ {
+ // recursively search child nodes, they get their own ids.
+ if ( Recursive_IsPortalViewID( pNode, id ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+
+
+
diff --git a/game/client/portal/PortalRender.h b/game/client/portal/PortalRender.h
new file mode 100644
index 0000000..b2c85f7
--- /dev/null
+++ b/game/client/portal/PortalRender.h
@@ -0,0 +1,333 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#ifndef PORTALRENDER_H
+#define PORTALRENDER_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "iviewrender.h"
+#include "view_shared.h"
+#include "viewrender.h"
+
+#define MAX_PORTAL_RECURSIVE_VIEWS 11 //maximum number of recursions we allow when drawing views through portals. Seeing as how 5 is extremely choppy under best conditions and is barely visible, 10 is a safe limit. Adding one because 0 tends to be the primary view in most arrays of this size
+
+class CPortalRenderable
+{
+public:
+ CPortalRenderable( void );
+ virtual ~CPortalRenderable( void );
+
+
+ //----------------------------------------------------------------------------
+ //Stencil-based drawing helpers, these are ONLY used in stencil drawing mode
+ //----------------------------------------------------------------------------
+ virtual void DrawPreStencilMask( void ) { }; //Do whatever drawing you need before cutting the stencil hole
+ virtual void DrawStencilMask( void ) { }; //Draw to wherever you should see through the portal. The mask will later be filled with the portal view.
+ virtual void DrawPostStencilFixes( void ) { }; //After done drawing to the portal mask, we need to fix the depth buffer as well as fog. So draw your mesh again, writing to z and with the fog color alpha'd in by distance
+
+
+ //----------------------------------------------------------------------------
+ //Rendering of views beyond the portal
+ //----------------------------------------------------------------------------
+ virtual void RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView ) { };
+ virtual void RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView ) { };
+
+
+ //----------------------------------------------------------------------------
+ //Visibility through portals
+ //----------------------------------------------------------------------------
+ virtual bool DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const { return false; };
+ virtual SkyboxVisibility_t SkyBoxVisibleFromPortal( void ) { return SKYBOX_NOT_VISIBLE; };
+
+ //-----------------------------------------------------------------------------
+ //Fog workarounds
+ //-----------------------------------------------------------------------------
+ virtual const Vector& GetFogOrigin( void ) const { return vec3_origin; };
+ virtual void ShiftFogForExitPortalView() const;
+
+ //-----------------------------------------------------------------------------
+ //Portal visibility testing
+ //-----------------------------------------------------------------------------
+ //Based on view, will the camera be able to see through the portal this frame? This will allow the stencil mask to start being tested for pixel visibility.
+ virtual bool ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, CUtlVector<VPlane> &currentComplexFrustum ) { return false; };
+
+ //Stencil mode only: You stated the portal was visible based on view, and this is how much of the screen your stencil mask took up last frame. Still want to draw this frame? Values less than zero indicate a lack of data from last frame
+ virtual bool ShouldUpdatePortalView_BasedOnPixelVisibility( float fScreenFilledByStencilMaskLastFrame_Normalized ) { return (fScreenFilledByStencilMaskLastFrame_Normalized != 0.0f); }; // < 0 is unknown visibility, > 0 is known to be partially visible
+
+
+ //-----------------------------------------------------------------------------
+ // Misc
+ //-----------------------------------------------------------------------------
+ virtual CPortalRenderable* GetLinkedPortal() const { return NULL; };
+ const VMatrix& MatrixThisToLinked() const;
+ virtual bool ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup ) { return false; };
+ virtual void DrawPortal( void ) { }; //sort of like what you'd expect to happen in C_BaseAnimating::DrawModel() if portals were fully compatible with models
+
+ virtual C_BaseEntity *PortalRenderable_GetPairedEntity( void ) { return NULL; }; //Pairing a portal with an entity is common but not required. Accessing that entity allows the CPortalRender system to better optimize.
+ VMatrix m_matrixThisToLinked; //Always going to need a matrix
+
+
+ //-----------------------------------------------------------------------------
+ //SFM related
+ //-----------------------------------------------------------------------------
+ bool m_bIsPlaybackPortal;
+ virtual void GetToolRecordingState( bool bActive, KeyValues *msg ) { };
+ virtual void HandlePortalPlaybackMessage( KeyValues *pKeyValues ) { };
+
+protected:
+ //-----------------------------------------------------------------------------
+ // Wrap the draw of the surface that makes use of your portal render targets with these. Only required for texture mode, but won't hurt stencil mode.
+ // Using these will allow you to know whether it's worth drawing the other side of a portal next frame.
+ //-----------------------------------------------------------------------------
+ void BeginPortalPixelVisibilityQuery( void );
+ void EndPortalPixelVisibilityQuery( void );
+
+ CPortalRenderable *FindRecordedPortal( int nPortalId ); //routed through here to get friend access to CPortalRender
+
+ //routed through here to get friend access to CViewRender
+ void CopyToCurrentView( CViewRender *pViewRender, const CViewSetup &viewSetup );
+ void ViewDrawScene_PortalStencil( CViewRender *pViewRender, const CViewSetup &viewIn, ViewCustomVisibility_t *pCustomVisibility );
+ void Draw3dSkyboxworld_Portal( CViewRender *pViewRender, const CViewSetup &viewIn, int &nClearFlags, bool &bDrew3dSkybox, SkyboxVisibility_t &nSkyboxVisible, ITexture *pRenderTarget = NULL );
+ void ViewDrawScene( CViewRender *pViewRender, bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, const CViewSetup &viewIn, int nClearFlags, view_id_t viewID, bool bDrawViewModel = false, int baseDrawFlags = 0, ViewCustomVisibility_t *pCustomVisibility = NULL );
+ void SetViewRecursionLevel( int iViewRecursionLevel );
+ void SetRemainingViewDepth( int iRemainingViewDepth );
+ void SetViewEntranceAndExitPortals( CPortalRenderable *pEntryPortal, CPortalRenderable *pExitPortal );
+
+private:
+ int m_iPortalViewIDNodeIndex; //each PortalViewIDNode_t has a child node link for each CPortalRenderable in CPortalRender::m_ActivePortals. This portal follows the same indexed link from each node
+ friend class CPortalRender;
+};
+
+//-----------------------------------------------------------------------------
+// inline state querying methods
+//-----------------------------------------------------------------------------
+inline const VMatrix& CPortalRenderable::MatrixThisToLinked() const
+{
+ return m_matrixThisToLinked;
+}
+
+//-----------------------------------------------------------------------------
+// Portal rendering materials
+//-----------------------------------------------------------------------------
+struct PortalRenderingMaterials_t
+{
+ CMaterialReference m_Wireframe;
+ CMaterialReference m_WriteZ_Model;
+ CMaterialReference m_TranslucentVertexColor;
+};
+
+typedef CPortalRenderable *(*PortalRenderableCreationFunc)( void );
+struct PortalRenderableCreationFunction_t
+{
+ CUtlString portalType;
+ PortalRenderableCreationFunc creationFunc;
+};
+
+struct PortalViewIDNode_t
+{
+ CUtlVector<PortalViewIDNode_t *> ChildNodes; //links will only be non-null if they're useful (can see through the portal at that depth and view setup)
+ int iPrimaryViewID;
+ //skybox view id is always primary + 1
+
+ //In stencil mode this wraps CPortalRenderable::DrawStencilMask() and gives previous frames' results to CPortalRenderable::RenderPortalViewToBackBuffer()
+ //In texture mode there's no good spot to auto-wrap occlusion tests. So you'll need to wrap it yourself for that.
+ OcclusionQueryObjectHandle_t occlusionQueryHandle;
+ int iWindowPixelsAtQueryTime;
+ int iOcclusionQueryPixelsRendered;
+ float fScreenFilledByPortalSurfaceLastFrame_Normalized;
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Portal rendering management class
+//-----------------------------------------------------------------------------
+class CPortalRender : public CAutoGameSystem
+{
+public:
+ CPortalRender();
+
+ // Inherited from IGameSystem
+ virtual void LevelInitPreEntity();
+ virtual void LevelShutdownPreEntity();
+
+ // Are we currently rendering a portal?
+ bool IsRenderingPortal() const;
+
+ // Returns the current View IDs. Portal View IDs will change often (especially with recursive views) and should not be cached
+ int GetCurrentViewId() const;
+ int GetCurrentSkyboxViewId() const;
+
+ // Returns view recursion level
+ int GetViewRecursionLevel() const;
+
+ float GetPixelVisilityForPortalSurface( const CPortalRenderable *pPortal ) const; //normalized for how many of the screen's possible pixels it takes up, less than zero indicates a lack of data from last frame
+
+ // Returns the remaining number of portals to render within other portals
+ // lets portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
+ int GetRemainingPortalViewDepth() const;
+
+ inline CPortalRenderable *GetCurrentViewEntryPortal( void ) const { return m_pRenderingViewForPortal; }; //if rendering a portal view, this is the portal the current view enters into
+ inline CPortalRenderable *GetCurrentViewExitPortal( void ) const { return m_pRenderingViewExitPortal; }; //if rendering a portal view, this is the portal the current view exits from
+
+ // true if the rendering path for portals uses stencils instead of textures
+ bool ShouldUseStencilsToRenderPortals() const;
+
+ //it's a good idea to force cheaper water when the ratio of performance gain to noticability is high
+ //0 = force no reflection/refraction
+ //1/2 = downgrade to simple/world reflections as seen in advanced video options
+ //3 = no downgrade
+ int ShouldForceCheaperWaterLevel() const;
+
+ bool ShouldObeyStencilForClears() const;
+
+ //sometimes we have to tweak some systems to render water properly with portals
+ void WaterRenderingHandler_PreReflection() const;
+ void WaterRenderingHandler_PostReflection() const;
+ void WaterRenderingHandler_PreRefraction() const;
+ void WaterRenderingHandler_PostRefraction() const;
+
+ // return value indicates that something was done, and render lists should be rebuilt afterwards
+ bool DrawPortalsUsingStencils( CViewRender *pViewRender );
+
+ void DrawPortalsToTextures( CViewRender *pViewRender, const CViewSetup &cameraView ); //updates portal textures
+ void OverlayPortalRenderTargets( float w, float h );
+
+ void UpdateDepthDoublerTexture( const CViewSetup &viewSetup ); //our chance to update all depth doubler texture before the view model is added to the back buffer
+
+ void EnteredPortal( CPortalRenderable *pEnteredPortal ); //does a bit of internal maintenance whenever the player/camera has logically passed the portal threshold
+
+ // adds, removes a portal to the set of renderable portals
+ void AddPortal( CPortalRenderable *pPortal );
+ void RemovePortal( CPortalRenderable *pPortal );
+
+ // Methods to query about the exit portal associated with the currently rendering portal
+ void ShiftFogForExitPortalView() const;
+ const Vector &GetExitPortalFogOrigin() const;
+ SkyboxVisibility_t IsSkyboxVisibleFromExitPortal() const;
+ bool DoesExitPortalViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const;
+
+ void HandlePortalPlaybackMessage( KeyValues *pKeyValues );
+
+ CPortalRenderable* FindRecordedPortal( IClientRenderable *pRenderable );
+ void AddPortalCreationFunc( const char *szPortalType, PortalRenderableCreationFunc creationFunc );
+
+ CViewSetup m_RecursiveViewSetups[MAX_PORTAL_RECURSIVE_VIEWS]; //before we recurse into a view, we backup the view setup here for reference
+
+ // tests if the parameter ID is being used by portal pixel vis queries
+ bool IsPortalViewID( view_id_t id );
+
+private:
+ struct RecordedPortalInfo_t
+ {
+ CPortalRenderable *m_pActivePortal;
+ int m_nPortalId;
+ IClientRenderable *m_pPlaybackRenderable;
+ };
+
+ PortalViewIDNode_t m_HeadPortalViewIDNode; //pseudo node. Primary view id will be VIEW_MAIN. The child links are what we really care about
+ PortalViewIDNode_t* m_PortalViewIDNodeChain[MAX_PORTAL_RECURSIVE_VIEWS]; //the view id node chain we're following, 0 always being &m_HeadPortalViewIDNode (offsetting by 1 seems like it'd cause bugs in the long run)
+
+ void UpdatePortalPixelVisibility( void ); //updates pixel visibility for portal surfaces
+
+ // Handles a portal update message
+ void HandlePortalUpdateMessage( KeyValues *pKeyValues );
+
+ // Finds a recorded portal
+ int FindRecordedPortalIndex( int nPortalId );
+ CPortalRenderable* FindRecordedPortal( int nPortalId );
+
+ CUtlVector<PortalRenderableCreationFunction_t> m_PortalRenderableCreators; //for SFM compatibility
+
+private:
+ PortalRenderingMaterials_t m_Materials;
+ int m_iViewRecursionLevel;
+ int m_iRemainingPortalViewDepth; //let's portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
+
+ CPortalRenderable *m_pRenderingViewForPortal; //the specific pointer for the portal that we're rending a view for
+ CPortalRenderable *m_pRenderingViewExitPortal; //the specific pointer for the portal that our view exits from
+
+ CUtlVector<CPortalRenderable *> m_AllPortals; //All portals currently in memory, active or not
+ CUtlVector<CPortalRenderable *> m_ActivePortals;
+ CUtlVector< RecordedPortalInfo_t > m_RecordedPortals;
+
+public:
+ //frustums with more (or less) than 6 planes. Store each recursion level's custom frustum here so further recursions can be better optimized.
+ //When going into further recursions, if you've failed to fill in a complex frustum, the standard frustum will be copied in.
+ //So all parent levels are guaranteed to contain valid data
+ CUtlVector<VPlane> m_RecursiveViewComplexFrustums[MAX_PORTAL_RECURSIVE_VIEWS];
+ const PortalRenderingMaterials_t& m_MaterialsAccess;
+
+ friend class CPortalRenderable;
+ friend void OnRenderStart();
+};
+
+extern CPortalRender* g_pPortalRender;
+
+
+inline CPortalRenderable *CPortalRenderable::FindRecordedPortal( int nPortalId )
+{
+ return g_pPortalRender->FindRecordedPortal( nPortalId );
+}
+
+
+class CPortalRenderableCreator //create one of these as a global and ensure you register exactly once
+{
+public:
+ CPortalRenderableCreator( const char *szPortalType, PortalRenderableCreationFunc creationFunction )
+ {
+ g_pPortalRender->AddPortalCreationFunc( szPortalType, creationFunction );
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------
+// inline friend access redirects
+//-----------------------------------------------------------------------------
+inline void CPortalRenderable::CopyToCurrentView( CViewRender *pViewRender, const CViewSetup &viewSetup )
+{
+ pViewRender->m_CurrentView = viewSetup;
+}
+
+inline void CPortalRenderable::ViewDrawScene_PortalStencil( CViewRender *pViewRender, const CViewSetup &viewIn, ViewCustomVisibility_t *pCustomVisibility )
+{
+ pViewRender->ViewDrawScene_PortalStencil( viewIn, pCustomVisibility );
+}
+
+inline void CPortalRenderable::Draw3dSkyboxworld_Portal( CViewRender *pViewRender, const CViewSetup &viewIn, int &nClearFlags, bool &bDrew3dSkybox, SkyboxVisibility_t &nSkyboxVisible, ITexture *pRenderTarget )
+{
+ pViewRender->Draw3dSkyboxworld_Portal( viewIn, nClearFlags, bDrew3dSkybox, nSkyboxVisible, pRenderTarget );
+}
+
+inline void CPortalRenderable::ViewDrawScene( CViewRender *pViewRender, bool bDrew3dSkybox, SkyboxVisibility_t nSkyboxVisible, const CViewSetup &viewIn, int nClearFlags, view_id_t viewID, bool bDrawViewModel, int baseDrawFlags, ViewCustomVisibility_t *pCustomVisibility )
+{
+ pViewRender->ViewDrawScene( bDrew3dSkybox, nSkyboxVisible, viewIn, nClearFlags, viewID, bDrawViewModel, baseDrawFlags, pCustomVisibility );
+}
+
+inline void CPortalRenderable::SetViewRecursionLevel( int iViewRecursionLevel )
+{
+ g_pPortalRender->m_iViewRecursionLevel = iViewRecursionLevel;
+}
+
+inline void CPortalRenderable::SetRemainingViewDepth( int iRemainingViewDepth )
+{
+ g_pPortalRender->m_iRemainingPortalViewDepth = iRemainingViewDepth;
+}
+
+inline void CPortalRenderable::SetViewEntranceAndExitPortals( CPortalRenderable *pEntryPortal, CPortalRenderable *pExitPortal )
+{
+ g_pPortalRender->m_pRenderingViewForPortal = pEntryPortal;
+ g_pPortalRender->m_pRenderingViewExitPortal = pExitPortal;
+}
+
+#endif //#ifndef PORTALRENDER_H
+
diff --git a/game/client/portal/Portal_DynamicMeshRenderingUtils.cpp b/game/client/portal/Portal_DynamicMeshRenderingUtils.cpp
new file mode 100644
index 0000000..f5b32fa
--- /dev/null
+++ b/game/client/portal/Portal_DynamicMeshRenderingUtils.cpp
@@ -0,0 +1,183 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "Portal_DynamicMeshRenderingUtils.h"
+#include "iviewrender.h"
+
+extern ConVar mat_wireframe;
+
+
+int ClipPolyToPlane_LerpTexCoords( PortalMeshPoint_t *inVerts, int vertCount, PortalMeshPoint_t *outVerts, const Vector& normal, float dist, float fOnPlaneEpsilon )
+{
+ vec_t *dists = (vec_t *)stackalloc( sizeof(vec_t) * vertCount * 4 ); //4x vertcount should cover all cases
+ int *sides = (int *)stackalloc( sizeof(int) * vertCount * 4 );
+ int counts[3];
+ vec_t dot;
+ int i, j;
+ Vector mid = vec3_origin;
+ int outCount;
+
+ counts[0] = counts[1] = counts[2] = 0;
+
+ // determine sides for each point
+ for ( i = 0; i < vertCount; i++ )
+ {
+ dot = DotProduct( inVerts[i].vWorldSpacePosition, normal) - dist;
+ dists[i] = dot;
+ if ( dot > fOnPlaneEpsilon )
+ sides[i] = SIDE_FRONT;
+ else if ( dot < -fOnPlaneEpsilon )
+ sides[i] = SIDE_BACK;
+ else
+ sides[i] = SIDE_ON;
+ counts[sides[i]]++;
+ }
+ sides[i] = sides[0];
+ dists[i] = dists[0];
+
+ if (!counts[0])
+ return 0;
+
+ if (!counts[1])
+ {
+ // Copy to output verts
+ //for ( i = 0; i < vertCount; i++ )
+ memcpy( outVerts, inVerts, sizeof( PortalMeshPoint_t ) * vertCount );
+ return vertCount;
+ }
+
+ outCount = 0;
+ for ( i = 0; i < vertCount; i++ )
+ {
+ if (sides[i] == SIDE_ON)
+ {
+ memcpy( &outVerts[outCount], &inVerts[i], sizeof( PortalMeshPoint_t ) );
+ ++outCount;
+ continue;
+ }
+ if (sides[i] == SIDE_FRONT)
+ {
+ memcpy( &outVerts[outCount], &inVerts[i], sizeof( PortalMeshPoint_t ) );
+ ++outCount;
+ }
+ if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
+ continue;
+
+ Vector& p1 = inVerts[i].vWorldSpacePosition;
+
+
+ // generate a split point
+ int i2 = (i+1)%vertCount;
+ Vector& p2 = inVerts[i2].vWorldSpacePosition;
+
+ dot = dists[i] / (dists[i]-dists[i+1]);
+ for (j=0 ; j<3 ; j++)
+ {
+ mid[j] = p1[j] + dot*(p2[j]-p1[j]);
+ }
+
+ VectorCopy (mid, outVerts[outCount].vWorldSpacePosition);
+
+ outVerts[outCount].texCoord.x = inVerts[i].texCoord.x + dot*(inVerts[i2].texCoord.x - inVerts[i].texCoord.x);
+ outVerts[outCount].texCoord.y = inVerts[i].texCoord.y + dot*(inVerts[i2].texCoord.y - inVerts[i].texCoord.y);
+
+ ++outCount;
+ }
+
+ return outCount;
+}
+
+void RenderPortalMeshConvexPolygon( PortalMeshPoint_t *pVerts, int iVertCount, const IMaterial *pMaterial, void *pBind )
+{
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( (IMaterial *)pMaterial, pBind );
+
+ //PortalMeshPoint_t *pMidVerts = (PortalMeshPoint_t *)stackalloc( sizeof( PortalMeshPoint_t ) * iVertCount );
+
+ CMeshBuilder meshBuilder;
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, iVertCount - 2 );
+
+ //any convex polygon can be rendered with a triangle strip by starting at a vertex and alternating vertices from each side
+ int iForwardCounter = 0;
+ int iReverseCounter = iVertCount - 1; //guaranteed to be >= 2 to start
+
+ do
+ {
+ PortalMeshPoint_t *pVertex = &pVerts[iForwardCounter];
+ meshBuilder.Position3fv( &pVertex->vWorldSpacePosition.x );
+ meshBuilder.TexCoord2fv( 0, &pVertex->texCoord.x );
+ meshBuilder.AdvanceVertex();
+ ++iForwardCounter;
+
+ if( iForwardCounter > iReverseCounter )
+ break;
+
+ pVertex = &pVerts[iReverseCounter];
+ meshBuilder.Position3fv( &pVertex->vWorldSpacePosition.x );
+ meshBuilder.TexCoord2fv( 0, &pVertex->texCoord.x );
+ meshBuilder.AdvanceVertex();
+ --iReverseCounter;
+ } while( iForwardCounter <= iReverseCounter );
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+void Clip_And_Render_Convex_Polygon( PortalMeshPoint_t *pVerts, int iVertCount, const IMaterial *pMaterial, void *pBind )
+{
+ PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( iVertCount * 4 * sizeof( PortalMeshPoint_t ) ); //really only should need 2x points, but I'm paranoid
+ PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( iVertCount * 4 * sizeof( PortalMeshPoint_t ) );
+ PortalMeshPoint_t *pTempVerts;
+
+
+ //clip by the viewing frustum
+ {
+ VPlane *pFrustum = view->GetFrustum();
+
+ //clip by first plane and put output into pInVerts
+ iVertCount = ClipPolyToPlane_LerpTexCoords( pVerts, iVertCount, pInVerts, pFrustum[0].m_Normal, pFrustum[0].m_Dist, 0.01f );
+
+ //clip by other planes and flipflop in and out pointers
+ for( int i = 1; i != FRUSTUM_NUMPLANES; ++i )
+ {
+ if( iVertCount < 3 )
+ return; //nothing to draw
+
+ iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, pFrustum[i].m_Normal, pFrustum[i].m_Dist, 0.01f );
+ pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
+ }
+
+ if( iVertCount < 3 )
+ return; //nothing to draw
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ RenderPortalMeshConvexPolygon( pOutVerts, iVertCount, pMaterial, pBind );
+ if( mat_wireframe.GetBool() )
+ RenderPortalMeshConvexPolygon( pOutVerts, iVertCount, materials->FindMaterial( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS, false ), pBind );
+
+ stackfree( pOutVerts );
+ stackfree( pInVerts );
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/game/client/portal/Portal_DynamicMeshRenderingUtils.h b/game/client/portal/Portal_DynamicMeshRenderingUtils.h
new file mode 100644
index 0000000..3154590
--- /dev/null
+++ b/game/client/portal/Portal_DynamicMeshRenderingUtils.h
@@ -0,0 +1,32 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PORTAL_DYNAMICMESHRENDERINGUTILS_H
+#define PORTAL_DYNAMICMESHRENDERINGUTILS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "materialsystem/imaterial.h"
+#include "mathlib/mathlib.h"
+#include "PortalRender.h"
+
+struct PortalMeshPoint_t
+{
+ Vector vWorldSpacePosition;
+ Vector2D texCoord;
+};
+
+int ClipPolyToPlane_LerpTexCoords( PortalMeshPoint_t *inVerts, int vertCount, PortalMeshPoint_t *outVerts, const Vector& normal, float dist, float fOnPlaneEpsilon );
+void RenderPortalMeshConvexPolygon( PortalMeshPoint_t *pVerts, int iVertCount, const IMaterial *pMaterial, void *pBind );
+
+void Clip_And_Render_Convex_Polygon( PortalMeshPoint_t *pVerts, int iVertCount, const IMaterial *pMaterial, void *pBind );
+
+
+#endif //#ifndef PORTAL_DYNAMICMESHRENDERINGUTILS_H
+
diff --git a/game/client/portal/c_env_lightraill_endpoint.cpp b/game/client/portal/c_env_lightraill_endpoint.cpp
new file mode 100644
index 0000000..bd1cfa4
--- /dev/null
+++ b/game/client/portal/c_env_lightraill_endpoint.cpp
@@ -0,0 +1,499 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "particles_simple.h"
+#include "env_lightrail_endpoint_shared.h"
+#include "particles_attractor.h"
+#include "fx_quad.h"
+
+class C_Env_Lightrail_Endpoint : public C_BaseEntity
+{
+ DECLARE_CLASS( C_Env_Lightrail_Endpoint, C_BaseEntity );
+ DECLARE_CLIENTCLASS();
+
+public:
+ void OnDataChanged( DataUpdateType_t updateType );
+ RenderGroup_t GetRenderGroup( void );
+
+ void ClientThink( void );
+ void NotifyShouldTransmit( ShouldTransmitState_t state );
+
+ void UpdateIdle( float percentage );
+ void UpdateCharging( float percentage ); //Update the charging state
+ void UpdateLargeFX( void ); //Update Discharging Large FX
+ void UpdateSmallFX( void ); //Update Discharging Small FX
+
+private:
+
+ bool SetupEmitters( void );
+ inline float GetStateDurationPercentage( void );
+
+ float m_flSmallScale;
+ float m_flLargeScale;
+ int m_nState;
+ float m_flDuration;
+ float m_flStartTime;
+ int m_spawnflags;
+
+ CSmartPtr<CSimpleEmitter> m_pSimpleEmitter;
+ CSmartPtr<CParticleAttractor> m_pAttractorEmitter;
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_Env_Lightrail_Endpoint, DT_Env_Lightrail_Endpoint, CEnv_Lightrail_Endpoint )
+ RecvPropFloat( RECVINFO(m_flSmallScale) ),
+ RecvPropFloat( RECVINFO(m_flLargeScale) ),
+ RecvPropInt( RECVINFO(m_nState) ),
+ RecvPropFloat( RECVINFO(m_flDuration) ),
+ RecvPropFloat( RECVINFO(m_flStartTime) ),
+ RecvPropInt( RECVINFO(m_spawnflags) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : RenderGroup_t
+//-----------------------------------------------------------------------------
+RenderGroup_t C_Env_Lightrail_Endpoint::GetRenderGroup( void )
+{
+ return RENDER_GROUP_TRANSLUCENT_ENTITY;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ SetupEmitters();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_Env_Lightrail_Endpoint::SetupEmitters( void )
+{
+ // Setup the basic core emitter
+ if ( m_pSimpleEmitter.IsValid() == false )
+ {
+ m_pSimpleEmitter = CSimpleEmitter::Create( "energycore" );
+
+ if ( m_pSimpleEmitter.IsValid() == false )
+ return false;
+ }
+
+ // Setup the attractor emitter
+ if ( m_pAttractorEmitter.IsValid() == false )
+ {
+ m_pAttractorEmitter = CParticleAttractor::Create( GetAbsOrigin(), "energyattractor" );
+
+ if ( m_pAttractorEmitter.IsValid() == false )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : percentage -
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::UpdateIdle( float percentage )
+{
+ // Only do these particles if required
+ //if ( m_spawnflags & SF_ENERGYCORE_NO_PARTICLES )
+ // return;
+
+ // Must be active
+ if ( percentage >= 1.0f )
+ return;
+
+ // Emitters must be valid
+ if ( SetupEmitters() == false )
+ return;
+
+ // Reset our sort origin
+ m_pSimpleEmitter->SetSortOrigin( GetAbsOrigin() );
+
+ SimpleParticle *sParticle;
+
+ // Do the charging particles
+ m_pAttractorEmitter->SetAttractorOrigin( GetAbsOrigin() );
+
+ Vector forward, right, up;
+ AngleVectors( GetAbsAngles(), &forward, &right, &up );
+
+ Vector offset;
+ float dist;
+
+ int numParticles = floor( 4.0f * percentage );
+
+ for ( int i = 0; i < numParticles; i++ )
+ {
+ dist = random->RandomFloat( 4.0f * percentage, 64.0f * percentage );
+
+ offset = forward * dist;
+
+ dist = RemapValClamped( dist, 4.0f * percentage, 64.0f * percentage, 6.0f, 1.0f );
+ offset += right * random->RandomFloat( -4.0f * dist, 4.0f * dist );
+ offset += up * random->RandomFloat( -4.0f * dist, 4.0f * dist );
+
+ offset += GetAbsOrigin();
+
+ sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), m_pAttractorEmitter->GetPMaterial( "effects/strider_muzzle" ), offset );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = Vector(0,0,8);
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255 * percentage;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 );
+ sParticle->m_uchEndSize = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : percentage -
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::UpdateCharging( float percentage )
+{
+ // Emitters must be valid
+ if ( SetupEmitters() == false )
+ return;
+
+ if ( percentage <= 0.0f )
+ return;
+
+ // Reset our sort origin
+ m_pSimpleEmitter->SetSortOrigin( GetAbsOrigin() );
+
+ float flLargeScale = 4.0f * m_flLargeScale * percentage;
+ //float flSmallScale = 4.0f * m_flSmallScale * percentage;
+
+ SimpleParticle *sParticle;
+
+ // Do the core effects
+ for ( int i = 0; i < 2; i++ )
+ {
+ sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/strider_muzzle" ), GetAbsOrigin() );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = vec3_origin;
+ sParticle->m_flDieTime = 0.1f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ if ( i < 2 )
+ {
+ sParticle->m_uchStartSize = flLargeScale * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
+ }
+ else
+ {
+ if ( random->RandomInt( 0, 20 ) == 0 )
+ {
+ sParticle->m_uchStartSize = flLargeScale * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 4.0f;
+ sParticle->m_flDieTime = 0.25f;
+ }
+ else
+ {
+ sParticle->m_uchStartSize = flLargeScale * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
+ }
+ }
+ }
+
+ // Only do these particles if required
+ //if ( m_spawnflags & SF_ENERGYCORE_NO_PARTICLES )
+ // return;
+
+ // Do the charging particles
+ m_pAttractorEmitter->SetAttractorOrigin( GetAbsOrigin() );
+
+ Vector forward, right, up;
+ AngleVectors( GetAbsAngles(), &forward, &right, &up );
+
+ Vector offset;
+ float dist;
+
+ int numParticles = floor( 4.0f * percentage );
+
+ for ( int i = 0; i < numParticles; i++ )
+ {
+ dist = random->RandomFloat( 4.0f * percentage, 64.0f * percentage );
+
+ offset = forward * dist;
+
+ dist = RemapValClamped( dist, 4.0f * percentage, 64.0f * percentage, 6.0f, 1.0f );
+ offset += right * random->RandomFloat( -4.0f * dist, 4.0f * dist );
+ offset += up * random->RandomFloat( -4.0f * dist, 4.0f * dist );
+
+ offset += GetAbsOrigin();
+
+ sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), m_pAttractorEmitter->GetPMaterial( "effects/strider_muzzle" ), offset );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = Vector(0,0,8);
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255 * percentage;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 );
+ sParticle->m_uchEndSize = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : percentage -
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::UpdateSmallFX( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : percentage -
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::UpdateLargeFX( void )
+{
+ // Emitters must be valid
+ if ( SetupEmitters() == false )
+ return;
+
+ // Reset our sort origin
+ m_pSimpleEmitter->SetSortOrigin( GetAbsOrigin() );
+
+ float flLargeScale = 8.0f * m_flLargeScale;
+
+ Vector forward, right, up;
+ AngleVectors( GetAbsAngles(), &forward, &right, &up );
+
+ SimpleParticle *sParticle;
+
+ // Base of the core effect
+ sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/strider_muzzle" ), GetAbsOrigin() );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = forward * 32.0f;
+ sParticle->m_flDieTime = 0.2f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 128;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_uchStartSize = flLargeScale * 2.0f;
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
+
+ // Make sure we encompass the complete particle here!
+ m_pSimpleEmitter->SetParticleCullRadius( sParticle->m_uchEndSize );
+
+ // Do the core effects
+ for ( int i = 0; i < 2; i++ )
+ {
+ sParticle = (SimpleParticle *) m_pSimpleEmitter->AddParticle( sizeof(SimpleParticle), m_pSimpleEmitter->GetPMaterial( "effects/combinemuzzle2" ), GetAbsOrigin() );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = forward * ( 32.0f * (i+1) );
+ sParticle->m_flDieTime = 0.2f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 100;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ if ( i < 1 )
+ {
+ sParticle->m_uchStartSize = flLargeScale * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
+ }
+ else
+ {
+ if ( random->RandomInt( 0, 20 ) == 0 )
+ {
+ sParticle->m_uchStartSize = flLargeScale * (i+1);
+ sParticle->m_uchEndSize = 0.0f;
+ sParticle->m_flDieTime = 0.25f;
+ }
+ else
+ {
+ sParticle->m_uchStartSize = flLargeScale * (i+1);
+ sParticle->m_uchEndSize = 0.0f;
+ }
+ }
+ }
+
+ // Only do these particles if required
+ //if ( m_spawnflags & SF_ENERGYCORE_NO_PARTICLES )
+ // return;
+
+ // Do the charging particles
+ m_pAttractorEmitter->SetAttractorOrigin( GetAbsOrigin() );
+
+ Vector offset;
+ float dist;
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ dist = random->RandomFloat( 4.0f * m_flLargeScale, 64.0f * m_flLargeScale );
+
+ offset = forward * dist;
+
+ dist = RemapValClamped( dist, 4.0f * m_flLargeScale, 64.0f * m_flLargeScale, 6.0f, 1.0f );
+ offset += right * random->RandomFloat( -2.0f * dist * m_flLargeScale, 2.0f * dist * m_flLargeScale );
+ offset += up * random->RandomFloat( -2.0f * dist * m_flLargeScale, 2.0f * dist * m_flLargeScale );
+
+ offset += GetAbsOrigin();
+
+ sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), m_pAttractorEmitter->GetPMaterial( "effects/combinemuzzle2_dark" ), offset );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = Vector(0,0,2);
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_uchStartSize = 1;
+ sParticle->m_uchEndSize = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : inline float
+//-----------------------------------------------------------------------------
+inline float C_Env_Lightrail_Endpoint::GetStateDurationPercentage( void )
+{
+ if ( m_flDuration == 0 )
+ return 0.0f;
+
+ return RemapValClamped( ( gpGlobals->curtime - m_flStartTime ), 0, m_flDuration, 0, 1.0f );;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit( state );
+
+ // Turn off
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ }
+
+ // Turn on
+ if ( state == SHOULDTRANSMIT_START )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_Env_Lightrail_Endpoint::ClientThink( void )
+{
+ if ( gpGlobals->frametime <= 0.0f )
+ return;
+
+ float flDuration = GetStateDurationPercentage();
+
+ switch( m_nState )
+ {
+ case ENDPOINT_STATE_OFF:
+ UpdateIdle( 1.0f - flDuration );
+ break;
+
+ case ENDPOINT_STATE_CHARGING:
+ UpdateCharging( flDuration );
+ break;
+
+ case ENDPOINT_STATE_SMALLFX:
+ UpdateSmallFX( );
+ break;
+
+ case ENDPOINT_STATE_LARGEFX:
+ UpdateLargeFX( );
+ break;
+ }
+}
diff --git a/game/client/portal/c_env_portal_path_track.cpp b/game/client/portal/c_env_portal_path_track.cpp
new file mode 100644
index 0000000..5456b35
--- /dev/null
+++ b/game/client/portal/c_env_portal_path_track.cpp
@@ -0,0 +1,203 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client Side Effects for the env_portal_path_track.
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "env_portal_path_track_shared.h"
+#include "particles_simple.h"
+#include "particles_attractor.h"
+
+class C_EnvPortalPathTrack : public C_BaseEntity
+{
+ DECLARE_CLASS( C_EnvPortalPathTrack, C_BaseEntity );
+ DECLARE_CLIENTCLASS();
+
+public:
+ void OnDataChanged( DataUpdateType_t updateType );
+ RenderGroup_t GetRenderGroup( void );
+
+ void ClientThink( void );
+ void NotifyShouldTransmit( ShouldTransmitState_t state );
+
+ void UpdateParticles_Inactive( void );
+ void UpdateParticles_Active( void );
+private:
+
+ float m_flScale;
+ int m_nState;
+ float m_flDuration;
+ float m_flStartTime;
+
+ bool m_bTrackActive;
+ bool m_bEndpointActive;
+
+ bool SetupEmitters( void ); // Init our particle emitters
+
+ CSmartPtr<CSimpleEmitter> m_pSimpleEmitter; // particle system, emits particles
+ CSmartPtr<CParticleAttractor> m_pAttractorEmitter; // particle system, attracts particles
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_EnvPortalPathTrack, DT_EnvPortalPathTrack, CEnvPortalPathTrack )
+
+ RecvPropBool( RECVINFO(m_bTrackActive) ),
+ RecvPropBool( RECVINFO(m_bEndpointActive) ),
+ RecvPropInt( RECVINFO(m_nState) ),
+
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : RenderGroup_t
+//-----------------------------------------------------------------------------
+RenderGroup_t C_EnvPortalPathTrack::GetRenderGroup( void )
+{
+ return RENDER_GROUP_TRANSLUCENT_ENTITY;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_EnvPortalPathTrack::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_EnvPortalPathTrack::SetupEmitters( void )
+{
+ // Setup the basic core emitter
+ if ( m_pSimpleEmitter.IsValid() == false )
+ {
+ m_pSimpleEmitter = CSimpleEmitter::Create( "portaltracktrainendpoint" );
+
+ if ( m_pSimpleEmitter.IsValid() == false )
+ return false;
+ }
+
+ // Setup the attractor emitter
+ if ( m_pAttractorEmitter.IsValid() == false )
+ {
+ m_pAttractorEmitter = CParticleAttractor::Create( GetAbsOrigin(), "portaltracktrainendpointattractor" );
+
+ if ( m_pAttractorEmitter.IsValid() == false )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EnvPortalPathTrack::ClientThink( void )
+{
+ if ( gpGlobals->frametime <= 0.0f )
+ return;
+
+ switch( m_nState )
+ {
+ case PORTAL_PATH_TRACK_STATE_OFF:
+ break;
+
+ case PORTAL_PATH_TRACK_STATE_INACTIVE:
+ UpdateParticles_Inactive();
+ break;
+
+ case PORTAL_PATH_TRACK_STATE_ACTIVE:
+ UpdateParticles_Active();
+ break;
+ }
+}
+
+void C_EnvPortalPathTrack::UpdateParticles_Inactive( void )
+{
+}
+
+void C_EnvPortalPathTrack::UpdateParticles_Active ( void )
+{
+ // Emitters must be valid
+ if ( SetupEmitters() == false )
+ return;
+
+ // Reset our sort origin
+ m_pSimpleEmitter->SetSortOrigin( GetAbsOrigin() );
+
+ SimpleParticle *sParticle;
+
+ // Do the charging particles
+ m_pAttractorEmitter->SetAttractorOrigin( GetAbsOrigin() );
+
+ Vector forward, right, up;
+ AngleVectors( GetAbsAngles(), &forward, &right, &up );
+
+ Vector offset;
+ float dist;
+
+ int numParticles = floor( 4.0f );
+
+ for ( int i = 0; i < numParticles; i++ )
+ {
+ dist = random->RandomFloat( 4.0f, 64.0f );
+
+ offset = forward * dist;
+
+ dist = RemapValClamped( dist, 4.0f, 64.0f, 6.0f, 1.0f );
+ offset += right * random->RandomFloat( -4.0f * dist, 4.0f * dist );
+ offset += up * random->RandomFloat( -4.0f * dist, 4.0f * dist );
+
+ offset += GetAbsOrigin();
+
+ sParticle = (SimpleParticle *) m_pAttractorEmitter->AddParticle( sizeof(SimpleParticle), m_pAttractorEmitter->GetPMaterial( "effects/strider_muzzle" ), offset );
+
+ if ( sParticle == NULL )
+ return;
+
+ sParticle->m_vecVelocity = Vector(0,0,8);
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 );
+ sParticle->m_uchEndSize = 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_EnvPortalPathTrack::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit( state );
+
+ // Turn off
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ }
+
+ // Turn on
+ if ( state == SHOULDTRANSMIT_START )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
diff --git a/game/client/portal/c_func_liquidportal.cpp b/game/client/portal/c_func_liquidportal.cpp
new file mode 100644
index 0000000..9365102
--- /dev/null
+++ b/game/client/portal/c_func_liquidportal.cpp
@@ -0,0 +1,630 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rising liquid that acts as a one-way portal
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_func_liquidportal.h"
+#include "debugoverlay_shared.h"
+#include "view_scene.h"
+#include "view.h"
+#include "ScreenSpaceEffects.h"
+#include "materialsystem/imaterialvar.h"
+
+
+LINK_ENTITY_TO_CLASS( func_liquidportal, C_Func_LiquidPortal );
+
+IMPLEMENT_CLIENTCLASS_DT( C_Func_LiquidPortal, DT_Func_LiquidPortal, CFunc_LiquidPortal )
+ RecvPropEHandle( RECVINFO(m_hLinkedPortal) ),
+ RecvPropFloat( RECVINFO(m_fFillStartTime) ),
+ RecvPropFloat( RECVINFO(m_fFillEndTime) ),
+END_RECV_TABLE()
+
+
+#define LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( vertnum, xtexbase, xtexscale, ytexbase, ytexscale )\
+ meshBuilder.Position3fv( &vVertices[vertnum].x );\
+ meshBuilder.TexCoord2f( 0, vVertices[vertnum].xtexbase * xtexscale, vVertices[vertnum].ytexbase * ytexscale );\
+ meshBuilder.AdvanceVertex();
+
+
+C_Func_LiquidPortal::C_Func_LiquidPortal( void )
+{
+ g_pPortalRender->AddPortal( this );
+}
+
+C_Func_LiquidPortal::~C_Func_LiquidPortal( void )
+{
+ g_pPortalRender->RemovePortal( this );
+}
+
+
+int C_Func_LiquidPortal::DrawModel( int flags )
+{
+ if( IsFillingNow() )
+ {
+ DrawPortal();
+ return 1;
+ }
+
+ return 0;
+}
+
+void C_Func_LiquidPortal::OnDataChanged( DataUpdateType_t updateType )
+{
+ GetRenderBoundsWorldspace( m_vAABBMins, m_vAABBMaxs );
+ m_pLinkedPortal = m_hLinkedPortal.Get();
+ ComputeLinkMatrix();
+ UpdateBoundingPlanes();
+}
+
+void C_Func_LiquidPortal::ComputeLinkMatrix( void )
+{
+ C_Func_LiquidPortal *pLinkedPortal = m_hLinkedPortal.Get();
+ if( pLinkedPortal )
+ {
+ VMatrix matLocalToWorld, matLocalToWorldInv, matRemoteToWorld;
+
+ //matLocalToWorld.Identity();
+ //matLocalToWorld.SetTranslation( CollisionProp()->WorldSpaceCenter() );
+ matLocalToWorld = EntityToWorldTransform();
+
+ //matRemoteToWorld.Identity();
+ //matRemoteToWorld.SetTranslation( pLinkedPortal->CollisionProp()->WorldSpaceCenter() );
+ matRemoteToWorld = pLinkedPortal->EntityToWorldTransform();
+
+ MatrixInverseTR( matLocalToWorld, matLocalToWorldInv );
+ m_matrixThisToLinked = matRemoteToWorld * matLocalToWorldInv;
+
+ MatrixInverseTR( m_matrixThisToLinked, pLinkedPortal->m_matrixThisToLinked );
+ }
+ else
+ {
+ m_matrixThisToLinked.Identity();
+ }
+}
+
+
+
+
+void CPortalRenderable_Func_LiquidPortal::UpdateBoundingPlanes( void )
+{
+ //x min
+ m_fBoundingPlanes[0][0] = 1.0f;
+ m_fBoundingPlanes[0][1] = 0.0f;
+ m_fBoundingPlanes[0][2] = 0.0f;
+ m_fBoundingPlanes[0][3] = m_vAABBMins.x;
+
+ //x max
+ m_fBoundingPlanes[1][0] = -1.0f;
+ m_fBoundingPlanes[1][1] = 0.0f;
+ m_fBoundingPlanes[1][2] = 0.0f;
+ m_fBoundingPlanes[1][3] = -m_vAABBMaxs.x;
+
+
+ //y min
+ m_fBoundingPlanes[2][0] = 0.0f;
+ m_fBoundingPlanes[2][1] = 1.0f;
+ m_fBoundingPlanes[2][2] = 0.0f;
+ m_fBoundingPlanes[2][3] = m_vAABBMins.y;
+
+ //y max
+ m_fBoundingPlanes[3][0] = 0.0f;
+ m_fBoundingPlanes[3][1] = -1.0f;
+ m_fBoundingPlanes[3][2] = 0.0f;
+ m_fBoundingPlanes[3][3] = -m_vAABBMaxs.y;
+
+
+ //z min
+ m_fBoundingPlanes[4][0] = 0.0f;
+ m_fBoundingPlanes[4][1] = 0.0f;
+ m_fBoundingPlanes[4][2] = 1.0f;
+ m_fBoundingPlanes[4][3] = m_vAABBMins.z;
+
+ //z max is too variable to store
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawPreStencilMask( void )
+{
+ // Should we do something here like flatbasic?
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawStencilMask( void )
+{
+ DrawOutwardBox( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
+ DrawInnerLiquid( true, 1.0f, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawPostStencilFixes( void )
+{
+ DrawOutwardBox( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
+ DrawInnerLiquid( true, 1.0f, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
+}
+
+
+void CPortalRenderable_Func_LiquidPortal::RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView )
+{
+ if( m_pLinkedPortal == NULL ) //not linked to any portal
+ return;
+
+ Frustum FrustumBackup;
+ memcpy( FrustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
+
+ Frustum seeThroughFrustum;
+ bool bUseSeeThroughFrustum;
+
+ if ( g_pPortalRender->GetViewRecursionLevel() == 0 )
+ {
+ bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum, pViewRender->GetFrustum(), FRUSTUM_NUMPLANES );
+ }
+ else
+ {
+ bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
+ }
+
+ Vector vCameraForward;
+ AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
+
+ // Setup fog state for the camera.
+ Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
+ Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
+
+ CViewSetup portalView = cameraView;
+
+ QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
+
+ portalView.width = cameraView.width;
+ portalView.height = cameraView.height;
+ portalView.x = 0;
+ portalView.y = 0;
+ portalView.origin = ptPOVOrigin;
+ portalView.angles = qPOVAngles;
+ portalView.fov = cameraView.fov;
+ portalView.m_bOrtho = false;
+ portalView.m_flAspectRatio = cameraView.m_flAspectRatio; //use the screen aspect ratio, 0.0f doesn't work as advertised
+
+ CopyToCurrentView( pViewRender, portalView );
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ {
+ ViewCustomVisibility_t customVisibility;
+ m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
+ render->Push3DView( portalView, 0, NULL, pViewRender->GetFrustum() );
+ {
+ if( bUseSeeThroughFrustum)
+ memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
+
+ render->OverrideViewFrustum( pViewRender->GetFrustum() );
+ SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() + 1 );
+
+ CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
+ CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
+ SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
+
+ //DRAW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ ViewDrawScene_PortalStencil( pViewRender, portalView, &customVisibility );
+
+ SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
+
+ SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() - 1 );
+ }
+ render->PopView( pViewRender->GetFrustum() );
+
+ //restore old frustum
+ memcpy( pViewRender->GetFrustum(), FrustumBackup, sizeof( Frustum ) );
+ render->OverrideViewFrustum( FrustumBackup );
+ }
+
+ //restore old vis data
+ CopyToCurrentView( pViewRender, cameraView );
+}
+
+void CPortalRenderable_Func_LiquidPortal::RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView )
+{
+
+}
+
+
+void CPortalRenderable_Func_LiquidPortal::AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility )
+{
+ if ( !pCustomVisibility )
+ return;
+
+ VisOverrideData_t visOverride;
+ Vector vOrigin = (m_vAABBMins + m_vAABBMaxs) * 0.5f;
+
+ visOverride.m_vecVisOrigin = vOrigin;
+ visOverride.m_fDistToAreaPortalTolerance = 64.0f;
+
+ // Specify which leaf to use for area portal culling
+ pCustomVisibility->ForceVisOverride( visOverride );
+ pCustomVisibility->ForceViewLeaf( enginetrace->GetLeafContainingPoint( vOrigin ) );
+
+ pCustomVisibility->AddVisOrigin( vOrigin );
+}
+
+bool CPortalRenderable_Func_LiquidPortal::DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
+{
+ return ((m_vAABBMins.z < waterZ) && (m_vAABBMaxs.z > waterZ));
+}
+
+SkyboxVisibility_t CPortalRenderable_Func_LiquidPortal::SkyBoxVisibleFromPortal( void )
+{
+ return SKYBOX_NOT_VISIBLE;
+}
+
+
+bool CPortalRenderable_Func_LiquidPortal::CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum, const VPlane *pInputFrustum, int iInputFrustumPlaneCount )
+{
+ return false;
+}
+
+
+const Vector& CPortalRenderable_Func_LiquidPortal::GetFogOrigin( void ) const
+{
+ return vec3_origin;
+}
+
+void CPortalRenderable_Func_LiquidPortal::ShiftFogForExitPortalView() const
+{
+
+}
+
+
+bool CPortalRenderable_Func_LiquidPortal::ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, Frustum currentFrustum )
+{
+ //return false;
+ return IsFillingNow();
+}
+
+CPortalRenderable* CPortalRenderable_Func_LiquidPortal::GetLinkedPortal() const
+{
+ return m_pLinkedPortal;
+}
+
+bool CPortalRenderable_Func_LiquidPortal::ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup )
+{
+ return false;
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawPortal( void )
+{
+ if( IsFillingNow() )
+ {
+ //"shadertest/gooinglass"
+ //"glass/glasswindow_refract01"
+ //IMaterial *pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER );
+ //UpdateFrontBufferTexturesForMaterial( (IMaterial *)pMaterial );
+
+ DrawOutwardBox();
+ //DrawInnerLiquid( pMaterial );
+ //DrawInwardBox( pMaterial );
+ }
+}
+
+void CPortalRenderable_Func_LiquidPortal::GetToolRecordingState( bool bActive, KeyValues *msg )
+{
+
+}
+
+void CPortalRenderable_Func_LiquidPortal::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
+{
+
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawOutwardBox( const IMaterial *pMaterial )
+{
+ if( pMaterial == NULL )
+ pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER );
+
+ const float fVerticalTextureScale = 1.0f / 100.0f;
+ const float fHorizontalTextureScale = 1.0f / 100.0f;
+
+ float fMaxZ = m_vAABBMins.z + ((m_vAABBMaxs.z - m_vAABBMins.z) * GetFillInterpolationAmount());
+
+ Vector vVertices[8];
+ for( int i = 0; i != 8; ++i )
+ {
+ vVertices[i].x = (i&(1<<0)) ? m_vAABBMaxs.x : m_vAABBMins.x;
+ vVertices[i].y = (i&(1<<1)) ? m_vAABBMaxs.y : m_vAABBMins.y;
+ vVertices[i].z = (i&(1<<2)) ? fMaxZ : m_vAABBMins.z;
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( (IMaterial *)pMaterial, (CPortalRenderable_Func_LiquidPortal*)this );
+
+ CMeshBuilder meshBuilder;
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 6 );
+
+ //x min
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+ //x max
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+ //y min
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+ //y max
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+
+ //z min
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+
+ //z max
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+
+ meshBuilder.End();
+ pMesh->Draw();
+ pRenderContext->Flush( false );
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawInwardBox( const IMaterial *pMaterial )
+{
+ if( pMaterial == NULL )
+ pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER );
+
+ const float fVerticalTextureScale = 1.0f / 100.0f;
+ const float fHorizontalTextureScale = 1.0f / 100.0f;
+
+ float fMaxZ = m_vAABBMins.z + ((m_vAABBMaxs.z - m_vAABBMins.z) * GetFillInterpolationAmount());
+
+ Vector vVertices[8];
+ for( int i = 0; i != 8; ++i )
+ {
+ vVertices[i].x = (i&(1<<0)) ? m_vAABBMaxs.x : m_vAABBMins.x;
+ vVertices[i].y = (i&(1<<1)) ? m_vAABBMaxs.y : m_vAABBMins.y;
+ vVertices[i].z = (i&(1<<2)) ? fMaxZ : m_vAABBMins.z;
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( (IMaterial *)pMaterial, (CPortalRenderable_Func_LiquidPortal*)this );
+
+ CMeshBuilder meshBuilder;
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 6 );
+
+ //x min
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+ //x max
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, y, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+ //y min
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+ //y max
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, z, -fVerticalTextureScale );
+
+
+ //z min
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 0, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 2, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 3, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 1, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+
+ //z max
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 5, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 7, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 6, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+ LIQUIDPORTAL_DYNAMICMESH_BOX_ADDVERTEX( 4, x, fHorizontalTextureScale, y, fVerticalTextureScale );
+
+ meshBuilder.End();
+ pMesh->Draw();
+ pRenderContext->Flush( false );
+}
+
+void CPortalRenderable_Func_LiquidPortal::DrawInnerLiquid( bool bClipToBounds, float fOpacity, const IMaterial *pMaterial ) //quads in front of camera clipped to box dimensions
+{
+ if( !IsFillingNow() && bClipToBounds )
+ return;
+
+ PortalMeshPoint_t WorkVertices[4];
+
+ //view->GetViewSetup()->zNear;
+ Vector vForward, vUp, vRight, vOrigin;
+ vForward = CurrentViewForward();
+ vUp = CurrentViewUp();
+ vRight = CurrentViewRight();
+
+ //vOrigin = CurrentViewOrigin() + vForward * (view->GetViewSetup()->zNear + 0.011f); //experimentation has shown this to be the optimal distance on the Nvidia 6800 cards we develop on
+ vOrigin = CurrentViewOrigin() + vForward * (view->GetViewSetup()->zNear + 1.0f );
+
+ const float fScalingAmount = 5.0f;
+
+ WorkVertices[0].texCoord.x = fScalingAmount;
+ WorkVertices[0].texCoord.y = fScalingAmount;
+
+ WorkVertices[1].texCoord.x = fScalingAmount;
+ WorkVertices[1].texCoord.y = 0.0f;
+
+ WorkVertices[2].texCoord.x = 0.0f;
+ WorkVertices[2].texCoord.y = 0.0f;
+
+ WorkVertices[3].texCoord.x = 0.0f;
+ WorkVertices[3].texCoord.y = fScalingAmount;
+
+
+ WorkVertices[0].vWorldSpacePosition = vOrigin + (vRight * 40.0f) + (vUp * -40.0f);
+ WorkVertices[1].vWorldSpacePosition = vOrigin + (vRight * 40.0f) + (vUp * 40.0f);
+ WorkVertices[2].vWorldSpacePosition = vOrigin + (vRight * -40.0f) + (vUp * 40.0f);
+ WorkVertices[3].vWorldSpacePosition = vOrigin + (vRight * -40.0f) + (vUp * -40.0f);
+
+ PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( 4 * (6) * 2 * sizeof( PortalMeshPoint_t ) ); //really only should need 2x points, but I'm paranoid
+ PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( 4 * (6) * 2 * sizeof( PortalMeshPoint_t ) );
+
+ PortalMeshPoint_t *pFinalVerts;
+ int iVertCount;
+ if( bClipToBounds )
+ {
+ PortalMeshPoint_t *pTempVerts;
+
+ //clip by first plane and put output into pInVerts
+ iVertCount = ClipPolyToPlane_LerpTexCoords( WorkVertices, 4, pInVerts, Vector( 0.0f, 0.0f, -1.0f ), -(m_vAABBMins.z + ((m_vAABBMaxs.z - m_vAABBMins.z) * GetFillInterpolationAmount())), 0.01f );
+
+ //clip by other planes and flipflop in and out pointers
+ for( int i = 0; i != 5; ++i )
+ {
+ if( iVertCount < 3 )
+ return; //nothing to draw
+
+ iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, *(Vector *)m_fBoundingPlanes[i], m_fBoundingPlanes[i][3], 0.01f );
+ pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
+ }
+
+ if( iVertCount < 3 )
+ return; //nothing to draw
+
+ pFinalVerts = pInVerts;
+ }
+ else
+ {
+ pFinalVerts = WorkVertices;
+ iVertCount = 4;
+ }
+
+ bool bInterpOpacity = false;
+ if( pMaterial == NULL )
+ {
+ pMaterial = materials->FindMaterial( "glass/glasswindow_refract01", TEXTURE_GROUP_OTHER );
+ bInterpOpacity = ( fOpacity != 1.0f );
+ }
+
+ if( bInterpOpacity )
+ {
+ IMaterial *pEditMaterial = (IMaterial *)pMaterial; //we'll be making changes, then changing it back
+
+ IMaterialVar *pRefractAmount = pEditMaterial->FindVar( "$refractamount", NULL );
+ IMaterialVar *pBlurAmount = pEditMaterial->FindVar( "$bluramount", NULL );
+ IMaterialVar *pTint = pEditMaterial->FindVar( "$refracttint", NULL );
+
+ float fOriginalRefractAmount = pRefractAmount->GetFloatValue();
+ float fOriginalBlurAmount = pBlurAmount->GetFloatValue();
+ Vector4D vOriginalTint;
+ pTint->GetVecValue( &vOriginalTint.x, 4 );
+
+ Vector4D vModdedTint = vOriginalTint;
+
+ pRefractAmount->SetFloatValue( fOriginalRefractAmount * fOpacity );
+ pBlurAmount->SetFloatValue( fOriginalBlurAmount * fOpacity );
+ vModdedTint.x = 1.0f - ((1.0f - vOriginalTint.x) * fOpacity);
+ vModdedTint.y = 1.0f - ((1.0f - vOriginalTint.y) * fOpacity);
+ vModdedTint.z = 1.0f - ((1.0f - vOriginalTint.z) * fOpacity);
+ pTint->SetVecValue( &vModdedTint.x, 4 );
+
+ Clip_And_Render_Convex_Polygon( pFinalVerts, iVertCount, pEditMaterial, this );
+ materials->Flush();
+
+ pRefractAmount->SetFloatValue( fOriginalRefractAmount );
+ pBlurAmount->SetFloatValue( fOriginalBlurAmount );
+ pTint->SetVecValue( &vOriginalTint.x, 4 );
+ }
+ else
+ {
+ Clip_And_Render_Convex_Polygon( pFinalVerts, iVertCount, pMaterial, this );
+ }
+
+
+}
+
+
+
+ADD_SCREENSPACE_EFFECT( CLiquidPortal_InnerLiquidEffect, LiquidPortal_InnerLiquid );
+const float CLiquidPortal_InnerLiquidEffect::s_fFadeBackEffectTime = 5.0f;
+
+
+CLiquidPortal_InnerLiquidEffect::CLiquidPortal_InnerLiquidEffect( void )
+: m_bEnable(true),
+ m_pImmersionPortal(NULL),
+ m_bFadeBackToReality(false),
+ m_fFadeBackTimeLeft(0.0f)
+{
+}
+
+
+void CLiquidPortal_InnerLiquidEffect::SetParameters( KeyValues *params )
+{
+ /*KeyValues *pImmersionPortal = params->FindKey( "immersion_portal" );
+ if( pImmersionPortal )
+ m_pImmersionPortal = (C_Func_LiquidPortal *)pImmersionPortal->GetPtr();*/
+}
+
+
+void CLiquidPortal_InnerLiquidEffect::Render( int x, int y, int w, int h )
+{
+ if( !m_pImmersionPortal || !m_bEnable )
+ return;
+
+ if( m_bFadeBackToReality )
+ {
+ //effect should cover whole screen and have a alpha-like fade back to normal view
+ m_fFadeBackTimeLeft -= gpGlobals->absoluteframetime;
+ if( m_fFadeBackTimeLeft > 0.0f )
+ {
+ float fInterp = m_fFadeBackTimeLeft/s_fFadeBackEffectTime;
+
+ //clear depth buffer so we can be all warpy on the view model too
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->ClearBuffers( false, true, false );
+ pRenderContext->OverrideDepthEnable( true, false );
+
+ m_pImmersionPortal->DrawInnerLiquid( false, fInterp );
+
+ pRenderContext->OverrideDepthEnable( false, true );
+ }
+ else
+ {
+ m_bFadeBackToReality = false;
+ m_pImmersionPortal = NULL;
+ }
+ }
+ else
+ {
+ //effect should only cover a portion of the screen and be in full warpiness
+
+ //clear depth buffer so we can be all warpy on the view model too
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->ClearBuffers( false, true, false );
+ pRenderContext->OverrideDepthEnable( true, false );
+
+ m_pImmersionPortal->DrawInnerLiquid();
+
+ pRenderContext->OverrideDepthEnable( false, true );
+ }
+}
+
+
diff --git a/game/client/portal/c_func_liquidportal.h b/game/client/portal/c_func_liquidportal.h
new file mode 100644
index 0000000..6fef420
--- /dev/null
+++ b/game/client/portal/c_func_liquidportal.h
@@ -0,0 +1,131 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#ifndef C_FUNC_LIQUIDPORTAL_H
+#define C_FUNC_LIQUIDPORTAL_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "c_baseentity.h"
+#include "PortalRender.h"
+#include "Portal_DynamicMeshRenderingUtils.h"
+#include "ScreenSpaceEffects.h"
+
+class CPortalRenderable_Func_LiquidPortal : public CPortalRenderable
+{
+public:
+ virtual void DrawPreStencilMask( void );
+ virtual void DrawStencilMask( void );
+ virtual void DrawPostStencilFixes( void );
+
+ virtual void RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView );
+ virtual void RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView );
+
+ void AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility );
+ virtual bool DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const;
+ virtual SkyboxVisibility_t SkyBoxVisibleFromPortal( void );
+
+ bool CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum, const VPlane *pInputFrustum = NULL, int iInputFrustumPlaneCount = 0 );
+
+ virtual const Vector& GetFogOrigin( void ) const;
+ virtual void ShiftFogForExitPortalView() const;
+
+ virtual bool ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, Frustum currentFrustum ); //portal is both visible, and will display at least some portion of a remote view
+ virtual CPortalRenderable* GetLinkedPortal() const;
+ virtual bool ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup );
+ virtual void DrawPortal( void ); //sort of like what you'd expect to happen in C_BaseAnimating::DrawModel() if portals were fully compatible with models
+
+ virtual void GetToolRecordingState( bool bActive, KeyValues *msg );
+ virtual void HandlePortalPlaybackMessage( KeyValues *pKeyValues );
+
+ void DrawOutwardBox( const IMaterial *pMaterial = NULL );
+ void DrawInwardBox( const IMaterial *pMaterial = NULL );
+ void DrawInnerLiquid( bool bClipToBounds = true, float fOpacity = 1.0f, const IMaterial *pMaterial = NULL ); //quads in front of camera clipped to box dimensions
+
+ float GetFillInterpolationAmount( void );
+ bool IsFillingNow( void );
+
+ void UpdateBoundingPlanes( void );
+
+ float m_fFillStartTime;
+ float m_fFillEndTime;
+
+ Vector m_vAABBMins;
+ Vector m_vAABBMaxs;
+
+ float m_fBoundingPlanes[5][4]; //top is highly variable and therefore not actually stored
+
+ CPortalRenderable_Func_LiquidPortal *m_pLinkedPortal;
+};
+
+inline float CPortalRenderable_Func_LiquidPortal::GetFillInterpolationAmount( void )
+{
+ if( m_fFillEndTime < gpGlobals->curtime )
+ return 0.0f;
+
+ return ((gpGlobals->curtime - m_fFillStartTime) / (m_fFillEndTime - m_fFillStartTime));
+}
+
+inline bool CPortalRenderable_Func_LiquidPortal::IsFillingNow( void )
+{
+ return ((m_fFillEndTime >= gpGlobals->curtime) && (m_fFillStartTime <= gpGlobals->curtime));
+}
+
+
+
+class C_Func_LiquidPortal : public C_BaseEntity, public CPortalRenderable_Func_LiquidPortal
+{
+public:
+ DECLARE_CLASS( C_Func_LiquidPortal, C_BaseEntity );
+ DECLARE_CLIENTCLASS();
+
+ C_Func_LiquidPortal( void );
+ ~C_Func_LiquidPortal( void );
+
+ virtual bool IsTransparent( void ) { return true; };
+ virtual bool UsesPowerOfTwoFrameBufferTexture() { return true; };
+ virtual int DrawModel( int flags );
+
+ void ComputeLinkMatrix( void );
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+
+ CHandle<C_Func_LiquidPortal> m_hLinkedPortal;
+};
+
+
+
+
+
+class CLiquidPortal_InnerLiquidEffect : public IScreenSpaceEffect
+{
+public:
+ CLiquidPortal_InnerLiquidEffect( void );
+
+ void Init( void ) { };
+ void Shutdown( void ) { };
+
+ void SetParameters( KeyValues *params );
+
+ void Render( int x, int y, int w, int h );
+
+ void Enable( bool bEnable ) { m_bEnable = bEnable; };
+ bool IsEnabled( void ) { return m_bEnable; };
+
+private:
+ bool m_bEnable;
+
+public:
+ C_Func_LiquidPortal* m_pImmersionPortal; //portal that did the filling + teleporting
+ bool m_bFadeBackToReality;
+ float m_fFadeBackTimeLeft;
+ static const float s_fFadeBackEffectTime; //how long the effect should take
+};
+
+#endif //#ifndef C_FUNC_LIQUIDPORTAL_H
diff --git a/game/client/portal/c_neurotoxin_countdown.cpp b/game/client/portal/c_neurotoxin_countdown.cpp
new file mode 100644
index 0000000..7a28b1d
--- /dev/null
+++ b/game/client/portal/c_neurotoxin_countdown.cpp
@@ -0,0 +1,56 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "c_neurotoxin_countdown.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+CUtlVector< C_NeurotoxinCountdown* > g_NeurotoxinCountdowns;
+
+
+IMPLEMENT_CLIENTCLASS_DT(C_NeurotoxinCountdown, DT_NeurotoxinCountdown, CNeurotoxinCountdown)
+
+ RecvPropBool( RECVINFO(m_bEnabled) ),
+
+END_RECV_TABLE()
+
+
+C_NeurotoxinCountdown::C_NeurotoxinCountdown()
+{
+ g_NeurotoxinCountdowns.AddToTail( this );
+}
+
+C_NeurotoxinCountdown::~C_NeurotoxinCountdown()
+{
+ g_NeurotoxinCountdowns.FindAndRemove( this );
+}
+
+int C_NeurotoxinCountdown::GetMinutes( void )
+{
+ C_BasePlayer *player = UTIL_PlayerByIndex( 1 );
+ if ( player )
+ return player->GetBonusProgress() / 60;
+
+ return 0.0f;
+}
+
+int C_NeurotoxinCountdown::GetSeconds( void )
+{
+ C_BasePlayer *player = UTIL_PlayerByIndex( 1 );
+ if ( player )
+ return player->GetBonusProgress() % 60;
+
+ return 0.0f;
+}
+
+int C_NeurotoxinCountdown::GetMilliseconds( void )
+{
+ return static_cast<int>( gpGlobals->curtime * 100.0f ) % 100;;
+}
diff --git a/game/client/portal/c_neurotoxin_countdown.h b/game/client/portal/c_neurotoxin_countdown.h
new file mode 100644
index 0000000..f258a01
--- /dev/null
+++ b/game/client/portal/c_neurotoxin_countdown.h
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#ifndef C_NEUROTOXIN_COUNTDOWN_H
+#define C_NEUROTOXIN_COUNTDOWN_H
+
+#include "cbase.h"
+#include "utlvector.h"
+
+
+class C_NeurotoxinCountdown : public C_BaseEntity
+{
+public:
+ DECLARE_CLASS( C_NeurotoxinCountdown, CBaseEntity );
+ DECLARE_CLIENTCLASS();
+
+ C_NeurotoxinCountdown();
+ virtual ~C_NeurotoxinCountdown();
+
+ bool IsEnabled( void ) { return m_bEnabled; }
+
+ int GetMinutes( void );
+ int GetSeconds( void );
+ int GetMilliseconds( void );
+
+private:
+
+ bool m_bEnabled;
+};
+
+
+extern CUtlVector< C_NeurotoxinCountdown* > g_NeurotoxinCountdowns;
+
+
+#endif //C_NEUROTOXIN_COUNTDOWN_H \ No newline at end of file
diff --git a/game/client/portal/c_npc_portal_turret_floor.cpp b/game/client/portal/c_npc_portal_turret_floor.cpp
new file mode 100644
index 0000000..73e6081
--- /dev/null
+++ b/game/client/portal/c_npc_portal_turret_floor.cpp
@@ -0,0 +1,168 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_ai_basenpc.h"
+#include "beam_shared.h"
+#include "prop_portal_shared.h"
+
+
+#define FLOOR_TURRET_PORTAL_EYE_ATTACHMENT 1
+#define FLOOR_TURRET_PORTAL_LASER_ATTACHMENT 2
+#define FLOOR_TURRET_PORTAL_LASER_RANGE 8192
+
+#define FLOOR_TURRET_PORTAL_END_POINT_PULSE_SCALE 4.0f
+
+
+class C_NPC_Portal_FloorTurret : public C_AI_BaseNPC
+{
+public:
+ DECLARE_CLASS( C_NPC_Portal_FloorTurret, C_AI_BaseNPC );
+ DECLARE_CLIENTCLASS();
+
+ virtual ~C_NPC_Portal_FloorTurret( void );
+
+ virtual void Spawn( void );
+ virtual void ClientThink( void );
+
+ bool IsLaserOn( void ) { return m_pBeam != NULL; }
+ void LaserOff( void );
+ void LaserOn( void );
+ float LaserEndPointSize( void );
+
+private:
+ CBeam *m_pBeam;
+
+ bool m_bOutOfAmmo;
+ bool m_bLaserOn;
+ int m_sLaserHaloSprite;
+ float m_fPulseOffset;
+
+ float m_bBeamFlickerOff;
+ float m_fBeamFlickerTime;
+
+};
+
+
+IMPLEMENT_CLIENTCLASS_DT( C_NPC_Portal_FloorTurret, DT_NPC_Portal_FloorTurret, CNPC_Portal_FloorTurret )
+
+ RecvPropBool( RECVINFO( m_bOutOfAmmo ) ),
+ RecvPropBool( RECVINFO( m_bLaserOn ) ),
+ RecvPropInt( RECVINFO( m_sLaserHaloSprite ) ),
+
+END_RECV_TABLE()
+
+
+C_NPC_Portal_FloorTurret::~C_NPC_Portal_FloorTurret( void )
+{
+ LaserOff();
+ if( m_pBeam )
+ m_pBeam->Remove();
+}
+
+
+void C_NPC_Portal_FloorTurret::Spawn( void )
+{
+ SetThink( &C_NPC_Portal_FloorTurret::ClientThink );
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ m_pBeam = NULL;
+ m_fPulseOffset = RandomFloat( 0.0f, 2.0f * M_PI );
+
+ m_bBeamFlickerOff = false;
+ m_fBeamFlickerTime = 0.0f;
+
+ BaseClass::Spawn();
+}
+
+void C_NPC_Portal_FloorTurret::ClientThink( void )
+{
+ if ( m_bOutOfAmmo && m_fBeamFlickerTime < gpGlobals->curtime )
+ {
+ m_fBeamFlickerTime = gpGlobals->curtime + RandomFloat( 0.05f, 0.3f );
+ m_bBeamFlickerOff = !m_bBeamFlickerOff;
+ }
+
+ if ( m_bLaserOn && !m_bBeamFlickerOff )
+ LaserOn();
+ else
+ LaserOff();
+}
+
+void C_NPC_Portal_FloorTurret::LaserOff( void )
+{
+ if( m_pBeam )
+ {
+ m_pBeam->AddEffects( EF_NODRAW );
+ }
+}
+
+void C_NPC_Portal_FloorTurret::LaserOn( void )
+{
+ if ( !IsBoneAccessAllowed() )
+ {
+ LaserOff();
+ return;
+ }
+
+ Vector vecMuzzle;
+ QAngle angMuzzleDir;
+ GetAttachment( FLOOR_TURRET_PORTAL_LASER_ATTACHMENT, vecMuzzle, angMuzzleDir );
+
+ Vector vecEye;
+ QAngle angEyeDir;
+ GetAttachment( FLOOR_TURRET_PORTAL_EYE_ATTACHMENT, vecEye, angEyeDir );
+
+ Vector vecMuzzleDir;
+ AngleVectors( angEyeDir, &vecMuzzleDir );
+
+ if (!m_pBeam)
+ {
+ m_pBeam = CBeam::BeamCreate( "effects/redlaser1.vmt", 0.2 );
+ m_pBeam->SetColor( 255, 32, 32 );
+ m_pBeam->SetBrightness( 255 );
+ m_pBeam->SetNoise( 0 );
+ m_pBeam->SetWidth( IsX360() ? ( 1.5f ) : ( 0.75f ) ); // On low end TVs these lasers are very hard to see at a distance
+ m_pBeam->SetEndWidth( 0 );
+ m_pBeam->SetScrollRate( 0 );
+ m_pBeam->SetFadeLength( 0 );
+ m_pBeam->SetHaloTexture( m_sLaserHaloSprite );
+ m_pBeam->SetHaloScale( 4.0f );
+ m_pBeam->SetCollisionGroup( COLLISION_GROUP_NONE );
+ m_pBeam->PointsInit( vecMuzzle + vecMuzzleDir, vecMuzzle );
+ m_pBeam->SetBeamFlag( FBEAM_REVERSED );
+ m_pBeam->SetStartEntity( this );
+ }
+ else
+ {
+ m_pBeam->RemoveEffects( EF_NODRAW );
+ }
+
+ // Trace to find an endpoint
+ Vector vEndPoint;
+ float fEndFraction;
+ Ray_t rayPath;
+ rayPath.Init( vecMuzzle, vecMuzzle + vecMuzzleDir * FLOOR_TURRET_PORTAL_LASER_RANGE );
+
+ CTraceFilterSkipClassname traceFilter( this, "prop_energy_ball", COLLISION_GROUP_NONE );
+
+ if ( UTIL_Portal_TraceRay_Beam( rayPath, MASK_SHOT, &traceFilter, &fEndFraction ) )
+ vEndPoint = vecMuzzle + vecMuzzleDir * FLOOR_TURRET_PORTAL_LASER_RANGE; // Trace went through portal and endpoint is unknown
+ else
+ vEndPoint = vecMuzzle + vecMuzzleDir * FLOOR_TURRET_PORTAL_LASER_RANGE * fEndFraction; // Trace hit a wall
+
+ // The beam is backwards, sort of. The endpoint is the sniper. This is
+ // so that the beam can be tapered to very thin where it emits from the turret.
+ m_pBeam->PointsInit( vEndPoint, vecMuzzle );
+
+ m_pBeam->SetHaloScale( LaserEndPointSize() );
+}
+
+float C_NPC_Portal_FloorTurret::LaserEndPointSize( void )
+{
+ return ( ( MAX( 0.0f, sinf( gpGlobals->curtime * M_PI + m_fPulseOffset ) ) ) * FLOOR_TURRET_PORTAL_END_POINT_PULSE_SCALE + 3.0f ) * ( IsX360() ? ( 3.0f ) : ( 1.5f ) );
+} \ No newline at end of file
diff --git a/game/client/portal/c_npc_rocket_turret.cpp b/game/client/portal/c_npc_rocket_turret.cpp
new file mode 100644
index 0000000..6f4147c
--- /dev/null
+++ b/game/client/portal/c_npc_rocket_turret.cpp
@@ -0,0 +1,177 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_ai_basenpc.h"
+#include "beam_shared.h"
+#include "prop_portal_shared.h"
+
+
+#define ROCKET_TURRET_LASER_ATTACHMENT 2
+#define ROCKET_TURRET_LASER_RANGE 8192
+
+#define ROCKET_TURRET_END_POINT_PULSE_SCALE 5.0f
+
+
+class C_NPC_RocketTurret : public C_AI_BaseNPC
+{
+public:
+ DECLARE_CLASS( C_NPC_RocketTurret, C_AI_BaseNPC );
+ DECLARE_CLIENTCLASS();
+
+ C_NPC_RocketTurret( void );
+ virtual ~C_NPC_RocketTurret( void );
+
+ virtual void Spawn( void );
+ virtual void ClientThink( void );
+
+ virtual ITraceFilter* GetBeamTraceFilter( void );
+
+ void LaserOff( void );
+ void LaserOn( void );
+ float LaserEndPointSize( void );
+
+private:
+ CBeam *m_pBeam;
+
+ int m_iLaserState;
+
+ int m_nSiteHalo;
+ float m_fPulseOffset;
+ QAngle m_vecCurrentAngles;
+
+ CTraceFilterSkipTwoEntities m_filterBeams;
+
+};
+
+
+IMPLEMENT_CLIENTCLASS_DT( C_NPC_RocketTurret, DT_NPC_RocketTurret, CNPC_RocketTurret )
+
+ RecvPropInt( RECVINFO( m_iLaserState ) ),
+ RecvPropInt( RECVINFO( m_nSiteHalo ) ),
+ RecvPropVector( RECVINFO( m_vecCurrentAngles ) ),
+
+END_RECV_TABLE()
+
+
+C_NPC_RocketTurret::C_NPC_RocketTurret( void )
+ : m_filterBeams( NULL, NULL, COLLISION_GROUP_DEBRIS )
+{
+ m_filterBeams.SetPassEntity( this );
+ m_filterBeams.SetPassEntity2( UTIL_PlayerByIndex( 1 ) );
+}
+
+C_NPC_RocketTurret::~C_NPC_RocketTurret( void )
+{
+ LaserOff();
+ if( m_pBeam )
+ m_pBeam->Remove();
+}
+
+
+void C_NPC_RocketTurret::Spawn( void )
+{
+ SetThink( &C_NPC_RocketTurret::ClientThink );
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ m_pBeam = NULL;
+ m_fPulseOffset = RandomFloat( 0.0f, 2.0f * M_PI );
+
+ BaseClass::Spawn();
+}
+
+void C_NPC_RocketTurret::ClientThink( void )
+{
+ if ( m_iLaserState == 0 )
+ LaserOff();
+ else
+ LaserOn();
+}
+
+ITraceFilter* C_NPC_RocketTurret::GetBeamTraceFilter( void )
+{
+ return &m_filterBeams;
+}
+
+void C_NPC_RocketTurret::LaserOff( void )
+{
+ if( m_pBeam )
+ m_pBeam->AddEffects( EF_NODRAW );
+}
+
+void C_NPC_RocketTurret::LaserOn( void )
+{
+ if ( !IsBoneAccessAllowed() )
+ {
+ LaserOff();
+ return;
+ }
+
+ Vector vecMuzzle;
+ QAngle angMuzzleDir;
+ GetAttachment( ROCKET_TURRET_LASER_ATTACHMENT, vecMuzzle, angMuzzleDir );
+
+ QAngle angAimDir = m_vecCurrentAngles;
+ Vector vecAimDir;
+ AngleVectors ( angAimDir, &vecAimDir );
+
+ if (!m_pBeam)
+ {
+ m_pBeam = CBeam::BeamCreate( "effects/bluelaser1.vmt", 0.1 );
+ m_pBeam->SetHaloTexture( m_nSiteHalo );
+ m_pBeam->SetColor( 100, 100, 255 );
+ m_pBeam->SetBrightness( 100 );
+ m_pBeam->SetNoise( 0 );
+ m_pBeam->SetWidth( 1 );
+ m_pBeam->SetEndWidth( 0 );
+ m_pBeam->SetScrollRate( 0 );
+ m_pBeam->SetFadeLength( 0 );
+ m_pBeam->SetHaloScale( 16.0f );
+ m_pBeam->SetCollisionGroup( COLLISION_GROUP_NONE );
+ m_pBeam->SetBeamFlag( FBEAM_REVERSED );
+ m_pBeam->PointsInit( vecMuzzle + vecAimDir, vecMuzzle );
+ m_pBeam->SetStartEntity( this );
+ }
+ else
+ {
+ m_pBeam->RemoveEffects( EF_NODRAW );
+ }
+
+ if ( m_iLaserState == 2 )
+ {
+ // Beam is freaking out
+ float fSize = RandomFloat( 0.5f, 5.0f );
+ int iRate = RandomInt( 4, 20 );
+ m_pBeam->SetWidth( fSize );
+ m_pBeam->SetScrollRate( iRate );
+ }
+ else
+ {
+ m_pBeam->SetWidth( 1 );
+ m_pBeam->SetScrollRate( 0 );
+ }
+
+ // Trace to find an endpoint (so the beam draws through portals)
+ Vector vEndPoint;
+ float fEndFraction;
+ Ray_t rayPath;
+ rayPath.Init( vecMuzzle, vecMuzzle + vecAimDir * ROCKET_TURRET_LASER_RANGE );
+
+ if ( UTIL_Portal_TraceRay_Beam( rayPath, MASK_SHOT, &m_filterBeams, &fEndFraction ) )
+ vEndPoint = vecMuzzle + vecAimDir * ROCKET_TURRET_LASER_RANGE; // Trace went through portal and endpoint is unknown
+ else
+ vEndPoint = vecMuzzle + vecAimDir * ROCKET_TURRET_LASER_RANGE * fEndFraction; // Trace hit a wall
+
+ m_pBeam->PointsInit( vEndPoint, vecMuzzle );
+
+ m_pBeam->SetHaloScale( LaserEndPointSize() );
+}
+
+float C_NPC_RocketTurret::LaserEndPointSize( void )
+{
+ return ( MAX( 0.0f, sinf( gpGlobals->curtime * M_PI + m_fPulseOffset ) ) ) * ROCKET_TURRET_END_POINT_PULSE_SCALE + 1.0f;
+} \ No newline at end of file
diff --git a/game/client/portal/c_portal_player.cpp b/game/client/portal/c_portal_player.cpp
new file mode 100644
index 0000000..59ce85b
--- /dev/null
+++ b/game/client/portal/c_portal_player.cpp
@@ -0,0 +1,1653 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Player for .
+//
+//===========================================================================//
+
+#include "cbase.h"
+#include "vcollide_parse.h"
+#include "c_portal_player.h"
+#include "view.h"
+#include "c_basetempentity.h"
+#include "takedamageinfo.h"
+#include "in_buttons.h"
+#include "iviewrender_beams.h"
+#include "r_efx.h"
+#include "dlight.h"
+#include "PortalRender.h"
+#include "toolframework/itoolframework.h"
+#include "toolframework_client.h"
+#include "tier1/KeyValues.h"
+#include "ScreenSpaceEffects.h"
+#include "portal_shareddefs.h"
+#include "ivieweffects.h" // for screenshake
+#include "prop_portal_shared.h"
+
+// NVNT for fov updates
+#include "haptics/ihaptics.h"
+
+
+// Don't alias here
+#if defined( CPortal_Player )
+#undef CPortal_Player
+#endif
+
+
+#define REORIENTATION_RATE 120.0f
+#define REORIENTATION_ACCELERATION_RATE 400.0f
+
+#define ENABLE_PORTAL_EYE_INTERPOLATION_CODE
+
+
+#define DEATH_CC_LOOKUP_FILENAME "materials/correction/cc_death.raw"
+#define DEATH_CC_FADE_SPEED 0.05f
+
+
+ConVar cl_reorient_in_air("cl_reorient_in_air", "1", FCVAR_ARCHIVE, "Allows the player to only reorient from being upside down while in the air." );
+
+
+// -------------------------------------------------------------------------------- //
+// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
+// -------------------------------------------------------------------------------- //
+
+class C_TEPlayerAnimEvent : public C_BaseTempEntity
+{
+public:
+ DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
+ DECLARE_CLIENTCLASS();
+
+ virtual void PostDataUpdate( DataUpdateType_t updateType )
+ {
+ // Create the effect.
+ C_Portal_Player *pPlayer = dynamic_cast< C_Portal_Player* >( m_hPlayer.Get() );
+ if ( pPlayer && !pPlayer->IsDormant() )
+ {
+ pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData );
+ }
+ }
+
+public:
+ CNetworkHandle( CBasePlayer, m_hPlayer );
+ CNetworkVar( int, m_iEvent );
+ CNetworkVar( int, m_nData );
+};
+
+IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
+
+BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
+RecvPropEHandle( RECVINFO( m_hPlayer ) ),
+RecvPropInt( RECVINFO( m_iEvent ) ),
+RecvPropInt( RECVINFO( m_nData ) )
+END_RECV_TABLE()
+
+
+//=================================================================================
+//
+// Ragdoll Entity
+//
+class C_PortalRagdoll : public C_BaseFlex
+{
+public:
+
+ DECLARE_CLASS( C_PortalRagdoll, C_BaseFlex );
+ DECLARE_CLIENTCLASS();
+
+ C_PortalRagdoll();
+ ~C_PortalRagdoll();
+
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ int GetPlayerEntIndex() const;
+ IRagdoll* GetIRagdoll() const;
+
+ virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
+
+private:
+
+ C_PortalRagdoll( const C_PortalRagdoll & ) {}
+
+ void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity );
+ void CreatePortalRagdoll();
+
+private:
+
+ EHANDLE m_hPlayer;
+ CNetworkVector( m_vecRagdollVelocity );
+ CNetworkVector( m_vecRagdollOrigin );
+
+};
+
+IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_PortalRagdoll, DT_PortalRagdoll, CPortalRagdoll )
+RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
+RecvPropEHandle( RECVINFO( m_hPlayer ) ),
+RecvPropInt( RECVINFO( m_nModelIndex ) ),
+RecvPropInt( RECVINFO(m_nForceBone) ),
+RecvPropVector( RECVINFO(m_vecForce) ),
+RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ),
+END_RECV_TABLE()
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+C_PortalRagdoll::C_PortalRagdoll()
+{
+ m_hPlayer = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+C_PortalRagdoll::~C_PortalRagdoll()
+{
+ ( this );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pSourceEntity -
+//-----------------------------------------------------------------------------
+void C_PortalRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity )
+{
+ if ( !pSourceEntity )
+ return;
+
+ VarMapping_t *pSrc = pSourceEntity->GetVarMapping();
+ VarMapping_t *pDest = GetVarMapping();
+
+ // Find all the VarMapEntry_t's that represent the same variable.
+ for ( int i = 0; i < pDest->m_Entries.Count(); i++ )
+ {
+ VarMapEntry_t *pDestEntry = &pDest->m_Entries[i];
+ for ( int j=0; j < pSrc->m_Entries.Count(); j++ )
+ {
+ VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j];
+ if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), pDestEntry->watcher->GetDebugName() ) )
+ {
+ pDestEntry->watcher->Copy( pSrcEntry->watcher );
+ break;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup vertex weights for drawing
+//-----------------------------------------------------------------------------
+void C_PortalRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
+{
+ // While we're dying, we want to mimic the facial animation of the player. Once they're dead, we just stay as we are.
+ if ( (m_hPlayer && m_hPlayer->IsAlive()) || !m_hPlayer )
+ {
+ BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
+ }
+ else if ( m_hPlayer )
+ {
+ m_hPlayer->SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+//-----------------------------------------------------------------------------
+void C_PortalRagdoll::CreatePortalRagdoll()
+{
+ // First, initialize all our data. If we have the player's entity on our client,
+ // then we can make ourselves start out exactly where the player is.
+ C_Portal_Player *pPlayer = dynamic_cast<C_Portal_Player*>( m_hPlayer.Get() );
+
+ if ( pPlayer && !pPlayer->IsDormant() )
+ {
+ // Move my current model instance to the ragdoll's so decals are preserved.
+ pPlayer->SnatchModelInstance( this );
+
+ VarMapping_t *varMap = GetVarMapping();
+
+ // This is the local player, so set them in a default
+ // pose and slam their velocity, angles and origin
+ SetAbsOrigin( /* m_vecRagdollOrigin : */ pPlayer->GetRenderOrigin() );
+ SetAbsAngles( pPlayer->GetRenderAngles() );
+ SetAbsVelocity( m_vecRagdollVelocity );
+
+ // Hack! Find a neutral standing pose or use the idle.
+ int iSeq = LookupSequence( "ragdoll" );
+ if ( iSeq == -1 )
+ {
+ Assert( false );
+ iSeq = 0;
+ }
+ SetSequence( iSeq );
+ SetCycle( 0.0 );
+
+ Interp_Reset( varMap );
+
+ m_nBody = pPlayer->GetBody();
+ SetModelIndex( m_nModelIndex );
+ // Make us a ragdoll..
+ m_nRenderFX = kRenderFxRagdoll;
+
+ matrix3x4_t boneDelta0[MAXSTUDIOBONES];
+ matrix3x4_t boneDelta1[MAXSTUDIOBONES];
+ matrix3x4_t currentBones[MAXSTUDIOBONES];
+ const float boneDt = 0.05f;
+
+ pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
+
+ InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : IRagdoll*
+//-----------------------------------------------------------------------------
+IRagdoll* C_PortalRagdoll::GetIRagdoll() const
+{
+ return m_pRagdoll;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : type -
+//-----------------------------------------------------------------------------
+void C_PortalRagdoll::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ CreatePortalRagdoll();
+ }
+}
+
+
+LINK_ENTITY_TO_CLASS( player, C_Portal_Player );
+
+IMPLEMENT_CLIENTCLASS_DT(C_Portal_Player, DT_Portal_Player, CPortal_Player)
+RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
+RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
+RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
+RecvPropInt( RECVINFO( m_iSpawnInterpCounter ) ),
+RecvPropInt( RECVINFO( m_iPlayerSoundType ) ),
+RecvPropBool( RECVINFO( m_bHeldObjectOnOppositeSideOfPortal ) ),
+RecvPropEHandle( RECVINFO( m_pHeldObjectPortal ) ),
+RecvPropBool( RECVINFO( m_bPitchReorientation ) ),
+RecvPropEHandle( RECVINFO( m_hPortalEnvironment ) ),
+RecvPropEHandle( RECVINFO( m_hSurroundingLiquidPortal ) ),
+RecvPropBool( RECVINFO( m_bSuppressingCrosshair ) ),
+END_RECV_TABLE()
+
+
+BEGIN_PREDICTION_DATA( C_Portal_Player )
+END_PREDICTION_DATA()
+
+#define _WALK_SPEED 150
+#define _NORM_SPEED 190
+#define _SPRINT_SPEED 320
+
+static ConVar cl_playermodel( "cl_playermodel", "none", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_SERVER_CAN_EXECUTE, "Default Player Model");
+
+extern bool g_bUpsideDown;
+
+//EHANDLE g_eKillTarget1;
+//EHANDLE g_eKillTarget2;
+
+void SpawnBlood (Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage);
+
+C_Portal_Player::C_Portal_Player()
+: m_iv_angEyeAngles( "C_Portal_Player::m_iv_angEyeAngles" )
+{
+ m_PlayerAnimState = CreatePortalPlayerAnimState( this );
+
+ m_iIDEntIndex = 0;
+ m_iSpawnInterpCounterCache = 0;
+ m_flDeathCCWeight = 0.0f;
+
+ m_hRagdoll.Set( NULL );
+ m_flStartLookTime = 0.0f;
+
+ m_bHeldObjectOnOppositeSideOfPortal = false;
+ m_pHeldObjectPortal = 0;
+
+ m_bPitchReorientation = false;
+ m_fReorientationRate = 0.0f;
+
+ m_angEyeAngles.Init();
+
+ AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
+
+ m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK;
+ m_blinkTimer.Invalidate();
+
+ m_CCDeathHandle = INVALID_CLIENT_CCHANDLE;
+}
+
+C_Portal_Player::~C_Portal_Player( void )
+{
+ if ( m_PlayerAnimState )
+ {
+ m_PlayerAnimState->Release();
+ }
+
+ g_pColorCorrectionMgr->RemoveColorCorrection( m_CCDeathHandle );
+}
+
+int C_Portal_Player::GetIDTarget() const
+{
+ return m_iIDEntIndex;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update this client's target entity
+//-----------------------------------------------------------------------------
+void C_Portal_Player::UpdateIDTarget()
+{
+ if ( !IsLocalPlayer() )
+ return;
+
+ // Clear old target and find a new one
+ m_iIDEntIndex = 0;
+
+ // don't show IDs in chase spec mode
+ if ( GetObserverMode() == OBS_MODE_CHASE ||
+ GetObserverMode() == OBS_MODE_DEATHCAM )
+ return;
+
+ trace_t tr;
+ Vector vecStart, vecEnd;
+ VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd );
+ VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
+ {
+ C_BaseEntity *pEntity = tr.m_pEnt;
+
+ if ( pEntity && (pEntity != this) )
+ {
+ m_iIDEntIndex = pEntity->entindex();
+ }
+ }
+}
+
+void C_Portal_Player::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
+{
+ Vector vecOrigin = ptr->endpos - vecDir * 4;
+
+ float flDistance = 0.0f;
+
+ if ( info.GetAttacker() )
+ {
+ flDistance = (ptr->endpos - info.GetAttacker()->GetAbsOrigin()).Length();
+ }
+
+ if ( m_takedamage )
+ {
+ AddMultiDamage( info, this );
+
+ int blood = BloodColor();
+
+ if ( blood != DONT_BLEED )
+ {
+ SpawnBlood( vecOrigin, vecDir, blood, flDistance );// a little surface blood.
+ TraceBleed( flDistance, vecDir, ptr, info.GetDamageType() );
+ }
+ }
+}
+
+void C_Portal_Player::Initialize( void )
+{
+ m_headYawPoseParam = LookupPoseParameter( "head_yaw" );
+ GetPoseParameterRange( m_headYawPoseParam, m_headYawMin, m_headYawMax );
+
+ m_headPitchPoseParam = LookupPoseParameter( "head_pitch" );
+ GetPoseParameterRange( m_headPitchPoseParam, m_headPitchMin, m_headPitchMax );
+
+ CStudioHdr *hdr = GetModelPtr();
+ for ( int i = 0; i < hdr->GetNumPoseParameters() ; i++ )
+ {
+ SetPoseParameter( hdr, i, 0.0 );
+ }
+}
+
+CStudioHdr *C_Portal_Player::OnNewModel( void )
+{
+ CStudioHdr *hdr = BaseClass::OnNewModel();
+
+ Initialize( );
+
+ return hdr;
+}
+
+//-----------------------------------------------------------------------------
+/**
+* Orient head and eyes towards m_lookAt.
+*/
+void C_Portal_Player::UpdateLookAt( void )
+{
+ // head yaw
+ if (m_headYawPoseParam < 0 || m_headPitchPoseParam < 0)
+ return;
+
+ // This is buggy with dt 0, just skip since there is no work to do.
+ if ( gpGlobals->frametime <= 0.0f )
+ return;
+
+ // Player looks at themselves through portals. Pick the portal we're turned towards.
+ const int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
+ CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
+ float *fPortalDot = (float *)stackalloc( sizeof( float ) * iPortalCount );
+ float flLowDot = 1.0f;
+ int iUsePortal = -1;
+
+ // defaults if no portals are around
+ Vector vPlayerForward;
+ GetVectors( &vPlayerForward, NULL, NULL );
+ Vector vCurLookTarget = EyePosition();
+
+ if ( !IsAlive() )
+ {
+ m_viewtarget = EyePosition() + vPlayerForward*10.0f;
+ return;
+ }
+
+ bool bNewTarget = false;
+ if ( UTIL_IntersectEntityExtentsWithPortal( this ) != NULL )
+ {
+ // player is in a portal
+ vCurLookTarget = EyePosition() + vPlayerForward*10.0f;
+ }
+ else if ( pPortals && pPortals[0] )
+ {
+ // Test through any active portals: This may be a shorter distance to the target
+ for( int i = 0; i != iPortalCount; ++i )
+ {
+ CProp_Portal *pTempPortal = pPortals[i];
+
+ if( pTempPortal && pTempPortal->m_bActivated && pTempPortal->m_hLinkedPortal.Get() )
+ {
+ Vector vEyeForward, vPortalForward;
+ EyeVectors( &vEyeForward );
+ pTempPortal->GetVectors( &vPortalForward, NULL, NULL );
+ fPortalDot[i] = vEyeForward.Dot( vPortalForward );
+ if ( fPortalDot[i] < flLowDot )
+ {
+ flLowDot = fPortalDot[i];
+ iUsePortal = i;
+ }
+ }
+ }
+
+ if ( iUsePortal >= 0 )
+ {
+ C_Prop_Portal* pPortal = pPortals[iUsePortal];
+ if ( pPortal )
+ {
+ vCurLookTarget = pPortal->MatrixThisToLinked()*vCurLookTarget;
+ if ( vCurLookTarget != m_vLookAtTarget )
+ {
+ bNewTarget = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ // No other look targets, look straight ahead
+ vCurLookTarget += vPlayerForward*10.0f;
+ }
+
+ // Figure out where we want to look in world space.
+ QAngle desiredAngles;
+ Vector to = vCurLookTarget - EyePosition();
+ VectorAngles( to, desiredAngles );
+ QAngle aheadAngles;
+ VectorAngles( vCurLookTarget, aheadAngles );
+
+ // Figure out where our body is facing in world space.
+ QAngle bodyAngles( 0, 0, 0 );
+ bodyAngles[YAW] = GetLocalAngles()[YAW];
+
+ m_flLastBodyYaw = bodyAngles[YAW];
+
+ // Set the head's yaw.
+ float desiredYaw = AngleNormalize( desiredAngles[YAW] - bodyAngles[YAW] );
+ desiredYaw = clamp( desiredYaw, m_headYawMin, m_headYawMax );
+
+ float desiredPitch = AngleNormalize( desiredAngles[PITCH] );
+ desiredPitch = clamp( desiredPitch, m_headPitchMin, m_headPitchMax );
+
+ if ( bNewTarget )
+ {
+ m_flStartLookTime = gpGlobals->curtime;
+ }
+
+ float dt = (gpGlobals->frametime);
+ float flSpeed = 1.0f - ExponentialDecay( 0.7f, 0.033f, dt );
+
+ m_flCurrentHeadYaw = m_flCurrentHeadYaw + flSpeed * ( desiredYaw - m_flCurrentHeadYaw );
+ m_flCurrentHeadYaw = AngleNormalize( m_flCurrentHeadYaw );
+ SetPoseParameter( m_headYawPoseParam, m_flCurrentHeadYaw );
+
+ m_flCurrentHeadPitch = m_flCurrentHeadPitch + flSpeed * ( desiredPitch - m_flCurrentHeadPitch );
+ m_flCurrentHeadPitch = AngleNormalize( m_flCurrentHeadPitch );
+ SetPoseParameter( m_headPitchPoseParam, m_flCurrentHeadPitch );
+
+ // This orients the eyes
+ m_viewtarget = m_vLookAtTarget = vCurLookTarget;
+}
+
+void C_Portal_Player::ClientThink( void )
+{
+ //PortalEyeInterpolation.m_bNeedToUpdateEyePosition = true;
+
+ Vector vForward;
+ AngleVectors( GetLocalAngles(), &vForward );
+
+ // Allow sprinting
+ HandleSpeedChanges();
+
+ FixTeleportationRoll();
+
+ //QAngle vAbsAngles = EyeAngles();
+
+ // Look at the thing that killed you
+ //if ( !IsAlive() )
+ //{
+ // C_BaseEntity *pEntity1 = g_eKillTarget1.Get();
+ // C_BaseEntity *pEntity2 = g_eKillTarget2.Get();
+
+ // if ( pEntity2 && pEntity1 )
+ // {
+ // //engine->GetViewAngles( vAbsAngles );
+
+ // Vector vLook = pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin();
+ // VectorNormalize( vLook );
+
+ // QAngle qLook;
+ // VectorAngles( vLook, qLook );
+
+ // if ( qLook[PITCH] > 180.0f )
+ // {
+ // qLook[PITCH] -= 360.0f;
+ // }
+
+ // if ( vAbsAngles[YAW] < 0.0f )
+ // {
+ // vAbsAngles[YAW] += 360.0f;
+ // }
+
+ // if ( vAbsAngles[PITCH] < qLook[PITCH] )
+ // {
+ // vAbsAngles[PITCH] += gpGlobals->frametime * 120.0f;
+ // if ( vAbsAngles[PITCH] > qLook[PITCH] )
+ // vAbsAngles[PITCH] = qLook[PITCH];
+ // }
+ // else if ( vAbsAngles[PITCH] > qLook[PITCH] )
+ // {
+ // vAbsAngles[PITCH] -= gpGlobals->frametime * 120.0f;
+ // if ( vAbsAngles[PITCH] < qLook[PITCH] )
+ // vAbsAngles[PITCH] = qLook[PITCH];
+ // }
+
+ // if ( vAbsAngles[YAW] < qLook[YAW] )
+ // {
+ // vAbsAngles[YAW] += gpGlobals->frametime * 240.0f;
+ // if ( vAbsAngles[YAW] > qLook[YAW] )
+ // vAbsAngles[YAW] = qLook[YAW];
+ // }
+ // else if ( vAbsAngles[YAW] > qLook[YAW] )
+ // {
+ // vAbsAngles[YAW] -= gpGlobals->frametime * 240.0f;
+ // if ( vAbsAngles[YAW] < qLook[YAW] )
+ // vAbsAngles[YAW] = qLook[YAW];
+ // }
+
+ // if ( vAbsAngles[YAW] > 180.0f )
+ // {
+ // vAbsAngles[YAW] -= 360.0f;
+ // }
+
+ // engine->SetViewAngles( vAbsAngles );
+ // }
+ //}
+
+ // If dead, fade in death CC lookup
+ if ( m_CCDeathHandle != INVALID_CLIENT_CCHANDLE )
+ {
+ if ( m_lifeState != LIFE_ALIVE )
+ {
+ if ( m_flDeathCCWeight < 1.0f )
+ {
+ m_flDeathCCWeight += DEATH_CC_FADE_SPEED;
+ clamp( m_flDeathCCWeight, 0.0f, 1.0f );
+ }
+ }
+ else
+ {
+ m_flDeathCCWeight = 0.0f;
+ }
+ g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCDeathHandle, m_flDeathCCWeight );
+ }
+
+ UpdateIDTarget();
+}
+
+void C_Portal_Player::FixTeleportationRoll( void )
+{
+ if( IsInAVehicle() ) //HL2 compatibility fix. do absolutely nothing to the view in vehicles
+ return;
+
+ if( !IsLocalPlayer() )
+ return;
+
+ // Normalize roll from odd portal transitions
+ QAngle vAbsAngles = EyeAngles();
+
+
+ Vector vCurrentForward, vCurrentRight, vCurrentUp;
+ AngleVectors( vAbsAngles, &vCurrentForward, &vCurrentRight, &vCurrentUp );
+
+ if ( vAbsAngles[ROLL] == 0.0f )
+ {
+ m_fReorientationRate = 0.0f;
+ g_bUpsideDown = ( vCurrentUp.z < 0.0f );
+ return;
+ }
+
+ bool bForcePitchReorient = ( vAbsAngles[ROLL] > 175.0f && vCurrentForward.z > 0.99f );
+ bool bOnGround = ( GetGroundEntity() != NULL );
+
+ if ( bForcePitchReorient )
+ {
+ m_fReorientationRate = REORIENTATION_RATE * ( ( bOnGround ) ? ( 2.0f ) : ( 1.0f ) );
+ }
+ else
+ {
+ // Don't reorient in air if they don't want to
+ if ( !cl_reorient_in_air.GetBool() && !bOnGround )
+ {
+ g_bUpsideDown = ( vCurrentUp.z < 0.0f );
+ return;
+ }
+ }
+
+ if ( vCurrentUp.z < 0.75f )
+ {
+ m_fReorientationRate += gpGlobals->frametime * REORIENTATION_ACCELERATION_RATE;
+
+ // Upright faster if on the ground
+ float fMaxReorientationRate = REORIENTATION_RATE * ( ( bOnGround ) ? ( 2.0f ) : ( 1.0f ) );
+ if ( m_fReorientationRate > fMaxReorientationRate )
+ m_fReorientationRate = fMaxReorientationRate;
+ }
+ else
+ {
+ if ( m_fReorientationRate > REORIENTATION_RATE * 0.5f )
+ {
+ m_fReorientationRate -= gpGlobals->frametime * REORIENTATION_ACCELERATION_RATE;
+ if ( m_fReorientationRate < REORIENTATION_RATE * 0.5f )
+ m_fReorientationRate = REORIENTATION_RATE * 0.5f;
+ }
+ else if ( m_fReorientationRate < REORIENTATION_RATE * 0.5f )
+ {
+ m_fReorientationRate += gpGlobals->frametime * REORIENTATION_ACCELERATION_RATE;
+ if ( m_fReorientationRate > REORIENTATION_RATE * 0.5f )
+ m_fReorientationRate = REORIENTATION_RATE * 0.5f;
+ }
+ }
+
+ if ( !m_bPitchReorientation && !bForcePitchReorient )
+ {
+ // Randomize which way we roll if we're completely upside down
+ if ( vAbsAngles[ROLL] == 180.0f && RandomInt( 0, 1 ) == 1 )
+ {
+ vAbsAngles[ROLL] = -180.0f;
+ }
+
+ if ( vAbsAngles[ROLL] < 0.0f )
+ {
+ vAbsAngles[ROLL] += gpGlobals->frametime * m_fReorientationRate;
+ if ( vAbsAngles[ROLL] > 0.0f )
+ vAbsAngles[ROLL] = 0.0f;
+ engine->SetViewAngles( vAbsAngles );
+ }
+ else if ( vAbsAngles[ROLL] > 0.0f )
+ {
+ vAbsAngles[ROLL] -= gpGlobals->frametime * m_fReorientationRate;
+ if ( vAbsAngles[ROLL] < 0.0f )
+ vAbsAngles[ROLL] = 0.0f;
+ engine->SetViewAngles( vAbsAngles );
+ m_angEyeAngles = vAbsAngles;
+ m_iv_angEyeAngles.Reset();
+ }
+ }
+ else
+ {
+ if ( vAbsAngles[ROLL] != 0.0f )
+ {
+ if ( vCurrentUp.z < 0.2f )
+ {
+ float fDegrees = gpGlobals->frametime * m_fReorientationRate;
+ if ( vCurrentForward.z > 0.0f )
+ {
+ fDegrees = -fDegrees;
+ }
+
+ // Rotate around the right axis
+ VMatrix mAxisAngleRot = SetupMatrixAxisRot( vCurrentRight, fDegrees );
+
+ vCurrentUp = mAxisAngleRot.VMul3x3( vCurrentUp );
+ vCurrentForward = mAxisAngleRot.VMul3x3( vCurrentForward );
+
+ VectorAngles( vCurrentForward, vCurrentUp, vAbsAngles );
+
+ engine->SetViewAngles( vAbsAngles );
+ m_angEyeAngles = vAbsAngles;
+ m_iv_angEyeAngles.Reset();
+ }
+ else
+ {
+ if ( vAbsAngles[ROLL] < 0.0f )
+ {
+ vAbsAngles[ROLL] += gpGlobals->frametime * m_fReorientationRate;
+ if ( vAbsAngles[ROLL] > 0.0f )
+ vAbsAngles[ROLL] = 0.0f;
+ engine->SetViewAngles( vAbsAngles );
+ m_angEyeAngles = vAbsAngles;
+ m_iv_angEyeAngles.Reset();
+ }
+ else if ( vAbsAngles[ROLL] > 0.0f )
+ {
+ vAbsAngles[ROLL] -= gpGlobals->frametime * m_fReorientationRate;
+ if ( vAbsAngles[ROLL] < 0.0f )
+ vAbsAngles[ROLL] = 0.0f;
+ engine->SetViewAngles( vAbsAngles );
+ m_angEyeAngles = vAbsAngles;
+ m_iv_angEyeAngles.Reset();
+ }
+ }
+ }
+ }
+
+ // Keep track of if we're upside down for look control
+ vAbsAngles = EyeAngles();
+ AngleVectors( vAbsAngles, NULL, NULL, &vCurrentUp );
+
+ if ( bForcePitchReorient )
+ g_bUpsideDown = ( vCurrentUp.z < 0.0f );
+ else
+ g_bUpsideDown = false;
+}
+
+const QAngle& C_Portal_Player::GetRenderAngles()
+{
+ if ( IsRagdoll() )
+ {
+ return vec3_angle;
+ }
+ else
+ {
+ return m_PlayerAnimState->GetRenderAngles();
+ }
+}
+
+void C_Portal_Player::UpdateClientSideAnimation( void )
+{
+ UpdateLookAt();
+
+ // Update the animation data. It does the local check here so this works when using
+ // a third-person camera (and we don't have valid player angles).
+ if ( this == C_Portal_Player::GetLocalPortalPlayer() )
+ m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
+ else
+ m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
+
+ BaseClass::UpdateClientSideAnimation();
+}
+
+void C_Portal_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ m_PlayerAnimState->DoAnimationEvent( event, nData );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int C_Portal_Player::DrawModel( int flags )
+{
+ if ( !m_bReadyToDraw )
+ return 0;
+
+ if( IsLocalPlayer() )
+ {
+ if ( !C_BasePlayer::ShouldDrawThisPlayer() )
+ {
+ if ( !g_pPortalRender->IsRenderingPortal() )
+ return 0;
+
+ if( (g_pPortalRender->GetViewRecursionLevel() == 1) && (m_iForceNoDrawInPortalSurface != -1) ) //CPortalRender::s_iRenderingPortalView )
+ return 0;
+ }
+ }
+
+ return BaseClass::DrawModel(flags);
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Should this object receive shadows?
+//-----------------------------------------------------------------------------
+bool C_Portal_Player::ShouldReceiveProjectedTextures( int flags )
+{
+ Assert( flags & SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK );
+
+ if ( IsEffectActive( EF_NODRAW ) )
+ return false;
+
+ if( flags & SHADOW_FLAGS_FLASHLIGHT )
+ {
+ return true;
+ }
+
+ return BaseClass::ShouldReceiveProjectedTextures( flags );
+}
+
+void C_Portal_Player::DoImpactEffect( trace_t &tr, int nDamageType )
+{
+ if ( GetActiveWeapon() )
+ {
+ GetActiveWeapon()->DoImpactEffect( tr, nDamageType );
+ return;
+ }
+
+ BaseClass::DoImpactEffect( tr, nDamageType );
+}
+
+void C_Portal_Player::PreThink( void )
+{
+ QAngle vTempAngles = GetLocalAngles();
+
+ if ( IsLocalPlayer() )
+ {
+ vTempAngles[PITCH] = EyeAngles()[PITCH];
+ }
+ else
+ {
+ vTempAngles[PITCH] = m_angEyeAngles[PITCH];
+ }
+
+ if ( vTempAngles[YAW] < 0.0f )
+ {
+ vTempAngles[YAW] += 360.0f;
+ }
+
+ SetLocalAngles( vTempAngles );
+
+ BaseClass::PreThink();
+
+ HandleSpeedChanges();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_Portal_Player::AddEntity( void )
+{
+ BaseClass::AddEntity();
+
+ QAngle vTempAngles = GetLocalAngles();
+ vTempAngles[PITCH] = m_angEyeAngles[PITCH];
+
+ SetLocalAngles( vTempAngles );
+
+ // Zero out model pitch, blending takes care of all of it.
+ SetLocalAnglesDim( X_INDEX, 0 );
+
+ if( this != C_BasePlayer::GetLocalPlayer() )
+ {
+ if ( IsEffectActive( EF_DIMLIGHT ) )
+ {
+ int iAttachment = LookupAttachment( "anim_attachment_RH" );
+
+ if ( iAttachment < 0 )
+ return;
+
+ Vector vecOrigin;
+ QAngle eyeAngles = m_angEyeAngles;
+
+ GetAttachment( iAttachment, vecOrigin, eyeAngles );
+
+ Vector vForward;
+ AngleVectors( eyeAngles, &vForward );
+
+ trace_t tr;
+ UTIL_TraceLine( vecOrigin, vecOrigin + (vForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+ }
+ }
+}
+
+ShadowType_t C_Portal_Player::ShadowCastType( void )
+{
+ // Drawing player shadows looks bad in first person when they get close to walls
+ // It doesn't make sense to have shadows in the portal view, but not in the main view
+ // So no shadows for the player
+ return SHADOWS_NONE;
+}
+
+bool C_Portal_Player::ShouldDraw( void )
+{
+ if ( !IsAlive() )
+ return false;
+
+ //return true;
+
+ // if( GetTeamNumber() == TEAM_SPECTATOR )
+ // return false;
+
+ if( IsLocalPlayer() && IsRagdoll() )
+ return true;
+
+ if ( IsRagdoll() )
+ return false;
+
+ return true;
+
+ return BaseClass::ShouldDraw();
+}
+
+const QAngle& C_Portal_Player::EyeAngles()
+{
+ if ( IsLocalPlayer() && g_nKillCamMode == OBS_MODE_NONE )
+ {
+ return BaseClass::EyeAngles();
+ }
+ else
+ {
+ //C_BaseEntity *pEntity1 = g_eKillTarget1.Get();
+ //C_BaseEntity *pEntity2 = g_eKillTarget2.Get();
+
+ //Vector vLook = Vector( 0.0f, 0.0f, 0.0f );
+
+ //if ( pEntity2 )
+ //{
+ // vLook = pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin();
+ // VectorNormalize( vLook );
+ //}
+ //else if ( pEntity1 )
+ //{
+ // return BaseClass::EyeAngles();
+ // //vLook = - pEntity1->GetAbsOrigin();
+ //}
+
+ //if ( vLook != Vector( 0.0f, 0.0f, 0.0f ) )
+ //{
+ // VectorAngles( vLook, m_angEyeAngles );
+ //}
+
+ return m_angEyeAngles;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : -
+// Output : IRagdoll*
+//-----------------------------------------------------------------------------
+IRagdoll* C_Portal_Player::GetRepresentativeRagdoll() const
+{
+ if ( m_hRagdoll.Get() )
+ {
+ C_PortalRagdoll *pRagdoll = static_cast<C_PortalRagdoll*>( m_hRagdoll.Get() );
+ if ( !pRagdoll )
+ return NULL;
+
+ return pRagdoll->GetIRagdoll();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+void C_Portal_Player::PlayerPortalled( C_Prop_Portal *pEnteredPortal )
+{
+ if( pEnteredPortal )
+ {
+ m_bPortalledMessagePending = true;
+ m_PendingPortalMatrix = pEnteredPortal->MatrixThisToLinked();
+
+ if( IsLocalPlayer() )
+ g_pPortalRender->EnteredPortal( pEnteredPortal );
+ }
+}
+
+void C_Portal_Player::OnPreDataChanged( DataUpdateType_t type )
+{
+ Assert( m_pPortalEnvironment_LastCalcView == m_hPortalEnvironment.Get() );
+ PreDataChanged_Backup.m_hPortalEnvironment = m_hPortalEnvironment;
+ PreDataChanged_Backup.m_hSurroundingLiquidPortal = m_hSurroundingLiquidPortal;
+ PreDataChanged_Backup.m_qEyeAngles = m_iv_angEyeAngles.GetCurrent();
+
+ BaseClass::OnPreDataChanged( type );
+}
+
+void C_Portal_Player::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if( m_hSurroundingLiquidPortal != PreDataChanged_Backup.m_hSurroundingLiquidPortal )
+ {
+ CLiquidPortal_InnerLiquidEffect *pLiquidEffect = (CLiquidPortal_InnerLiquidEffect *)g_pScreenSpaceEffects->GetScreenSpaceEffect( "LiquidPortal_InnerLiquid" );
+ if( pLiquidEffect )
+ {
+ C_Func_LiquidPortal *pSurroundingPortal = m_hSurroundingLiquidPortal.Get();
+ if( pSurroundingPortal != NULL )
+ {
+ C_Func_LiquidPortal *pOldSurroundingPortal = PreDataChanged_Backup.m_hSurroundingLiquidPortal.Get();
+ if( pOldSurroundingPortal != pSurroundingPortal->m_hLinkedPortal.Get() )
+ {
+ pLiquidEffect->m_pImmersionPortal = pSurroundingPortal;
+ pLiquidEffect->m_bFadeBackToReality = false;
+ }
+ else
+ {
+ pLiquidEffect->m_bFadeBackToReality = true;
+ pLiquidEffect->m_fFadeBackTimeLeft = pLiquidEffect->s_fFadeBackEffectTime;
+ }
+ }
+ else
+ {
+ pLiquidEffect->m_pImmersionPortal = NULL;
+ pLiquidEffect->m_bFadeBackToReality = false;
+ }
+ }
+ }
+
+ DetectAndHandlePortalTeleportation();
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ // Load color correction lookup for the death effect
+ m_CCDeathHandle = g_pColorCorrectionMgr->AddColorCorrection( DEATH_CC_LOOKUP_FILENAME );
+ if ( m_CCDeathHandle != INVALID_CLIENT_CCHANDLE )
+ {
+ g_pColorCorrectionMgr->SetColorCorrectionWeight( m_CCDeathHandle, 0.0f );
+ }
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+
+ UpdateVisibility();
+}
+
+//CalcView() gets called between OnPreDataChanged() and OnDataChanged(), and these changes need to be known about in both before CalcView() gets called, and if CalcView() doesn't get called
+bool C_Portal_Player::DetectAndHandlePortalTeleportation( void )
+{
+ if( m_bPortalledMessagePending )
+ {
+ m_bPortalledMessagePending = false;
+
+ //C_Prop_Portal *pOldPortal = PreDataChanged_Backup.m_hPortalEnvironment.Get();
+ //Assert( pOldPortal );
+ //if( pOldPortal )
+ {
+ Vector ptNewPosition = GetNetworkOrigin();
+
+ UTIL_Portal_PointTransform( m_PendingPortalMatrix, PortalEyeInterpolation.m_vEyePosition_Interpolated, PortalEyeInterpolation.m_vEyePosition_Interpolated );
+ UTIL_Portal_PointTransform( m_PendingPortalMatrix, PortalEyeInterpolation.m_vEyePosition_Uninterpolated, PortalEyeInterpolation.m_vEyePosition_Uninterpolated );
+
+ PortalEyeInterpolation.m_bEyePositionIsInterpolating = true;
+
+ UTIL_Portal_AngleTransform( m_PendingPortalMatrix, m_qEyeAngles_LastCalcView, m_angEyeAngles );
+ m_angEyeAngles.x = AngleNormalize( m_angEyeAngles.x );
+ m_angEyeAngles.y = AngleNormalize( m_angEyeAngles.y );
+ m_angEyeAngles.z = AngleNormalize( m_angEyeAngles.z );
+ m_iv_angEyeAngles.Reset(); //copies from m_angEyeAngles
+
+ if( engine->IsPlayingDemo() )
+ {
+ pl.v_angle = m_angEyeAngles;
+ engine->SetViewAngles( pl.v_angle );
+ }
+
+ engine->ResetDemoInterpolation();
+ if( IsLocalPlayer() )
+ {
+ //DevMsg( "FPT: %.2f %.2f %.2f\n", m_angEyeAngles.x, m_angEyeAngles.y, m_angEyeAngles.z );
+ SetLocalAngles( m_angEyeAngles );
+ }
+
+ m_PlayerAnimState->Teleport ( &ptNewPosition, &GetNetworkAngles(), this );
+
+ // Reorient last facing direction to fix pops in view model lag
+ for ( int i = 0; i < MAX_VIEWMODELS; i++ )
+ {
+ CBaseViewModel *vm = GetViewModel( i );
+ if ( !vm )
+ continue;
+
+ UTIL_Portal_VectorTransform( m_PendingPortalMatrix, vm->m_vecLastFacing, vm->m_vecLastFacing );
+ }
+ }
+ m_bPortalledMessagePending = false;
+ }
+
+ return false;
+}
+
+/*bool C_Portal_Player::ShouldInterpolate( void )
+{
+if( !IsInterpolationEnabled() )
+return false;
+
+return BaseClass::ShouldInterpolate();
+}*/
+
+
+void C_Portal_Player::PostDataUpdate( DataUpdateType_t updateType )
+{
+ // C_BaseEntity assumes we're networking the entity's angles, so pretend that it
+ // networked the same value we already have.
+ SetNetworkAngles( GetLocalAngles() );
+
+ if ( m_iSpawnInterpCounter != m_iSpawnInterpCounterCache )
+ {
+ MoveToLastReceivedPosition( true );
+ ResetLatched();
+ m_iSpawnInterpCounterCache = m_iSpawnInterpCounter;
+ }
+
+ BaseClass::PostDataUpdate( updateType );
+}
+
+float C_Portal_Player::GetFOV( void )
+{
+ //Find our FOV with offset zoom value
+ float flFOVOffset = C_BasePlayer::GetFOV() + GetZoom();
+
+ // Clamp FOV in MP
+ int min_fov = GetMinFOV();
+
+ // Don't let it go too low
+ flFOVOffset = MAX( min_fov, flFOVOffset );
+
+ return flFOVOffset;
+}
+
+//=========================================================
+// Autoaim
+// set crosshair position to point to enemey
+//=========================================================
+Vector C_Portal_Player::GetAutoaimVector( float flDelta )
+{
+ // Never autoaim a predicted weapon (for now)
+ Vector forward;
+ AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle, &forward );
+ return forward;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether or not we are allowed to sprint now.
+//-----------------------------------------------------------------------------
+bool C_Portal_Player::CanSprint( void )
+{
+ return ( (!m_Local.m_bDucked && !m_Local.m_bDucking) && (GetWaterLevel() != 3) );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void C_Portal_Player::StartSprinting( void )
+{
+ //if( m_HL2Local.m_flSuitPower < 10 )
+ //{
+ // // Don't sprint unless there's a reasonable
+ // // amount of suit power.
+ // CPASAttenuationFilter filter( this );
+ // filter.UsePredictionRules();
+ // EmitSound( filter, entindex(), "Player.SprintNoPower" );
+ // return;
+ //}
+
+ CPASAttenuationFilter filter( this );
+ filter.UsePredictionRules();
+ EmitSound( filter, entindex(), "Player.SprintStart" );
+
+ SetMaxSpeed( _SPRINT_SPEED );
+ m_fIsSprinting = true;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void C_Portal_Player::StopSprinting( void )
+{
+ SetMaxSpeed( _NORM_SPEED );
+ m_fIsSprinting = false;
+}
+
+void C_Portal_Player::HandleSpeedChanges( void )
+{
+ int buttonsChanged = m_afButtonPressed | m_afButtonReleased;
+
+ if( buttonsChanged & IN_SPEED )
+ {
+ if ( !(m_afButtonPressed & IN_SPEED) && IsSprinting() )
+ {
+ StopSprinting();
+ }
+ else if ( (m_afButtonPressed & IN_SPEED) && !IsSprinting() )
+ {
+ if ( CanSprint() )
+ {
+ StartSprinting();
+ }
+ else
+ {
+ // Reset key, so it will be activated post whatever is suppressing it.
+ m_nButtons &= ~IN_SPEED;
+ }
+ }
+ }
+}
+
+void C_Portal_Player::ItemPreFrame( void )
+{
+ if ( GetFlags() & FL_FROZEN )
+ return;
+
+ // Disallow shooting while zooming
+ if ( m_nButtons & IN_ZOOM )
+ {
+ //FIXME: Held weapons like the grenade get sad when this happens
+ m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
+ }
+
+ BaseClass::ItemPreFrame();
+
+}
+
+void C_Portal_Player::ItemPostFrame( void )
+{
+ if ( GetFlags() & FL_FROZEN )
+ return;
+
+ BaseClass::ItemPostFrame();
+}
+
+C_BaseAnimating *C_Portal_Player::BecomeRagdollOnClient()
+{
+ // Let the C_CSRagdoll entity do this.
+ // m_builtRagdoll = true;
+ return NULL;
+}
+
+void C_Portal_Player::UpdatePortalEyeInterpolation( void )
+{
+#ifdef ENABLE_PORTAL_EYE_INTERPOLATION_CODE
+ //PortalEyeInterpolation.m_bEyePositionIsInterpolating = false;
+ if( PortalEyeInterpolation.m_bUpdatePosition_FreeMove )
+ {
+ PortalEyeInterpolation.m_bUpdatePosition_FreeMove = false;
+
+ C_Prop_Portal *pOldPortal = PreDataChanged_Backup.m_hPortalEnvironment.Get();
+ if( pOldPortal )
+ {
+ UTIL_Portal_PointTransform( pOldPortal->MatrixThisToLinked(), PortalEyeInterpolation.m_vEyePosition_Interpolated, PortalEyeInterpolation.m_vEyePosition_Interpolated );
+ //PortalEyeInterpolation.m_vEyePosition_Interpolated = pOldPortal->m_matrixThisToLinked * PortalEyeInterpolation.m_vEyePosition_Interpolated;
+
+ //Vector vForward;
+ //m_hPortalEnvironment.Get()->GetVectors( &vForward, NULL, NULL );
+
+ PortalEyeInterpolation.m_vEyePosition_Interpolated = EyeFootPosition();
+
+ PortalEyeInterpolation.m_bEyePositionIsInterpolating = true;
+ }
+ }
+
+ if( IsInAVehicle() )
+ PortalEyeInterpolation.m_bEyePositionIsInterpolating = false;
+
+ if( !PortalEyeInterpolation.m_bEyePositionIsInterpolating )
+ {
+ PortalEyeInterpolation.m_vEyePosition_Uninterpolated = EyeFootPosition();
+ PortalEyeInterpolation.m_vEyePosition_Interpolated = PortalEyeInterpolation.m_vEyePosition_Uninterpolated;
+ return;
+ }
+
+ Vector vThisFrameUninterpolatedPosition = EyeFootPosition();
+
+ //find offset between this and last frame's uninterpolated movement, and apply this as freebie movement to the interpolated position
+ PortalEyeInterpolation.m_vEyePosition_Interpolated += (vThisFrameUninterpolatedPosition - PortalEyeInterpolation.m_vEyePosition_Uninterpolated);
+ PortalEyeInterpolation.m_vEyePosition_Uninterpolated = vThisFrameUninterpolatedPosition;
+
+ Vector vDiff = vThisFrameUninterpolatedPosition - PortalEyeInterpolation.m_vEyePosition_Interpolated;
+ float fLength = vDiff.Length();
+ float fFollowSpeed = gpGlobals->frametime * 100.0f;
+ const float fMaxDiff = 150.0f;
+ if( fLength > fMaxDiff )
+ {
+ //camera lagging too far behind, give it a speed boost to bring it within maximum range
+ fFollowSpeed = fLength - fMaxDiff;
+ }
+ else if( fLength < fFollowSpeed )
+ {
+ //final move
+ PortalEyeInterpolation.m_bEyePositionIsInterpolating = false;
+ PortalEyeInterpolation.m_vEyePosition_Interpolated = vThisFrameUninterpolatedPosition;
+ return;
+ }
+
+ if ( fLength > 0.001f )
+ {
+ vDiff *= (fFollowSpeed/fLength);
+ PortalEyeInterpolation.m_vEyePosition_Interpolated += vDiff;
+ }
+ else
+ {
+ PortalEyeInterpolation.m_vEyePosition_Interpolated = vThisFrameUninterpolatedPosition;
+ }
+
+
+
+#else
+ PortalEyeInterpolation.m_vEyePosition_Interpolated = BaseClass::EyePosition();
+#endif
+}
+
+Vector C_Portal_Player::EyePosition()
+{
+ return PortalEyeInterpolation.m_vEyePosition_Interpolated;
+}
+
+Vector C_Portal_Player::EyeFootPosition( const QAngle &qEyeAngles )
+{
+#if 0
+ static int iPrintCounter = 0;
+ ++iPrintCounter;
+ if( iPrintCounter == 50 )
+ {
+ QAngle vAbsAngles = qEyeAngles;
+ DevMsg( "Eye Angles: %f %f %f\n", vAbsAngles.x, vAbsAngles.y, vAbsAngles.z );
+ iPrintCounter = 0;
+ }
+#endif
+
+ //interpolate between feet and normal eye position based on view roll (gets us wall/ceiling & ceiling/ceiling teleportations without an eye position pop)
+ float fFootInterp = fabs(qEyeAngles[ROLL]) * ((1.0f/180.0f) * 0.75f); //0 when facing straight up, 0.75 when facing straight down
+ return (BaseClass::EyePosition() - (fFootInterp * m_vecViewOffset)); //TODO: Find a good Up vector for this rolled player and interpolate along actual eye/foot axis
+}
+
+void C_Portal_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov )
+{
+ DetectAndHandlePortalTeleportation();
+ //if( DetectAndHandlePortalTeleportation() )
+ // DevMsg( "Teleported within OnDataChanged\n" );
+
+ m_iForceNoDrawInPortalSurface = -1;
+ bool bEyeTransform_Backup = m_bEyePositionIsTransformedByPortal;
+ m_bEyePositionIsTransformedByPortal = false; //assume it's not transformed until it provably is
+ UpdatePortalEyeInterpolation();
+
+ QAngle qEyeAngleBackup = EyeAngles();
+ Vector ptEyePositionBackup = EyePosition();
+ C_Prop_Portal *pPortalBackup = m_hPortalEnvironment.Get();
+
+ if ( m_lifeState != LIFE_ALIVE )
+ {
+ if ( g_nKillCamMode != 0 )
+ {
+ return;
+ }
+
+ Vector origin = EyePosition();
+
+ C_BaseEntity* pRagdoll = m_hRagdoll.Get();
+
+ if ( pRagdoll )
+ {
+ origin = pRagdoll->GetAbsOrigin();
+#if !PORTAL_HIDE_PLAYER_RAGDOLL
+ origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through
+#endif //PORTAL_HIDE_PLAYER_RAGDOLL
+ }
+
+ BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
+
+ eyeOrigin = origin;
+
+ Vector vForward;
+ AngleVectors( eyeAngles, &vForward );
+
+ VectorNormalize( vForward );
+#if !PORTAL_HIDE_PLAYER_RAGDOLL
+ VectorMA( origin, -CHASE_CAM_DISTANCE_MAX, vForward, eyeOrigin );
+#endif //PORTAL_HIDE_PLAYER_RAGDOLL
+
+ Vector WALL_MIN( -WALL_OFFSET, -WALL_OFFSET, -WALL_OFFSET );
+ Vector WALL_MAX( WALL_OFFSET, WALL_OFFSET, WALL_OFFSET );
+
+ trace_t trace; // clip against world
+ C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
+ UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trace );
+ C_BaseEntity::PopEnableAbsRecomputations();
+
+ if (trace.fraction < 1.0)
+ {
+ eyeOrigin = trace.endpos;
+ }
+ }
+ else
+ {
+ IClientVehicle *pVehicle;
+ pVehicle = GetVehicle();
+
+ if ( !pVehicle )
+ {
+ if ( IsObserver() )
+ {
+ CalcObserverView( eyeOrigin, eyeAngles, fov );
+ }
+ else
+ {
+ CalcPlayerView( eyeOrigin, eyeAngles, fov );
+ if( m_hPortalEnvironment.Get() != NULL )
+ {
+ //time for hax
+ m_bEyePositionIsTransformedByPortal = bEyeTransform_Backup;
+ CalcPortalView( eyeOrigin, eyeAngles );
+ }
+ }
+ }
+ else
+ {
+ CalcVehicleView( pVehicle, eyeOrigin, eyeAngles, zNear, zFar, fov );
+ }
+ }
+
+ m_qEyeAngles_LastCalcView = qEyeAngleBackup;
+ m_ptEyePosition_LastCalcView = ptEyePositionBackup;
+ m_pPortalEnvironment_LastCalcView = pPortalBackup;
+
+#ifdef WIN32
+ // NVNT Inform haptics module of fov
+ if(IsLocalPlayer())
+ haptics->UpdatePlayerFOV(fov);
+#endif
+}
+
+void C_Portal_Player::SetLocalViewAngles( const QAngle &viewAngles )
+{
+ // Nothing
+ if ( engine->IsPlayingDemo() )
+ return;
+ BaseClass::SetLocalViewAngles( viewAngles );
+}
+
+void C_Portal_Player::SetViewAngles( const QAngle& ang )
+{
+ BaseClass::SetViewAngles( ang );
+
+ if ( engine->IsPlayingDemo() )
+ {
+ pl.v_angle = ang;
+ }
+}
+
+void C_Portal_Player::CalcPortalView( Vector &eyeOrigin, QAngle &eyeAngles )
+{
+ //although we already ran CalcPlayerView which already did these copies, they also fudge these numbers in ways we don't like, so recopy
+ VectorCopy( EyePosition(), eyeOrigin );
+ VectorCopy( EyeAngles(), eyeAngles );
+
+ //Re-apply the screenshake (we just stomped it)
+ vieweffects->ApplyShake( eyeOrigin, eyeAngles, 1.0 );
+
+ C_Prop_Portal *pPortal = m_hPortalEnvironment.Get();
+ assert( pPortal );
+
+ C_Prop_Portal *pRemotePortal = pPortal->m_hLinkedPortal;
+ if( !pRemotePortal )
+ {
+ return; //no hacks possible/necessary
+ }
+
+ Vector ptPortalCenter;
+ Vector vPortalForward;
+
+ ptPortalCenter = pPortal->GetNetworkOrigin();
+ pPortal->GetVectors( &vPortalForward, NULL, NULL );
+ float fPortalPlaneDist = vPortalForward.Dot( ptPortalCenter );
+
+ bool bOverrideSpecialEffects = false; //sometimes to get the best effect we need to kill other effects that are simply for cleanliness
+
+ float fEyeDist = vPortalForward.Dot( eyeOrigin ) - fPortalPlaneDist;
+ bool bTransformEye = false;
+ if( fEyeDist < 0.0f ) //eye behind portal
+ {
+ if( pPortal->m_PortalSimulator.EntityIsInPortalHole( this ) ) //player standing in portal
+ {
+ bTransformEye = true;
+ }
+ else if( vPortalForward.z < -0.01f ) //there's a weird case where the player is ducking below a ceiling portal. As they unduck their eye moves beyond the portal before the code detects that they're in the portal hole.
+ {
+ Vector ptPlayerOrigin = GetAbsOrigin();
+ float fOriginDist = vPortalForward.Dot( ptPlayerOrigin ) - fPortalPlaneDist;
+
+ if( fOriginDist > 0.0f )
+ {
+ float fInvTotalDist = 1.0f / (fOriginDist - fEyeDist); //fEyeDist is negative
+ Vector ptPlaneIntersection = (eyeOrigin * fOriginDist * fInvTotalDist) - (ptPlayerOrigin * fEyeDist * fInvTotalDist);
+ Assert( fabs( vPortalForward.Dot( ptPlaneIntersection ) - fPortalPlaneDist ) < 0.01f );
+
+ Vector vIntersectionTest = ptPlaneIntersection - ptPortalCenter;
+
+ Vector vPortalRight, vPortalUp;
+ pPortal->GetVectors( NULL, &vPortalRight, &vPortalUp );
+
+ if( (vIntersectionTest.Dot( vPortalRight ) <= PORTAL_HALF_WIDTH) &&
+ (vIntersectionTest.Dot( vPortalUp ) <= PORTAL_HALF_HEIGHT) )
+ {
+ bTransformEye = true;
+ }
+ }
+ }
+ }
+
+ if( bTransformEye )
+ {
+ m_bEyePositionIsTransformedByPortal = true;
+
+ //DevMsg( 2, "transforming portal view from <%f %f %f> <%f %f %f>\n", eyeOrigin.x, eyeOrigin.y, eyeOrigin.z, eyeAngles.x, eyeAngles.y, eyeAngles.z );
+
+ VMatrix matThisToLinked = pPortal->MatrixThisToLinked();
+ UTIL_Portal_PointTransform( matThisToLinked, eyeOrigin, eyeOrigin );
+ UTIL_Portal_AngleTransform( matThisToLinked, eyeAngles, eyeAngles );
+
+ //DevMsg( 2, "transforming portal view to <%f %f %f> <%f %f %f>\n", eyeOrigin.x, eyeOrigin.y, eyeOrigin.z, eyeAngles.x, eyeAngles.y, eyeAngles.z );
+
+ if ( IsToolRecording() )
+ {
+ static EntityTeleportedRecordingState_t state;
+
+ KeyValues *msg = new KeyValues( "entity_teleported" );
+ msg->SetPtr( "state", &state );
+ state.m_bTeleported = false;
+ state.m_bViewOverride = true;
+ state.m_vecTo = eyeOrigin;
+ state.m_qaTo = eyeAngles;
+ MatrixInvert( matThisToLinked.As3x4(), state.m_teleportMatrix );
+
+ // Post a message back to all IToolSystems
+ Assert( (int)GetToolHandle() != 0 );
+ ToolFramework_PostToolMessage( GetToolHandle(), msg );
+
+ msg->deleteThis();
+ }
+
+ bOverrideSpecialEffects = true;
+ }
+ else
+ {
+ m_bEyePositionIsTransformedByPortal = false;
+ }
+
+ if( bOverrideSpecialEffects )
+ {
+ m_iForceNoDrawInPortalSurface = ((pRemotePortal->m_bIsPortal2)?(2):(1));
+ pRemotePortal->m_fStaticAmount = 0.0f;
+ }
+}
+
+extern float g_fMaxViewModelLag;
+void C_Portal_Player::CalcViewModelView( const Vector& eyeOrigin, const QAngle& eyeAngles)
+{
+ // HACK: Manually adjusting the eye position that view model looking up and down are similar
+ // (solves view model "pop" on floor to floor transitions)
+ Vector vInterpEyeOrigin = eyeOrigin;
+
+ Vector vForward;
+ Vector vRight;
+ Vector vUp;
+ AngleVectors( eyeAngles, &vForward, &vRight, &vUp );
+
+ if ( vForward.z < 0.0f )
+ {
+ float fT = vForward.z * vForward.z;
+ vInterpEyeOrigin += vRight * ( fT * 4.7f ) + vForward * ( fT * 5.0f ) + vUp * ( fT * 4.0f );
+ }
+
+ if ( UTIL_IntersectEntityExtentsWithPortal( this ) )
+ g_fMaxViewModelLag = 0.0f;
+ else
+ g_fMaxViewModelLag = 1.5f;
+
+ for ( int i = 0; i < MAX_VIEWMODELS; i++ )
+ {
+ CBaseViewModel *vm = GetViewModel( i );
+ if ( !vm )
+ continue;
+
+ vm->CalcViewModelView( this, vInterpEyeOrigin, eyeAngles );
+ }
+}
+
+bool LocalPlayerIsCloseToPortal( void )
+{
+ return C_Portal_Player::GetLocalPlayer()->IsCloseToPortal();
+}
+
diff --git a/game/client/portal/c_portal_player.h b/game/client/portal/c_portal_player.h
new file mode 100644
index 0000000..52819ac
--- /dev/null
+++ b/game/client/portal/c_portal_player.h
@@ -0,0 +1,222 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#ifndef PORTAL_PLAYER_H
+#define PORTAL_PLAYER_H
+#pragma once
+
+#include "portal_playeranimstate.h"
+#include "c_basehlplayer.h"
+#include "portal_player_shared.h"
+#include "c_prop_portal.h"
+#include "weapon_portalbase.h"
+#include "c_func_liquidportal.h"
+#include "colorcorrectionmgr.h"
+
+//=============================================================================
+// >> Portal_Player
+//=============================================================================
+class C_Portal_Player : public C_BaseHLPlayer
+{
+public:
+ DECLARE_CLASS( C_Portal_Player, C_BaseHLPlayer );
+
+ DECLARE_CLIENTCLASS();
+ DECLARE_PREDICTABLE();
+ DECLARE_INTERPOLATION();
+
+
+ C_Portal_Player();
+ ~C_Portal_Player( void );
+
+ void ClientThink( void );
+ void FixTeleportationRoll( void );
+
+ static inline C_Portal_Player* GetLocalPortalPlayer()
+ {
+ return (C_Portal_Player*)C_BasePlayer::GetLocalPlayer();
+ }
+
+ static inline C_Portal_Player* GetLocalPlayer()
+ {
+ return (C_Portal_Player*)C_BasePlayer::GetLocalPlayer();
+ }
+
+ virtual const QAngle& GetRenderAngles();
+
+ virtual void UpdateClientSideAnimation();
+ void DoAnimationEvent( PlayerAnimEvent_t event, int nData );
+
+ virtual int DrawModel( int flags );
+ virtual void AddEntity( void );
+
+ QAngle GetAnimEyeAngles( void ) { return m_angEyeAngles; }
+ Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL );
+
+ // Used by prediction, sets the view angles for the player
+ virtual void SetLocalViewAngles( const QAngle &viewAngles );
+ virtual void SetViewAngles( const QAngle &ang );
+
+ // Should this object cast shadows?
+ virtual ShadowType_t ShadowCastType( void );
+ virtual C_BaseAnimating* BecomeRagdollOnClient();
+ virtual bool ShouldDraw( void );
+ virtual const QAngle& EyeAngles();
+ virtual void OnPreDataChanged( DataUpdateType_t type );
+ virtual void OnDataChanged( DataUpdateType_t type );
+ bool DetectAndHandlePortalTeleportation( void ); //detects if the player has portalled and fixes views
+ virtual float GetFOV( void );
+ virtual CStudioHdr* OnNewModel( void );
+ virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr );
+ virtual void ItemPreFrame( void );
+ virtual void ItemPostFrame( void );
+ virtual float GetMinFOV() const { return 5.0f; }
+ virtual Vector GetAutoaimVector( float flDelta );
+ virtual bool ShouldReceiveProjectedTextures( int flags );
+ virtual void PostDataUpdate( DataUpdateType_t updateType );
+ virtual void GetStepSoundVelocities( float *velwalk, float *velrun );
+ virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force );
+ virtual void PreThink( void );
+ virtual void DoImpactEffect( trace_t &tr, int nDamageType );
+
+ virtual Vector EyePosition();
+ Vector EyeFootPosition( const QAngle &qEyeAngles );//interpolates between eyes and feet based on view angle roll
+ inline Vector EyeFootPosition( void ) { return EyeFootPosition( EyeAngles() ); };
+ void PlayerPortalled( C_Prop_Portal *pEnteredPortal );
+
+ virtual void CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov );
+ void CalcPortalView( Vector &eyeOrigin, QAngle &eyeAngles );
+ virtual void CalcViewModelView( const Vector& eyeOrigin, const QAngle& eyeAngles);
+
+ CBaseEntity* FindUseEntity( void );
+ CBaseEntity* FindUseEntityThroughPortal( void );
+
+ inline bool IsCloseToPortal( void ) //it's usually a good idea to turn on draw hacks when this is true
+ {
+ return ((PortalEyeInterpolation.m_bEyePositionIsInterpolating) || (m_hPortalEnvironment.Get() != NULL));
+ }
+
+ bool CanSprint( void );
+ void StartSprinting( void );
+ void StopSprinting( void );
+ void HandleSpeedChanges( void );
+ void UpdateLookAt( void );
+ void Initialize( void );
+ int GetIDTarget() const;
+ void UpdateIDTarget( void );
+
+ void ToggleHeldObjectOnOppositeSideOfPortal( void ) { m_bHeldObjectOnOppositeSideOfPortal = !m_bHeldObjectOnOppositeSideOfPortal; }
+ void SetHeldObjectOnOppositeSideOfPortal( bool p_bHeldObjectOnOppositeSideOfPortal ) { m_bHeldObjectOnOppositeSideOfPortal = p_bHeldObjectOnOppositeSideOfPortal; }
+ bool IsHeldObjectOnOppositeSideOfPortal( void ) { return m_bHeldObjectOnOppositeSideOfPortal; }
+ CProp_Portal *GetHeldObjectPortal( void ) { return m_pHeldObjectPortal; }
+
+ Activity TranslateActivity( Activity baseAct, bool *pRequired = NULL );
+ CWeaponPortalBase* GetActivePortalWeapon() const;
+
+ bool IsSuppressingCrosshair( void ) { return m_bSuppressingCrosshair; }
+
+private:
+
+ C_Portal_Player( const C_Portal_Player & );
+
+ void UpdatePortalEyeInterpolation( void );
+
+ CPortalPlayerAnimState *m_PlayerAnimState;
+
+ QAngle m_angEyeAngles;
+ CInterpolatedVar< QAngle > m_iv_angEyeAngles;
+
+ virtual IRagdoll *GetRepresentativeRagdoll() const;
+ EHANDLE m_hRagdoll;
+
+ int m_headYawPoseParam;
+ int m_headPitchPoseParam;
+ float m_headYawMin;
+ float m_headYawMax;
+ float m_headPitchMin;
+ float m_headPitchMax;
+ bool m_bSuppressingCrosshair;
+
+ bool m_isInit;
+ Vector m_vLookAtTarget;
+
+ float m_flLastBodyYaw;
+ float m_flCurrentHeadYaw;
+ float m_flCurrentHeadPitch;
+ float m_flStartLookTime;
+
+ int m_iIDEntIndex;
+
+ CountdownTimer m_blinkTimer;
+
+ int m_iSpawnInterpCounter;
+ int m_iSpawnInterpCounterCache;
+
+ int m_iPlayerSoundType;
+
+ bool m_bHeldObjectOnOppositeSideOfPortal;
+ CProp_Portal *m_pHeldObjectPortal;
+
+ int m_iForceNoDrawInPortalSurface; //only valid for one frame, used to temp disable drawing of the player model in a surface because of freaky artifacts
+
+ struct PortalEyeInterpolation_t
+ {
+ bool m_bEyePositionIsInterpolating; //flagged when the eye position would have popped between two distinct positions and we're smoothing it over
+ Vector m_vEyePosition_Interpolated; //we'll be giving the interpolation a certain amount of instant movement per frame based on how much an uninterpolated eye would have moved
+ Vector m_vEyePosition_Uninterpolated; //can't have smooth movement without tracking where we just were
+ //bool m_bNeedToUpdateEyePosition;
+ //int m_iFrameLastUpdated;
+
+ int m_iTickLastUpdated;
+ float m_fTickInterpolationAmountLastUpdated;
+ bool m_bDisableFreeMovement; //used for one frame usually when error in free movement is likely to be high
+ bool m_bUpdatePosition_FreeMove;
+
+ PortalEyeInterpolation_t( void ) : m_iTickLastUpdated(0), m_fTickInterpolationAmountLastUpdated(0.0f), m_bDisableFreeMovement(false), m_bUpdatePosition_FreeMove(false) { };
+ } PortalEyeInterpolation;
+
+ struct PreDataChanged_Backup_t
+ {
+ CHandle<C_Prop_Portal> m_hPortalEnvironment;
+ CHandle<C_Func_LiquidPortal> m_hSurroundingLiquidPortal;
+ //Vector m_ptPlayerPosition;
+ QAngle m_qEyeAngles;
+ } PreDataChanged_Backup;
+
+ Vector m_ptEyePosition_LastCalcView;
+ QAngle m_qEyeAngles_LastCalcView; //we've got some VERY persistent single frame errors while teleporting, this will be updated every frame in CalcView() and will serve as a central source for fixed angles
+ C_Prop_Portal *m_pPortalEnvironment_LastCalcView;
+
+ ClientCCHandle_t m_CCDeathHandle; // handle to death cc effect
+ float m_flDeathCCWeight; // for fading in cc effect
+
+ bool m_bPortalledMessagePending; //Player portalled. It's easier to wait until we get a OnDataChanged() event or a CalcView() before we do anything about it. Otherwise bits and pieces can get undone
+ VMatrix m_PendingPortalMatrix;
+
+public:
+ bool m_bPitchReorientation;
+ float m_fReorientationRate;
+ bool m_bEyePositionIsTransformedByPortal; //when the eye and body positions are not on the same side of a portal
+
+ CHandle<C_Prop_Portal> m_hPortalEnvironment; //a portal whose environment the player is currently in, should be invalid most of the time
+ CHandle<C_Func_LiquidPortal> m_hSurroundingLiquidPortal; //a liquid portal whose volume the player is standing in
+};
+
+inline C_Portal_Player *ToPortalPlayer( CBaseEntity *pEntity )
+{
+ if ( !pEntity || !pEntity->IsPlayer() )
+ return NULL;
+
+ return dynamic_cast<C_Portal_Player*>( pEntity );
+}
+
+inline C_Portal_Player *GetPortalPlayer( void )
+{
+ return static_cast<C_Portal_Player*>( C_BasePlayer::GetLocalPlayer() );
+}
+
+#endif //Portal_PLAYER_H
diff --git a/game/client/portal/c_portal_radio.cpp b/game/client/portal/c_portal_radio.cpp
new file mode 100644
index 0000000..c1c8a28
--- /dev/null
+++ b/game/client/portal/c_portal_radio.cpp
@@ -0,0 +1,256 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//
+//=====================================================================================//
+#include "cbase.h"
+#include "c_physicsprop.h"
+#include "portal_gamerules.h"
+#include "igameevents.h"
+#include "soundenvelope.h"
+#include "beamdraw.h"
+
+
+//#define RADIO_DEBUG_CLIENT
+
+
+class C_Dinosaur_Signal : public C_BaseEntity
+{
+public:
+ DECLARE_CLIENTCLASS();
+ DECLARE_CLASS( C_Dinosaur_Signal, C_BaseEntity );
+
+ char m_szSoundName[128];
+ float m_flInnerRadius;
+ float m_flOuterRadius;
+ int m_nSignalID;
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_Dinosaur_Signal, DT_DinosaurSignal, CDinosaurSignal )
+ RecvPropString( RECVINFO(m_szSoundName) ),
+ RecvPropFloat( RECVINFO(m_flOuterRadius) ),
+ RecvPropFloat( RECVINFO(m_flInnerRadius) ),
+ RecvPropInt( RECVINFO(m_nSignalID) ),
+END_RECV_TABLE()
+
+class C_Portal_Dinosaur : public C_PhysicsProp
+{
+public:
+ DECLARE_CLIENTCLASS();
+ DECLARE_CLASS( C_Portal_Dinosaur, C_PhysicsProp );
+
+ ~C_Portal_Dinosaur( void );
+
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void ClientThink();
+ virtual int DrawModel( int flags );
+
+ void ScanForSounds();
+ void SetupSounds();
+
+ CHandle<C_Dinosaur_Signal> m_hDinosaur_Signal;
+ float m_flOldBlend;
+ bool m_bAlreadyDiscovered;
+ bool m_bDinosaurExtinct;
+
+ // Sound envelopes
+ CSoundPatch *m_pNormalSound;
+ CSoundPatch *m_pStaticSound;
+ CSoundPatch *m_pSignalSound;
+};
+
+IMPLEMENT_CLIENTCLASS_DT( C_Portal_Dinosaur, DT_PropDinosaur, CPortal_Dinosaur )
+ RecvPropEHandle( RECVINFO( m_hDinosaur_Signal) ),
+ RecvPropBool( RECVINFO(m_bAlreadyDiscovered) ),
+END_RECV_TABLE()
+
+void C_Portal_Dinosaur::Spawn()
+{
+ Precache();
+
+ m_flOldBlend = 0.0f;
+ BaseClass::Spawn();
+ SetThink( &C_Portal_Dinosaur::ClientThink );
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+}
+
+C_Portal_Dinosaur::~C_Portal_Dinosaur( void )
+{
+ if ( m_pNormalSound != NULL )
+ {
+ CSoundEnvelopeController::GetController().Shutdown( m_pNormalSound );
+ }
+
+ if ( m_pStaticSound != NULL )
+ {
+ CSoundEnvelopeController::GetController().Shutdown( m_pStaticSound );
+ }
+
+ if ( m_pSignalSound != NULL )
+ {
+ CSoundEnvelopeController::GetController().Shutdown( m_pSignalSound );
+ }
+}
+
+void C_Portal_Dinosaur::Precache( void )
+{
+ PrecacheScriptSound( "Portal.room1_radio" );
+ PrecacheScriptSound( "UpdateItem.Static" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur01" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur02" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur03" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur04" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur05" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur06" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur07" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur08" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur09" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur10" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur11" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur12" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur13" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur14" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur15" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur16" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur17" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur18" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur19" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur20" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur21" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur22" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur23" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur24" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur25" );
+ PrecacheScriptSound( "UpdateItem.Dinosaur26" );
+}
+
+void C_Portal_Dinosaur::SetupSounds()
+{
+ CPASAttenuationFilter filter( this );
+
+ if ( m_pNormalSound == NULL )
+ {
+ m_pNormalSound = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), "Portal.room1_radio" );
+ CSoundEnvelopeController::GetController().Play( m_pNormalSound, 0.0, PITCH_NORM );
+ }
+
+ if ( m_pStaticSound == NULL )
+ {
+ m_pStaticSound = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), "UpdateItem.Static" );
+ CSoundEnvelopeController::GetController().Play( m_pStaticSound, 0.0, PITCH_NORM );
+ }
+
+ if ( m_pSignalSound == NULL && m_hDinosaur_Signal.Get() != NULL )
+ {
+ m_pSignalSound = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), m_hDinosaur_Signal->m_szSoundName );
+ //m_pSignalSound = CSoundEnvelopeController::GetController().SoundCreate( filter, entindex(), "UpdateItem.Signal" );
+ CSoundEnvelopeController::GetController().Play( m_pSignalSound, 0.0, PITCH_NORM );
+ }
+}
+
+void C_Portal_Dinosaur::ClientThink()
+{
+ SetupSounds();
+
+ //if ( V_stristr( engine->GetLevelName(), "testchmb_a_00" ) != 0 )
+ if ( 0 )//m_hDinosaur_Signal.Get() && m_hDinosaur_Signal.Get()->m_nSignalID == 0 )
+ {
+ // TODO:Play either morse code or special sstv content based on
+ // game account client state given from gc.
+ }
+ else
+ {
+ // On any other map, we scan for the partnered Dinosaur_Signal signal
+ ScanForSounds();
+ }
+
+ if ( m_bDinosaurExtinct == false && m_bDinosaurExtinct != m_bAlreadyDiscovered )
+ m_bDinosaurExtinct = m_bAlreadyDiscovered;
+}
+
+void C_Portal_Dinosaur::ScanForSounds()
+{
+ if ( m_hDinosaur_Signal.Get() == NULL )
+ {
+ AssertMsgOnce( 0, "Unpaired radio exists on this map." );
+ return;
+ }
+
+ // How much of the real signal to blend in
+ // 1.0 at > outerrad, 0.0 at < inner rad, blend when in between the two.
+ float flRadiusDelta = fabs( m_hDinosaur_Signal.Get()->m_flOuterRadius - m_hDinosaur_Signal.Get()->m_flInnerRadius );
+ float flDist = m_hDinosaur_Signal.Get()->GetAbsOrigin().DistTo( GetAbsOrigin() );
+
+ float flOuterBlend = RemapValClamped( flDist, m_hDinosaur_Signal.Get()->m_flOuterRadius, m_hDinosaur_Signal.Get()->m_flOuterRadius-(flRadiusDelta*0.5f), 1.0f, 0.0f );
+ float flInnerBlend = RemapValClamped( flDist, m_hDinosaur_Signal.Get()->m_flOuterRadius-(flRadiusDelta*0.5f), m_hDinosaur_Signal.Get()->m_flInnerRadius, 0.0f, 1.0f );
+ flInnerBlend = Bias( flInnerBlend, 0.1f );
+
+ if ( m_pNormalSound )
+ {
+ CSoundEnvelopeController::GetController().SoundChangeVolume( m_pNormalSound, flOuterBlend, 0.1f );
+ }
+
+ float flMidBlend = 0.0f;
+ if ( m_pStaticSound )
+ {
+ if ( flOuterBlend <= 0.0f )
+ {
+ flMidBlend = RemapValClamped( flDist, m_hDinosaur_Signal.Get()->m_flOuterRadius-(flRadiusDelta*0.5f), m_hDinosaur_Signal.Get()->m_flInnerRadius, 1.0f, 0.0f );
+ }
+ else
+ {
+ flMidBlend = RemapValClamped( flDist, m_hDinosaur_Signal.Get()->m_flOuterRadius, m_hDinosaur_Signal.Get()->m_flOuterRadius-(flRadiusDelta*0.5f), 0.0f, 1.0f );
+ flMidBlend = Bias( flMidBlend, 0.9f );
+ }
+ CSoundEnvelopeController::GetController().SoundChangeVolume( m_pStaticSound, flMidBlend, 0.1f );
+ }
+
+ if ( m_pSignalSound )
+ {
+ CSoundEnvelopeController::GetController().SoundChangeVolume( m_pSignalSound, flInnerBlend, 0.1f );
+ }
+
+#if defined ( RADIO_DEBUG_CLIENT )
+ // Msg( "Dinosaur(%d) listening for signal... dist: %f, blend amt: %f\n", entindex(), flDist, flMidBlend );
+#endif
+
+ // If we've fully heard this signal, mark the achievement
+ if ( flInnerBlend >= 1.0f && m_flOldBlend < 1.0f )
+ {
+ m_bDinosaurExtinct = true;
+ int id = m_hDinosaur_Signal.Get()->m_nSignalID;
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dinosaur_signal_found" );
+ if ( event )
+ {
+ event->SetInt( "id", id );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ m_flOldBlend = flInnerBlend;
+}
+
+int C_Portal_Dinosaur::DrawModel( int flags )
+{
+ int nRet = BaseClass::DrawModel( flags );
+
+ CMaterialReference hMaterial;
+ hMaterial.Init( "sprites/grav_light", TEXTURE_GROUP_CLIENT_EFFECTS );
+
+ // Draw the sprite
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( hMaterial, this );
+ color32 color;
+ color.r = ( m_bDinosaurExtinct ) ? 0 : 255;
+ color.g = ( m_bDinosaurExtinct ) ? 255 : 0;
+ color.b = 0;
+ color.a = 128;
+
+ Vector vForward, vRight, vUp;
+ GetVectors( &vForward, &vRight, &vUp );
+ Vector vOffset = GetAbsOrigin() + ( vForward * 4.0f ) + ( vUp * 1.85f );
+ DrawSprite( vOffset, 6.0f, 6.0f, color );
+
+ return nRet;
+}
diff --git a/game/client/portal/c_prop_energy_ball.cpp b/game/client/portal/c_prop_energy_ball.cpp
new file mode 100644
index 0000000..e4d5990
--- /dev/null
+++ b/game/client/portal/c_prop_energy_ball.cpp
@@ -0,0 +1,133 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// c_prop_energy_ball.cpp
+//
+// Purpose: Portal version of the combine ball. This client code is needed to provide a different
+// look when the energy ball has infinite life and to have modified client effects.
+//
+//=====================================================================================//
+
+
+#include "cbase.h" // precompiled headers
+#include "c_prop_combine_ball.h" // Our parent class
+#include "clienteffectprecachesystem.h" // To precache our new material
+
+ConVar cl_energy_ball_start_fade_time ( "cl_energy_ball_start_fade_time", "8", FCVAR_CHEAT );
+
+//-----------------------------------------------------------------------------
+// Purpose: Portal version of a combine ball
+//-----------------------------------------------------------------------------
+class C_PropEnergyBall : public C_PropCombineBall
+{
+public:
+ DECLARE_CLASS( C_PropEnergyBall, C_PropCombineBall );
+ DECLARE_CLIENTCLASS();
+ DECLARE_PREDICTABLE();
+
+ C_PropEnergyBall();
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+
+protected:
+ bool InitMaterials();
+
+ bool m_bIsInfiniteLife; // if this energy ball is an infinite life variety
+ float m_fTimeTillDeath; // If this is a finite life energy ball, the time remaining until detonation
+ float m_fCurAlpha; // The amount of alpha to apply at DrawModel, to simulate a decaying energy ball
+};
+
+LINK_ENTITY_TO_CLASS( prop_energy_ball, C_PropEnergyBall );
+
+// precache our different materials for the infinite life energy balls
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectEnergyBall )
+
+ CLIENTEFFECT_MATERIAL( "effects/eball_infinite_life" )
+ CLIENTEFFECT_MATERIAL( "effects/eball_finite_life" )
+
+CLIENTEFFECT_REGISTER_END()
+
+
+IMPLEMENT_CLIENTCLASS_DT( C_PropEnergyBall, DT_PropEnergyBall, CPropEnergyBall )
+
+ RecvPropBool( RECVINFO( m_bIsInfiniteLife ) ),
+ RecvPropFloat( RECVINFO( m_fTimeTillDeath ) ),
+
+END_RECV_TABLE()
+
+
+BEGIN_PREDICTION_DATA( C_PropEnergyBall )
+
+ DEFINE_PRED_FIELD( m_bIsInfiniteLife, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fTimeTillDeath, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_PropEnergyBall::C_PropEnergyBall(): m_bIsInfiniteLife(false), m_fTimeTillDeath(-1), m_fCurAlpha ( 1.0f )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Flag our data as new this frame
+// Input : DataUpdateType_t, either created or updated
+// Output : void
+//-----------------------------------------------------------------------------
+void C_PropEnergyBall::OnDataChanged(DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ // If our data changed this frame, then operate based on it next think
+ if ( updateType == DATA_UPDATE_DATATABLE_CHANGED )
+ {
+ float fStartFadeTime = cl_energy_ball_start_fade_time.GetFloat();
+
+ if ( fStartFadeTime < 1.0f )
+ {
+ fStartFadeTime = 1.0f;
+ }
+
+ // The last x seconds of life, fade
+ if ( (m_fTimeTillDeath > 0.0f) )
+ {
+ float fNewAlpha = m_fTimeTillDeath / fStartFadeTime;
+ clamp( fNewAlpha, 0.0f, 1.0f );
+ m_fCurAlpha = fNewAlpha;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Use our custom body materials for energy ball, but otherwise use the base class materials (base being C_PropCombineBall)
+// Output : bool
+//-----------------------------------------------------------------------------
+bool C_PropEnergyBall::InitMaterials()
+{
+ // Use the same materials as a combine ball
+ bool bRetVal = BaseClass::InitMaterials();
+
+ // If we're an infinite life combine ball, swap out the body material (and the base implementation didnt fail)
+ IMaterial* pBodyMat;
+ if ( m_bIsInfiniteLife )
+ {
+ pBodyMat = materials->FindMaterial( "effects/eball_infinite_life", NULL, false );
+ }
+ else
+ {
+ pBodyMat = materials->FindMaterial( "effects/eball_finite_life", NULL, false );
+ }
+
+ // If we can find our custom material, use it.
+ if ( pBodyMat == NULL )
+ {
+ bRetVal = false;
+ }
+ else
+ {
+ m_pBodyMaterial = pBodyMat;
+ m_pBodyMaterial->AlphaModulate( m_fCurAlpha );
+ }
+
+ return bRetVal;
+} \ No newline at end of file
diff --git a/game/client/portal/c_prop_portal.cpp b/game/client/portal/c_prop_portal.cpp
new file mode 100644
index 0000000..7ca3812
--- /dev/null
+++ b/game/client/portal/c_prop_portal.cpp
@@ -0,0 +1,952 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "cbase.h"
+#include "c_prop_portal.h"
+#include "portal_shareddefs.h"
+#include "clientsideeffects.h"
+#include "tier0/vprof.h"
+#include "materialsystem/itexture.h"
+#include "hud_macros.h"
+#include "igamesystem.h"
+#include "view.h" // For MainViewOrigin()
+#include "clientleafsystem.h" // For finding the leaves our portals are in
+#include "portal_render_targets.h" // Access to static references to Portal-specific render textures
+#include "toolframework/itoolframework.h"
+#include "toolframework_client.h"
+#include "tier1/KeyValues.h"
+#include "rendertexture.h"
+#include "prop_portal_shared.h"
+#include "particles_new.h"
+
+#include "c_portal_player.h"
+
+#include "c_pixel_visibility.h"
+
+#include "glow_overlay.h"
+
+#include "dlight.h"
+#include "iefx.h"
+
+#include "simple_keys.h"
+
+#ifdef _DEBUG
+#include "filesystem.h"
+#endif
+
+#include "debugoverlay_shared.h"
+
+
+LINK_ENTITY_TO_CLASS( prop_portal, C_Prop_Portal );
+
+IMPLEMENT_CLIENTCLASS_DT( C_Prop_Portal, DT_Prop_Portal, CProp_Portal )
+ RecvPropEHandle( RECVINFO(m_hLinkedPortal) ),
+ RecvPropBool( RECVINFO(m_bActivated) ),
+ RecvPropBool( RECVINFO(m_bIsPortal2) ),
+END_RECV_TABLE()
+
+
+void __MsgFunc_EntityPortalled(bf_read &msg)
+{
+ long iEncodedEHandle;
+ int iEntity, iSerialNum;
+
+
+ //grab portal EHANDLE
+ iEncodedEHandle = msg.ReadLong();
+
+ if( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE )
+ return;
+
+ iEntity = iEncodedEHandle & ((1 << MAX_EDICT_BITS) - 1);
+ iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS;
+
+ EHANDLE hPortal( iEntity, iSerialNum );
+ C_Prop_Portal *pPortal = ( C_Prop_Portal *)(hPortal.Get());
+
+ if( pPortal == NULL )
+ return;
+
+
+
+ //grab other entity's EHANDLE
+
+ iEncodedEHandle = msg.ReadLong();
+
+ if( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE )
+ return;
+
+ iEntity = iEncodedEHandle & ((1 << MAX_EDICT_BITS) - 1);
+ iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS;
+
+ EHANDLE hEntity( iEntity, iSerialNum );
+ C_BaseEntity *pEntity = hEntity.Get();
+ if( pEntity == NULL )
+ return;
+
+
+
+ bool bIsPlayer = pEntity->IsPlayer();
+
+ Vector ptNewPosition;
+ ptNewPosition.x = msg.ReadFloat();
+ ptNewPosition.y = msg.ReadFloat();
+ ptNewPosition.z = msg.ReadFloat();
+ QAngle qNewAngles;
+ qNewAngles.x = msg.ReadFloat();
+ qNewAngles.y = msg.ReadFloat();
+ qNewAngles.z = msg.ReadFloat();
+
+ Vector vecOldInterpolatedPos;
+ QAngle qaOldInterpolatedRot;
+ if ( pEntity->IsToolRecording() )
+ {
+ vecOldInterpolatedPos = pEntity->GetOriginInterpolator().GetCurrent();
+ qaOldInterpolatedRot = pEntity->GetRotationInterpolator().GetCurrent();
+ }
+
+ pEntity->AddEFlags( EFL_DIRTY_ABSTRANSFORM );
+
+ VMatrix matTransform = pPortal->MatrixThisToLinked();
+ //VMatrix matInvTransform = pPortal->m_hLinkedPortal->MatrixThisToLinked();
+
+ CInterpolatedVar< QAngle > &rotInterp = pEntity->GetRotationInterpolator();
+ CInterpolatedVar< Vector > &posInterp = pEntity->GetOriginInterpolator();
+
+ Vector ptCurrentPosition = posInterp.GetCurrent();
+ Vector ptInvCurrentPosition = matTransform * ptCurrentPosition;
+ bool bAlreadyTeleported = ((ptCurrentPosition - ptNewPosition).LengthSqr() < (ptInvCurrentPosition - ptNewPosition).LengthSqr()); //newest network update closer to destination than transformed to destination indicates it's already been transformed
+
+
+ UTIL_TransformInterpolatedAngle( rotInterp, matTransform.As3x4(), bAlreadyTeleported );
+
+ if( bIsPlayer ) //the player's origin != player's center, transforms are performed about centers
+ {
+ VMatrix matShiftOrigin;
+ matShiftOrigin.Identity();
+ Vector vTranslate( 0.0f, 0.0f, 36.0f );
+ matShiftOrigin.SetTranslation( vTranslate );
+ matTransform = matTransform * matShiftOrigin;
+ vTranslate = -vTranslate;
+ matShiftOrigin.SetTranslation( vTranslate );
+ matTransform = matShiftOrigin * matTransform;
+ }
+
+ UTIL_TransformInterpolatedPosition( posInterp, matTransform, bAlreadyTeleported );
+
+
+ if( bIsPlayer )
+ ((C_Portal_Player *)pEntity)->PlayerPortalled( pPortal );
+
+ if ( pEntity->IsToolRecording() )
+ {
+ static EntityTeleportedRecordingState_t state;
+
+ KeyValues *msg = new KeyValues( "entity_teleported" );
+ msg->SetPtr( "state", &state );
+ state.m_bTeleported = true;
+ state.m_bViewOverride = false;
+ state.m_vecTo = ptNewPosition;
+ state.m_qaTo = qNewAngles;
+ state.m_teleportMatrix = matTransform.As3x4();
+
+ // Post a message back to all IToolSystems
+ Assert( (int)pEntity->GetToolHandle() != 0 );
+ ToolFramework_PostToolMessage( pEntity->GetToolHandle(), msg );
+
+ msg->deleteThis();
+ }
+}
+
+static ConVar portal_demohack( "portal_demohack", "0", FCVAR_ARCHIVE, "Do the demo_legacy_rollback setting to help during demo playback of going through portals." );
+
+class C_PortalInitHelper : public CAutoGameSystem
+{
+ virtual bool Init()
+ {
+ //HOOK_MESSAGE( PlayerPortalled );
+ HOOK_MESSAGE( EntityPortalled );
+ if ( portal_demohack.GetBool() )
+ {
+ ConVarRef demo_legacy_rollback_ref( "demo_legacy_rollback" );
+ demo_legacy_rollback_ref.SetValue( false ); //Portal demos are wrong if the eyes rollback as far as regular demos
+ }
+ // However, there are probably bugs with this when jump ducking, etc.
+ return true;
+ }
+};
+static C_PortalInitHelper s_PortalInitHelper;
+
+
+
+C_Prop_Portal::C_Prop_Portal( void )
+{
+ TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
+ CProp_Portal_Shared::AllPortals.AddToTail( this );
+}
+
+C_Prop_Portal::~C_Prop_Portal( void )
+{
+ CProp_Portal_Shared::AllPortals.FindAndRemove( this );
+ g_pPortalRender->RemovePortal( this );
+
+ for( int i = m_GhostRenderables.Count(); --i >= 0; )
+ {
+ delete m_GhostRenderables[i];
+ }
+ m_GhostRenderables.RemoveAll();
+}
+
+void C_Prop_Portal::Spawn( void )
+{
+ SetThink( &C_Prop_Portal::ClientThink );
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ m_matrixThisToLinked.Identity(); //don't accidentally teleport objects to zero space
+ BaseClass::Spawn();
+}
+
+void C_Prop_Portal::Activate( void )
+{
+ BaseClass::Activate();
+}
+
+void C_Prop_Portal::ClientThink( void )
+{
+ bool bDidAnything = false;
+ if( m_fStaticAmount > 0.0f )
+ {
+ m_fStaticAmount -= gpGlobals->absoluteframetime;
+ if( m_fStaticAmount < 0.0f )
+ m_fStaticAmount = 0.0f;
+
+ bDidAnything = true;
+ }
+ if( m_fSecondaryStaticAmount > 0.0f )
+ {
+ m_fSecondaryStaticAmount -= gpGlobals->absoluteframetime;
+ if( m_fSecondaryStaticAmount < 0.0f )
+ m_fSecondaryStaticAmount = 0.0f;
+
+ bDidAnything = true;
+ }
+
+ if( m_fOpenAmount < 1.0f )
+ {
+ m_fOpenAmount += gpGlobals->absoluteframetime * 2.0f;
+ if( m_fOpenAmount > 1.0f )
+ m_fOpenAmount = 1.0f;
+
+ bDidAnything = true;
+ }
+
+ if( bDidAnything == false )
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ }
+}
+
+void C_Prop_Portal::Simulate()
+{
+ BaseClass::Simulate();
+
+ //clear list of ghosted entities from last frame, and clear the clipping planes we put on them
+ for( int i = m_hGhostingEntities.Count(); --i >= 0; )
+ {
+ C_BaseEntity *pEntity = m_hGhostingEntities[i].Get();
+
+ if( pEntity != NULL )
+ pEntity->m_bEnableRenderingClipPlane = false;
+ }
+ m_hGhostingEntities.RemoveAll();
+
+
+ if( !IsActivedAndLinked() )
+ {
+ //remove all ghost renderables
+ for( int i = m_GhostRenderables.Count(); --i >= 0; )
+ {
+ delete m_GhostRenderables[i];
+ }
+
+ m_GhostRenderables.RemoveAll();
+
+ return;
+ }
+
+
+
+ //Find objects that are intersecting the portal and mark them for later replication on the remote portal's side
+ C_Portal_Player *pLocalPlayer = C_Portal_Player::GetLocalPlayer();
+ C_BaseViewModel *pLocalPlayerViewModel = pLocalPlayer->GetViewModel();
+
+ CBaseEntity *pEntsNearPortal[1024];
+ int iEntsNearPortal = UTIL_EntitiesInSphere( pEntsNearPortal, 1024, GetNetworkOrigin(), PORTAL_HALF_HEIGHT, 0, PARTITION_CLIENT_NON_STATIC_EDICTS );
+
+ if( iEntsNearPortal != 0 )
+ {
+ float fClipPlane[4];
+ fClipPlane[0] = m_plane_Origin.normal.x;
+ fClipPlane[1] = m_plane_Origin.normal.y;
+ fClipPlane[2] = m_plane_Origin.normal.z;
+ fClipPlane[3] = m_plane_Origin.dist - 0.3f;
+
+ for( int i = 0; i != iEntsNearPortal; ++i )
+ {
+ CBaseEntity *pEntity = pEntsNearPortal[i];
+ Assert( pEntity != NULL );
+
+ bool bIsMovable = false;
+
+ C_BaseEntity *pMoveEntity = pEntity;
+ MoveType_t moveType = MOVETYPE_NONE;
+
+ //unmoveables and doors can never get halfway in the portal
+ while ( pMoveEntity )
+ {
+ moveType = pMoveEntity->GetMoveType();
+
+ if ( !( moveType == MOVETYPE_NONE || moveType == MOVETYPE_PUSH ) )
+ {
+ bIsMovable = true;
+ pMoveEntity = NULL;
+ }
+ else
+ pMoveEntity = pMoveEntity->GetMoveParent();
+ }
+
+ if ( !bIsMovable )
+ continue;
+
+ Assert( dynamic_cast<C_Prop_Portal *>(pEntity) == NULL ); //should have been killed with (pEntity->GetMoveType() == MOVETYPE_NONE) check. Infinite recursion is infinitely bad.
+
+ if( pEntity == pLocalPlayerViewModel )
+ continue; //avoid ghosting view models
+
+ bool bActivePlayerWeapon = false;
+
+ C_BaseCombatWeapon *pWeapon = dynamic_cast<C_BaseCombatWeapon*>( pEntity );
+ if ( pWeapon )
+ {
+ C_Portal_Player *pPortalPlayer = ToPortalPlayer( pWeapon->GetOwner() );
+ if ( pPortalPlayer )
+ {
+ if ( pPortalPlayer->GetActiveWeapon() != pWeapon )
+ continue; // don't ghost player owned non selected weapons
+ else
+ bActivePlayerWeapon = true;
+ }
+ }
+
+ Vector ptEntCenter = pEntity->WorldSpaceCenter();
+ if( bActivePlayerWeapon )
+ ptEntCenter = pWeapon->GetOwner()->WorldSpaceCenter();
+
+ if( (m_plane_Origin.normal.Dot( ptEntCenter ) - m_plane_Origin.dist) < -5.0f )
+ continue; //entity is behind the portal, most likely behind the wall the portal is placed on
+
+ if( !CProp_Portal_Shared::IsEntityTeleportable( pEntity ) )
+ continue;
+
+ if ( bActivePlayerWeapon )
+ {
+ if( !m_PortalSimulator.EntityHitBoxExtentIsInPortalHole( pWeapon->GetOwner() ) &&
+ !m_PortalSimulator.EntityHitBoxExtentIsInPortalHole( pWeapon ) )
+ continue;
+ }
+ else if( pEntity->IsPlayer() )
+ {
+ if( !m_PortalSimulator.EntityHitBoxExtentIsInPortalHole( (C_BaseAnimating*)pEntity ) )
+ continue;
+ }
+ else
+ {
+ if( !m_PortalSimulator.EntityIsInPortalHole( pEntity ) )
+ continue;
+ }
+
+ pEntity->m_bEnableRenderingClipPlane = true;
+ memcpy( pEntity->m_fRenderingClipPlane, fClipPlane, sizeof( float ) * 4 );
+
+ EHANDLE hEnt = pEntity;
+ m_hGhostingEntities.AddToTail( hEnt );
+ }
+ }
+
+ //now, fix up our list of ghosted renderables.
+ {
+ bool *bStillInUse = (bool *)stackalloc( sizeof( bool ) * (m_GhostRenderables.Count() + m_hGhostingEntities.Count()) );
+ memset( bStillInUse, 0, sizeof( bool ) * (m_GhostRenderables.Count() + m_hGhostingEntities.Count()) );
+
+ for( int i = m_hGhostingEntities.Count(); --i >= 0; )
+ {
+ C_BaseEntity *pRenderable = m_hGhostingEntities[i].Get();
+
+ int j;
+ for( j = m_GhostRenderables.Count(); --j >= 0; )
+ {
+ if( pRenderable == m_GhostRenderables[j]->m_pGhostedRenderable )
+ {
+ bStillInUse[j] = true;
+ m_GhostRenderables[j]->PerFrameUpdate();
+ break;
+ }
+ }
+
+ if ( j >= 0 )
+ continue;
+
+ //newly added
+ C_BaseEntity *pEntity = m_hGhostingEntities[i];
+
+ bool bIsHeldWeapon = false;
+ C_BaseCombatWeapon *pWeapon = dynamic_cast<C_BaseCombatWeapon*>( pEntity );
+ if ( pWeapon && ToPortalPlayer( pWeapon->GetOwner() ) )
+ bIsHeldWeapon = true;
+
+ C_PortalGhostRenderable *pNewGhost = new C_PortalGhostRenderable( this,
+ pRenderable,
+ pEntity->GetRenderGroup(),
+ m_matrixThisToLinked,
+ m_fGhostRenderablesClip,
+ (pEntity == pLocalPlayer || bIsHeldWeapon) );
+ Assert( pNewGhost );
+
+ bStillInUse[ m_GhostRenderables.AddToTail( pNewGhost ) ] = true;
+ pNewGhost->PerFrameUpdate();
+
+ // HACK - I just copied the CClientTools::OnEntityCreated code here,
+ // since the ghosts aren't really entities - they don't have an entindex,
+ // they're not in the entitylist, and they get created during Simulate(),
+ // which isn't valid for real entities, since it changes the simulate list
+ // -jd
+ if ( ToolsEnabled() && clienttools->IsInRecordingMode() )
+ {
+ // Send deletion message to tool interface
+ KeyValues *kv = new KeyValues( "created" );
+ HTOOLHANDLE h = clienttools->AttachToEntity( pNewGhost );
+ ToolFramework_PostToolMessage( h, kv );
+
+ kv->deleteThis();
+ }
+ }
+
+ //remove unused ghosts
+ for ( int i = m_GhostRenderables.Count(); --i >= 0; )
+ {
+ if ( bStillInUse[i] )
+ continue;
+
+ // HACK - I just copied the CClientTools::OnEntityDeleted code here,
+ // since the ghosts aren't really entities - they don't have an entindex,
+ // they're not in the entitylist, and they get created during Simulate(),
+ // which isn't valid for real entities, since it changes the simulate list
+ // -jd
+ C_PortalGhostRenderable *pGhost = m_GhostRenderables[i];
+ if ( ToolsEnabled() )
+ {
+ HTOOLHANDLE handle = pGhost ? pGhost->GetToolHandle() : (HTOOLHANDLE)0;
+ if ( handle != (HTOOLHANDLE)0 )
+ {
+ if ( clienttools->IsInRecordingMode() )
+ {
+ // Send deletion message to tool interface
+ KeyValues *kv = new KeyValues( "deleted" );
+ ToolFramework_PostToolMessage( handle, kv );
+ kv->deleteThis();
+ }
+
+ clienttools->DetachFromEntity( pGhost );
+ }
+ }
+
+ delete pGhost;
+ m_GhostRenderables.FastRemove( i );
+ }
+ }
+
+ //ensure the shared clip plane is up to date
+ C_Prop_Portal *pLinkedPortal = m_hLinkedPortal.Get();
+
+ m_fGhostRenderablesClip[0] = pLinkedPortal->m_plane_Origin.normal.x;
+ m_fGhostRenderablesClip[1] = pLinkedPortal->m_plane_Origin.normal.y;
+ m_fGhostRenderablesClip[2] = pLinkedPortal->m_plane_Origin.normal.z;
+ m_fGhostRenderablesClip[3] = pLinkedPortal->m_plane_Origin.dist - 0.75f;
+}
+
+void C_Prop_Portal::UpdateOnRemove( void )
+{
+ if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
+ {
+ g_pClientShadowMgr->DestroyFlashlight( TransformedLighting.m_LightShadowHandle );
+ TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
+ }
+
+ g_pPortalRender->RemovePortal( this );
+
+ BaseClass::UpdateOnRemove();
+}
+
+void C_Prop_Portal::OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect )
+{
+ if ( Q_stricmp( pszParticleName, "portal_1_overlap" ) == 0 || Q_stricmp( pszParticleName, "portal_2_overlap" ) == 0 )
+ {
+ float fClosestDistanceSqr = -1.0f;
+ Vector vClosestPosition;
+
+ int iPortalCount = CProp_Portal_Shared::AllPortals.Count();
+ if( iPortalCount != 0 )
+ {
+ CProp_Portal **pPortals = CProp_Portal_Shared::AllPortals.Base();
+ for( int i = 0; i != iPortalCount; ++i )
+ {
+ CProp_Portal *pTempPortal = pPortals[i];
+ if ( pTempPortal != this && pTempPortal->m_bActivated )
+ {
+ Vector vPosition = pTempPortal->GetAbsOrigin();
+
+ float fDistanceSqr = pNewParticleEffect->GetRenderOrigin().DistToSqr( vPosition );
+
+ if ( fClosestDistanceSqr == -1.0f || fClosestDistanceSqr > fDistanceSqr )
+ {
+ fClosestDistanceSqr = fDistanceSqr;
+ vClosestPosition = vPosition;
+ }
+ }
+ }
+ }
+
+ if ( fClosestDistanceSqr != -1.0f )
+ {
+ pNewParticleEffect->SetControlPoint( 1, vClosestPosition );
+ }
+ }
+}
+
+void C_Prop_Portal::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ //PreDataChanged.m_matrixThisToLinked = m_matrixThisToLinked;
+ PreDataChanged.m_bIsPortal2 = m_bIsPortal2;
+ PreDataChanged.m_bActivated = m_bActivated;
+ PreDataChanged.m_vOrigin = GetNetworkOrigin();
+ PreDataChanged.m_qAngles = GetNetworkAngles();
+ PreDataChanged.m_hLinkedTo = m_hLinkedPortal.Get();
+
+ BaseClass::OnPreDataChanged( updateType );
+}
+
+//ConVar r_portal_light_innerangle( "r_portal_light_innerangle", "90.0", FCVAR_CLIENTDLL );
+//ConVar r_portal_light_outerangle( "r_portal_light_outerangle", "90.0", FCVAR_CLIENTDLL );
+//ConVar r_portal_light_forward( "r_portal_light_forward", "0.0", FCVAR_CLIENTDLL );
+
+void C_Prop_Portal::OnDataChanged( DataUpdateType_t updateType )
+{
+ C_Prop_Portal *pRemote = m_hLinkedPortal;
+ m_pLinkedPortal = pRemote;
+ GetVectors( &m_vForward, &m_vRight, &m_vUp );
+ m_ptOrigin = GetNetworkOrigin();
+
+ bool bPortalMoved = ( (PreDataChanged.m_vOrigin != m_ptOrigin ) ||
+ (PreDataChanged.m_qAngles != GetNetworkAngles()) ||
+ (PreDataChanged.m_bActivated == false) ||
+ (PreDataChanged.m_bIsPortal2 != m_bIsPortal2) );
+
+ bool bNewLinkage = ( (PreDataChanged.m_hLinkedTo.Get() != m_hLinkedPortal.Get()) );
+ if( bNewLinkage )
+ m_PortalSimulator.DetachFromLinked(); //detach now so moves are theoretically faster
+
+ if( m_bActivated )
+ {
+ //generic stuff we'll need
+ Vector vRemoteUp, vRemoteRight, vRemoteForward, ptRemoteOrigin;
+ if( pRemote )
+ {
+ pRemote->GetVectors( &vRemoteForward, &vRemoteRight, &vRemoteUp );
+ ptRemoteOrigin = pRemote->GetNetworkOrigin();
+ }
+ g_pPortalRender->AddPortal( this ); //will know if we're already added and avoid adding twice
+
+ if( bPortalMoved )
+ {
+ Vector ptForwardOrigin = m_ptOrigin + m_vForward;// * 3.0f;
+ Vector vScaledRight = m_vRight * (PORTAL_HALF_WIDTH * 0.95f);
+ Vector vScaledUp = m_vUp * (PORTAL_HALF_HEIGHT * 0.95f);
+
+ m_PortalSimulator.MoveTo( GetNetworkOrigin(), GetNetworkAngles() );
+
+ //update our associated portal environment
+ //CPortal_PhysicsEnvironmentMgr::CreateEnvironment( this );
+
+ m_fOpenAmount = 0.0f;
+ //m_fStaticAmount = 1.0f; // This will cause the portal we are opening to show the static effect
+ SetNextClientThink( CLIENT_THINK_ALWAYS ); //we need this to help open up
+
+ //add static to the remote
+ if( pRemote )
+ {
+ pRemote->m_fStaticAmount = 1.0f; // This will cause the other portal to show the static effect
+ pRemote->SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+
+ dlight_t *pFakeLight = NULL;
+ ClientShadowHandle_t ShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
+ if( pRemote )
+ {
+ pFakeLight = pRemote->TransformedLighting.m_pEntityLight;
+ ShadowHandle = pRemote->TransformedLighting.m_LightShadowHandle;
+ AssertMsg( (ShadowHandle == CLIENTSHADOW_INVALID_HANDLE) || (TransformedLighting.m_LightShadowHandle == CLIENTSHADOW_INVALID_HANDLE), "Two shadow handles found, should only have one shared handle" );
+ AssertMsg( (pFakeLight == NULL) || (TransformedLighting.m_pEntityLight == NULL), "two lights found, should only have one shared light" );
+ pRemote->TransformedLighting.m_pEntityLight = NULL;
+ pRemote->TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
+ }
+
+ if( TransformedLighting.m_pEntityLight )
+ {
+ pFakeLight = TransformedLighting.m_pEntityLight;
+ TransformedLighting.m_pEntityLight = NULL;
+ }
+
+ if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
+ {
+ ShadowHandle = TransformedLighting.m_LightShadowHandle;
+ TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
+ }
+
+
+
+
+ if( pFakeLight != NULL )
+ {
+ //turn off the light so it doesn't interfere with absorbed light calculations
+ pFakeLight->color.r = 0;
+ pFakeLight->color.g = 0;
+ pFakeLight->color.b = 0;
+ pFakeLight->flags = DLIGHT_NO_WORLD_ILLUMINATION | DLIGHT_NO_MODEL_ILLUMINATION;
+ pFakeLight->radius = 0.0f;
+ render->TouchLight( pFakeLight );
+ }
+
+ if( pRemote ) //now, see if we need to fake light coming through a portal
+ {
+#if 0
+ Vector vLightAtRemotePortal( vec3_origin ), vLightAtLocalPortal( vec3_origin );
+
+ if( pRemote ) //get lighting at remote portal
+ {
+ engine->ComputeLighting( ptRemoteOrigin, NULL, false, vLightAtRemotePortal, NULL );
+ }
+
+ //now get lighting at the local portal
+ {
+ engine->ComputeLighting( ptOrigin, NULL, false, vLightAtLocalPortal, NULL );
+ }
+
+ //Vector vLightDiff = vLightAtLocalPortal - vLightAtRemotePortal;
+ //if( vLightDiff.Length() > 0.6f ) //a significant difference in lighting, remember that the light vectors are NOT normalized in length
+ {
+ //time to fake some light coming through the greater intensity portal to the lower intensity
+
+ //are we transferring light from the local portal to the remote?
+ bool bLocalToRemote = (vLightAtLocalPortal.Length() > vLightAtRemotePortal.Length());
+
+ Vector ptLightOrigin, vLightForward, vColor, vClampedColor;
+ float fColorScale;
+ {
+ if( bLocalToRemote )
+ {
+ vColor = vLightAtLocalPortal;
+ vLightForward = vRemoteForward;
+ ptLightOrigin = ptRemoteOrigin;
+ }
+ else
+ {
+ vColor = vLightAtRemotePortal;
+ vLightForward = vForward;
+ ptLightOrigin = ptOrigin;
+ }
+
+ //clamp color values
+ fColorScale = vColor.x;
+ if( vColor.y > fColorScale )
+ fColorScale = vColor.y;
+ if( vColor.z > fColorScale )
+ fColorScale = vColor.z;
+
+ if( fColorScale > 1.0f )
+ vClampedColor = vColor * (1.0f / fColorScale);
+ else
+ vClampedColor = vColor;
+
+ /*if( vColor.x < 0.0f )
+ vColor.x = 0.0f;
+ if( vColor.x > 1.0f )
+ vColor.x = 1.0f;
+
+ if( vColor.y < 0.0f )
+ vColor.y = 0.0f;
+ if( vColor.y > 1.0f )
+ vColor.y = 1.0f;
+
+ if( vColor.z < 0.0f )
+ vColor.z = 0.0f;
+ if( vColor.z > 1.0f )
+ vColor.z = 1.0f;*/
+ }
+
+
+ if( pFakeLight == NULL )
+ pFakeLight = effects->CL_AllocElight( 0 ); //is there a difference between DLight and ELight when only lighting ents?
+
+ if( pFakeLight != NULL ) //be absolutely sure that light allocation hasn't failed
+ {
+ if( bLocalToRemote )
+ {
+ //local light is greater, fake at remote portal
+ pRemote->TransformedLighting.m_pEntityLight = pFakeLight;
+ pFakeLight->key = pRemote->index;
+ }
+ else
+ {
+ //remote light is greater, fake at local portal
+ TransformedLighting.m_pEntityLight = pFakeLight;
+ pFakeLight->key = index;
+ }
+
+ pFakeLight->die = gpGlobals->curtime + 1e10;
+ pFakeLight->flags = DLIGHT_NO_WORLD_ILLUMINATION;
+ pFakeLight->minlight = 0.0f;
+ pFakeLight->radius = 500.0f;
+ pFakeLight->m_InnerAngle = 0.0f; //r_portal_light_innerangle.GetFloat();
+ pFakeLight->m_OuterAngle = 120.0f; //r_portal_light_outerangle.GetFloat();
+ pFakeLight->style = 0;
+
+ pFakeLight->origin = ptLightOrigin;
+ pFakeLight->m_Direction = vLightForward;
+
+ pFakeLight->color.r = vClampedColor.x * 255;
+ pFakeLight->color.g = vClampedColor.y * 255;
+ pFakeLight->color.b = vClampedColor.z * 255;
+ pFakeLight->color.exponent = ((signed int)(((*((unsigned int *)(&fColorScale))) & 0x7F800000) >> 23)) - 125; //strip the exponent from our maximum color
+ //if( pFakeLight->color.exponent < 4 )
+ // pFakeLight->color.exponent = 4;
+
+ render->TouchLight( pFakeLight );
+ }
+
+ FlashlightState_t state;
+ {
+ state.m_NearZ = 4.0f;
+ state.m_FarZ = 500.0f;
+ state.m_nSpotlightTextureFrame = 0;
+ state.m_pSpotlightTexture = PortalDrawingMaterials::PortalLightTransfer_ShadowTexture;
+ state.m_fConstantAtten = 0.0f;
+ state.m_fLinearAtten = 500.0f;
+ state.m_fQuadraticAtten = 0.0f;
+ state.m_fHorizontalFOVDegrees = 120.0f;
+ state.m_fVerticalFOVDegrees = 120.0f;
+
+ state.m_bEnableShadows = false;
+ state.m_vecLightOrigin = ptLightOrigin;
+
+ Vector vLightRight, vLightUp( 0.0f, 0.0f, 1.0f );
+ if( fabs( DotProduct( vLightUp, vLightForward ) ) > 0.99f )
+ vLightUp.Init( 0.0f, 1.0f, 0.0f ); // Don't want vLightUp and vLightForward to be parallel
+
+ CrossProduct( vLightUp, vLightForward, vLightRight );
+ VectorNormalize( vLightRight );
+ CrossProduct( vLightForward, vLightRight, vLightUp );
+ VectorNormalize( vLightUp );
+
+ BasisToQuaternion( vLightForward, vLightRight, vLightUp, state.m_quatOrientation );
+
+ state.m_Color[0] = vColor.x * 0.35f;
+ state.m_Color[1] = vColor.y * 0.35f;
+ state.m_Color[2] = vColor.z * 0.35f;
+ state.m_Color[3] = 1.0f;
+ }
+
+ if( ShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
+ {
+ g_pClientShadowMgr->UpdateFlashlightState( ShadowHandle, state ); //simpler update for existing handle
+ g_pClientShadowMgr->UpdateProjectedTexture( ShadowHandle, true );
+ }
+
+ if( ShadowHandle == CLIENTSHADOW_INVALID_HANDLE )
+ {
+ ShadowHandle = g_pClientShadowMgr->CreateFlashlight( state );
+ if( ShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
+ g_pClientShadowMgr->UpdateProjectedTexture( ShadowHandle, true );
+ }
+
+
+ if( bLocalToRemote )
+ pRemote->TransformedLighting.m_LightShadowHandle = ShadowHandle;
+ else
+ TransformedLighting.m_LightShadowHandle = ShadowHandle;
+ }
+#endif
+ }
+ }
+ }
+ else
+ {
+ g_pPortalRender->RemovePortal( this );
+
+ m_PortalSimulator.DetachFromLinked();
+
+ if( TransformedLighting.m_pEntityLight )
+ {
+ TransformedLighting.m_pEntityLight->die = gpGlobals->curtime;
+ TransformedLighting.m_pEntityLight = NULL;
+ }
+
+ if( TransformedLighting.m_LightShadowHandle != CLIENTSHADOW_INVALID_HANDLE )
+ {
+ g_pClientShadowMgr->DestroyFlashlight( TransformedLighting.m_LightShadowHandle );
+ TransformedLighting.m_LightShadowHandle = CLIENTSHADOW_INVALID_HANDLE;
+ }
+ }
+
+ if( (PreDataChanged.m_hLinkedTo.Get() != m_hLinkedPortal.Get()) && m_hLinkedPortal.Get() )
+ m_PortalSimulator.AttachTo( &m_hLinkedPortal.Get()->m_PortalSimulator );
+
+
+ BaseClass::OnDataChanged( updateType );
+
+ if( bNewLinkage || bPortalMoved )
+ {
+ PortalMoved(); //updates link matrix and internals
+ }
+
+ if ( bPortalMoved )
+ {
+ UpdateOriginPlane();
+ }
+
+ if( bPortalMoved || bNewLinkage )
+ {
+ UpdateGhostRenderables();
+ if( pRemote )
+ pRemote->UpdateGhostRenderables();
+ }
+}
+
+void C_Prop_Portal::UpdateGhostRenderables( void )
+{
+ //lastly, update all ghost renderables
+ for( int i = m_GhostRenderables.Count(); --i >= 0; )
+ {
+ m_GhostRenderables[i]->m_matGhostTransform = m_matrixThisToLinked;;
+ }
+}
+
+extern ConVar building_cubemaps;
+
+int C_Prop_Portal::DrawModel( int flags )
+{
+ // Don't draw in cube maps because it makes an ugly colored splotch
+ if( m_bActivated == false || building_cubemaps.GetBool() )
+ return 0;
+
+ int iRetVal = 0;
+
+ C_Prop_Portal *pLinkedPortal = m_hLinkedPortal.Get();
+
+ if ( pLinkedPortal == NULL )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS ); // we need this to help fade out
+ }
+
+ if ( !g_pPortalRender->ShouldUseStencilsToRenderPortals() )
+ {
+ DrawPortal();
+ }
+
+ if( WillUseDepthDoublerThisDraw() )
+ m_fSecondaryStaticAmount = 0.0f;
+
+ iRetVal = BaseClass::DrawModel( flags );
+
+ return iRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Handle recording for the SFM
+//-----------------------------------------------------------------------------
+void C_Prop_Portal::GetToolRecordingState( KeyValues *msg )
+{
+ if ( !ToolsEnabled() )
+ return;
+
+ VPROF_BUDGET( "C_Prop_Portal::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
+ BaseClass::GetToolRecordingState( m_bActivated, msg );
+
+ if ( !m_bActivated )
+ {
+ BaseEntityRecordingState_t *pBaseEntity = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
+ pBaseEntity->m_bVisible = false;
+ }
+}
+
+void C_Prop_Portal::UpdateOriginPlane( void )
+{
+ //setup our origin plane
+ GetVectors( &m_plane_Origin.normal, NULL, NULL );
+ m_plane_Origin.dist = m_plane_Origin.normal.Dot( GetAbsOrigin() );
+ m_plane_Origin.signbits = SignbitsForPlane( &m_plane_Origin );
+
+ Vector vAbsNormal;
+ vAbsNormal.x = fabs(m_plane_Origin.normal.x);
+ vAbsNormal.y = fabs(m_plane_Origin.normal.y);
+ vAbsNormal.z = fabs(m_plane_Origin.normal.z);
+
+ if( vAbsNormal.x > vAbsNormal.y )
+ {
+ if( vAbsNormal.x > vAbsNormal.z )
+ {
+ if( vAbsNormal.x > 0.999f )
+ m_plane_Origin.type = PLANE_X;
+ else
+ m_plane_Origin.type = PLANE_ANYX;
+ }
+ else
+ {
+ if( vAbsNormal.z > 0.999f )
+ m_plane_Origin.type = PLANE_Z;
+ else
+ m_plane_Origin.type = PLANE_ANYZ;
+ }
+ }
+ else
+ {
+ if( vAbsNormal.y > vAbsNormal.z )
+ {
+ if( vAbsNormal.y > 0.999f )
+ m_plane_Origin.type = PLANE_Y;
+ else
+ m_plane_Origin.type = PLANE_ANYY;
+ }
+ else
+ {
+ if( vAbsNormal.z > 0.999f )
+ m_plane_Origin.type = PLANE_Z;
+ else
+ m_plane_Origin.type = PLANE_ANYZ;
+ }
+ }
+}
+
+void C_Prop_Portal::SetIsPortal2( bool bValue )
+{
+ m_bIsPortal2 = bValue;
+}
+
+bool C_Prop_Portal::IsActivedAndLinked( void ) const
+{
+ return ( m_bActivated && m_hLinkedPortal.Get() != NULL );
+}
diff --git a/game/client/portal/c_prop_portal.h b/game/client/portal/c_prop_portal.h
new file mode 100644
index 0000000..f5871f4
--- /dev/null
+++ b/game/client/portal/c_prop_portal.h
@@ -0,0 +1,96 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef C_PROP_PORTAL_H
+#define C_PROP_PORTAL_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "portalrenderable_flatbasic.h"
+#include "iviewrender.h"
+#include "view_shared.h"
+#include "viewrender.h"
+#include "PortalSimulation.h"
+#include "C_PortalGhostRenderable.h"
+
+struct dlight_t;
+class C_DynamicLight;
+
+class C_Prop_Portal : public CPortalRenderable_FlatBasic
+{
+public:
+ DECLARE_CLASS( C_Prop_Portal, CPortalRenderable_FlatBasic );
+ DECLARE_CLIENTCLASS();
+
+ C_Prop_Portal( void );
+ virtual ~C_Prop_Portal( void );
+
+ // Handle recording for the SFM
+ virtual void GetToolRecordingState( KeyValues *msg );
+
+ CHandle<C_Prop_Portal> m_hLinkedPortal; //the portal this portal is linked to
+ bool m_bActivated; //a portal can exist and not be active
+
+ bool m_bSharedEnvironmentConfiguration; //this will be set by an instance of CPortal_Environment when two environments are in close proximity
+
+ cplane_t m_plane_Origin; // The plane on which this portal is placed, normal facing outward (matching model forward vec)
+
+ virtual void Spawn( void );
+ virtual void Activate( void );
+ virtual void ClientThink( void );
+
+ virtual void Simulate();
+
+ virtual void UpdateOnRemove( void );
+
+ virtual void OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect );
+
+ struct Portal_PreDataChanged
+ {
+ bool m_bActivated;
+ bool m_bIsPortal2;
+ Vector m_vOrigin;
+ QAngle m_qAngles;
+ EHANDLE m_hLinkedTo;
+ } PreDataChanged;
+
+ struct TransformedLightingData_t
+ {
+ ClientShadowHandle_t m_LightShadowHandle;
+ dlight_t *m_pEntityLight;
+ } TransformedLighting;
+
+ virtual void OnPreDataChanged( DataUpdateType_t updateType );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual int DrawModel( int flags );
+ void UpdateOriginPlane( void );
+ void UpdateGhostRenderables( void );
+
+ void SetIsPortal2( bool bValue );
+
+ bool IsActivedAndLinked( void ) const;
+
+ CPortalSimulator m_PortalSimulator;
+
+ virtual C_BaseEntity * PortalRenderable_GetPairedEntity( void ) { return this; };
+
+private:
+
+ CUtlVector<EHANDLE> m_hGhostingEntities;
+ CUtlVector<C_PortalGhostRenderable *> m_GhostRenderables;
+ float m_fGhostRenderablesClip[4];
+
+
+ friend void __MsgFunc_EntityPortalled(bf_read &msg);
+
+};
+
+typedef C_Prop_Portal CProp_Portal;
+
+#endif //#ifndef C_PROP_PORTAL_H
diff --git a/game/client/portal/c_prop_portal_stats_display.cpp b/game/client/portal/c_prop_portal_stats_display.cpp
new file mode 100644
index 0000000..0035d75
--- /dev/null
+++ b/game/client/portal/c_prop_portal_stats_display.cpp
@@ -0,0 +1,341 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "c_prop_portal_stats_display.h"
+#include "c_te_legacytempents.h"
+#include "tempent.h"
+#include "engine/IEngineSound.h"
+#include "dlight.h"
+#include "iefx.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "point_bonusmaps_accessor.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern const ConVar *sv_cheats;
+
+
+CUtlVector< C_PropPortalStatsDisplay* > g_PropPortalStatsDisplays;
+
+
+IMPLEMENT_CLIENTCLASS_DT(C_PropPortalStatsDisplay, DT_PropPortalStatsDisplay, CPropPortalStatsDisplay)
+ RecvPropBool( RECVINFO(m_bEnabled) ),
+
+ RecvPropInt( RECVINFO(m_iNumPortalsPlaced) ),
+ RecvPropInt( RECVINFO(m_iNumStepsTaken) ),
+ RecvPropFloat( RECVINFO(m_fNumSecondsTaken) ),
+
+ RecvPropInt( RECVINFO(m_iBronzeObjective) ),
+ RecvPropInt( RECVINFO(m_iSilverObjective) ),
+ RecvPropInt( RECVINFO(m_iGoldObjective) ),
+
+ RecvPropString( RECVINFO( szChallengeFileName ) ),
+ RecvPropString( RECVINFO( szChallengeMapName ) ),
+ RecvPropString( RECVINFO( szChallengeName ) ),
+
+ RecvPropInt( RECVINFO(m_iDisplayObjective) ),
+END_RECV_TABLE()
+
+
+C_PropPortalStatsDisplay::C_PropPortalStatsDisplay()
+{
+ g_PropPortalStatsDisplays.AddToTail( this );
+
+ m_fEnabledCounter = -2.0f;
+}
+
+C_PropPortalStatsDisplay::~C_PropPortalStatsDisplay()
+{
+ g_PropPortalStatsDisplays.FindAndRemove( this );
+}
+
+void C_PropPortalStatsDisplay::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ m_bHasCheated = false;
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+}
+
+void C_PropPortalStatsDisplay::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ // Remember the enable status from before the update
+ if ( updateType == DATA_UPDATE_DATATABLE_CHANGED )
+ m_bPrevEnabled = m_bEnabled;
+
+ BaseClass::OnPreDataChanged( updateType );
+}
+
+void C_PropPortalStatsDisplay::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_DATATABLE_CHANGED )
+ {
+ // Check if it was just enabled
+ if ( m_bEnabled && !m_bPrevEnabled )
+ {
+ ResetDisplayAnimation();
+
+ // Don't update if the player has cheated (and isn't at least developer level 2)
+ if ( !m_bHasCheated || developer.GetInt() >= 2 )
+ {
+ int iStat = 0;
+ if ( m_iDisplayObjective == 0 )
+ iStat = m_iNumPortalsPlaced;
+ else if ( m_iDisplayObjective == 1 )
+ iStat = m_iNumStepsTaken;
+ else if ( m_iDisplayObjective == 2 )
+ iStat = m_fNumSecondsTaken;
+
+ BonusMapChallengeUpdate( szChallengeFileName, szChallengeMapName, szChallengeName, iStat );
+ }
+ }
+ }
+}
+
+void C_PropPortalStatsDisplay::ClientThink( void )
+{
+ BaseClass::ClientThink();
+
+ // Remembers if the player has cheated
+ // This doesn't persist across save/loads,
+ // this is only to prevent players from accidentally wiping their stats while no clipping through a map
+ if ( !sv_cheats )
+ {
+ sv_cheats = cvar->FindVar( "sv_cheats" );
+ }
+ if ( sv_cheats && sv_cheats->GetInt() != 0 )
+ m_bHasCheated = true;
+
+ if ( !m_bEnabled )
+ return;
+
+ // Check if the display needs to be initialized
+ if ( m_fEnabledCounter < -1.0f )
+ {
+ m_fEnabledCounter = -1.0f;
+
+ if ( m_iDisplayObjective == 0 )
+ m_fNumPlayerDisplay = m_iNumPortalsPlaced;
+ else if ( m_iDisplayObjective == 1 )
+ m_fNumPlayerDisplay = m_iNumStepsTaken;
+ else
+ m_fNumPlayerDisplay = m_fNumSecondsTaken;
+
+ SetDisplayMedals();
+
+ if ( m_iGoalLevelDisplay == 0 )
+ m_iGoalDisplay = m_iBronzeObjective;
+ else if ( m_iGoalLevelDisplay == 1 )
+ m_iGoalDisplay = m_iSilverObjective;
+ else
+ m_iGoalDisplay = m_iGoldObjective;
+
+ m_bGoalVisible = true;
+ m_iGoalSuccess = -1;
+
+ return;
+ }
+
+ //Check if the display is done animating
+ if ( m_fEnabledCounter < 0.0f )
+ return;
+
+ // Add up how much time has passed
+ m_fEnabledCounter += gpGlobals->frametime;
+
+ switch ( m_iCurrentDisplayStep )
+ {
+ // Count down to making objective visible
+ case 0:
+ if ( m_fEnabledCounter > 1.0f )
+ {
+ m_fEnabledCounter = 0.0f;
+
+ EmitSound( "Portal.stair_clack" );
+ m_bGoalVisible = true;
+ ++m_iCurrentDisplayStep;
+ }
+ break;
+
+ // Count up how the player actually did
+ case 1:
+ switch ( m_iDisplayObjective )
+ {
+ case PORTAL_LEVEL_STAT_NUM_PORTALS:
+ if ( m_fEnabledCounter > 1.5f / static_cast<float>( m_iNumPortalsPlaced + 1 ) )
+ {
+ m_fEnabledCounter = 0.0f;
+
+ if ( m_fNumPlayerDisplay < m_iNumPortalsPlaced )
+ {
+ m_fNumPlayerDisplay += 1.0f;
+
+ if ( static_cast<int>( m_fNumPlayerDisplay ) % 2 == 0 )
+ EmitSound( "Weapon_Portalgun.fire_blue" );
+ else
+ EmitSound( "Weapon_Portalgun.fire_red" );
+ }
+ else
+ ++m_iCurrentDisplayStep;
+ }
+ break;
+
+ case PORTAL_LEVEL_STAT_NUM_STEPS:
+ if ( m_fEnabledCounter > 1.5f / static_cast<float>( m_iNumStepsTaken + 1 ) )
+ {
+ int iNumStepsThisFrame = m_fEnabledCounter * static_cast<float>( m_iNumStepsTaken + 1 );
+ m_fEnabledCounter = 0.0f;
+
+ if ( m_fNumPlayerDisplay < m_iNumStepsTaken )
+ {
+ m_fNumPlayerDisplay += MIN( iNumStepsThisFrame, m_iNumStepsTaken - m_fNumPlayerDisplay );
+
+ int iStepsMod10 = static_cast<int>( m_fNumPlayerDisplay ) % 10;
+ if ( iStepsMod10 == 0 )
+ EmitSound( "NPC_Citizen.RunFootstepLeft" );
+ else if ( iStepsMod10 == 5 )
+ EmitSound( "NPC_Citizen.RunFootstepRight" );
+ }
+ else
+ ++m_iCurrentDisplayStep;
+ }
+ break;
+
+ case PORTAL_LEVEL_STAT_NUM_SECONDS:
+ if ( m_fNumPlayerDisplay < m_fNumSecondsTaken )
+ {
+ if ( m_fEnabledCounter > 1.5f / ( m_fNumSecondsTaken + 1.0f ) )
+ {
+ m_fEnabledCounter = 0.0f;
+
+ EmitSound( "Portal.room1_Clock" );
+ }
+
+ m_fNumPlayerDisplay += gpGlobals->frametime * m_fNumSecondsTaken;
+
+ if ( m_fNumPlayerDisplay >= m_fNumSecondsTaken )
+ {
+ m_fNumPlayerDisplay = m_fNumSecondsTaken;
+
+ m_fEnabledCounter = 0.0f;
+ ++m_iCurrentDisplayStep;
+ }
+ }
+ break;
+ }
+
+ break;
+
+ // Count down to reveal success or failure
+ case 2:
+ if ( m_fEnabledCounter > 1.5f )
+ {
+ m_fEnabledCounter = 0.0f;
+
+ int iCurretStat = -1;
+
+ switch ( m_iDisplayObjective )
+ {
+ case 0:
+ iCurretStat = m_iNumPortalsPlaced;
+ break;
+
+ case 1:
+ iCurretStat = m_iNumStepsTaken;
+ break;
+
+ case 2:
+ iCurretStat = static_cast<int>( m_fNumSecondsTaken );
+ break;
+ }
+
+ int (*(pObjectives[ 3 ])) = { &m_iBronzeObjective, &m_iSilverObjective, &m_iGoldObjective };
+
+ if ( iCurretStat <= (*(pObjectives[ m_iGoalLevelDisplay ])) )
+ {
+ m_bMedalCompleted[ m_iGoalLevelDisplay ] = true;
+ m_iGoalSuccess = 1;
+
+ EmitSound( "Portal.button_down" );
+
+ if ( m_iGoalLevelDisplay == 2 )
+ ++m_iCurrentDisplayStep; // skip the next step
+ }
+ else
+ {
+ m_iGoalSuccess = 0;
+
+ EmitSound( "Portal.button_up" );
+ ++m_iCurrentDisplayStep; // skip the next step
+ }
+
+ ++m_iCurrentDisplayStep;
+ }
+ break;
+
+ // Count down to reveal new objective
+ case 3:
+ if ( m_fEnabledCounter > 1.5f )
+ {
+ m_fEnabledCounter = 0.0f;
+
+ ++m_iGoalLevelDisplay;
+
+ int (*(pObjectives[ 3 ])) = { &m_iBronzeObjective, &m_iSilverObjective, &m_iGoldObjective };
+ m_iGoalDisplay = (*(pObjectives[ m_iGoalLevelDisplay ]));
+
+ m_iGoalSuccess = -1;
+
+ EmitSound( "Portal.stair_clack" );
+ --m_iCurrentDisplayStep; // go back to success or failure step
+ }
+ break;
+
+ // Short pause before going to next objective
+ case 4:
+ if ( m_fEnabledCounter > 1.5f )
+ {
+ m_fEnabledCounter = 0.0f;
+
+ m_iGoalSuccess = -1;
+
+ m_fEnabledCounter = -1.0f;
+ m_iCurrentDisplayStep = 0;
+ }
+ break;
+ }
+}
+
+void C_PropPortalStatsDisplay::ResetDisplayAnimation( void )
+{
+ EmitSound( "Portal.elevator_ding" );
+
+ m_fEnabledCounter = 0.0f;
+ m_iCurrentDisplayStep = 0;
+
+ m_fNumPlayerDisplay = 0.0f;
+
+ SetDisplayMedals();
+
+ m_bGoalVisible = false;
+}
+
+void C_PropPortalStatsDisplay::SetDisplayMedals( void )
+{
+ m_bMedalCompleted[ 0 ] = false;
+ m_bMedalCompleted[ 1 ] = false;
+ m_bMedalCompleted[ 2 ] = false;
+
+ m_iGoalDisplay = m_iBronzeObjective;
+ m_iGoalLevelDisplay = 0;
+ m_iGoalSuccess = -1;
+} \ No newline at end of file
diff --git a/game/client/portal/c_prop_portal_stats_display.h b/game/client/portal/c_prop_portal_stats_display.h
new file mode 100644
index 0000000..e33e4b5
--- /dev/null
+++ b/game/client/portal/c_prop_portal_stats_display.h
@@ -0,0 +1,87 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#ifndef C_PROP_PORTAL_STATS_DISPLAY_H
+#define C_PROP_PORTAL_STATS_DISPLAY_H
+
+#include "cbase.h"
+#include "c_portal_player.h"
+#include "portal_shareddefs.h"
+#include "utlvector.h"
+
+
+class C_PropPortalStatsDisplay : public C_BaseAnimating
+{
+public:
+ DECLARE_CLASS( C_PropPortalStatsDisplay, CBaseAnimating );
+ DECLARE_CLIENTCLASS();
+
+ C_PropPortalStatsDisplay();
+ virtual ~C_PropPortalStatsDisplay();
+
+ void Spawn( void );
+
+ virtual void OnPreDataChanged( DataUpdateType_t updateType );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+
+ void ClientThink( void );
+
+ bool HasCheated( void ) { return m_bHasCheated; }
+
+ bool IsEnabled( void ) { return m_bEnabled; }
+
+ int GetDisplayObjective( void ) { return m_iDisplayObjective; }
+
+ float GetNumPlayerDisplay( void ) { return m_fNumPlayerDisplay; }
+ bool IsTime( void ) { return m_iDisplayObjective == PORTAL_LEVEL_STAT_NUM_SECONDS; }
+
+ bool GetGoalVisible( void ) { return m_bGoalVisible; }
+ int GetNumGoalDisplay( void ) { return m_iGoalDisplay; }
+ int GetGoalSuccess( void ) { return m_iGoalSuccess; }
+
+ int GetGoalLevelDisplay( void ) { return m_iGoalLevelDisplay; }
+ bool IsMedalCompleted( int iLevel ) { return m_bMedalCompleted[ iLevel ]; }
+
+private:
+
+ void ResetDisplayAnimation( void );
+ void SetDisplayMedals( void );
+
+private:
+
+ bool m_bPrevEnabled;
+ bool m_bHasCheated;
+
+ bool m_bEnabled;
+
+ int m_iNumPortalsPlaced;
+ int m_iNumStepsTaken;
+ float m_fNumSecondsTaken;
+
+ int m_iBronzeObjective;
+ int m_iSilverObjective;
+ int m_iGoldObjective;
+
+ char szChallengeFileName[128];
+ char szChallengeMapName[32];
+ char szChallengeName[32];
+
+ int m_iDisplayObjective;
+ int m_iCurrentDisplayStep;
+ float m_fEnabledCounter;
+
+ float m_fNumPlayerDisplay;
+ bool m_bGoalVisible;
+ int m_iGoalDisplay;
+ int m_iGoalSuccess;
+ int m_iGoalLevelDisplay;
+ bool m_bMedalCompleted[ 3 ];
+};
+
+extern CUtlVector< C_PropPortalStatsDisplay* > g_PropPortalStatsDisplays;
+
+#endif //C_PROP_PORTAL_STATS_DISPLAY_H \ No newline at end of file
diff --git a/game/client/portal/c_weapon_physcannon.cpp b/game/client/portal/c_weapon_physcannon.cpp
new file mode 100644
index 0000000..4c50d98
--- /dev/null
+++ b/game/client/portal/c_weapon_physcannon.cpp
@@ -0,0 +1,388 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_weapon__stubs.h"
+#include "weapon_portalbasecombatweapon.h"
+#include "fx.h"
+#include "particles_localspace.h"
+#include "view.h"
+#include "particles_attractor.h"
+
+class C_WeaponPhysCannon: public CBasePortalCombatWeapon
+{
+ DECLARE_CLASS( C_WeaponPhysCannon, CBasePortalCombatWeapon );
+public:
+ C_WeaponPhysCannon( void );
+
+ DECLARE_CLIENTCLASS();
+ DECLARE_PREDICTABLE();
+
+ virtual int DrawModel( int flags );
+
+private:
+
+ bool SetupEmitter( void );
+
+ bool m_bIsCurrentlyUpgrading;
+ bool m_bWasUpgraded;
+
+ CSmartPtr<CLocalSpaceEmitter> m_pLocalEmitter;
+ CSmartPtr<CSimpleEmitter> m_pEmitter;
+ CSmartPtr<CParticleAttractor> m_pAttractor;
+};
+
+STUB_WEAPON_CLASS_IMPLEMENT( weapon_physcannon, C_WeaponPhysCannon );
+
+IMPLEMENT_CLIENTCLASS_DT( C_WeaponPhysCannon, DT_WeaponPhysCannon, CWeaponPhysCannon )
+ RecvPropBool( RECVINFO( m_bIsCurrentlyUpgrading ) ),
+END_RECV_TABLE()
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+C_WeaponPhysCannon::C_WeaponPhysCannon( void )
+{
+ m_bWasUpgraded = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool C_WeaponPhysCannon::SetupEmitter( void )
+{
+ if ( !m_pLocalEmitter.IsValid() )
+ {
+ m_pLocalEmitter = CLocalSpaceEmitter::Create( "physpowerup", GetRefEHandle(), LookupAttachment( "core" ) );
+
+ if ( m_pLocalEmitter.IsValid() == false )
+ return false;
+ }
+
+ if ( !m_pAttractor.IsValid() )
+ {
+ m_pAttractor = CParticleAttractor::Create( vec3_origin, "physpowerup_att" );
+
+ if ( m_pAttractor.IsValid() == false )
+ return false;
+ }
+
+ if ( !m_pEmitter.IsValid() )
+ {
+ m_pEmitter = CSimpleEmitter::Create( "physpowerup_glow" );
+
+ if ( m_pEmitter.IsValid() == false )
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Sorts the components of a vector
+//-----------------------------------------------------------------------------
+static inline void SortAbsVectorComponents( const Vector& src, int* pVecIdx )
+{
+ Vector absVec( fabs(src[0]), fabs(src[1]), fabs(src[2]) );
+
+ int maxIdx = (absVec[0] > absVec[1]) ? 0 : 1;
+ if (absVec[2] > absVec[maxIdx])
+ {
+ maxIdx = 2;
+ }
+
+ // always choose something right-handed....
+ switch( maxIdx )
+ {
+ case 0:
+ pVecIdx[0] = 1;
+ pVecIdx[1] = 2;
+ pVecIdx[2] = 0;
+ break;
+ case 1:
+ pVecIdx[0] = 2;
+ pVecIdx[1] = 0;
+ pVecIdx[2] = 1;
+ break;
+ case 2:
+ pVecIdx[0] = 0;
+ pVecIdx[1] = 1;
+ pVecIdx[2] = 2;
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Compute the bounding box's center, size, and basis
+//-----------------------------------------------------------------------------
+void ComputeRenderInfo( mstudiobbox_t *pHitBox, const matrix3x4_t &hitboxToWorld,
+ Vector *pVecAbsOrigin, Vector *pXVec, Vector *pYVec )
+{
+ // Compute the center of the hitbox in worldspace
+ Vector vecHitboxCenter;
+ VectorAdd( pHitBox->bbmin, pHitBox->bbmax, vecHitboxCenter );
+ vecHitboxCenter *= 0.5f;
+ VectorTransform( vecHitboxCenter, hitboxToWorld, *pVecAbsOrigin );
+
+ // Get the object's basis
+ Vector vec[3];
+ MatrixGetColumn( hitboxToWorld, 0, vec[0] );
+ MatrixGetColumn( hitboxToWorld, 1, vec[1] );
+ MatrixGetColumn( hitboxToWorld, 2, vec[2] );
+// vec[1] *= -1.0f;
+
+ Vector vecViewDir;
+ VectorSubtract( CurrentViewOrigin(), *pVecAbsOrigin, vecViewDir );
+ VectorNormalize( vecViewDir );
+
+ // Project the shadow casting direction into the space of the hitbox
+ Vector localViewDir;
+ localViewDir[0] = DotProduct( vec[0], vecViewDir );
+ localViewDir[1] = DotProduct( vec[1], vecViewDir );
+ localViewDir[2] = DotProduct( vec[2], vecViewDir );
+
+ // Figure out which vector has the largest component perpendicular
+ // to the view direction...
+ // Sort by how perpendicular it is
+ int vecIdx[3];
+ SortAbsVectorComponents( localViewDir, vecIdx );
+
+ // Here's our hitbox basis vectors; namely the ones that are
+ // most perpendicular to the view direction
+ *pXVec = vec[vecIdx[0]];
+ *pYVec = vec[vecIdx[1]];
+
+ // Project them into a plane perpendicular to the view direction
+ *pXVec -= vecViewDir * DotProduct( vecViewDir, *pXVec );
+ *pYVec -= vecViewDir * DotProduct( vecViewDir, *pYVec );
+ VectorNormalize( *pXVec );
+ VectorNormalize( *pYVec );
+
+ // Compute the hitbox size
+ Vector boxSize;
+ VectorSubtract( pHitBox->bbmax, pHitBox->bbmin, boxSize );
+
+ // We project the two longest sides into the vectors perpendicular
+ // to the projection direction, then add in the projection of the perp direction
+ Vector2D size( boxSize[vecIdx[0]], boxSize[vecIdx[1]] );
+ size.x *= fabs( DotProduct( vec[vecIdx[0]], *pXVec ) );
+ size.y *= fabs( DotProduct( vec[vecIdx[1]], *pYVec ) );
+
+ // Add the third component into x and y
+ size.x += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pXVec ) );
+ size.y += boxSize[vecIdx[2]] * fabs( DotProduct( vec[vecIdx[2]], *pYVec ) );
+
+ // Bloat a bit, since the shadow wants to extend outside the model a bit
+ size *= 2.0f;
+
+ // Clamp the minimum size
+ Vector2DMax( size, Vector2D(10.0f, 10.0f), size );
+
+ // Factor the size into the xvec + yvec
+ (*pXVec) *= size.x * 0.5f;
+ (*pYVec) *= size.y * 0.5f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flags -
+// Output : int
+//-----------------------------------------------------------------------------
+int C_WeaponPhysCannon::DrawModel( int flags )
+{
+ // If we're not ugrading, don't do anything special
+ if ( m_bIsCurrentlyUpgrading == false && m_bWasUpgraded == false )
+ return BaseClass::DrawModel( flags );
+
+ if ( gpGlobals->frametime == 0 )
+ return BaseClass::DrawModel( flags );
+
+ if ( !m_bReadyToDraw )
+ return 0;
+
+ m_bWasUpgraded = true;
+
+ // Create the particle emitter if it's not already
+ if ( SetupEmitter() )
+ {
+ // Add the power-up particles
+
+ // See if we should draw
+ if ( m_bReadyToDraw == false )
+ return 0;
+
+ C_BaseAnimating *pAnimating = GetBaseAnimating();
+ if (!pAnimating)
+ return 0;
+
+ matrix3x4_t *hitboxbones[MAXSTUDIOBONES];
+ if ( !pAnimating->HitboxToWorldTransforms( hitboxbones ) )
+ return 0;
+
+ studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( pAnimating->GetModel() );
+ if (!pStudioHdr)
+ return false;
+
+ mstudiohitboxset_t *set = pStudioHdr->pHitboxSet( pAnimating->GetHitboxSet() );
+ if ( !set )
+ return false;
+
+ int i;
+
+ float fadePerc = 1.0f;
+
+ if ( m_bIsCurrentlyUpgrading )
+ {
+ Vector vecSkew = vec3_origin;
+
+ // Skew the particles in front or in back of their targets
+ vecSkew = CurrentViewForward() * 4.0f;
+
+ float spriteScale = 1.0f;
+ spriteScale = clamp( spriteScale, 0.75f, 1.0f );
+
+ SimpleParticle *sParticle;
+
+ for ( i = 0; i < set->numhitboxes; ++i )
+ {
+ Vector vecAbsOrigin, xvec, yvec;
+ mstudiobbox_t *pBox = set->pHitbox(i);
+ ComputeRenderInfo( pBox, *hitboxbones[pBox->bone], &vecAbsOrigin, &xvec, &yvec );
+
+ Vector offset;
+ Vector xDir, yDir;
+
+ xDir = xvec;
+ float xScale = VectorNormalize( xDir ) * 0.75f;
+
+ yDir = yvec;
+ float yScale = VectorNormalize( yDir ) * 0.75f;
+
+ int numParticles = clamp( 4.0f * fadePerc, 1, 3 );
+
+ for ( int j = 0; j < numParticles; j++ )
+ {
+ offset = xDir * Helper_RandomFloat( -xScale*0.5f, xScale*0.5f ) + yDir * Helper_RandomFloat( -yScale*0.5f, yScale*0.5f );
+ offset += vecSkew;
+
+ sParticle = (SimpleParticle *) m_pEmitter->AddParticle( sizeof(SimpleParticle), m_pEmitter->GetPMaterial( "effects/combinemuzzle1" ), vecAbsOrigin + offset );
+
+ if ( sParticle == NULL )
+ return 1;
+
+ sParticle->m_vecVelocity = vec3_origin;
+ sParticle->m_uchStartSize = 16.0f * spriteScale;
+ sParticle->m_flDieTime = 0.2f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = Helper_RandomFloat( -2.0f, 2.0f );
+
+ float alpha = 40;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2;
+ }
+ }
+ }
+ }
+
+ int attachment = LookupAttachment( "core" );
+ Vector coreOrigin;
+ QAngle coreAngles;
+
+ GetAttachment( attachment, coreOrigin, coreAngles );
+
+ SimpleParticle *sParticle;
+
+ // Do the core effects
+ for ( int i = 0; i < 4; i++ )
+ {
+ sParticle = (SimpleParticle *) m_pLocalEmitter->AddParticle( sizeof(SimpleParticle), m_pLocalEmitter->GetPMaterial( "effects/strider_muzzle" ), vec3_origin );
+
+ if ( sParticle == NULL )
+ return 1;
+
+ sParticle->m_vecVelocity = vec3_origin;
+ sParticle->m_flDieTime = 0.1f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ if ( i < 2 )
+ {
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
+ }
+ else
+ {
+ if ( random->RandomInt( 0, 20 ) == 0 )
+ {
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 4.0f;
+ sParticle->m_flDieTime = 0.25f;
+ }
+ else
+ {
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 ) * (i+1);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize * 2.0f;
+ }
+ }
+ }
+
+ if ( m_bWasUpgraded && m_bIsCurrentlyUpgrading )
+ {
+ // Update our attractor point
+ m_pAttractor->SetAttractorOrigin( coreOrigin );
+
+ Vector offset;
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ offset = coreOrigin + RandomVector( -32.0f, 32.0f );
+
+ sParticle = (SimpleParticle *) m_pAttractor->AddParticle( sizeof(SimpleParticle), m_pAttractor->GetPMaterial( "effects/strider_muzzle" ), offset );
+
+ if ( sParticle == NULL )
+ return 1;
+
+ sParticle->m_vecVelocity = Vector(0,0,8);
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_flLifetime = 0.0f;
+
+ sParticle->m_flRoll = Helper_RandomInt( 0, 360 );
+ sParticle->m_flRollDelta = 0.0f;
+
+ float alpha = 255;
+
+ sParticle->m_uchColor[0] = alpha;
+ sParticle->m_uchColor[1] = alpha;
+ sParticle->m_uchColor[2] = alpha;
+ sParticle->m_uchStartAlpha = alpha;
+ sParticle->m_uchEndAlpha = 0;
+
+ sParticle->m_uchStartSize = random->RandomFloat( 1, 2 );
+ sParticle->m_uchEndSize = 0;
+ }
+ }
+
+ return BaseClass::DrawModel( flags );
+}
+
diff --git a/game/client/portal/c_weapon_portalgun.cpp b/game/client/portal/c_weapon_portalgun.cpp
new file mode 100644
index 0000000..9695158
--- /dev/null
+++ b/game/client/portal/c_weapon_portalgun.cpp
@@ -0,0 +1,1112 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "c_portal_player.h"
+#include "c_te_effect_dispatch.h"
+#include "iviewrender_beams.h"
+#include "model_types.h"
+#include "fx_interpvalue.h"
+#include "clienteffectprecachesystem.h"
+#include "bone_setup.h"
+#include "c_rumble.h"
+#include "rumble_shared.h"
+
+#include "weapon_portalgun_shared.h"
+
+
+#define SPRITE_SCALE 128.0f
+
+ConVar cl_portalgun_effects_min_alpha ("cl_portalgun_effects_min_alpha", "96", FCVAR_CLIENTDLL );
+ConVar cl_portalgun_effects_max_alpha ("cl_portalgun_effects_max_alpha", "128", FCVAR_CLIENTDLL );
+
+ConVar cl_portalgun_effects_min_size ("cl_portalgun_effects_min_size", "3.0", FCVAR_CLIENTDLL );
+ConVar cl_portalgun_beam_size ("cl_portalgun_beam_size", "0.04", FCVAR_CLIENTDLL );
+
+// HACK HACK! Used to make the gun visually change when going through a cleanser!
+//ConVar cl_portalgun_effects_max_size ("cl_portalgun_effects_max_size", "2.5", FCVAR_REPLICATED );
+
+
+//Precahce the effects
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectPortalgun )
+CLIENTEFFECT_MATERIAL( PORTALGUN_BEAM_SPRITE )
+CLIENTEFFECT_MATERIAL( PORTALGUN_BEAM_SPRITE_NOZ )
+CLIENTEFFECT_MATERIAL( PORTALGUN_GLOW_SPRITE )
+CLIENTEFFECT_MATERIAL( PORTALGUN_ENDCAP_SPRITE )
+CLIENTEFFECT_MATERIAL( PORTALGUN_GRAV_ACTIVE_GLOW )
+CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL1_FIRED_LAST_GLOW )
+CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL2_FIRED_LAST_GLOW )
+CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE )
+CLIENTEFFECT_MATERIAL( PORTALGUN_PORTAL_TUBE_BEAM_SPRITE )
+CLIENTEFFECT_REGISTER_END()
+
+
+CPortalgunEffectBeam::CPortalgunEffectBeam( void )
+ : m_pBeam( NULL ),
+ m_fBrightness( 255.0f )
+{}
+
+CPortalgunEffectBeam::~CPortalgunEffectBeam( void )
+{
+ Release();
+}
+
+void CPortalgunEffectBeam::Release( void )
+{
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->flags = 0;
+ m_pBeam->die = gpGlobals->curtime - 1;
+
+ m_pBeam = NULL;
+ }
+}
+
+void CPortalgunEffectBeam::Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson )
+{
+ if ( m_pBeam != NULL )
+ return;
+
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_pStartEnt = pEntity;
+ beamInfo.m_nStartAttachment = startAttachment;
+ beamInfo.m_pEndEnt = pEntity;
+ beamInfo.m_nEndAttachment = endAttachment;
+ beamInfo.m_nType = TE_BEAMPOINTS;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = vec3_origin;
+
+ beamInfo.m_pszModelName = ( firstPerson ) ? PORTALGUN_BEAM_SPRITE_NOZ : PORTALGUN_BEAM_SPRITE;
+
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+
+ if ( firstPerson )
+ {
+ beamInfo.m_flWidth = 0.0f;
+ beamInfo.m_flEndWidth = 2.0f;
+ }
+ else
+ {
+ beamInfo.m_flWidth = 0.5f;
+ beamInfo.m_flEndWidth = 2.0f;
+ }
+
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 16;
+ beamInfo.m_flBrightness = 128.0;
+ beamInfo.m_flSpeed = 150.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 255.0;
+ beamInfo.m_flBlue = 255.0;
+ beamInfo.m_nSegments = 8;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = FBEAM_FOREVER;
+
+ m_pBeam = beams->CreateBeamEntPoint( beamInfo );
+
+ if ( m_pBeam )
+ {
+ m_pBeam->m_bDrawInMainRender = false;
+ m_pBeam->m_bDrawInPortalRender = false;
+ }
+}
+
+void CPortalgunEffectBeam::SetVisibleViewModel( bool visible /*= true*/ )
+{
+ if ( m_pBeam == NULL )
+ return;
+
+ m_pBeam->m_bDrawInMainRender = visible;
+}
+
+int CPortalgunEffectBeam::IsVisibleViewModel( void ) const
+{
+ if ( m_pBeam == NULL )
+ return false;
+
+ return m_pBeam->m_bDrawInMainRender;
+}
+
+void CPortalgunEffectBeam::SetVisible3rdPerson( bool visible /*= true*/ )
+{
+ if ( m_pBeam == NULL )
+ return;
+
+ m_pBeam->m_bDrawInPortalRender = visible;
+}
+
+int CPortalgunEffectBeam::SetVisible3rdPerson( void ) const
+{
+ if ( m_pBeam == NULL )
+ return false;
+
+ return m_pBeam->m_bDrawInPortalRender;
+}
+
+void CPortalgunEffectBeam::SetBrightness( float fBrightness )
+{
+ m_fBrightness = clamp( fBrightness, 0.0f, 255.0f );
+}
+
+void CPortalgunEffectBeam::DrawBeam( void )
+{
+ if ( m_pBeam )
+ m_pBeam->DrawModel( 0 );
+}
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPortalgun, DT_WeaponPortalgun )
+
+BEGIN_NETWORK_TABLE( C_WeaponPortalgun, DT_WeaponPortalgun )
+ RecvPropBool( RECVINFO( m_bCanFirePortal1 ) ),
+ RecvPropBool( RECVINFO( m_bCanFirePortal2 ) ),
+ RecvPropInt( RECVINFO( m_iLastFiredPortal ) ),
+ RecvPropBool( RECVINFO( m_bOpenProngs ) ),
+ RecvPropFloat( RECVINFO( m_fCanPlacePortal1OnThisSurface ) ),
+ RecvPropFloat( RECVINFO( m_fCanPlacePortal2OnThisSurface ) ),
+ RecvPropFloat( RECVINFO( m_fEffectsMaxSize1 ) ), // HACK HACK! Used to make the gun visually change when going through a cleanser!
+ RecvPropFloat( RECVINFO( m_fEffectsMaxSize2 ) ),
+ RecvPropInt( RECVINFO( m_EffectState ) ),
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( C_WeaponPortalgun )
+ DEFINE_PRED_FIELD( m_bCanFirePortal1, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bCanFirePortal2, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_iLastFiredPortal, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bOpenProngs, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fCanPlacePortal1OnThisSurface, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fCanPlacePortal2OnThisSurface, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_EffectState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_portalgun, C_WeaponPortalgun );
+PRECACHE_WEAPON_REGISTER(weapon_portalgun);
+
+
+void C_WeaponPortalgun::Spawn( void )
+{
+ Precache();
+
+ BaseClass::Spawn();
+
+ //SetThink( &C_BaseEntity::Think );
+ SetNextThink( gpGlobals->curtime + 0.1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::StartEffects( void )
+{
+ int i;
+
+ CBaseEntity *pModelView = ( ( GetOwner() ) ? ( ToBasePlayer( GetOwner() )->GetViewModel() ) : ( 0 ) );
+ CBaseEntity *pModelWorld = this;
+
+ if ( !pModelView )
+ {
+ pModelView = pModelWorld;
+ }
+
+ // ------------------------------------------
+ // Lights
+ // ------------------------------------------
+
+ if ( m_Parameters[PORTALGUN_GRAVLIGHT].GetMaterial() == NULL )
+ {
+ m_Parameters[PORTALGUN_GRAVLIGHT].GetScale().SetAbsolute( 0.018f * SPRITE_SCALE );
+ m_Parameters[PORTALGUN_GRAVLIGHT].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[PORTALGUN_GRAVLIGHT].SetAttachment( pModelView->LookupAttachment( "Body_light" ) );
+ m_Parameters[PORTALGUN_GRAVLIGHT].SetVisible( false );
+
+ if ( m_Parameters[PORTALGUN_GRAVLIGHT].SetMaterial( PORTALGUN_GRAV_ACTIVE_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ if ( m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].GetMaterial() == NULL )
+ {
+ m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].GetScale().SetAbsolute( 0.03f * SPRITE_SCALE );
+ m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetAttachment( pModelWorld->LookupAttachment( "Body_light" ) );
+ m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible( false );
+
+ if ( m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetMaterial( PORTALGUN_GRAV_ACTIVE_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ if ( m_Parameters[PORTALGUN_PORTAL1LIGHT].GetMaterial() == NULL )
+ {
+ m_Parameters[PORTALGUN_PORTAL1LIGHT].GetScale().SetAbsolute( 0.018f * SPRITE_SCALE );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT].SetAttachment( pModelView->LookupAttachment( "Body_light" ) );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisible( false );
+
+ if ( m_Parameters[PORTALGUN_PORTAL1LIGHT].SetMaterial( PORTALGUN_PORTAL1_FIRED_LAST_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ if ( m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].GetMaterial() == NULL )
+ {
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].GetScale().SetAbsolute( 0.03f * SPRITE_SCALE );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetAttachment( pModelWorld->LookupAttachment( "Body_light" ) );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible( false );
+
+ if ( m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetMaterial( PORTALGUN_PORTAL1_FIRED_LAST_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ if ( m_Parameters[PORTALGUN_PORTAL2LIGHT].GetMaterial() == NULL )
+ {
+ m_Parameters[PORTALGUN_PORTAL2LIGHT].GetScale().SetAbsolute( 0.018f * SPRITE_SCALE );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT].SetAttachment( pModelView->LookupAttachment( "Body_light" ) );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisible( false );
+
+ if ( m_Parameters[PORTALGUN_PORTAL2LIGHT].SetMaterial( PORTALGUN_PORTAL2_FIRED_LAST_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ if ( m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].GetMaterial() == NULL )
+ {
+ m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].GetScale().SetAbsolute( 0.03f * SPRITE_SCALE );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetAttachment( pModelWorld->LookupAttachment( "Body_light" ) );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetVisible( false );
+
+ if ( m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetMaterial( PORTALGUN_PORTAL2_FIRED_LAST_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Glows
+ // ------------------------------------------
+
+ const char *attachNamesGlow[NUM_GLOW_SPRITES] =
+ {
+ "Arm1_attach1",
+ "Arm1_attach2",
+ "Arm2_attach1",
+ "Arm2_attach2",
+ "Arm3_attach1",
+ "Arm3_attach2"
+ };
+
+ //Create the view glow sprites
+ for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 24.0f );
+
+ // Different for different views
+ m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesGlow[i-PORTALGUN_GLOW1] ) );
+ m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_GLOW_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ //Create the world glow sprites
+ for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.1f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 24.0f );
+
+ // Different for different views
+ m_Parameters[i].SetAttachment( pModelWorld->LookupAttachment( attachNamesGlow[i-PORTALGUN_GLOW1_WORLD] ) );
+ m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_GLOW_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // End caps
+ // ------------------------------------------
+
+ const char *attachNamesEndCap[NUM_ENDCAP_SPRITES] =
+ {
+ "Arm1_attach3",
+ "Arm2_attach3",
+ "Arm3_attach3",
+ };
+
+ //Create the endcap sprites
+ for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.02f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesEndCap[i-PORTALGUN_ENDCAP1] ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_ENDCAP_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ //Create the world endcap sprites
+ for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.04f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 128.0f );
+ m_Parameters[i].SetAttachment( pModelWorld->LookupAttachment( attachNamesEndCap[i-PORTALGUN_ENDCAP1_WORLD] ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_ENDCAP_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Internals
+ // ------------------------------------------
+
+ //Create the muzzle glow sprites
+ i = PORTALGUN_MUZZLE_GLOW;
+
+ if ( m_Parameters[i].GetMaterial() == NULL )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( 0.025f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
+ m_Parameters[i].SetAttachment( pModelView->LookupAttachment( "Inside_effects" ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ //Create the world muzzle glow sprites
+ i = PORTALGUN_MUZZLE_GLOW_WORLD;
+
+ if ( m_Parameters[i].GetMaterial() == NULL )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( 0.025f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
+ m_Parameters[i].SetAttachment( pModelWorld->LookupAttachment( "Inside_effects" ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_MUZZLE_GLOW_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Tube sprites
+ // ------------------------------------------
+
+ const char *attachNamesTubeBeam[NUM_TUBE_BEAM_SPRITES] =
+ {
+ "Beam_point1",
+ "Beam_point2",
+ "Beam_point3",
+ "Beam_point4",
+ "Beam_point5",
+ };
+
+ //Create the tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() == NULL )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_beam_size.GetFloat() * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
+ m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesTubeBeam[i-PORTALGUN_TUBE_BEAM1] ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_TUBE_BEAM_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+ }
+
+ //Create the world tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() == NULL )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_beam_size.GetFloat() * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
+ m_Parameters[i].SetAttachment( pModelView->LookupAttachment( attachNamesTubeBeam[i-PORTALGUN_TUBE_BEAM1_WORLD] ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PORTALGUN_PORTAL_TUBE_BEAM_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+ }
+
+ // ------------------------------------------
+ // Beams
+ // ------------------------------------------
+
+ // Setup the beams
+ int iBeam = 0;
+
+ if ( pModelView != pModelWorld )
+ {
+ m_Beams[iBeam++].Init( pModelView->LookupAttachment( "Arm1_attach3" ), pModelView->LookupAttachment( "muzzle" ), pModelView, true );
+ m_Beams[iBeam++].Init( pModelView->LookupAttachment( "Arm2_attach3" ), pModelView->LookupAttachment( "muzzle" ), pModelView, true );
+ m_Beams[iBeam++].Init( pModelView->LookupAttachment( "Arm3_attach3" ), pModelView->LookupAttachment( "muzzle" ), pModelView, true );
+ }
+ else
+ {
+ iBeam += 3;
+ }
+
+ m_Beams[iBeam++].Init( pModelWorld->LookupAttachment( "Arm1_attach3" ), pModelWorld->LookupAttachment( "muzzle" ), pModelWorld, false );
+ m_Beams[iBeam++].Init( pModelWorld->LookupAttachment( "Arm2_attach3" ), pModelWorld->LookupAttachment( "muzzle" ), pModelWorld, false );
+ m_Beams[iBeam++].Init( pModelWorld->LookupAttachment( "Arm3_attach3" ), pModelWorld->LookupAttachment( "muzzle" ), pModelWorld, false );
+}
+
+void C_WeaponPortalgun::DestroyEffects( void )
+{
+ // Free our beams
+ for ( int i = 0; i < NUM_PORTALGUN_BEAMS; ++i )
+ {
+ m_Beams[i].Release();
+ }
+
+ // Stop everything
+ StopEffects();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ready effects
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::DoEffectReady( void )
+{
+ int i;
+
+ // Turn on the glow sprites
+ for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.4f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 32.0f, 0.2f );
+ m_Parameters[i].SetVisibleViewModel();
+ }
+
+ for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.8f * SPRITE_SCALE, 0.4f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 32.0f, 0.2f );
+ m_Parameters[i].SetVisible3rdPerson();
+ }
+
+ // Turn on the endcap sprites
+ for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ // Turn on the world endcap sprites
+ for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ // Turn on the internal sprites
+ i = PORTALGUN_MUZZLE_GLOW;
+
+ Vector colorMagSprites = GetEffectColor( i );
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisibleViewModel();
+
+ // Turn on the world internal sprites
+ i = PORTALGUN_MUZZLE_GLOW_WORLD;
+
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisible3rdPerson();
+
+ // Turn on the tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
+ {
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisibleViewModel();
+ }
+
+ // Turn on the world tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisible3rdPerson();
+ }
+
+ // Turn off beams off
+ for ( i = 0; i < NUM_PORTALGUN_BEAMS; ++i )
+ {
+ m_Beams[i].SetVisibleViewModel( false );
+ m_Beams[i].SetVisible3rdPerson( false );
+ }
+
+ CPortal_Player* pPlayer = (CPortal_Player*)GetOwner();
+ if ( pPlayer )
+ {
+ RumbleEffect( RUMBLE_PHYSCANNON_OPEN, 0, RUMBLE_FLAG_STOP );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Holding effects
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::DoEffectHolding( void )
+{
+ int i;
+
+ // Turn on the glow sprites
+ for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisibleViewModel();
+ }
+
+ for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 1.0f * SPRITE_SCALE, 0.4f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible3rdPerson();
+ }
+
+ // Turn on the endcap sprites
+ for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisibleViewModel();
+ }
+
+ // Turn on the world endcap sprites
+ for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].SetVisible3rdPerson();
+ }
+
+ // Turn on the internal sprites
+ i = PORTALGUN_MUZZLE_GLOW;
+
+ Vector colorMagSprites = GetEffectColor( i );
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisibleViewModel();
+
+ // Turn on the world internal sprites
+ i = PORTALGUN_MUZZLE_GLOW_WORLD;
+
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisible3rdPerson();
+
+ // Turn on the tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
+ {
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisibleViewModel();
+ }
+
+ // Turn on the world tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
+ {
+ m_Parameters[i].SetColor( colorMagSprites );
+ m_Parameters[i].SetVisible3rdPerson();
+ }
+
+ // Set beams them visible
+ for ( i = 0; i < NUM_PORTALGUN_BEAMS / 2; ++i )
+ {
+ m_Beams[i].SetVisible3rdPerson( false );
+ m_Beams[i].SetVisibleViewModel();
+ m_Beams[i].SetBrightness( 128.0f );
+ }
+
+ for ( i; i < NUM_PORTALGUN_BEAMS; ++i )
+ {
+ m_Beams[i].SetVisibleViewModel( false );
+ m_Beams[i].SetVisible3rdPerson();
+ m_Beams[i].SetBrightness( 128.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shutdown for the weapon when it's holstered
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::DoEffectNone( void )
+{
+ int i;
+
+ //Turn off main glows
+ m_Parameters[PORTALGUN_GRAVLIGHT].SetVisible( false );
+ m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible( false );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisible( false );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible( false );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisible( false );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible( false );
+
+ for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ m_Parameters[PORTALGUN_MUZZLE_GLOW].SetVisible( false );
+ m_Parameters[PORTALGUN_MUZZLE_GLOW_WORLD].SetVisible( false );
+
+ for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ for ( i = 0; i < NUM_PORTALGUN_BEAMS; ++i )
+ {
+ m_Beams[i].SetVisibleViewModel( false );
+ m_Beams[i].SetVisible3rdPerson( false );
+ }
+}
+
+void C_WeaponPortalgun::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ //PreDataChanged.m_matrixThisToLinked = m_matrixThisToLinked;
+ m_bOldCanFirePortal1 = m_bCanFirePortal1;
+ m_bOldCanFirePortal1 = m_bCanFirePortal2;
+
+ BaseClass::OnPreDataChanged( updateType );
+}
+
+void C_WeaponPortalgun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ // Start thinking (Baseclass stops it)
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ {
+ C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
+ StartEffects();
+ }
+
+ DoEffect( m_EffectState );
+ }
+
+ // Update effect state when out of parity with the server
+ else if ( m_nOldEffectState != m_EffectState || m_bOldCanFirePortal1 != m_bCanFirePortal1 || m_bOldCanFirePortal2 != m_bCanFirePortal2 )
+ {
+ DoEffect( m_EffectState );
+ m_nOldEffectState = m_EffectState;
+
+ m_bOldCanFirePortal1 = m_bCanFirePortal1;
+ m_bOldCanFirePortal2 = m_bCanFirePortal2;
+ }
+}
+
+void C_WeaponPortalgun::ClientThink( void )
+{
+ CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );
+
+ if ( pPlayer && dynamic_cast<C_WeaponPortalgun*>( pPlayer->GetActiveWeapon() ) )
+ {
+ if ( m_EffectState == EFFECT_NONE )
+ {
+ //DoEffect( ( GetPlayerHeldEntity( pPlayer ) ) ? ( EFFECT_HOLDING ) : ( EFFECT_READY ) );
+ }
+
+ if ( m_EffectState != EFFECT_NONE )
+ {
+ // Showing a special color for holding is confusing... just use the last fired color -Jeep
+
+ //if ( m_bOpenProngs )
+ //{
+ // //Turn on the grav light
+ // m_Parameters[PORTALGUN_GRAVLIGHT].SetVisibleViewModel();
+ // m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible3rdPerson();
+
+ // m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisibleViewModel( false );
+ // m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible3rdPerson( false );
+ // m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisibleViewModel( false );
+ // m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetVisible3rdPerson( false );
+ //}
+ //else
+ {
+ m_Parameters[PORTALGUN_GRAVLIGHT].SetVisibleViewModel( false );
+ m_Parameters[PORTALGUN_GRAVLIGHT_WORLD].SetVisible3rdPerson( false );
+
+ //Turn on and off the correct fired last lights
+ m_Parameters[PORTALGUN_PORTAL1LIGHT].SetVisibleViewModel( m_iLastFiredPortal == 1 );
+ m_Parameters[PORTALGUN_PORTAL1LIGHT_WORLD].SetVisible3rdPerson( m_iLastFiredPortal == 1 );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT].SetVisibleViewModel( m_iLastFiredPortal == 2 );
+ m_Parameters[PORTALGUN_PORTAL2LIGHT_WORLD].SetVisible3rdPerson( m_iLastFiredPortal == 2 );
+ }
+ }
+ }
+
+ // Update our effects
+ DoEffectIdle();
+
+ NetworkStateChanged();
+}
+
+Vector C_WeaponPortalgun::GetEffectColor( int iPalletIndex )
+{
+ Color color;
+
+ // Showing a special color for holding is confusing... just use the last fired color -Jeep
+ /*if ( m_bOpenProngs )
+ {
+ color = UTIL_Portal_Color( 0 );
+ }
+ else */if ( m_iLastFiredPortal == 1 )
+ {
+ color = UTIL_Portal_Color( 1 );
+ }
+ else if ( m_iLastFiredPortal == 2 )
+ {
+ color = UTIL_Portal_Color( 2 );
+ }
+ else
+ {
+ color = Color( 128, 128, 128, 255 );
+ }
+
+ Vector vColor;
+ vColor.x = color.r();
+ vColor.y = color.g();
+ vColor.z = color.b();
+
+ return vColor;
+}
+
+extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the complete list of values needed to render an effect from an
+// effect parameter
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment, bool b3rdPerson )
+{
+ const float dt = gpGlobals->curtime;
+
+ // Get alpha
+ float alpha = m_Parameters[effectID].GetAlpha().Interp( dt );
+
+ // Get scale
+ scale = m_Parameters[effectID].GetScale().Interp( dt );
+
+ // Get material
+ *pMaterial = (IMaterial *) m_Parameters[effectID].GetMaterial();
+
+ // Setup the color
+ color.r = (int) m_Parameters[effectID].GetColor().x;
+ color.g = (int) m_Parameters[effectID].GetColor().y;
+ color.b = (int) m_Parameters[effectID].GetColor().z;
+ color.a = (int) alpha;
+
+ // Setup the attachment
+ int attachment = m_Parameters[effectID].GetAttachment();
+ QAngle angles;
+
+ // Format for first-person
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ C_BaseAnimating *pModel;
+ int originalModelIndex = 0;
+
+ if ( b3rdPerson )
+ {
+ pModel = this;
+ originalModelIndex = GetModelIndex();
+ SetModelIndex( GetWorldModelIndex() );
+ }
+ else
+ {
+ pModel = pOwner->GetViewModel();
+ }
+
+ pModel->GetAttachment( attachment, vecAttachment, angles );
+
+ if ( !b3rdPerson )
+ {
+ ::FormatViewModelAttachment( vecAttachment, true );
+ }
+ else
+ {
+ SetModelIndex( originalModelIndex );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Whether or not an effect is set to display
+//-----------------------------------------------------------------------------
+bool C_WeaponPortalgun::IsEffectVisible( EffectType_t effectID, bool b3rdPerson )
+{
+ if ( b3rdPerson )
+ {
+ return m_Parameters[effectID].IsVisible3rdPerson();
+ }
+ else
+ {
+ return m_Parameters[effectID].IsVisibleViewModel();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the effect sprite, given an effect parameter ID
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::DrawEffectSprite( EffectType_t effectID, bool b3rdPerson )
+{
+ color32 color;
+ float scale;
+ IMaterial *pMaterial;
+ Vector vecAttachment;
+
+ // Don't draw invisible effects
+ if ( !IsEffectVisible( effectID, b3rdPerson ) )
+ return;
+
+ // Get all of our parameters
+ GetEffectParameters( effectID, color, scale, &pMaterial, vecAttachment, b3rdPerson );
+
+ // Don't render fully translucent objects
+ if ( color.a <= 0.0f )
+ return;
+
+ // Draw the sprite
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( pMaterial, this );
+ DrawSprite( vecAttachment, scale, scale, color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render our third-person effects
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::DrawEffects( bool b3rdPerson )
+{
+ for ( int i = 0; i < NUM_PORTALGUN_PARAMETERS; i++ )
+ {
+ DrawEffectSprite( (EffectType_t) i, b3rdPerson );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Third-person function call to render world model
+//-----------------------------------------------------------------------------
+int C_WeaponPortalgun::DrawModel( int flags )
+{
+ // Only render these on the transparent pass
+ /*if ( flags & STUDIO_TRANSPARENCY )
+ {
+ DrawEffects( true );
+ return 1;
+ }*/
+
+ int iRetValue = BaseClass::DrawModel( flags );
+
+ if ( iRetValue )
+ {
+ DrawEffects( true );
+ }
+
+ return iRetValue;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: First-person function call after viewmodel has been drawn
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
+{
+ // Render our effects
+ DrawEffects( false );
+
+ // Pass this back up
+ BaseClass::ViewModelDrawn( pBaseViewModel );
+}
+
+void UpdatePoseParameter( C_BaseAnimating *pBaseAnimating, int iPose, float fValue )
+{
+ pBaseAnimating->SetPoseParameter( iPose, fValue );
+}
+
+void InterpToward( float *pfCurrent, float fGoal, float fRate )
+{
+ if ( *pfCurrent < fGoal )
+ {
+ *pfCurrent += fRate;
+
+ if ( *pfCurrent > fGoal )
+ {
+ *pfCurrent = fGoal;
+ }
+ }
+ else if ( *pfCurrent > fGoal )
+ {
+ *pfCurrent -= fRate;
+
+ if ( *pfCurrent < fGoal )
+ {
+ *pfCurrent = fGoal;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle effect (pulsing)
+//-----------------------------------------------------------------------------
+void C_WeaponPortalgun::DoEffectIdle( void )
+{
+ StartEffects();
+
+ int i;
+
+ // Turn on the glow sprites
+ for ( i = PORTALGUN_GLOW1; i < (PORTALGUN_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( RandomFloat( 0.0075f, 0.05f ) * SPRITE_SCALE, 0.1f );
+ m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 10, 24 ) );
+ }
+
+ // Turn on the world glow sprites
+ for ( i = PORTALGUN_GLOW1_WORLD; i < (PORTALGUN_GLOW1_WORLD+NUM_GLOW_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( RandomFloat( 0.015f, 0.1f ) * SPRITE_SCALE, 0.1f );
+ m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 10, 24 ) );
+ }
+
+ // Turn on the endcap sprites
+ for ( i = PORTALGUN_ENDCAP1; i < (PORTALGUN_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( RandomFloat( 1.0f, 3.0f ) );
+ m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 96, 128 ) );
+ }
+
+ // Turn on the world endcap sprites
+ for ( i = PORTALGUN_ENDCAP1_WORLD; i < (PORTALGUN_ENDCAP1_WORLD+NUM_ENDCAP_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( RandomFloat( 2.0f, 6.0f ) );
+ m_Parameters[i].GetAlpha().SetAbsolute( RandomInt( 96, 128 ) );
+ }
+
+ // Turn on the internal sprites
+ i = PORTALGUN_MUZZLE_GLOW;
+
+ if ( m_bPulseUp )
+ {
+ m_fPulse += gpGlobals->frametime;
+ if ( m_fPulse > 1.0f )
+ {
+ m_fPulse = 1.0f;
+ m_bPulseUp = !m_bPulseUp;
+ }
+ }
+ else
+ {
+ m_fPulse -= gpGlobals->frametime;
+ if ( m_fPulse < 0.0f )
+ {
+ m_fPulse = 0.0f;
+ m_bPulseUp = !m_bPulseUp;
+ }
+ }
+
+ m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_effects_min_size.GetFloat() + ( m_fEffectsMaxSize1 - cl_portalgun_effects_min_size.GetFloat() ) * m_fPulse );
+ m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
+ Vector colorMagSprites = GetEffectColor( i );
+ m_Parameters[i].SetColor( colorMagSprites );
+
+ // Turn on the world internal sprites
+ i = PORTALGUN_MUZZLE_GLOW_WORLD;
+
+ m_Parameters[i].GetScale().SetAbsolute( cl_portalgun_effects_min_size.GetFloat() + ( m_fEffectsMaxSize1 - cl_portalgun_effects_min_size.GetFloat() ) * m_fPulse );
+ m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
+ m_Parameters[i].SetColor( colorMagSprites );
+
+ // Turn on the tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1; i < (PORTALGUN_TUBE_BEAM1+NUM_TUBE_BEAM_SPRITES); i++ )
+ {
+ m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
+ m_Parameters[i].SetColor( colorMagSprites );
+ }
+
+ // Turn on the world tube beam sprites
+ for ( i = PORTALGUN_TUBE_BEAM1_WORLD; i < (PORTALGUN_TUBE_BEAM1_WORLD+NUM_TUBE_BEAM_SPRITES_WORLD); i++ )
+ {
+ m_Parameters[i].GetAlpha().SetAbsolute( cl_portalgun_effects_min_alpha.GetInt() + ( cl_portalgun_effects_max_alpha.GetInt() - cl_portalgun_effects_min_alpha.GetInt() ) * m_fPulse );
+ m_Parameters[i].SetColor( colorMagSprites );
+ }
+}
diff --git a/game/client/portal/c_weapon_portalgun.h b/game/client/portal/c_weapon_portalgun.h
new file mode 100644
index 0000000..7349b79
--- /dev/null
+++ b/game/client/portal/c_weapon_portalgun.h
@@ -0,0 +1,278 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef C_WEAPON_PORTALGUN_H
+#define C_WEAPON_PORTALGUN_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_portalbasecombatweapon.h"
+
+#include "c_prop_portal.h"
+#include "fx_interpvalue.h"
+#include "beamdraw.h"
+#include "iviewrender_beams.h"
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CPhysCannonEffect class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+class CPortalgunEffect
+{
+public:
+
+ CPortalgunEffect( void )
+ : m_vecColor( 255, 255, 255 ),
+ m_bVisibleViewModel( true ),
+ m_bVisible3rdPerson( true ),
+ m_nAttachment( -1 )
+ {}
+
+ void SetAttachment( int attachment ) { m_nAttachment = attachment; }
+ int GetAttachment( void ) const { return m_nAttachment; }
+
+ void SetVisible( bool visible = true ) { m_bVisibleViewModel = visible; m_bVisible3rdPerson = visible; }
+
+ void SetVisibleViewModel( bool visible = true ) { m_bVisibleViewModel = visible; }
+ int IsVisibleViewModel( void ) const { return m_bVisibleViewModel; }
+
+ void SetVisible3rdPerson( bool visible = true ) { m_bVisible3rdPerson = visible; }
+ int IsVisible3rdPerson( void ) const { return m_bVisible3rdPerson; }
+
+ void SetColor( const Vector &color ) { m_vecColor = color; }
+ const Vector &GetColor( void ) const { return m_vecColor; }
+
+ bool SetMaterial( const char *materialName )
+ {
+ m_hMaterial.Init( materialName, TEXTURE_GROUP_CLIENT_EFFECTS );
+ return ( m_hMaterial != NULL );
+ }
+
+ CMaterialReference &GetMaterial( void ) { return m_hMaterial; }
+
+ CInterpolatedValue &GetAlpha( void ) { return m_Alpha; }
+ CInterpolatedValue &GetScale( void ) { return m_Scale; }
+
+ virtual PortalWeaponID GetWeaponID( void ) const { return WEAPON_PORTALGUN; }
+
+private:
+ CInterpolatedValue m_Alpha;
+ CInterpolatedValue m_Scale;
+
+ Vector m_vecColor;
+ bool m_bVisibleViewModel;
+ bool m_bVisible3rdPerson;
+ int m_nAttachment;
+ CMaterialReference m_hMaterial;
+};
+
+
+class CPortalgunEffectBeam
+{
+public:
+ CPortalgunEffectBeam( void );;
+ ~CPortalgunEffectBeam( void );
+
+ void Release( void );
+
+ void Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson );
+
+ void SetVisibleViewModel( bool visible = true );
+ int IsVisibleViewModel( void ) const;
+
+ void SetVisible3rdPerson( bool visible = true );
+ int SetVisible3rdPerson( void ) const;
+
+ void SetBrightness( float fBrightness );
+
+ void DrawBeam( void );
+
+private:
+ Beam_t *m_pBeam;
+
+ float m_fBrightness;
+};
+
+
+class C_WeaponPortalgun : public CBasePortalCombatWeapon
+{
+
+public:
+ DECLARE_CLASS( C_WeaponPortalgun, CBasePortalCombatWeapon );
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+private:
+ CNetworkVar( bool, m_bCanFirePortal1 ); // Is able to use primary fire
+ CNetworkVar( bool, m_bCanFirePortal2 ); // Is able to use secondary fire
+ CNetworkVar( int, m_iLastFiredPortal ); // Which portal was placed last
+ CNetworkVar( bool, m_bOpenProngs ); // Which portal was placed last
+ CNetworkVar( float, m_fCanPlacePortal1OnThisSurface ); // Tells the gun if it can place on the surface it's pointing at
+ CNetworkVar( float, m_fCanPlacePortal2OnThisSurface ); // Tells the gun if it can place on the surface it's pointing at
+
+ CNetworkVar( float, m_fEffectsMaxSize1 );
+ CNetworkVar( float, m_fEffectsMaxSize2 );
+
+public:
+ virtual const Vector& GetBulletSpread( void )
+ {
+ static Vector cone = VECTOR_CONE_10DEGREES;
+ return cone;
+ }
+
+ void Precache ( void );
+
+ virtual void OnRestore( void );
+ virtual void UpdateOnRemove( void );
+ void Spawn( void );
+ void DoEffectCreate( Vector &vDir, Vector &ptStart, Vector &ptEnd, bool bPortal1, bool bPlayer );
+
+ virtual bool ShouldDrawCrosshair( void );
+ float GetPortal1Placablity( void ) { return m_fCanPlacePortal1OnThisSurface; }
+ float GetPortal2Placablity( void ) { return m_fCanPlacePortal2OnThisSurface; }
+ int GetLastFiredPortal( void ) { return m_iLastFiredPortal; }
+ bool IsHoldingObject( void ) { return m_bOpenProngs; }
+
+ bool Reload( void );
+ void FillClip( void );
+ void CheckHolsterReload( void );
+ void ItemHolsterFrame( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ bool Deploy( void );
+
+ void SetCanFirePortal1( bool bCanFire = true );
+ void SetCanFirePortal2( bool bCanFire = true );
+ float CanFirePortal1( void ) { return m_bCanFirePortal1; }
+ float CanFirePortal2( void ) { return m_bCanFirePortal2; }
+
+ void PrimaryAttack( void );
+ void SecondaryAttack( void );
+
+ void DelayAttack( float fDelay );
+
+ void DryFire( void );
+ virtual float GetFireRate( void ) { return 0.7; };
+ void WeaponIdle( void );
+
+protected:
+
+ void StartEffects( void ); // Initialize all sprites and beams
+ void StopEffects( bool stopSound = true ); // Hide all effects temporarily
+ void DestroyEffects( void ); // Destroy all sprites and beams
+
+ // Portalgun effects
+ void DoEffect( int effectType, Vector *pos = NULL );
+
+ void DoEffectClosed( void );
+ void DoEffectReady( void );
+ void DoEffectHolding( void );
+ void DoEffectNone( void );
+
+ enum EffectType_t
+ {
+ PORTALGUN_GRAVLIGHT = 0,
+ PORTALGUN_GRAVLIGHT_WORLD,
+ PORTALGUN_PORTAL1LIGHT,
+ PORTALGUN_PORTAL1LIGHT_WORLD,
+ PORTALGUN_PORTAL2LIGHT,
+ PORTALGUN_PORTAL2LIGHT_WORLD,
+
+ PORTALGUN_GLOW1, // Must be in order!
+ PORTALGUN_GLOW2,
+ PORTALGUN_GLOW3,
+ PORTALGUN_GLOW4,
+ PORTALGUN_GLOW5,
+ PORTALGUN_GLOW6,
+
+ PORTALGUN_GLOW1_WORLD,
+ PORTALGUN_GLOW2_WORLD,
+ PORTALGUN_GLOW3_WORLD,
+ PORTALGUN_GLOW4_WORLD,
+ PORTALGUN_GLOW5_WORLD,
+ PORTALGUN_GLOW6_WORLD,
+
+ PORTALGUN_ENDCAP1, // Must be in order!
+ PORTALGUN_ENDCAP2,
+ PORTALGUN_ENDCAP3,
+
+ PORTALGUN_ENDCAP1_WORLD,
+ PORTALGUN_ENDCAP2_WORLD,
+ PORTALGUN_ENDCAP3_WORLD,
+
+ PORTALGUN_MUZZLE_GLOW,
+
+ PORTALGUN_MUZZLE_GLOW_WORLD,
+
+ PORTALGUN_TUBE_BEAM1,
+ PORTALGUN_TUBE_BEAM2,
+ PORTALGUN_TUBE_BEAM3,
+ PORTALGUN_TUBE_BEAM4,
+ PORTALGUN_TUBE_BEAM5,
+
+ PORTALGUN_TUBE_BEAM1_WORLD,
+ PORTALGUN_TUBE_BEAM2_WORLD,
+ PORTALGUN_TUBE_BEAM3_WORLD,
+ PORTALGUN_TUBE_BEAM4_WORLD,
+ PORTALGUN_TUBE_BEAM5_WORLD,
+
+ NUM_PORTALGUN_PARAMETERS // Must be last!
+ };
+
+ #define NUM_GLOW_SPRITES ((C_WeaponPortalgun::PORTALGUN_GLOW6-C_WeaponPortalgun::PORTALGUN_GLOW1)+1)
+ #define NUM_GLOW_SPRITES_WORLD ((C_WeaponPortalgun::PORTALGUN_GLOW6_WORLD-C_WeaponPortalgun::PORTALGUN_GLOW1_WORLD)+1)
+ #define NUM_ENDCAP_SPRITES ((C_WeaponPortalgun::PORTALGUN_ENDCAP3-C_WeaponPortalgun::PORTALGUN_ENDCAP1)+1)
+ #define NUM_ENDCAP_SPRITES_WORLD ((C_WeaponPortalgun::PORTALGUN_ENDCAP3_WORLD-C_WeaponPortalgun::PORTALGUN_ENDCAP1_WORLD)+1)
+ #define NUM_TUBE_BEAM_SPRITES ((C_WeaponPortalgun::PORTALGUN_TUBE_BEAM5-C_WeaponPortalgun::PORTALGUN_TUBE_BEAM1)+1)
+ #define NUM_TUBE_BEAM_SPRITES_WORLD ((C_WeaponPortalgun::PORTALGUN_TUBE_BEAM5_WORLD-C_WeaponPortalgun::PORTALGUN_TUBE_BEAM1_WORLD)+1)
+
+ #define NUM_PORTALGUN_BEAMS 6
+
+ void DrawEffects( bool b3rdPerson );
+ Vector GetEffectColor( int iPalletIndex );
+ void GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment, bool b3rdPerson );
+ void DrawEffectSprite( EffectType_t effectID, bool b3rdPerson );
+ inline bool IsEffectVisible( EffectType_t effectID, bool b3rdPerson );
+ void UpdateElementPosition( void );
+
+ CPortalgunEffect m_Parameters[NUM_PORTALGUN_PARAMETERS]; // Interpolated parameters for the effects
+ CPortalgunEffectBeam m_Beams[NUM_PORTALGUN_BEAMS]; // Beams
+
+ int m_nOldEffectState; // Used for parity checks
+ bool m_bOldCanFirePortal1;
+ bool m_bOldCanFirePortal2;
+
+ bool m_bPulseUp;
+ float m_fPulse;
+
+ CNetworkVar( int, m_EffectState ); // Current state of the effects on the gun
+
+public:
+
+ virtual int DrawModel( int flags );
+ virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
+ virtual void OnPreDataChanged( DataUpdateType_t updateType );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void ClientThink( void );
+
+ void DoEffectIdle( void );
+
+public:
+
+ DECLARE_ACTTABLE();
+
+ C_WeaponPortalgun(void);
+
+private:
+ C_WeaponPortalgun( const C_WeaponPortalgun & );
+
+};
+
+
+#endif // C_WEAPON_PORTALGUN_H
diff --git a/game/client/portal/c_weapon_stubs_portal.cpp b/game/client/portal/c_weapon_stubs_portal.cpp
new file mode 100644
index 0000000..bbaa58c
--- /dev/null
+++ b/game/client/portal/c_weapon_stubs_portal.cpp
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_weapon__stubs.h"
+#include "basehlcombatweapon_shared.h"
+#include "c_basehlcombatweapon.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+STUB_WEAPON_CLASS( cycler_weapon, WeaponCycler, C_BaseCombatWeapon );
+
+STUB_WEAPON_CLASS( weapon_binoculars, WeaponBinoculars, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_bugbait, WeaponBugBait, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_flaregun, Flaregun, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_annabelle, WeaponAnnabelle, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_gauss, WeaponGaussGun, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_cubemap, WeaponCubemap, C_BaseCombatWeapon );
+STUB_WEAPON_CLASS( weapon_alyxgun, WeaponAlyxGun, C_HLSelectFireMachineGun );
+STUB_WEAPON_CLASS( weapon_citizenpackage, WeaponCitizenPackage, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_citizensuitcase, WeaponCitizenSuitcase, C_WeaponCitizenPackage );
+
+STUB_WEAPON_CLASS( weapon_ar2, WeaponAR2, C_HLMachineGun );
+STUB_WEAPON_CLASS( weapon_frag, WeaponFrag, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_rpg, WeaponRPG, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_pistol, WeaponPistol, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_shotgun, WeaponShotgun, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_smg1, WeaponSMG1, C_HLSelectFireMachineGun );
+STUB_WEAPON_CLASS( weapon_357, Weapon357, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_crossbow, WeaponCrossbow, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_slam, Weapon_SLAM, C_BaseHLCombatWeapon );
+STUB_WEAPON_CLASS( weapon_crowbar, WeaponCrowbar, C_BaseHLBludgeonWeapon );
+STUB_WEAPON_CLASS( weapon_hopwire, WeaponHopwire, C_BaseHLCombatWeapon );
+
+
diff --git a/game/client/portal/clientmode_portal.cpp b/game/client/portal/clientmode_portal.cpp
new file mode 100644
index 0000000..d1fbb00
--- /dev/null
+++ b/game/client/portal/clientmode_portal.cpp
@@ -0,0 +1,227 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "ivmodemanager.h"
+#include "clientmode_hlnormal.h"
+#include "panelmetaclassmgr.h"
+#include "c_playerresource.h"
+#include "c_portal_player.h"
+#include "clientmode_portal.h"
+#include "usermessages.h"
+#include "prediction.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// default FOV for HL2
+ConVar default_fov( "default_fov", "75", FCVAR_CHEAT );
+ConVar fov_desired( "fov_desired", "75", FCVAR_ARCHIVE | FCVAR_USERINFO, "Sets the base field-of-view.", true, 75.0, true, 90.0 );
+
+
+// The current client mode. Always ClientModeNormal in HL.
+IClientMode *g_pClientMode = NULL;
+
+//extern EHANDLE g_eKillTarget1;
+//extern EHANDLE g_eKillTarget2;
+
+vgui::HScheme g_hVGuiCombineScheme = 0;
+
+#define SCREEN_FILE "scripts/vgui_screens.txt"
+
+//void MsgFunc_KillCam(bf_read &msg)
+//{
+// C_Portal_Player *pPlayer = C_Portal_Player::GetLocalPortalPlayer();
+//
+// if ( !pPlayer )
+// return;
+//
+// g_eKillTarget1 = 0;
+// g_eKillTarget2 = 0;
+// g_nKillCamTarget1 = 0;
+// g_nKillCamTarget1 = 0;
+//
+// long iEncodedEHandle = msg.ReadLong();
+//
+// if( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE )
+// return;
+//
+// int iEntity = iEncodedEHandle & ((1 << MAX_EDICT_BITS) - 1);
+// int iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS;
+//
+// EHANDLE hEnt1( iEntity, iSerialNum );
+//
+// iEncodedEHandle = msg.ReadLong();
+//
+// if( iEncodedEHandle == INVALID_NETWORKED_EHANDLE_VALUE )
+// return;
+//
+// iEntity = iEncodedEHandle & ((1 << MAX_EDICT_BITS) - 1);
+// iSerialNum = iEncodedEHandle >> MAX_EDICT_BITS;
+//
+// EHANDLE hEnt2( iEntity, iSerialNum );
+//
+// g_eKillTarget1 = hEnt1;
+// g_eKillTarget2 = hEnt2;
+//
+// if ( g_eKillTarget1.Get() )
+// {
+// g_nKillCamTarget1 = g_eKillTarget1.Get()->entindex();
+// }
+//
+// if ( g_eKillTarget2.Get() )
+// {
+// g_nKillCamTarget2 = g_eKillTarget2.Get()->entindex();
+// }
+//}
+
+//-----------------------------------------------------------------------------
+// Purpose: this is the viewport that contains all the hud elements
+//-----------------------------------------------------------------------------
+class CHudViewport : public CBaseViewport
+{
+private:
+ DECLARE_CLASS_SIMPLE( CHudViewport, CBaseViewport );
+
+protected:
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme )
+ {
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ gHUD.InitColors( pScheme );
+
+ SetPaintBackgroundEnabled( false );
+ }
+
+ virtual IViewPortPanel *CreatePanelByName( const char *szPanelName );
+};
+
+IViewPortPanel* CHudViewport::CreatePanelByName( const char *szPanelName )
+{
+ /*IViewPortPanel* newpanel = NULL;
+
+ if ( Q_strcmp( PANEL_SCOREBOARD, szPanelName) == 0 )
+ {
+ newpanel = new CHL2MPClientScoreBoardDialog( this );
+ return newpanel;
+ }
+ else if ( Q_strcmp(PANEL_INFO, szPanelName) == 0 )
+ {
+ newpanel = new CHL2MPTextWindow( this );
+ return newpanel;
+ }*/
+
+ return BaseClass::CreatePanelByName( szPanelName );
+}
+
+
+class CHLModeManager : public IVModeManager
+{
+public:
+ CHLModeManager( void );
+ virtual ~CHLModeManager( void );
+
+ virtual void Init( void );
+ virtual void SwitchMode( bool commander, bool force );
+ virtual void OverrideView( CViewSetup *pSetup );
+ virtual void CreateMove( float flInputSampleTime, CUserCmd *cmd );
+ virtual void LevelInit( const char *newmap );
+ virtual void LevelShutdown( void );
+};
+
+CHLModeManager::CHLModeManager( void )
+{
+}
+
+CHLModeManager::~CHLModeManager( void )
+{
+}
+
+void CHLModeManager::Init( void )
+{
+ g_pClientMode = GetClientModeNormal();
+ PanelMetaClassMgr()->LoadMetaClassDefinitionFile( SCREEN_FILE );
+}
+
+void CHLModeManager::SwitchMode( bool commander, bool force )
+{
+}
+
+void CHLModeManager::OverrideView( CViewSetup *pSetup )
+{
+}
+
+void CHLModeManager::CreateMove( float flInputSampleTime, CUserCmd *cmd )
+{
+}
+
+void CHLModeManager::LevelInit( const char *newmap )
+{
+ g_pClientMode->LevelInit( newmap );
+
+ if ( g_nKillCamMode > OBS_MODE_NONE )
+ {
+ g_bForceCLPredictOff = false;
+ }
+
+ g_nKillCamMode = OBS_MODE_NONE;
+ //g_nKillCamTarget1 = 0;
+ //g_nKillCamTarget2 = 0;
+}
+
+void CHLModeManager::LevelShutdown( void )
+{
+ g_pClientMode->LevelShutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+ClientModePortalNormal::ClientModePortalNormal()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If you don't know what a destructor is by now, you are probably going to get fired
+//-----------------------------------------------------------------------------
+ClientModePortalNormal::~ClientModePortalNormal()
+{
+}
+
+void ClientModePortalNormal::Init()
+{
+ BaseClass::Init();
+
+ //usermessages->HookMessage( "KillCam", MsgFunc_KillCam );
+}
+
+void ClientModePortalNormal::InitViewport()
+{
+ m_pViewport = new CHudViewport();
+ m_pViewport->Start( gameuifuncs, gameeventmanager );
+}
+
+ClientModePortalNormal g_ClientModeNormal;
+
+IClientMode *GetClientModeNormal()
+{
+ return &g_ClientModeNormal;
+}
+
+
+ClientModePortalNormal* GetClientModePortalNormal()
+{
+ Assert( dynamic_cast< ClientModePortalNormal* >( GetClientModeNormal() ) );
+
+ return static_cast< ClientModePortalNormal* >( GetClientModeNormal() );
+}
+
+
+static CHLModeManager g_HLModeManager;
+IVModeManager *modemanager = &g_HLModeManager;
+
diff --git a/game/client/portal/clientmode_portal.h b/game/client/portal/clientmode_portal.h
new file mode 100644
index 0000000..5a4ddd6
--- /dev/null
+++ b/game/client/portal/clientmode_portal.h
@@ -0,0 +1,51 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef PORTAL_CLIENTMODE_H
+#define PORTAL_CLIENTMODE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "clientmode_shared.h"
+#include <vgui_controls/EditablePanel.h>
+#include <vgui/Cursor.h>
+
+class CHudViewport;
+
+namespace vgui
+{
+ typedef unsigned long HScheme;
+}
+
+class ClientModePortalNormal : public ClientModeShared
+{
+DECLARE_CLASS( ClientModePortalNormal, ClientModeShared );
+
+private:
+
+// IClientMode overrides.
+public:
+
+ ClientModePortalNormal();
+ virtual ~ClientModePortalNormal();
+
+ virtual void Init();
+ virtual void InitViewport();
+
+
+private:
+
+ // void UpdateSpectatorMode( void );
+
+};
+
+
+extern IClientMode *GetClientModeNormal();
+extern ClientModePortalNormal* GetClientModePortalNormal();
+
+
+#endif // PORTAL_CLIENTMODE_H
diff --git a/game/client/portal/fx_portal.cpp b/game/client/portal/fx_portal.cpp
new file mode 100644
index 0000000..e814d29
--- /dev/null
+++ b/game/client/portal/fx_portal.cpp
@@ -0,0 +1,123 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Fizzle effects for portal.
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "clienteffectprecachesystem.h"
+#include "fx.h"
+#include "fx_sparks.h"
+#include "iefx.h"
+#include "c_te_effect_dispatch.h"
+#include "particles_ez.h"
+#include "decals.h"
+#include "engine/IEngineSound.h"
+#include "fx_quad.h"
+#include "engine/ivdebugoverlay.h"
+#include "shareddefs.h"
+#include "portal_shareddefs.h"
+#include "effect_color_tables.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+class C_PortalBlast : public C_BaseEntity
+{
+ DECLARE_CLASS( C_PortalBlast, C_BaseAnimating );
+
+public:
+
+ static void Create( bool bIsPortal2, PortalPlacedByType ePlacedBy, const Vector &vStart, const Vector &vEnd, const QAngle &qAngles, float fDeathTime );
+
+ void Init( bool bIsPortal2, PortalPlacedByType ePlacedBy, const Vector &vStart, const Vector &vEnd, const QAngle &qAngles, float fDeathTime );
+
+ virtual void ClientThink( void );
+
+private:
+
+ Vector m_ptCreationPoint;
+ Vector m_ptDeathPoint;
+ Vector m_ptAimPoint;
+
+ float m_fCreationTime;
+ float m_fDeathTime;
+};
+
+
+void C_PortalBlast::Create( bool bIsPortal2, PortalPlacedByType ePlacedBy, const Vector &vStart, const Vector &vEnd, const QAngle &qAngles, float fDeathTime )
+{
+ C_PortalBlast *pPortalBlast = new C_PortalBlast;
+ pPortalBlast->Init( bIsPortal2, ePlacedBy, vStart, vEnd, qAngles, fDeathTime );
+}
+
+
+void C_PortalBlast::Init( bool bIsPortal2, PortalPlacedByType ePlacedBy, const Vector &vStart, const Vector &vEnd, const QAngle &qAngles, float fDeathTime )
+{
+ ClientEntityList().AddNonNetworkableEntity( this );
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
+
+ AddToLeafSystem( RENDER_GROUP_OPAQUE_ENTITY );
+
+ SetThink( &C_PortalBlast::ClientThink );
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ m_ptCreationPoint = vStart;
+ m_ptDeathPoint = vEnd;
+
+ SetAbsOrigin( m_ptCreationPoint );
+
+ m_fCreationTime = gpGlobals->curtime;
+ m_fDeathTime = fDeathTime;
+
+ if ( m_fDeathTime - 0.1f < m_fCreationTime )
+ {
+ m_fDeathTime = m_fCreationTime + 0.1f;
+ }
+
+ Vector vForward;
+ AngleVectors( qAngles, &vForward );
+
+ m_ptAimPoint = m_ptCreationPoint + vForward * m_ptCreationPoint.DistTo( m_ptDeathPoint );
+
+ if ( ePlacedBy == PORTAL_PLACED_BY_PLAYER )
+ ParticleProp()->Create( ( ( bIsPortal2 ) ? ( "portal_2_projectile_stream" ) : ( "portal_1_projectile_stream" ) ), PATTACH_ABSORIGIN_FOLLOW );
+ else
+ ParticleProp()->Create( ( ( bIsPortal2 ) ? ( "portal_2_projectile_stream_pedestal" ) : ( "portal_1_projectile_stream_pedestal" ) ), PATTACH_ABSORIGIN_FOLLOW );
+}
+
+void C_PortalBlast::ClientThink( void )
+{
+ if ( m_fCreationTime == 0.0f && m_fDeathTime == 0.0f )
+ {
+ // Die!
+ Remove();
+ return;
+ }
+
+ float fT = ( gpGlobals->curtime - m_fCreationTime ) / ( m_fDeathTime - m_fCreationTime );
+
+ if ( fT >= 1.0f )
+ {
+ // Ready to die! But we want one more frame in the final position
+ SetAbsOrigin( m_ptDeathPoint );
+
+ m_fCreationTime = 0.0f;
+ m_fDeathTime = 0.0f;
+
+ return;
+ }
+
+ // Set the interpolated position
+ Vector vTarget = m_ptAimPoint * ( 1.0f - fT ) + m_ptDeathPoint * fT;
+ SetAbsOrigin( m_ptCreationPoint * ( 1.0f - fT ) + vTarget * fT );
+}
+
+
+void PortalBlastCallback( const CEffectData & data )
+{
+ C_PortalBlast::Create( ( data.m_nColor == 1 ) ? ( false ) : ( true ), (PortalPlacedByType)data.m_nDamageType, data.m_vOrigin, data.m_vStart, data.m_vAngles, data.m_flScale );
+}
+
+DECLARE_CLIENT_EFFECT( "PortalBlast", PortalBlastCallback ); \ No newline at end of file
diff --git a/game/client/portal/hud_quickinfo.cpp b/game/client/portal/hud_quickinfo.cpp
new file mode 100644
index 0000000..29fe09e
--- /dev/null
+++ b/game/client/portal/hud_quickinfo.cpp
@@ -0,0 +1,383 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "hud.h"
+#include "hudelement.h"
+#include "iclientmode.h"
+#include "engine/IEngineSound.h"
+#include "vgui_controls/AnimationController.h"
+#include "vgui_controls/Controls.h"
+#include "vgui_controls/Panel.h"
+#include "vgui/ISurface.h"
+#include "c_portal_player.h"
+#include "c_weapon_portalgun.h"
+#include "IGameUIFuncs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define HEALTH_WARNING_THRESHOLD 25
+
+
+static ConVar hud_quickinfo( "hud_quickinfo", "1", FCVAR_ARCHIVE );
+static ConVar hud_quickinfo_swap( "hud_quickinfo_swap", "0", FCVAR_ARCHIVE );
+
+extern ConVar crosshair;
+
+#define QUICKINFO_EVENT_DURATION 1.0f
+#define QUICKINFO_BRIGHTNESS_FULL 255
+#define QUICKINFO_BRIGHTNESS_DIM 64
+#define QUICKINFO_FADE_IN_TIME 0.5f
+#define QUICKINFO_FADE_OUT_TIME 2.0f
+
+/*
+==================================================
+CHUDQuickInfo
+==================================================
+*/
+
+using namespace vgui;
+
+class CHUDQuickInfo : public CHudElement, public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CHUDQuickInfo, vgui::Panel );
+public:
+ CHUDQuickInfo( const char *pElementName );
+ void Init( void );
+ void VidInit( void );
+ bool ShouldDraw( void );
+ //virtual void OnThink();
+ virtual void Paint();
+
+ virtual void ApplySchemeSettings( IScheme *scheme );
+private:
+
+ void DrawWarning( int x, int y, CHudTexture *icon, float &time );
+ void UpdateEventTime( void );
+ bool EventTimeElapsed( void );
+
+ float m_flLastEventTime;
+
+ float m_fLastPlacedAlpha[2];
+ bool m_bLastPlacedAlphaCountingUp[2];
+
+ CHudTexture *m_icon_c;
+
+ CHudTexture *m_icon_rbn; // right bracket
+ CHudTexture *m_icon_lbn; // left bracket
+
+ CHudTexture *m_icon_rb; // right bracket, full
+ CHudTexture *m_icon_lb; // left bracket, full
+ CHudTexture *m_icon_rbe; // right bracket, empty
+ CHudTexture *m_icon_lbe; // left bracket, empty
+
+ CHudTexture *m_icon_rbnone; // right bracket
+ CHudTexture *m_icon_lbnone; // left bracket
+};
+
+DECLARE_HUDELEMENT( CHUDQuickInfo );
+
+CHUDQuickInfo::CHUDQuickInfo( const char *pElementName ) :
+ CHudElement( pElementName ), BaseClass( NULL, "HUDQuickInfo" )
+{
+ vgui::Panel *pParent = g_pClientMode->GetViewport();
+ SetParent( pParent );
+
+ SetHiddenBits( HIDEHUD_CROSSHAIR );
+
+ m_fLastPlacedAlpha[0] = m_fLastPlacedAlpha[1] = 80;
+ m_bLastPlacedAlphaCountingUp[0] = m_bLastPlacedAlphaCountingUp[1] = true;
+}
+
+void CHUDQuickInfo::ApplySchemeSettings( IScheme *scheme )
+{
+ BaseClass::ApplySchemeSettings( scheme );
+
+ SetPaintBackgroundEnabled( false );
+}
+
+
+void CHUDQuickInfo::Init( void )
+{
+ m_flLastEventTime = 0.0f;
+}
+
+
+void CHUDQuickInfo::VidInit( void )
+{
+ Init();
+
+ m_icon_c = gHUD.GetIcon( "crosshair" );
+
+ if ( IsX360() )
+ {
+ m_icon_rb = gHUD.GetIcon( "portal_crosshair_right_valid_x360" );
+ m_icon_lb = gHUD.GetIcon( "portal_crosshair_left_valid_x360" );
+ m_icon_rbe = gHUD.GetIcon( "portal_crosshair_last_placed_x360" );
+ m_icon_lbe = gHUD.GetIcon( "portal_crosshair_last_placed_x360" );
+ m_icon_rbn = gHUD.GetIcon( "portal_crosshair_right_invalid_x360" );
+ m_icon_lbn = gHUD.GetIcon( "portal_crosshair_left_invalid_x360" );
+ }
+ else
+ {
+ m_icon_rb = gHUD.GetIcon( "portal_crosshair_right_valid" );
+ m_icon_lb = gHUD.GetIcon( "portal_crosshair_left_valid" );
+ m_icon_rbe = gHUD.GetIcon( "portal_crosshair_last_placed" );
+ m_icon_lbe = gHUD.GetIcon( "portal_crosshair_last_placed" );
+ m_icon_rbn = gHUD.GetIcon( "portal_crosshair_right_invalid" );
+ m_icon_lbn = gHUD.GetIcon( "portal_crosshair_left_invalid" );
+ m_icon_rbnone = gHUD.GetIcon( "crosshair_right" );
+ m_icon_lbnone = gHUD.GetIcon( "crosshair_left" );
+ }
+}
+
+
+void CHUDQuickInfo::DrawWarning( int x, int y, CHudTexture *icon, float &time )
+{
+ float scale = (int)( fabs(sin(gpGlobals->curtime*8.0f)) * 128.0);
+
+ // Only fade out at the low point of our blink
+ if ( time <= (gpGlobals->frametime * 200.0f) )
+ {
+ if ( scale < 40 )
+ {
+ time = 0.0f;
+ return;
+ }
+ else
+ {
+ // Counteract the offset below to survive another frame
+ time += (gpGlobals->frametime * 200.0f);
+ }
+ }
+
+ // Update our time
+ time -= (gpGlobals->frametime * 200.0f);
+ Color caution = gHUD.m_clrCaution;
+ caution[3] = scale * 255;
+
+ icon->DrawSelf( x, y, caution );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Save CPU cycles by letting the HUD system early cull
+// costly traversal. Called per frame, return true if thinking and
+// painting need to occur.
+//-----------------------------------------------------------------------------
+bool CHUDQuickInfo::ShouldDraw( void )
+{
+ if ( !m_icon_c || !m_icon_rb || !m_icon_rbe || !m_icon_lb || !m_icon_lbe )
+ return false;
+
+ C_Portal_Player *player = ToPortalPlayer(C_BasePlayer::GetLocalPlayer());
+ if ( player == NULL )
+ return false;
+
+ if ( !crosshair.GetBool() )
+ return false;
+
+ if ( player->IsSuppressingCrosshair() )
+ return false;
+
+ return ( CHudElement::ShouldDraw() && !engine->IsDrawingLoadingImage() );
+}
+
+
+void CHUDQuickInfo::Paint()
+{
+ C_Portal_Player *pPortalPlayer = (C_Portal_Player*)( C_BasePlayer::GetLocalPlayer() );
+ if ( pPortalPlayer == NULL )
+ return;
+
+ C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
+ if ( pWeapon == NULL )
+ return;
+
+ int xCenter = ( ScreenWidth() - m_icon_c->Width() ) / 2;
+ int yCenter = ( ScreenHeight() - m_icon_c->Height() ) / 2;
+
+ Color clrNormal = gHUD.m_clrNormal;
+ clrNormal[3] = 255;
+
+ SetActive( true );
+
+ m_icon_c->DrawSelf( xCenter, yCenter, clrNormal );
+
+ // adjust center for the bigger crosshairs
+ xCenter = ScreenWidth() / 2;
+ yCenter = ( ScreenHeight() - m_icon_lb->Height() ) / 2;
+
+ C_WeaponPortalgun *pPortalgun = dynamic_cast<C_WeaponPortalgun*>( pWeapon );
+
+ bool bPortalPlacability[2];
+
+ if ( pPortalgun )
+ {
+ bPortalPlacability[0] = pPortalgun->GetPortal1Placablity() > 0.5f;
+ bPortalPlacability[1] = pPortalgun->GetPortal2Placablity() > 0.5f;
+ }
+
+ if ( !hud_quickinfo.GetInt() || !pPortalgun || ( !pPortalgun->CanFirePortal1() && !pPortalgun->CanFirePortal2() ) )
+ {
+ // no quickinfo or we can't fire either portal, just draw the small versions of the crosshairs
+ clrNormal[3] = 196;
+ m_icon_lbnone->DrawSelf(xCenter - (m_icon_lbnone->Width() * 2), yCenter, clrNormal);
+ m_icon_rbnone->DrawSelf(xCenter + m_icon_rbnone->Width(), yCenter, clrNormal);
+ return;
+ }
+
+ const unsigned char iAlphaStart = 150;
+
+ Color portal1Color = UTIL_Portal_Color( 1 );
+ Color portal2Color = UTIL_Portal_Color( 2 );
+
+ portal1Color[ 3 ] = iAlphaStart;
+ portal2Color[ 3 ] = iAlphaStart;
+
+ const int iBaseLastPlacedAlpha = 128;
+ Color lastPlaced1Color = Color( portal1Color[0], portal1Color[1], portal1Color[2], iBaseLastPlacedAlpha );
+ Color lastPlaced2Color = Color( portal2Color[0], portal2Color[1], portal2Color[2], iBaseLastPlacedAlpha );
+
+ const float fLastPlacedAlphaLerpSpeed = 300.0f;
+
+
+ float fLeftPlaceBarFill = 0.0f;
+ float fRightPlaceBarFill = 0.0f;
+
+ if ( pPortalgun->CanFirePortal1() && pPortalgun->CanFirePortal2() )
+ {
+ int iDrawLastPlaced = 0;
+
+ //do last placed indicator effects
+ if ( pPortalgun->GetLastFiredPortal() == 1 )
+ {
+ iDrawLastPlaced = 0;
+ fLeftPlaceBarFill = 1.0f;
+ }
+ else if ( pPortalgun->GetLastFiredPortal() == 2 )
+ {
+ iDrawLastPlaced = 1;
+ fRightPlaceBarFill = 1.0f;
+ }
+
+ if( m_bLastPlacedAlphaCountingUp[iDrawLastPlaced] )
+ {
+ m_fLastPlacedAlpha[iDrawLastPlaced] += gpGlobals->absoluteframetime * fLastPlacedAlphaLerpSpeed * 2.0f;
+ if( m_fLastPlacedAlpha[iDrawLastPlaced] > 255.0f )
+ {
+ m_bLastPlacedAlphaCountingUp[iDrawLastPlaced] = false;
+ m_fLastPlacedAlpha[iDrawLastPlaced] = 255.0f - (m_fLastPlacedAlpha[iDrawLastPlaced] - 255.0f);
+ }
+ }
+ else
+ {
+ m_fLastPlacedAlpha[iDrawLastPlaced] -= gpGlobals->absoluteframetime * fLastPlacedAlphaLerpSpeed;
+ if( m_fLastPlacedAlpha[iDrawLastPlaced] < (float)iBaseLastPlacedAlpha )
+ {
+ m_fLastPlacedAlpha[iDrawLastPlaced] = (float)iBaseLastPlacedAlpha;
+ }
+ }
+
+ //reset the last placed indicator on the other side
+ m_fLastPlacedAlpha[1 - iDrawLastPlaced] -= gpGlobals->absoluteframetime * fLastPlacedAlphaLerpSpeed;
+ if( m_fLastPlacedAlpha[1 - iDrawLastPlaced] < 0.0f )
+ {
+ m_fLastPlacedAlpha[1 - iDrawLastPlaced] = 0.0f;
+ }
+ m_bLastPlacedAlphaCountingUp[1 - iDrawLastPlaced] = true;
+
+ if ( pPortalgun->GetLastFiredPortal() != 0 )
+ {
+ lastPlaced1Color[3] = m_fLastPlacedAlpha[0];
+ lastPlaced2Color[3] = m_fLastPlacedAlpha[1];
+ }
+ else
+ {
+ lastPlaced1Color[3] = 0.0f;
+ lastPlaced2Color[3] = 0.0f;
+ }
+ }
+ //can't fire both portals, and we want the crosshair to remain somewhat symmetrical without being confusing
+ else if ( !pPortalgun->CanFirePortal1() )
+ {
+ // clone portal2 info to portal 1
+ portal1Color = portal2Color;
+ lastPlaced1Color[3] = 0.0f;
+ lastPlaced2Color[3] = 0.0f;
+ bPortalPlacability[0] = bPortalPlacability[1];
+ }
+ else if ( !pPortalgun->CanFirePortal2() )
+ {
+ // clone portal1 info to portal 2
+ portal2Color = portal1Color;
+ lastPlaced1Color[3] = 0.0f;
+ lastPlaced2Color[3] = 0.0f;
+ bPortalPlacability[1] = bPortalPlacability[0];
+ }
+
+ if ( pPortalgun->IsHoldingObject() )
+ {
+ // Change the middle to orange
+ portal1Color = portal2Color = UTIL_Portal_Color( 0 );
+ bPortalPlacability[0] = bPortalPlacability[1] = false;
+ }
+
+ if ( !hud_quickinfo_swap.GetBool() )
+ {
+ if ( bPortalPlacability[0] )
+ m_icon_lb->DrawSelf(xCenter - (m_icon_lb->Width() * 0.64f ), yCenter - ( m_icon_rb->Height() * 0.17f ), portal1Color);
+ else
+ m_icon_lbn->DrawSelf(xCenter - (m_icon_lbn->Width() * 0.64f ), yCenter - ( m_icon_rb->Height() * 0.17f ), portal1Color);
+
+ if ( bPortalPlacability[1] )
+ m_icon_rb->DrawSelf(xCenter + ( m_icon_rb->Width() * -0.35f ), yCenter + ( m_icon_rb->Height() * 0.17f ), portal2Color);
+ else
+ m_icon_rbn->DrawSelf(xCenter + ( m_icon_rbn->Width() * -0.35f ), yCenter + ( m_icon_rb->Height() * 0.17f ), portal2Color);
+
+ //last placed portal indicator
+ m_icon_lbe->DrawSelf( xCenter - (m_icon_lbe->Width() * 1.85f), yCenter, lastPlaced1Color );
+ m_icon_rbe->DrawSelf( xCenter + (m_icon_rbe->Width() * 0.75f), yCenter, lastPlaced2Color );
+ }
+ else
+ {
+ if ( bPortalPlacability[1] )
+ m_icon_lb->DrawSelf(xCenter - (m_icon_lb->Width() * 0.64f ), yCenter - ( m_icon_rb->Height() * 0.17f ), portal2Color);
+ else
+ m_icon_lbn->DrawSelf(xCenter - (m_icon_lbn->Width() * 0.64f ), yCenter - ( m_icon_rb->Height() * 0.17f ), portal2Color);
+
+ if ( bPortalPlacability[0] )
+ m_icon_rb->DrawSelf(xCenter + ( m_icon_rb->Width() * -0.35f ), yCenter + ( m_icon_rb->Height() * 0.17f ), portal1Color);
+ else
+ m_icon_rbn->DrawSelf(xCenter + ( m_icon_rbn->Width() * -0.35f ), yCenter + ( m_icon_rb->Height() * 0.17f ), portal1Color);
+
+ //last placed portal indicator
+ m_icon_lbe->DrawSelf( xCenter - (m_icon_lbe->Width() * 1.85f), yCenter, lastPlaced2Color );
+ m_icon_rbe->DrawSelf( xCenter + (m_icon_rbe->Width() * 0.75f), yCenter, lastPlaced1Color );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHUDQuickInfo::UpdateEventTime( void )
+{
+ m_flLastEventTime = gpGlobals->curtime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CHUDQuickInfo::EventTimeElapsed( void )
+{
+ if (( gpGlobals->curtime - m_flLastEventTime ) > QUICKINFO_EVENT_DURATION )
+ return true;
+
+ return false;
+}
+
diff --git a/game/client/portal/materialproxy_portalstatic.cpp b/game/client/portal/materialproxy_portalstatic.cpp
new file mode 100644
index 0000000..238fa04
--- /dev/null
+++ b/game/client/portal/materialproxy_portalstatic.cpp
@@ -0,0 +1,147 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+#include "cbase.h"
+#include "materialsystem/imaterialproxy.h"
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/imaterial.h"
+#include "portalrenderable_flatbasic.h"
+#include "c_prop_portal.h"
+#include "toolframework_client.h"
+#include <KeyValues.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+class CPortalStaticProxy : public IMaterialProxy
+{
+protected:
+ IMaterialVar *m_StaticOutput;
+public:
+ virtual bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
+ virtual void OnBind( void *pBind );
+ virtual void Release( void ) { delete this; }
+
+ virtual IMaterial * GetMaterial() { return ( m_StaticOutput ) ? m_StaticOutput->GetOwningMaterial() : NULL; }
+
+ float ComputeStaticAmount( CPortalRenderable_FlatBasic *pPortal );
+};
+
+bool CPortalStaticProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
+{
+ char const* pszResultVar = pKeyValues->GetString( "resultVar" );
+ if( !pszResultVar )
+ return false;
+
+ bool foundVar;
+ m_StaticOutput = pMaterial->FindVar( pszResultVar, &foundVar, false );
+ if( !foundVar )
+ return false;
+
+ return true;
+}
+
+float CPortalStaticProxy::ComputeStaticAmount( CPortalRenderable_FlatBasic *pFlatBasic )
+{
+ float flStaticAmount = pFlatBasic->m_fStaticAmount;
+
+ if ( !pFlatBasic->GetLinkedPortal() )
+ {
+ flStaticAmount = 1.0f;
+ }
+ if ( pFlatBasic->WillUseDepthDoublerThisDraw() )
+ {
+ flStaticAmount = 0.0f;
+ }
+ else if ( g_pPortalRender->GetRemainingPortalViewDepth() == 0 ) //end of the line, no more views
+ {
+ flStaticAmount = 1.0f;
+ }
+ else if ( (g_pPortalRender->GetRemainingPortalViewDepth() == 1) && (pFlatBasic->m_fSecondaryStaticAmount > flStaticAmount) ) //fading in from no views to another view (player just walked through it)
+ {
+ flStaticAmount = pFlatBasic->m_fSecondaryStaticAmount;
+ }
+ return flStaticAmount;
+}
+
+void CPortalStaticProxy::OnBind( void *pBind )
+{
+ if ( pBind == NULL )
+ return;
+
+ float flStaticAmount;
+ IClientRenderable *pRenderable = (IClientRenderable*)pBind;
+ CPortalRenderable *pRecordedPortal = g_pPortalRender->FindRecordedPortal( pRenderable );
+
+ if ( pRecordedPortal )
+ {
+ CPortalRenderable_FlatBasic *pRecordedFlatBasic = dynamic_cast<CPortalRenderable_FlatBasic *>(pRecordedPortal);
+ if ( !pRecordedFlatBasic )
+ return;
+
+ flStaticAmount = ComputeStaticAmount( pRecordedFlatBasic );
+ }
+ else
+ {
+ CPortalRenderable_FlatBasic *pFlatBasic = dynamic_cast<CPortalRenderable_FlatBasic*>( pRenderable );
+ if ( !pFlatBasic )
+ return;
+
+ flStaticAmount = ComputeStaticAmount( pFlatBasic );
+ }
+
+ m_StaticOutput->SetFloatValue( flStaticAmount );
+
+ if ( ToolsEnabled() )
+ {
+ ToolFramework_RecordMaterialParams( GetMaterial() );
+ }
+}
+
+EXPOSE_INTERFACE( CPortalStaticProxy, IMaterialProxy, "PortalStaticModel" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+class CPortalStaticPortalProxy : public CPortalStaticProxy
+{
+public:
+ virtual void OnBind( void *pBind );
+};
+
+void CPortalStaticPortalProxy::OnBind( void *pBind )
+{
+ if ( pBind == NULL )
+ return;
+
+ IClientRenderable *pRenderable = (IClientRenderable*)( pBind );
+ C_Prop_Portal *pPortal = (C_Prop_Portal *)pRenderable;
+
+ float flStaticAmount = ComputeStaticAmount( pPortal );
+ m_StaticOutput->SetFloatValue( flStaticAmount );
+}
+
+EXPOSE_INTERFACE( CPortalStaticPortalProxy, IMaterialProxy, "PortalStatic" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+class CPortalOpenAmountProxy : public CPortalStaticProxy
+{
+public:
+ virtual void OnBind( void *pBind );
+};
+
+void CPortalOpenAmountProxy::OnBind( void *pBind )
+{
+ if ( pBind == NULL )
+ return;
+
+ IClientRenderable *pRenderable = (IClientRenderable*)( pBind );
+ C_Prop_Portal *pPortal = (C_Prop_Portal *)pRenderable;
+
+ m_StaticOutput->SetFloatValue( pPortal->m_fOpenAmount );
+}
+
+EXPOSE_INTERFACE( CPortalOpenAmountProxy, IMaterialProxy, "PortalOpenAmount" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
diff --git a/game/client/portal/portal_credits.cpp b/game/client/portal/portal_credits.cpp
new file mode 100644
index 0000000..86ad0bc
--- /dev/null
+++ b/game/client/portal/portal_credits.cpp
@@ -0,0 +1,1574 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "hudelement.h"
+#include "hud_numericdisplay.h"
+#include <vgui_controls/Panel.h>
+#include "hud.h"
+#include "hud_suitpower.h"
+#include "hud_macros.h"
+#include "iclientmode.h"
+#include <vgui_controls/AnimationController.h>
+#include <vgui/ISurface.h>
+#include <vgui/ILocalize.h>
+#include "KeyValues.h"
+#include "filesystem.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+struct portalcreditname_t
+{
+ char szCreditName[256];
+ char szFontName[256];
+ wchar_t szLyricLine[256];
+ float flYPos;
+ float flXPos;
+ bool bActive;
+ float flTime;
+ float flTimeAdd;
+ float flTimeStart;
+ float flTimeEnd;
+ float flTimeInit;
+ int iAsciiIndex;
+ bool bReset;
+ int iXOffset;
+ int iSlot;
+};
+
+#define CREDITS_FILE "scripts/credits.txt"
+#define CREDITS_FILE_PORTAL "scripts/credits.txt"
+
+enum
+{
+ LOGO_FADEIN = 0,
+ LOGO_FADEHOLD,
+ LOGO_FADEOUT,
+ LOGO_FADEOFF,
+};
+
+#define CREDITS_LOGO 1
+#define CREDITS_INTRO 2
+#define CREDITS_OUTRO 3
+#define CREDITS_OUTRO_PORTAL 4
+
+bool g_bPortalRollingCredits = false;
+
+int g_iPortalCreditsPixelHeight = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose: Shows the flashlight icon
+//-----------------------------------------------------------------------------
+class CHudPortalCredits : public CHudElement, public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CHudPortalCredits, vgui::Panel );
+
+public:
+ CHudPortalCredits( const char *pElementName );
+ virtual void Init( void );
+ virtual void LevelShutdown( void );
+
+ int GetStringPixelWidth ( wchar_t *pString, vgui::HFont hFont );
+ int GetPixelWidth( char *pString, vgui::HFont hFont );
+
+ void MsgFunc_CreditsPortalMsg( bf_read &msg );
+ void MsgFunc_LogoTimeMsg( bf_read &msg );
+
+ virtual bool ShouldDraw( void )
+ {
+ g_bPortalRollingCredits = IsActive();
+
+ if ( g_bPortalRollingCredits && m_iCreditsType == CREDITS_INTRO )
+ g_bPortalRollingCredits = false;
+
+ return IsActive();
+ }
+
+protected:
+ virtual void Paint();
+ virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
+
+private:
+
+ void Clear();
+
+ void ReadNames( KeyValues *pKeyValue );
+ void ReadLyrics( KeyValues *pKeyValue );
+ void ReadAscii( KeyValues *pKeyValue );
+ void ReadParams( KeyValues *pKeyValue );
+ void PrepareCredits( const char *pKeyName );
+ void DrawOutroCreditsName( void );
+ void DrawPortalOutroCreditsName( void );
+ void DrawPortalOutroCreditsLyrics( void );
+ void DrawIntroCreditsName( void );
+ void DrawPortalAsciiArt( void );
+ void DrawLogo( void );
+
+ void PrepareLogo( float flTime );
+ void PrepareOutroCredits( void );
+ void PreparePortalOutroCredits( void );
+ void PrepareIntroCredits( void );
+
+ float FadeBlend( float fadein, float fadeout, float hold, float localTime );
+
+ CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "Default" );
+ CPanelAnimationVar( Color, m_TextColor, "TextColor", "FgColor" );
+
+ CUtlVector<portalcreditname_t> m_CreditsList;
+
+ CUtlVector<portalcreditname_t> m_LyricsList;
+
+ CUtlVector<portalcreditname_t> m_AsciiList;
+
+ char m_szCreditPrint[256];
+ char m_szBorderH[512];
+
+ float m_flScrollTime;
+ float m_flSeparation;
+ float m_flFadeTime;
+ float m_flCursorBlinkTime;
+ bool m_bLastOneInPlace;
+ int m_Alpha;
+ char m_szAsciiArtFont[256];
+
+ int m_iCreditsType;
+ int m_iLogoState;
+ int m_iCurrentAsciiArt;
+
+ int m_iCurrentLowY;
+ int m_iYOffset;
+ int m_iYOffsetNames;
+ int m_iYDrawOffset;
+ int m_iXOffset;
+ int m_iXOffsetNames;
+ int m_iLongestName;
+
+ float m_flFadeInTime;
+ float m_flFadeHoldTime;
+ float m_flFadeOutTime;
+ float m_flNextStartTime;
+ float m_flPauseBetweenWaves;
+ float m_flLastPaintTime;
+ float m_flLastBlinkTime;
+ float m_flCurrentDelayTime;
+ float m_flNameCharTime;
+ float m_flScrollCreditsStart;
+ float m_flSongStartTime;
+
+
+ //Screen adjustment properties
+ int m_iScreenXOffset;
+ int m_iScreenYOffset;
+ int m_iScreenWidthAdjustment;
+ int m_iScreenHeightAdjustment;
+
+ //Ascii Art positioning
+ int m_iAASeparation;
+ int m_iAAScreenXOffset;
+ int m_iAAScreenYOffset;
+
+
+ float m_flLogoTimeMod;
+ float m_flLogoTime;
+ float m_flLogoDesiredLength;
+
+ float m_flX;
+ float m_flY;
+
+ bool m_bStartSong;
+ float m_flLyricsStartTime;
+
+ Color m_cColor;
+};
+
+void CHudPortalCredits::PrepareCredits( const char *pKeyName )
+{
+ Clear();
+
+ KeyValues *pKV= new KeyValues( "CreditsFile" );
+
+ if (m_iCreditsType == CREDITS_OUTRO_PORTAL)
+ {
+ if ( !pKV->LoadFromFile( filesystem, CREDITS_FILE_PORTAL, "MOD" ) )
+ {
+ pKV->deleteThis();
+
+ Assert( !"env_portal_credits couldn't be initialized!" );
+ return;
+ }
+ }
+ else
+ {
+ if ( !pKV->LoadFromFile( filesystem, CREDITS_FILE, "MOD" ) )
+ {
+ pKV->deleteThis();
+
+ Assert( !"env_portal_credits couldn't be initialized!" );
+ return;
+ }
+ }
+
+ KeyValues *pKVSubkey = pKV->FindKey( pKeyName );
+
+ ReadNames( pKVSubkey );
+
+ pKVSubkey = pKV->FindKey( "CreditsParams" );
+
+ ReadParams( pKVSubkey );
+
+ pKVSubkey = pKV->FindKey( "OutroSongLyrics" );
+
+ ReadLyrics( pKVSubkey );
+
+ pKVSubkey = pKV->FindKey( "OutroAsciiArt" );
+
+ ReadAscii( pKVSubkey );
+
+ pKV->deleteThis();
+}
+
+using namespace vgui;
+
+DECLARE_HUDELEMENT( CHudPortalCredits );
+DECLARE_HUD_MESSAGE( CHudPortalCredits, CreditsPortalMsg );
+DECLARE_HUD_MESSAGE( CHudPortalCredits, LogoTimeMsg );
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CHudPortalCredits::CHudPortalCredits( const char *pElementName ) : CHudElement( pElementName ), BaseClass( NULL, "HudCredits" )
+{
+ vgui::Panel *pParent = g_pClientMode->GetViewport();
+ SetParent( pParent );
+}
+
+void CHudPortalCredits::LevelShutdown()
+{
+ Clear();
+}
+
+void CHudPortalCredits::Clear( void )
+{
+ SetActive( false );
+ m_CreditsList.RemoveAll();
+ m_LyricsList.RemoveAll();
+ m_bLastOneInPlace = false;
+ m_Alpha = m_TextColor[3];
+ m_iLogoState = LOGO_FADEOFF;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudPortalCredits::Init()
+{
+ HOOK_HUD_MESSAGE( CHudPortalCredits, CreditsPortalMsg );
+ HOOK_HUD_MESSAGE( CHudPortalCredits, LogoTimeMsg );
+ SetActive( false );
+}
+
+void CHudPortalCredits::ReadNames( KeyValues *pKeyValue )
+{
+ if ( pKeyValue == NULL )
+ {
+ Assert( !"CHudPortalCredits couldn't be initialized!" );
+ return;
+ }
+
+ // Now try and parse out each act busy anim
+ KeyValues *pKVNames = pKeyValue->GetFirstSubKey();
+
+ char *cTmp;
+
+ while ( pKVNames )
+ {
+ portalcreditname_t Credits;
+ V_strcpy_safe( Credits.szCreditName, pKVNames->GetName() );
+ V_strcpy_safe( Credits.szFontName, pKeyValue->GetString( Credits.szCreditName, "Default" ) );
+
+ Credits.flTimeInit = 0.00;
+
+ cTmp=Q_strstr(Credits.szCreditName,"[");
+ if (!(cTmp == NULL)) {
+ Credits.flTimeInit = Q_atof(cTmp+1);
+ }
+
+ cTmp=Q_strstr(Credits.szCreditName,"]");
+ if (!(cTmp == NULL)) {
+ Q_strcpy(Credits.szCreditName,cTmp+1);
+ }
+
+ m_CreditsList.AddToTail( Credits );
+ pKVNames = pKVNames->GetNextKey();
+ }
+}
+
+void CHudPortalCredits::ReadLyrics( KeyValues *pKeyValue )
+{
+ if ( pKeyValue == NULL )
+ {
+ Assert( !"CHudPortalCredits couldn't be initialized!" );
+ return;
+ }
+
+ KeyValues *pKVNames = pKeyValue->GetFirstSubKey();
+
+ char *cTmp;
+
+ int iHeight = 0;
+ int iXOffsetTemp = 0;
+ bool bNextOnSameLine = false;
+ bool bNoY = false;
+
+ char tmpstr[255] = "";
+
+ float flTotalTime = 0.00;
+
+ while ( pKVNames )
+ {
+ bNoY = false;
+ portalcreditname_t Credits;
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CreditsOutroText", true );
+ V_strcpy_safe( Credits.szCreditName, pKVNames->GetName());
+ V_strcpy_safe( Credits.szFontName, pKeyValue->GetString( Credits.szCreditName, "Default" ) );
+
+ Credits.flTimeInit = 0.00;
+
+ cTmp=Q_strstr(Credits.szCreditName,"[");
+ if (!(cTmp == NULL)) {
+ Credits.flTimeInit = Q_atof(cTmp+1);
+ }
+
+ cTmp=Q_strstr(Credits.szCreditName,"]");
+ if (!(cTmp == NULL)) {
+ Q_strcpy(Credits.szCreditName,cTmp+1);
+ }
+
+ Credits.iAsciiIndex = 0;
+
+ cTmp=Q_strstr(Credits.szCreditName,"<<<");
+ if (!(cTmp == NULL)) {
+ Credits.iAsciiIndex = Q_atoi(cTmp+3);
+ }
+
+ cTmp=Q_strstr(Credits.szCreditName,">>>");
+ if (!(cTmp == NULL)) {
+ Q_strcpy(Credits.szCreditName,cTmp+3);
+ }
+
+ if (Q_strcmp("&",Credits.szCreditName)==0) //clear screen code
+ {
+ iHeight = 0;
+ Credits.bReset = true;
+ Q_strcpy(Credits.szCreditName," ");
+ }
+ else Credits.bReset = false;
+
+ Credits.flYPos = iHeight;
+ Credits.bActive = false;
+
+ if (bNextOnSameLine)
+ {
+ Credits.iXOffset=iXOffsetTemp;
+ //GetPixelWidth( tmpstr, m_hTFont )
+ bNextOnSameLine = false;
+ }
+ else
+ {
+ iXOffsetTemp=0;
+ Credits.iXOffset = 0;
+ }
+
+ if (Credits.szCreditName[0] == '*')
+ {
+ bNextOnSameLine = true;
+ Q_strcpy(tmpstr,&Credits.szCreditName[1]);
+ Q_strcpy(Credits.szCreditName,tmpstr);
+ bNoY = true;
+ }
+
+ if ((!(Q_strcmp(" ",Credits.szCreditName)==0)) && !bNoY)
+ {
+ iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation;
+ if (Q_strcmp("^",Credits.szCreditName)==0)
+ {
+ Q_strcpy(Credits.szCreditName," ");
+ }
+ }
+
+ if ( Credits.szCreditName[0] == '#' )
+ {
+ g_pVGuiLocalize->ConstructString( Credits.szLyricLine, sizeof(Credits.szLyricLine), g_pVGuiLocalize->Find(Credits.szCreditName), 0 );
+ if (bNextOnSameLine)
+ {
+ iXOffsetTemp+=GetStringPixelWidth( Credits.szLyricLine, m_hTFont );
+ }
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( Credits.szCreditName, Credits.szLyricLine, sizeof( Credits.szLyricLine ) );
+ if (bNextOnSameLine)
+ {
+ iXOffsetTemp+=GetStringPixelWidth( Credits.szLyricLine, m_hTFont );
+ }
+ }
+
+ Credits.flTimeStart = flTotalTime;
+ flTotalTime+=Credits.flTimeInit;
+ Credits.flTimeEnd = flTotalTime;
+
+
+ m_LyricsList.AddToTail( Credits );
+ pKVNames = pKVNames->GetNextKey();
+ }
+
+ g_iPortalCreditsPixelHeight = iHeight;
+
+}
+
+
+void CHudPortalCredits::ReadAscii( KeyValues *pKeyValue )
+{
+ if ( pKeyValue == NULL )
+ {
+ Assert( !"CHudPortalCredits couldn't be initialized!" );
+ return;
+ }
+
+ KeyValues *pKVNames = pKeyValue->GetFirstSubKey();
+
+ char *cTmp;
+
+ while ( pKVNames )
+ {
+ portalcreditname_t Credits;
+ //Q_strcpy( Credits.szCreditName, pKVNames->GetName());
+ //Q_strcpy( Credits.szFontName, pKeyValue->GetString( Credits.szCreditName, "Default" ) );
+
+ V_strcpy_safe( Credits.szFontName, pKVNames->GetName() );
+ V_strcpy_safe( Credits.szCreditName, pKeyValue->GetString( Credits.szFontName, "Default" ) );
+
+ Credits.flTimeInit = 0.00;
+
+ cTmp=Q_strstr(Credits.szCreditName,"[");
+ if (!(cTmp == NULL)) {
+ Credits.flTimeInit = Q_atof(cTmp+1);
+ }
+
+ cTmp=Q_strstr(Credits.szCreditName,"]");
+ if (!(cTmp == NULL)) {
+ Q_strcpy(Credits.szCreditName,cTmp+1);
+ }
+
+ m_AsciiList.AddToTail( Credits );
+ pKVNames = pKVNames->GetNextKey();
+ }
+}
+
+void CHudPortalCredits::ReadParams( KeyValues *pKeyValue )
+{
+ if ( pKeyValue == NULL )
+ {
+ Assert( !"CHudPortalCredits couldn't be initialized!" );
+ return;
+ }
+
+ m_flScrollTime = pKeyValue->GetFloat( "scrolltime", 57 );
+ m_flSeparation = pKeyValue->GetFloat( "separation", 5 );
+
+ m_flFadeInTime = pKeyValue->GetFloat( "fadeintime", 1 );
+ m_flFadeHoldTime = pKeyValue->GetFloat( "fadeholdtime", 3 );
+ m_flFadeOutTime = pKeyValue->GetFloat( "fadeouttime", 2 );
+ m_flNextStartTime = pKeyValue->GetFloat( "nextfadetime", 2 );
+ m_flPauseBetweenWaves = pKeyValue->GetFloat( "pausebetweenwaves", 2 );
+
+ m_flCursorBlinkTime = pKeyValue->GetFloat( "cursorblinktime", 0.3 );
+
+ m_flLogoTimeMod = pKeyValue->GetFloat( "logotime", 2 );
+
+ m_flScrollCreditsStart = pKeyValue->GetFloat( "scrollcreditsstart", 6 );
+ m_flSongStartTime = pKeyValue->GetFloat( "songstarttime", 6.85 );
+
+ m_iScreenXOffset = pKeyValue->GetInt( "screenxoffset", 0 );
+ m_iScreenYOffset = pKeyValue->GetInt( "screenyoffset", 0 );
+ m_iScreenWidthAdjustment = pKeyValue->GetInt( "screenwidthadjustment", 0 );
+ m_iScreenHeightAdjustment = pKeyValue->GetInt( "screenheightadjustment", 0 );
+
+ V_strcpy_safe( m_szAsciiArtFont, pKeyValue->GetString( "asciiartfont", "Default" ) );
+
+ m_iAAScreenXOffset = pKeyValue->GetInt( "aascreenxoffset", 0 );
+ m_iAAScreenYOffset = pKeyValue->GetInt( "aascreenyoffset", 0 );
+ m_iAASeparation = pKeyValue->GetInt( "aaseparation", 0 );
+
+
+
+ m_flX = pKeyValue->GetFloat( "posx", 2 );
+ m_flY = pKeyValue->GetFloat( "posy", 2 );
+
+ m_cColor = pKeyValue->GetColor( "color" );
+}
+
+int CHudPortalCredits::GetStringPixelWidth( wchar_t *pString, vgui::HFont hFont )
+{
+ int iLength = 0;
+
+ for ( wchar_t *wch = pString; *wch != 0; wch++ )
+ {
+ iLength += surface()->GetCharacterWidth( hFont, *wch );
+ }
+
+ return iLength;
+}
+
+int CHudPortalCredits::GetPixelWidth( char *pString, vgui::HFont hFont )
+{
+ int iLength = 0;
+ int iStrLen = Q_strlen(pString);
+
+ for ( int i=0; i<iStrLen; i++ )
+ {
+ iLength += surface()->GetCharacterWidth( hFont, pString[i] );
+ }
+
+ return iLength;
+}
+
+
+void CHudPortalCredits::DrawOutroCreditsName( void )
+{
+ if ( m_CreditsList.Count() == 0 )
+ return;
+
+ // fill the screen
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ SetSize( iWidth, iTall );
+
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+
+ int iFontTall = surface()->GetFontTall ( m_hTFont );
+
+ if ( pCredit->flYPos < -iFontTall || pCredit->flYPos > iTall )
+ {
+ pCredit->bActive = false;
+ }
+ else
+ {
+ pCredit->bActive = true;
+ }
+
+ Color cColor = m_TextColor;
+
+ //HACKHACK
+ //Last one stays on screen and fades out
+ if ( i == m_CreditsList.Count()-1 )
+ {
+ if ( m_bLastOneInPlace == false )
+ {
+ pCredit->flYPos -= gpGlobals->frametime * ( (float)g_iPortalCreditsPixelHeight / m_flScrollTime );
+
+ if ( (int)pCredit->flYPos + ( iFontTall / 2 ) <= iTall / 2 )
+ {
+ m_bLastOneInPlace = true;
+ m_flFadeTime = gpGlobals->curtime + 10.0f;
+ }
+ }
+ else
+ {
+ if ( m_flFadeTime <= gpGlobals->curtime )
+ {
+ if ( m_Alpha > 0 )
+ {
+ m_Alpha -= gpGlobals->frametime * ( m_flScrollTime * 2 );
+
+ if ( m_Alpha <= 0 )
+ {
+ pCredit->bActive = false;
+ engine->ClientCmd( "creditsdone" );
+ }
+ }
+ }
+
+ cColor[3] = MAX( 0, m_Alpha );
+ }
+ }
+ else
+ {
+ pCredit->flYPos -= gpGlobals->frametime * ( (float)g_iPortalCreditsPixelHeight / m_flScrollTime );
+
+ }
+
+ if ( pCredit->bActive == false )
+ continue;
+
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
+
+ wchar_t unicode[256];
+
+ if ( pCredit->szCreditName[0] == '#' )
+ {
+ g_pVGuiLocalize->ConstructString( unicode, sizeof(unicode), g_pVGuiLocalize->Find(pCredit->szCreditName), 0 );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( pCredit->szCreditName, unicode, sizeof( unicode ) );
+ }
+
+ int iStringWidth = GetStringPixelWidth( unicode, m_hTFont );
+
+ surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), pCredit->flYPos );
+ surface()->DrawUnicodeString( unicode );
+ }
+}
+
+
+void CHudPortalCredits::DrawPortalOutroCreditsName( void )
+{
+
+ if ( m_CreditsList.Count() == 0 )
+ return;
+
+ static int iCounter=0;
+ static bool bCursor = false;
+
+ // fill the screen
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ iWidth += m_iScreenWidthAdjustment;
+ iTall += m_iScreenHeightAdjustment;
+ SetSize( iWidth+m_iScreenXOffset, iTall+m_iScreenYOffset );
+
+ if (gpGlobals->curtime - m_flLastPaintTime > 0.05)
+ {
+ m_flLastPaintTime = gpGlobals->curtime;
+ iCounter += 100;
+ if (iCounter> (int)m_flScrollTime && m_iYOffset<(m_CreditsList.Count()-1))
+ {
+ bCursor = !bCursor;
+ iCounter = 0;
+ m_iXOffset += 1;
+
+ if (m_iXOffset > Q_strlen(m_CreditsList[m_iYOffset].szCreditName) && m_iYOffset<(m_CreditsList.Count()-1))
+ {
+ m_iXOffset = 1;
+ m_iYOffset += 1;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( m_CreditsList[m_iYOffset].szFontName, true );
+ int iFontTall = surface()->GetFontTall ( m_hTFont )+ m_flSeparation;
+
+ if ( m_CreditsList[m_iYOffset].flYPos +iFontTall > iTall )
+ {
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+ if ( pCredit == NULL )
+ continue;
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+ int iFontTall = surface()->GetFontTall ( m_hTFont )+ m_flSeparation;
+ pCredit->flYPos -= iFontTall;
+ }
+ }
+ }
+ }
+ }
+
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+
+ int iFontTall = surface()->GetFontTall ( m_hTFont );
+
+ if ( pCredit->flYPos < -iFontTall || pCredit->flYPos > iTall )
+ {
+ pCredit->bActive = false;
+ }
+ else
+ {
+ if (i <= m_iYOffset)
+ pCredit->bActive = true;
+ }
+
+ Color cColor = m_TextColor;
+
+ //HACKHACK
+ //Last one stays on screen and fades out
+ if ( i == m_CreditsList.Count()-1 )
+ {
+ if ( m_bLastOneInPlace == false )
+ {
+ pCredit->flYPos -= gpGlobals->frametime * ( (float)g_iPortalCreditsPixelHeight / m_flScrollTime );
+
+ if ( (int)pCredit->flYPos + ( iFontTall / 2 ) <= iTall / 2 )
+ {
+ m_bLastOneInPlace = true;
+ m_flFadeTime = gpGlobals->curtime + 10.0f;
+ }
+ }
+ else
+ {
+ if ( m_flFadeTime <= gpGlobals->curtime )
+ {
+ if ( m_Alpha > 0 )
+ {
+ m_Alpha -= gpGlobals->frametime * ( m_flScrollTime * 2 );
+
+ if ( m_Alpha <= 0 )
+ {
+ pCredit->bActive = false;
+ engine->ClientCmd( "creditsdone" );
+ }
+ }
+ }
+
+ cColor[3] = MAX( 0, m_Alpha );
+ }
+ }
+ else
+ {
+ //m_iYOffset += (int) (gpGlobals->frametime * ( (float)g_iPortalCreditsPixelHeight / m_flScrollTime ));
+ //m_iYOffset += (int) (gpGlobals->frametime * ( (float)g_iPortalCreditsPixelHeight / 1000 ));
+ //iCounter += 1;
+ //pCredit->flYPos -= gpGlobals->frametime * ( (float)g_iPortalCreditsPixelHeight / m_flScrollTime );
+ }
+
+ if ( pCredit->bActive == false )
+ continue;
+
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
+
+ wchar_t unicode[256];
+
+ char tmpstr[256];
+ if (m_iYOffset == i)
+ {
+ char tmpstr2[256];
+ Q_strncpy( tmpstr2, pCredit->szCreditName, m_iXOffset );
+ if (bCursor)
+ Q_snprintf( tmpstr, 256, "%s_",tmpstr2);
+ else
+ Q_snprintf( tmpstr, 256, "%s",tmpstr2);
+ }
+ else
+ {
+ Q_strncpy( tmpstr, pCredit->szCreditName, Q_strlen( pCredit->szCreditName )+1 );
+ }
+
+ if ( tmpstr[0] == '#' )
+ {
+ g_pVGuiLocalize->ConstructString( unicode, sizeof(unicode), g_pVGuiLocalize->Find(tmpstr), 0 );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( tmpstr, unicode, sizeof( unicode ) );
+ }
+
+ // int iStringWidth = GetStringPixelWidth( unicode, m_hTFont );
+ // surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), pCredit->flYPos );
+ surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], 255 );
+ surface()->DrawSetTextPos( 20+m_iScreenXOffset, pCredit->flYPos + m_iScreenYOffset );
+ surface()->DrawUnicodeString( unicode );
+ }
+}
+
+void CHudPortalCredits::DrawPortalAsciiArt( void )
+{
+ if ( m_AsciiList.Count() == 0 )
+ return;
+
+ if (m_iCurrentAsciiArt == 0)
+ return;
+
+ //get the screen stats
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ iWidth += m_iScreenWidthAdjustment;
+ iTall += m_iScreenHeightAdjustment;
+ SetSize( iWidth+m_iScreenXOffset, iTall+m_iScreenYOffset );
+ int ift = 0;
+
+ for ( int i = 0; i < 20; i++ )
+ {
+ portalcreditname_t *pCredit = &m_AsciiList[((m_iCurrentAsciiArt - 1) *20)+i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ //vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( m_szAsciiArtFont, true );
+
+ int iFontTall = surface()->GetFontTall ( m_hTFont );
+ if (ift == 0) {
+ ift = iFontTall;
+ ift+=m_iAASeparation;
+ }
+ iFontTall+= (int) m_flSeparation;
+
+ Color cColor = m_TextColor;
+
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
+
+ wchar_t unicode[256];
+ char tmpstr[256];
+ Q_strncpy( tmpstr, pCredit->szCreditName, Q_strlen( pCredit->szCreditName )+1 );
+
+ //Q_strncpy( tmpstr, pCredit->szFontName, Q_strlen( pCredit->szFontName )+1 );
+
+ //if ( tmpstr[0] == '#' )
+ //{
+ // g_pVGuiLocalize->ConstructString( unicode, sizeof(unicode), g_pVGuiLocalize->Find(tmpstr), 0 );
+ //}
+ //else
+ //{
+ g_pVGuiLocalize->ConvertANSIToUnicode( tmpstr, unicode, sizeof( unicode ) );
+ //}
+ surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], cColor[3] );
+ //surface()->DrawSetTextPos( 20+(iWidth/2), 10+(iTall/2)+(i*iFontTall) );
+ surface()->DrawSetTextPos( 20+(iWidth/2)+m_iAAScreenXOffset, 10+(iTall/2)+m_iAAScreenYOffset+(i*ift) );
+ surface()->DrawUnicodeString( unicode );
+ }
+
+}
+
+
+void CHudPortalCredits::DrawPortalOutroCreditsLyrics( void )
+{
+ if ( m_LyricsList.Count() == 0 )
+ return;
+
+ static bool bCursor = false;
+ static int iLastNameY = 0;
+
+ if (m_flLyricsStartTime == -1) m_flLyricsStartTime = gpGlobals->curtime;
+
+ //get the screen stats
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ iWidth += m_iScreenWidthAdjustment;
+ iTall += m_iScreenHeightAdjustment;
+ SetSize( iWidth+m_iScreenXOffset, iTall+m_iScreenYOffset );
+
+ if (gpGlobals->curtime - m_flLastBlinkTime > m_flCursorBlinkTime)
+ {
+ bCursor = !bCursor;
+ m_flLastBlinkTime = gpGlobals->curtime;
+ }
+
+
+ float flCurTime = gpGlobals->curtime - m_flLyricsStartTime;
+
+ if (m_bStartSong && flCurTime>= m_flSongStartTime)
+ {
+ surface()->PlaySound( "music/portal_still_alive.mp3" );
+ m_bStartSong=false;
+ //engine->ClientCmd( "play music/portal_still_alive.mp3" );
+ }
+
+ //Draw Lyrics
+ bool bBorders = false;
+ for ( int i = 0; i < m_LyricsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_LyricsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ if (pCredit->flTimeStart > flCurTime) break;
+ if (pCredit->flTimeStart <= flCurTime && pCredit->flTimeEnd > flCurTime)
+ {
+ m_iYOffset = i;
+ if (pCredit->bReset)
+ {
+ m_iCurrentLowY = i;
+ }
+ m_iXOffset = (int) (((flCurTime-pCredit->flTimeStart) / pCredit->flTimeInit)*Q_wcslen(pCredit->szLyricLine))+1;
+ if ( m_iXOffset<1 )
+ m_iXOffset = 1;
+ else if ( m_iXOffset>Q_wcslen(pCredit->szLyricLine) )
+ m_iXOffset = Q_wcslen(pCredit->szLyricLine);
+ }
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+
+ int iFontTall = surface()->GetFontTall ( m_hTFont )+ (int) m_flSeparation;
+
+ if ( pCredit->flYPos < -iFontTall || pCredit->flYPos > iTall || i<m_iCurrentLowY)
+ {
+ pCredit->bActive = false;
+ }
+ else
+ {
+ if (i <= m_iYOffset)
+ pCredit->bActive = true;
+ }
+
+ Color cColor = m_TextColor;
+
+ if ( pCredit->bActive == false )
+ continue;
+
+ if (pCredit->iAsciiIndex>0)
+ {
+ m_iCurrentAsciiArt = pCredit->iAsciiIndex;
+ }
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
+
+ if (!bBorders)
+ {
+ wchar_t bunicode[512];
+ wchar_t bVunicode[2];
+ wchar_t bVunicode2[3];
+
+ int iBorderEndX = GetPixelWidth( m_szBorderH, m_hTFont );
+
+ g_pVGuiLocalize->ConvertANSIToUnicode( m_szBorderH, bunicode, sizeof( bunicode ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( "|", bVunicode, sizeof( bVunicode ) );
+ g_pVGuiLocalize->ConvertANSIToUnicode( "||", bVunicode2, sizeof( bVunicode2 ) );
+ int iBorderPipeWidth = GetPixelWidth( "||", m_hTFont);
+ surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], cColor[3] );
+ surface()->DrawSetTextPos( 5+m_iScreenXOffset, 10+m_iScreenYOffset);
+ surface()->DrawUnicodeString( bunicode );
+ surface()->DrawSetTextPos( (iWidth/2)+m_iScreenXOffset, 10+m_iScreenYOffset);
+ surface()->DrawUnicodeString( bunicode );
+ surface()->DrawSetTextPos( 5+m_iScreenXOffset, (iTall - (iFontTall*2))+m_iScreenYOffset);
+ surface()->DrawUnicodeString( bunicode );
+ surface()->DrawSetTextPos( (iWidth/2)+m_iScreenXOffset, (iTall/2)+m_iScreenYOffset);
+ surface()->DrawUnicodeString( bunicode );
+
+ int iBorderY = 10+iFontTall;
+ while (iBorderY < (iTall - (iFontTall*3)))
+ {
+ surface()->DrawSetTextPos( 5+m_iScreenXOffset, iBorderY+m_iScreenYOffset);
+ surface()->DrawUnicodeString( bVunicode );
+ surface()->DrawSetTextPos( iBorderEndX+m_iScreenXOffset, iBorderY+m_iScreenYOffset);
+ if (iBorderY<(iTall/2))
+ {
+ surface()->DrawUnicodeString( bVunicode2 );
+ surface()->DrawSetTextPos( (iWidth-iBorderPipeWidth)+m_iScreenXOffset, iBorderY+m_iScreenYOffset);
+ surface()->DrawUnicodeString( bVunicode );
+ }
+ else
+ {
+ surface()->DrawUnicodeString( bVunicode );
+ }
+ iBorderY+=iFontTall;
+ }
+ bBorders = true;
+ }
+
+ wchar_t tmpstr[256];
+ memset( tmpstr, 0, sizeof(tmpstr));
+ if (m_iYOffset == i)
+ {
+ Q_wcsncpy( tmpstr, pCredit->szLyricLine, m_iXOffset * sizeof( wchar_t ) );
+ if (bCursor)
+ tmpstr[m_iXOffset - 1] = '_';
+
+ }
+ else
+ {
+ Q_wcsncpy( tmpstr, pCredit->szLyricLine, (Q_wcslen( pCredit->szLyricLine ) + 1) * sizeof( wchar_t ));
+ }
+
+ surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], cColor[3] );
+ surface()->DrawSetTextPos( 20+pCredit->iXOffset+m_iScreenXOffset, 30+pCredit->flYPos+m_iScreenYOffset );
+ surface()->DrawUnicodeString( tmpstr );
+ }
+
+ DrawPortalAsciiArt();
+
+ //Draw Credits
+ int iCreditsListCount = m_CreditsList.Count();
+ for ( int i = iCreditsListCount-1; i >=0; i-- )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ if (pCredit->flYPos < 0) break;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+ int iFontTall = surface()->GetFontTall ( m_hTFont );
+
+ if (pCredit->flTimeStart <= flCurTime && pCredit->flTimeEnd > flCurTime)
+ {
+ if (iLastNameY!=i)
+ {
+ int iYDelta = iFontTall + (int) m_flSeparation;
+ for (int j = 0; j <i; j++)
+ {
+ m_CreditsList[j].flYPos -= (float) iYDelta;
+ }
+ m_CreditsList[i].flYPos = (iTall/2) - iYDelta;
+ iLastNameY=i;
+ }
+ m_iYOffsetNames = i;
+ m_iXOffsetNames = (int) (((flCurTime-pCredit->flTimeStart) / pCredit->flTimeInit)*Q_strlen(pCredit->szCreditName))+1;
+ if (m_iXOffsetNames<1) m_iXOffsetNames = 1;
+ else if (m_iXOffsetNames>Q_strlen(pCredit->szCreditName) ) m_iXOffsetNames = Q_strlen(pCredit->szCreditName);
+ }
+
+
+ //if ( pCredit->flYPos < -iFontTall || pCredit->flYPos > iTall)
+ if ( pCredit->flYPos < 20 || pCredit->flYPos > (iTall/2))
+ {
+ pCredit->bActive = false;
+ }
+ else
+ {
+ if (i <= m_iYOffsetNames)
+ pCredit->bActive = true;
+ }
+
+ Color cColor = m_TextColor;
+
+ if ( pCredit->bActive == false )
+ continue;
+
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
+
+ wchar_t unicode[256];
+
+ char tmpstr[256];
+ if (m_iYOffsetNames == i)
+ {
+ char tmpstr2[256];
+ Q_strncpy( tmpstr2, pCredit->szCreditName, m_iXOffsetNames );
+ if (!bCursor)
+ Q_snprintf( tmpstr, 256, "%s_",tmpstr2);
+ else
+ Q_snprintf( tmpstr, 256, "%s",tmpstr2);
+ }
+ else
+ {
+ Q_strncpy( tmpstr, pCredit->szCreditName, Q_strlen( pCredit->szCreditName )+1 );
+ }
+
+ if ( tmpstr[0] == '#' )
+ {
+ g_pVGuiLocalize->ConstructString( unicode, sizeof(unicode), g_pVGuiLocalize->Find(tmpstr), 0 );
+ }
+ else
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( tmpstr, unicode, sizeof( unicode ) );
+ }
+
+ // int iStringWidth = GetStringPixelWidth( unicode, m_hTFont );
+ // surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), pCredit->flYPos );
+ surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], cColor[3] );
+ surface()->DrawSetTextPos( (iWidth/2)+10+m_iScreenXOffset, pCredit->flYPos+m_iScreenYOffset );
+ surface()->DrawUnicodeString( unicode );
+
+ }
+}
+
+
+void CHudPortalCredits::DrawLogo( void )
+{
+ if( m_iLogoState == LOGO_FADEOFF )
+ {
+ SetActive( false );
+ return;
+ }
+
+ switch( m_iLogoState )
+ {
+ case LOGO_FADEIN:
+ {
+ float flDeltaTime = ( m_flFadeTime - gpGlobals->curtime );
+
+ m_Alpha = MAX( 0, RemapValClamped( flDeltaTime, 5.0f, 0, -128, 255 ) );
+
+ if ( flDeltaTime <= 0.0f )
+ {
+ m_iLogoState = LOGO_FADEHOLD;
+ m_flFadeTime = gpGlobals->curtime + m_flLogoDesiredLength;
+ }
+
+ break;
+ }
+
+ case LOGO_FADEHOLD:
+ {
+ if ( m_flFadeTime <= gpGlobals->curtime )
+ {
+ m_iLogoState = LOGO_FADEOUT;
+ m_flFadeTime = gpGlobals->curtime + 2.0f;
+ }
+ break;
+ }
+
+ case LOGO_FADEOUT:
+ {
+ float flDeltaTime = ( m_flFadeTime - gpGlobals->curtime );
+
+ m_Alpha = RemapValClamped( flDeltaTime, 0.0f, 2.0f, 0, 255 );
+
+ if ( flDeltaTime <= 0.0f )
+ {
+ m_iLogoState = LOGO_FADEOFF;
+ SetActive( false );
+ }
+
+ break;
+ }
+ }
+
+ // fill the screen
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ iWidth += m_iScreenWidthAdjustment;
+ iTall += m_iScreenHeightAdjustment;
+
+ SetSize( iWidth, iTall );
+
+ char szLogoFont[64];
+
+ if ( IsXbox() )
+ {
+ Q_snprintf( szLogoFont, sizeof( szLogoFont ), "WeaponIcons_Small" );
+ }
+ else if ( hl2_episodic.GetBool() )
+ {
+ Q_snprintf( szLogoFont, sizeof( szLogoFont ), "ClientTitleFont" );
+ }
+ else
+ {
+ Q_snprintf( szLogoFont, sizeof( szLogoFont ), "WeaponIcons" );
+ }
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( szLogoFont );
+
+ int iFontTall = surface()->GetFontTall ( m_hTFont );
+
+ Color cColor = m_TextColor;
+ cColor[3] = m_Alpha;
+
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( cColor[0], cColor[1], cColor[2], cColor[3] );
+
+ wchar_t unicode[256];
+ g_pVGuiLocalize->ConvertANSIToUnicode( "HALF-LIFE'", unicode, sizeof( unicode ) );
+
+ int iStringWidth = GetStringPixelWidth( unicode, m_hTFont );
+
+ surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), ( iTall / 2 ) - ( iFontTall / 2 ) );
+ surface()->DrawUnicodeString( unicode );
+
+ //Adrian: This should really be exposed.
+ if ( hl2_episodic.GetBool() )
+ {
+ g_pVGuiLocalize->ConvertANSIToUnicode( "== episode one==", unicode, sizeof( unicode ) );
+
+ iStringWidth = GetStringPixelWidth( unicode, m_hTFont );
+
+ surface()->DrawSetTextPos( ( iWidth / 2 ) - ( iStringWidth / 2 ), ( iTall / 2 ) + ( iFontTall / 2 ));
+ surface()->DrawUnicodeString( unicode );
+ }
+
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CHudPortalCredits::FadeBlend( float fadein, float fadeout, float hold, float localTime )
+{
+ float fadeTime = fadein + hold;
+ float fadeBlend;
+
+ if ( localTime < 0 )
+ return 0;
+
+ if ( localTime < fadein )
+ {
+ fadeBlend = 1 - ((fadein - localTime) / fadein);
+ }
+ else if ( localTime > fadeTime )
+ {
+ if ( fadeout > 0 )
+ fadeBlend = 1 - ((localTime - fadeTime) / fadeout);
+ else
+ fadeBlend = 0;
+ }
+ else
+ fadeBlend = 1;
+
+ if ( fadeBlend < 0 )
+ fadeBlend = 0;
+
+ return fadeBlend;
+}
+
+void CHudPortalCredits::DrawIntroCreditsName( void )
+{
+ if ( m_CreditsList.Count() == 0 )
+ return;
+
+ // fill the screen
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ SetSize( iWidth, iTall );
+
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ if ( pCredit->bActive == false )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName );
+
+ float localTime = gpGlobals->curtime - pCredit->flTimeStart;
+
+ surface()->DrawSetTextFont( m_hTFont );
+ surface()->DrawSetTextColor( m_cColor[0], m_cColor[1], m_cColor[2], FadeBlend( m_flFadeInTime, m_flFadeOutTime, m_flFadeHoldTime + pCredit->flTimeAdd, localTime ) * m_cColor[3] );
+
+ wchar_t unicode[256];
+ g_pVGuiLocalize->ConvertANSIToUnicode( pCredit->szCreditName, unicode, sizeof( unicode ) );
+
+ surface()->DrawSetTextPos( XRES( pCredit->flXPos ), YRES( pCredit->flYPos ) );
+ surface()->DrawUnicodeString( unicode );
+
+ if ( m_flLogoTime > gpGlobals->curtime )
+ continue;
+
+ if ( pCredit->flTime - m_flNextStartTime <= gpGlobals->curtime )
+ {
+ if ( m_CreditsList.IsValidIndex( i + 3 ) )
+ {
+ portalcreditname_t *pNextCredits = &m_CreditsList[i + 3];
+
+ if ( pNextCredits && pNextCredits->flTime == 0.0f )
+ {
+ pNextCredits->bActive = true;
+
+ if ( i < 3 )
+ {
+ pNextCredits->flTimeAdd = ( i + 1.0f );
+ pNextCredits->flTime = gpGlobals->curtime + m_flFadeInTime + m_flFadeOutTime + m_flFadeHoldTime + pNextCredits->flTimeAdd;
+ }
+ else
+ {
+ pNextCredits->flTimeAdd = m_flPauseBetweenWaves;
+ pNextCredits->flTime = gpGlobals->curtime + m_flFadeInTime + m_flFadeOutTime + m_flFadeHoldTime + pNextCredits->flTimeAdd;
+ }
+
+ pNextCredits->flTimeStart = gpGlobals->curtime;
+
+ pNextCredits->iSlot = pCredit->iSlot;
+ }
+ }
+ }
+
+ if ( pCredit->flTime <= gpGlobals->curtime )
+ {
+ pCredit->bActive = false;
+
+ if ( i == m_CreditsList.Count()-1 )
+ {
+ Clear();
+ }
+ }
+ }
+}
+
+void CHudPortalCredits::ApplySchemeSettings( IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ SetVisible( ShouldDraw() );
+
+ SetBgColor( Color(0, 0, 0, 0) );
+}
+
+void CHudPortalCredits::Paint()
+{
+ if ( m_iCreditsType == CREDITS_LOGO )
+ {
+ DrawLogo();
+ }
+ else if ( m_iCreditsType == CREDITS_INTRO )
+ {
+ DrawIntroCreditsName();
+ }
+ else if ( m_iCreditsType == CREDITS_OUTRO )
+ {
+ DrawOutroCreditsName();
+ }
+ else if ( m_iCreditsType == CREDITS_OUTRO_PORTAL )
+ {
+ //DrawPortalOutroCreditsName();
+ DrawPortalOutroCreditsLyrics();
+ }
+
+}
+
+void CHudPortalCredits::PrepareLogo( float flTime )
+{
+ m_Alpha = 0;
+ m_flLogoDesiredLength = flTime;
+ m_flFadeTime = gpGlobals->curtime + 5.0f;
+ m_iLogoState = LOGO_FADEIN;
+ SetActive( true );
+}
+
+void CHudPortalCredits::PrepareOutroCredits( void )
+{
+ PrepareCredits( "OutroCreditsNames" );
+
+ if ( m_CreditsList.Count() == 0 )
+ return;
+
+ // fill the screen
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ SetSize( iWidth, iTall );
+
+ int iHeight = iTall;
+
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+
+ pCredit->flYPos = iHeight;
+ pCredit->bActive = false;
+
+ iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation;
+ }
+
+ SetActive( true );
+
+ g_iPortalCreditsPixelHeight = iHeight;
+}
+
+void CHudPortalCredits::PreparePortalOutroCredits( void )
+{
+ m_iCurrentLowY = 0;
+ m_bStartSong = true;
+ m_flLyricsStartTime = -1;
+
+ m_iCurrentAsciiArt = 0;
+
+ PrepareCredits( "OutroCreditsNames" );
+
+ if ( m_CreditsList.Count() == 0 )
+ return;
+
+ // fill the screen
+ int iWidth, iTall;
+ GetHudSize(iWidth, iTall);
+
+ iWidth = static_cast<int>( static_cast<float>(iTall) * (4.0f/3.0f) );
+
+ iWidth += m_iScreenWidthAdjustment;
+ iTall += m_iScreenHeightAdjustment;
+ SetSize( iWidth+m_iScreenXOffset, iTall +m_iScreenYOffset );
+
+ m_iYOffset = 0;
+ m_iXOffset = 1;
+ m_iYOffsetNames = 0;
+ m_iXOffsetNames = 1;
+
+ //int iHeight = iTall;
+ int iHeight = -1;
+ int iTotalNameLen = 0;
+
+
+ //Prepare Credit Names
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+
+
+ if ( pCredit == NULL )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName, true );
+
+ if (iHeight == -1) iHeight = (iTall/2) - (surface()->GetFontTall ( m_hTFont ) + m_flSeparation);
+ else iHeight += surface()->GetFontTall ( m_hTFont ) + m_flSeparation;
+
+ if (i==0)
+ {
+ Q_strcpy(m_szBorderH,"-");
+ while(GetPixelWidth( m_szBorderH, m_hTFont )< ((iWidth/2)-GetPixelWidth( "---", m_hTFont )))
+ {
+ int iCurrentLength = Q_strlen(m_szBorderH);
+ if (iCurrentLength < sizeof(m_szBorderH))
+ {
+ m_szBorderH[iCurrentLength] = '-';
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+
+ pCredit->flYPos = iHeight;
+ pCredit->bActive = false;
+
+ iTotalNameLen += Q_strlen(pCredit->szCreditName);
+
+ }
+
+ m_flNameCharTime = m_flScrollTime / iTotalNameLen;
+
+ float flStart = m_flScrollCreditsStart;
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ m_CreditsList[i].flTimeStart=flStart;
+ m_CreditsList[i].flTimeInit = Q_strlen(m_CreditsList[i].szCreditName)*m_flNameCharTime;
+ flStart += m_CreditsList[i].flTimeInit;
+ m_CreditsList[i].flTimeEnd=flStart;
+ }
+
+ SetActive( true );
+
+
+ m_flScrollTime = 2;
+ m_flLastPaintTime = gpGlobals->curtime;
+ m_flLastBlinkTime = gpGlobals->curtime;
+}
+
+void CHudPortalCredits::PrepareIntroCredits( void )
+{
+ PrepareCredits( "IntroCreditsNames" );
+
+ int iSlot = 0;
+
+ for ( int i = 0; i < m_CreditsList.Count(); i++ )
+ {
+ portalcreditname_t *pCredit = &m_CreditsList[i];
+
+ if ( pCredit == NULL )
+ continue;
+
+ vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" );
+ vgui::HFont m_hTFont = vgui::scheme()->GetIScheme(scheme)->GetFont( pCredit->szFontName );
+
+ pCredit->flYPos = m_flY + ( iSlot * surface()->GetFontTall ( m_hTFont ) );
+ pCredit->flXPos = m_flX;
+
+ if ( i < 3 )
+ {
+ pCredit->bActive = true;
+ pCredit->iSlot = iSlot;
+ pCredit->flTime = gpGlobals->curtime + m_flFadeInTime + m_flFadeOutTime + m_flFadeHoldTime;
+ pCredit->flTimeStart = gpGlobals->curtime;
+ m_flLogoTime = pCredit->flTime + m_flLogoTimeMod;
+ }
+ else
+ {
+ pCredit->bActive = false;
+ pCredit->flTime = 0.0f;
+ }
+
+ iSlot = ( iSlot + 1 ) % 3;
+ }
+
+ SetActive( true );
+}
+
+void CHudPortalCredits::MsgFunc_CreditsPortalMsg( bf_read &msg )
+{
+ m_iCreditsType = msg.ReadByte();
+
+ switch ( m_iCreditsType )
+ {
+ case CREDITS_LOGO:
+ {
+ PrepareLogo( 5.0f );
+ break;
+ }
+ case CREDITS_INTRO:
+ {
+ PrepareIntroCredits();
+ break;
+ }
+ case CREDITS_OUTRO:
+ {
+ PrepareOutroCredits();
+ break;
+ }
+ case CREDITS_OUTRO_PORTAL:
+ {
+ PreparePortalOutroCredits();
+ break;
+ }
+
+ }
+}
+
+void CHudPortalCredits::MsgFunc_LogoTimeMsg( bf_read &msg )
+{
+ m_iCreditsType = CREDITS_LOGO;
+ PrepareLogo( msg.ReadFloat() );
+}
+
+
diff --git a/game/client/portal/portal_hud_crosshair.cpp b/game/client/portal/portal_hud_crosshair.cpp
new file mode 100644
index 0000000..33da2d9
--- /dev/null
+++ b/game/client/portal/portal_hud_crosshair.cpp
@@ -0,0 +1,150 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "hud.h"
+#include "portal_hud_crosshair.h"
+#include "iclientmode.h"
+#include "c_portal_player.h"
+#include "view.h"
+#include "weapon_portalbase.h"
+#include "vgui_controls/Controls.h"
+#include "vgui/ISurface.h"
+#include "ivrenderview.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern ConVar crosshair;
+extern ConVar cl_observercrosshair;
+
+using namespace vgui;
+
+int ScreenTransform( const Vector& point, Vector& screen );
+
+DECLARE_HUDELEMENT( CHudPortalCrosshair );
+
+CHudPortalCrosshair::CHudPortalCrosshair( const char *pElementName ) :
+CHudElement( pElementName ), BaseClass( NULL, "HudPortalCrosshair" )
+{
+ vgui::Panel *pParent = g_pClientMode->GetViewport();
+ SetParent( pParent );
+
+ m_pCrosshair = 0;
+
+ m_clrCrosshair = Color( 0, 0, 0, 0 );
+
+ m_vecCrossHairOffsetAngle.Init();
+
+ SetHiddenBits( HIDEHUD_PLAYERDEAD | HIDEHUD_CROSSHAIR );
+}
+
+void CHudPortalCrosshair::ApplySchemeSettings( IScheme *scheme )
+{
+ BaseClass::ApplySchemeSettings( scheme );
+
+ m_pDefaultCrosshair = gHUD.GetIcon("crosshair_default");
+ SetPaintBackgroundEnabled( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Save CPU cycles by letting the HUD system early cull
+// costly traversal. Called per frame, return true if thinking and
+// painting need to occur.
+//-----------------------------------------------------------------------------
+bool CHudPortalCrosshair::ShouldDraw()
+{
+ // NOTE: Portal crosshair should no longer be in use, but I'm leaving the code here until X360 lock down... we don't want to draw this ever. -Jeep
+ return false;
+ C_Portal_Player *pPlayer = C_Portal_Player::GetLocalPortalPlayer();
+
+ if ( !pPlayer )
+ return false;
+
+ CWeaponPortalBase *pWeapon = dynamic_cast<CWeaponPortalBase*>( pPlayer->GetActiveWeapon() );
+
+ if ( !pWeapon )
+ return false;
+
+ bool bNeedsDraw = m_pCrosshair &&
+ crosshair.GetInt() &&
+ !engine->IsDrawingLoadingImage() &&
+ !engine->IsPaused() &&
+ g_pClientMode->ShouldDrawCrosshair() &&
+ !( pPlayer->GetFlags() & FL_FROZEN ) &&
+ ( pPlayer->entindex() == render->GetViewEntity() ) &&
+ !pPlayer->IsInVGuiInputMode() &&
+ ( pPlayer->IsAlive() || ( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE ) || ( cl_observercrosshair.GetBool() && pPlayer->GetObserverMode() == OBS_MODE_ROAMING ) );
+
+ return ( bNeedsDraw && CHudElement::ShouldDraw() );
+}
+
+void CHudPortalCrosshair::Paint( void )
+{
+ if ( !m_pCrosshair )
+ return;
+
+ if ( !IsCurrentViewAccessAllowed() )
+ return;
+
+ m_curViewAngles = CurrentViewAngles();
+ m_curViewOrigin = CurrentViewOrigin();
+
+ float x, y;
+ x = ScreenWidth()/2;
+ y = ScreenHeight()/2;
+
+ // MattB - m_vecCrossHairOffsetAngle is the autoaim angle.
+ // if we're not using autoaim, just draw in the middle of the
+ // screen
+ if ( m_vecCrossHairOffsetAngle != vec3_angle )
+ {
+ QAngle angles;
+ Vector forward;
+ Vector point, screen;
+
+ // this code is wrong
+ angles = m_curViewAngles + m_vecCrossHairOffsetAngle;
+ AngleVectors( angles, &forward );
+ VectorAdd( m_curViewOrigin, forward, point );
+ ScreenTransform( point, screen );
+
+ x += 0.5f * screen[0] * ScreenWidth() + 0.5f;
+ y += 0.5f * screen[1] * ScreenHeight() + 0.5f;
+ }
+
+ m_pCrosshair->DrawSelf(
+ x - 0.5f * m_pCrosshair->Width(),
+ y - 0.5f * m_pCrosshair->Height(),
+ m_clrCrosshair );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudPortalCrosshair::SetCrosshairAngle( const QAngle& angle )
+{
+ VectorCopy( angle, m_vecCrossHairOffsetAngle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHudPortalCrosshair::SetCrosshair( CHudTexture *texture, Color& clr )
+{
+ m_pCrosshair = texture;
+ m_clrCrosshair = clr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Resets the crosshair back to the default
+//-----------------------------------------------------------------------------
+void CHudPortalCrosshair::ResetCrosshair()
+{
+ Color white(255, 255, 255, 255);
+ SetCrosshair( m_pDefaultCrosshair, white );
+}
diff --git a/game/client/portal/portal_hud_crosshair.h b/game/client/portal/portal_hud_crosshair.h
new file mode 100644
index 0000000..2b2c843
--- /dev/null
+++ b/game/client/portal/portal_hud_crosshair.h
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef HUD_PORTAL_CROSSHAIR_H
+#define HUD_PORTAL_CROSSHAIR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "hudelement.h"
+#include <vgui_controls/Panel.h>
+
+namespace vgui
+{
+ class IScheme;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CHudPortalCrosshair : public CHudElement, public vgui::Panel
+{
+ DECLARE_CLASS_SIMPLE( CHudPortalCrosshair, vgui::Panel );
+public:
+ CHudPortalCrosshair( const char *pElementName );
+
+ void SetCrosshairAngle( const QAngle& angle );
+ void SetCrosshair( CHudTexture *texture, Color& clr );
+ void ResetCrosshair();
+ void DrawCrosshair( void );
+ bool HasCrosshair( void ) { return ( m_pCrosshair != NULL ); }
+ bool ShouldDraw();
+
+protected:
+ virtual void ApplySchemeSettings( vgui::IScheme *scheme );
+ virtual void Paint();
+
+private:
+ // Crosshair sprite and colors
+ CHudTexture *m_pCrosshair;
+ CHudTexture *m_pDefaultCrosshair;
+ Color m_clrCrosshair;
+ QAngle m_vecCrossHairOffsetAngle;
+
+ QAngle m_curViewAngles;
+ Vector m_curViewOrigin;
+};
+
+
+// Enable/disable crosshair rendering.
+extern ConVar crosshair;
+
+
+#endif // HUD_PORTAL_CROSSHAIR_H
diff --git a/game/client/portal/portal_render_targets.cpp b/game/client/portal/portal_render_targets.cpp
new file mode 100644
index 0000000..4ba8893
--- /dev/null
+++ b/game/client/portal/portal_render_targets.cpp
@@ -0,0 +1,229 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Portal mod render targets are specified by and accessable through this singleton
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "portal_render_targets.h"
+#include "materialsystem/imaterialsystem.h"
+#include "rendertexture.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Called in CClientRenderTargets::InitClientRenderTargets, used to set
+// the CTextureReference member
+// Input : pMaterialSystem - material system passed in from the engine
+// Output : ITexture* - the created texture
+//-----------------------------------------------------------------------------
+ITexture* CPortalRenderTargets::InitPortal1Texture( IMaterialSystem* pMaterialSystem )
+{
+ if ( IsX360() )
+ {
+ // shouldn't be using
+ Assert( 0 );
+ return NULL;
+ }
+
+ return pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_Portal1",
+ 1, 1, RT_SIZE_FULL_FRAME_BUFFER,
+ pMaterialSystem->GetBackBufferFormat(),
+ MATERIAL_RT_DEPTH_SHARED,
+ 0,
+ CREATERENDERTARGETFLAGS_HDR );
+}
+
+ITexture* CPortalRenderTargets::GetPortal1Texture()
+{
+ return m_Portal1Texture;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called in CClientRenderTargets::InitClientRenderTargets, used to set
+// the CTextureReference member
+// Input : pMaterialSystem - material system passed in from the engine
+// Output : ITexture* - the created texture
+//-----------------------------------------------------------------------------
+ITexture* CPortalRenderTargets::InitPortal2Texture( IMaterialSystem* pMaterialSystem )
+{
+ if ( IsX360() )
+ {
+ // shouldn't be using
+ Assert( 0 );
+ return NULL;
+ }
+
+ return pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_Portal2",
+ 1, 1, RT_SIZE_FULL_FRAME_BUFFER,
+ pMaterialSystem->GetBackBufferFormat(),
+ MATERIAL_RT_DEPTH_SHARED,
+ 0,
+ CREATERENDERTARGETFLAGS_HDR );
+}
+
+ITexture* CPortalRenderTargets::GetPortal2Texture()
+{
+ return m_Portal2Texture;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called in CClientRenderTargets::InitClientRenderTargets, used to set
+// the CTextureReference member
+// Input : pMaterialSystem - material system passed in from the engine
+// Output : ITexture* - the created texture
+//-----------------------------------------------------------------------------
+ITexture* CPortalRenderTargets::InitDepthDoublerTexture( IMaterialSystem* pMaterialSystem )
+{
+ return pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_DepthDoubler",
+ 512, 512, RT_SIZE_DEFAULT,
+ pMaterialSystem->GetBackBufferFormat(),
+ MATERIAL_RT_DEPTH_SHARED,
+ 0,
+ CREATERENDERTARGETFLAGS_HDR );
+}
+
+ITexture* CPortalRenderTargets::GetDepthDoublerTexture()
+{
+ return m_DepthDoublerTexture;
+}
+
+
+void CPortalRenderTargets::InitPortalWaterTextures( IMaterialSystem* pMaterialSystem )
+{
+ if ( IsX360() )
+ {
+ return;
+ }
+
+ //Reflections
+ m_WaterReflectionTextures[0].Init(
+ pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_PortalWaterReflection_Depth1",
+ 512, 512, RT_SIZE_PICMIP,
+ pMaterialSystem->GetBackBufferFormat(),
+ MATERIAL_RT_DEPTH_SEPARATE,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT,
+ CREATERENDERTARGETFLAGS_HDR ) );
+
+ m_WaterReflectionTextures[1].Init(
+ pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_PortalWaterReflection_Depth2",
+ 256, 256, RT_SIZE_PICMIP,
+ pMaterialSystem->GetBackBufferFormat(),
+ MATERIAL_RT_DEPTH_SEPARATE,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT,
+ CREATERENDERTARGETFLAGS_HDR ) );
+
+
+ //Refractions
+ m_WaterRefractionTextures[0].Init(
+ pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_PortalWaterRefraction_Depth1",
+ 512, 512, RT_SIZE_PICMIP,
+ // This is different than reflection because it has to have alpha for fog factor.
+ IMAGE_FORMAT_RGBA8888,
+ MATERIAL_RT_DEPTH_SEPARATE,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT,
+ CREATERENDERTARGETFLAGS_HDR ) );
+
+ m_WaterRefractionTextures[1].Init(
+ pMaterialSystem->CreateNamedRenderTargetTextureEx2(
+ "_rt_PortalWaterRefraction_Depth2",
+ 256, 256, RT_SIZE_PICMIP,
+ // This is different than reflection because it has to have alpha for fog factor.
+ IMAGE_FORMAT_RGBA8888,
+ MATERIAL_RT_DEPTH_SEPARATE,
+ TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT,
+ CREATERENDERTARGETFLAGS_HDR ) );
+}
+
+ITexture* CPortalRenderTargets::GetWaterReflectionTextureForStencilDepth( int iStencilDepth )
+{
+ if ( IsX360() )
+ {
+ return NULL;
+ }
+
+ if ( iStencilDepth > 2 )
+ return NULL;
+
+ if ( iStencilDepth == 0 )
+ return m_WaterReflectionTexture; //from CBaseClientRenderTargets
+
+ return m_WaterReflectionTextures[ iStencilDepth - 1 ];
+}
+
+ITexture* CPortalRenderTargets::GetWaterRefractionTextureForStencilDepth( int iStencilDepth )
+{
+ if ( IsX360() )
+ {
+ return NULL;
+ }
+
+ if ( iStencilDepth > 2 )
+ return NULL;
+
+ if ( iStencilDepth == 0 )
+ return m_WaterRefractionTexture; //from CBaseClientRenderTargets
+
+ return m_WaterRefractionTextures[ iStencilDepth - 1 ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: InitClientRenderTargets, interface called by the engine at material system init in the engine
+// Input : pMaterialSystem - the interface to the material system from the engine (our singleton hasn't been set up yet)
+// pHardwareConfig - the user's hardware config, useful for conditional render targets setup
+//-----------------------------------------------------------------------------
+void CPortalRenderTargets::InitClientRenderTargets( IMaterialSystem* pMaterialSystem, IMaterialSystemHardwareConfig* pHardwareConfig )
+{
+ // If they don't support stencils, allocate render targets for drawing portals.
+ // TODO: When stencils are default, do the below check before bothering to allocate the RTs
+ // and make sure that switching from Stencil<->RT mode reinits the material system.
+// if ( materials->StencilBufferBits() == 0 )
+ if ( IsPC() || !IsX360() )
+ {
+ m_Portal1Texture.Init( InitPortal1Texture( pMaterialSystem ) );
+ m_Portal2Texture.Init( InitPortal2Texture( pMaterialSystem ) );
+ }
+
+ m_DepthDoublerTexture.Init( InitDepthDoublerTexture( pMaterialSystem ) );
+
+ if ( IsPC() || !IsX360() )
+ {
+ InitPortalWaterTextures( pMaterialSystem );
+ }
+
+ // Water effects & camera from the base class (standard HL2 targets)
+ BaseClass::InitClientRenderTargets( pMaterialSystem, pHardwareConfig, 512, 256 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shutdown client render targets. This gets called during shutdown in the engine
+// Input : -
+//-----------------------------------------------------------------------------
+void CPortalRenderTargets::ShutdownClientRenderTargets()
+{
+ m_Portal1Texture.Shutdown();
+ m_Portal2Texture.Shutdown();
+ m_DepthDoublerTexture.Shutdown();
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ m_WaterReflectionTextures[i].Shutdown();
+ m_WaterRefractionTextures[i].Shutdown();
+ }
+
+ // Clean up standard HL2 RTs (camera and water)
+ BaseClass::ShutdownClientRenderTargets();
+}
+
+
+static CPortalRenderTargets g_PortalRenderTargets;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CPortalRenderTargets, IClientRenderTargets, CLIENTRENDERTARGETS_INTERFACE_VERSION, g_PortalRenderTargets );
+CPortalRenderTargets* portalrendertargets = &g_PortalRenderTargets; \ No newline at end of file
diff --git a/game/client/portal/portal_render_targets.h b/game/client/portal/portal_render_targets.h
new file mode 100644
index 0000000..59f3010
--- /dev/null
+++ b/game/client/portal/portal_render_targets.h
@@ -0,0 +1,61 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Portal mod render targets are specified by and accessable through this singleton
+//
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+#ifndef PORTALRENDERTARGETS_H_
+#define PORTALRENDERTARGETS_H_
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "baseclientrendertargets.h" // Base class, with interfaces called by engine and inherited members to init common render targets
+
+#ifndef PORTAL
+#pragma message ( "This file should only be built with portal builds" )
+#endif
+
+// externs
+class IMaterialSystem;
+class IMaterialSystemHardwareConfig;
+
+class CPortalRenderTargets : public CBaseClientRenderTargets
+{
+ // no networked vars
+ DECLARE_CLASS_GAMEROOT( CPortalRenderTargets, CBaseClientRenderTargets );
+public:
+ virtual void InitClientRenderTargets( IMaterialSystem* pMaterialSystem, IMaterialSystemHardwareConfig* pHardwareConfig );
+ virtual void ShutdownClientRenderTargets();
+
+ ITexture* GetPortal1Texture( void );
+ ITexture* GetPortal2Texture( void );
+ ITexture* GetDepthDoublerTexture( void );
+
+ //recursive views require different water textures
+ ITexture* GetWaterReflectionTextureForStencilDepth( int iStencilDepth );
+ ITexture* GetWaterRefractionTextureForStencilDepth( int iStencilDepth );
+
+private:
+ CTextureReference m_Portal1Texture;
+ CTextureReference m_Portal2Texture;
+ CTextureReference m_DepthDoublerTexture;
+
+ CTextureReference m_WaterRefractionTextures[2];
+ CTextureReference m_WaterReflectionTextures[2];
+
+ ITexture* InitPortal1Texture ( IMaterialSystem* pMaterialSystem );
+ ITexture* InitPortal2Texture ( IMaterialSystem* pMaterialSystem );
+ ITexture* InitDepthDoublerTexture ( IMaterialSystem* pMaterialSystem );
+
+ void InitPortalWaterTextures ( IMaterialSystem* pMaterialSystem );
+
+};
+
+extern CPortalRenderTargets* portalrendertargets;
+
+
+#endif //PORTALRENDERTARGETS_H_ \ No newline at end of file
diff --git a/game/client/portal/portalrenderable_flatbasic.cpp b/game/client/portal/portalrenderable_flatbasic.cpp
new file mode 100644
index 0000000..35bfef8
--- /dev/null
+++ b/game/client/portal/portalrenderable_flatbasic.cpp
@@ -0,0 +1,1304 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "cbase.h"
+#include "portalrenderable_flatbasic.h"
+#include "clienteffectprecachesystem.h"
+#include "Portal_DynamicMeshRenderingUtils.h"
+#include "portal_shareddefs.h"
+#include "view.h"
+#include "c_pixel_visibility.h"
+#include "glow_overlay.h"
+#include "portal_render_targets.h"
+#include "materialsystem/itexture.h"
+#include "toolframework/itoolframework.h"
+#include "toolframework_client.h"
+#include "tier1/KeyValues.h"
+#include "prop_portal_shared.h"
+#include "view_scene.h"
+#include "materialsystem/imaterialvar.h"
+#include "tier0/vprof.h"
+
+
+#define PORTALRENDERABLE_FLATBASIC_MINPIXELVIS 0.0f
+
+
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheFlatBasicPortalDrawingMaterials )
+#if !defined( _X360 ) //XBox 360 is guaranteed to use stencil mode, and therefore doesn't need texture mode materials
+CLIENTEFFECT_MATERIAL( "models/portals/portal_1_dynamicmesh" )
+CLIENTEFFECT_MATERIAL( "models/portals/portal_2_dynamicmesh" )
+CLIENTEFFECT_MATERIAL( "models/portals/portal_1_renderfix_dynamicmesh" )
+CLIENTEFFECT_MATERIAL( "models/portals/portal_2_renderfix_dynamicmesh" )
+#endif
+CLIENTEFFECT_MATERIAL( "models/portals/portal_depthdoubler" )
+CLIENTEFFECT_MATERIAL( "models/portals/portalstaticoverlay_1" )
+CLIENTEFFECT_MATERIAL( "models/portals/portalstaticoverlay_2" )
+CLIENTEFFECT_MATERIAL( "models/portals/portal_stencil_hole" )
+CLIENTEFFECT_MATERIAL( "models/portals/portal_refract_1" )
+CLIENTEFFECT_MATERIAL( "models/portals/portal_refract_2" )
+//CLIENTEFFECT_MATERIAL( "effects/flashlight001" ) //light transfers disabled indefinitely
+CLIENTEFFECT_REGISTER_END()
+
+class CAutoInitFlatBasicPortalDrawingMaterials : public CAutoGameSystem
+{
+public:
+ FlatBasicPortalRenderingMaterials_t m_Materials;
+ void LevelInitPreEntity()
+ {
+ m_Materials.m_PortalMaterials[0].Init( "models/portals/portal_1_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_PortalMaterials[1].Init( "models/portals/portal_2_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_PortalRenderFixMaterials[0].Init( "models/portals/portal_1_renderfix_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_PortalRenderFixMaterials[1].Init( "models/portals/portal_2_renderfix_dynamicmesh", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_PortalDepthDoubler.Init( "models/portals/portal_depthdoubler", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_PortalStaticOverlay[0].Init( "models/portals/portalstaticoverlay_1", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_PortalStaticOverlay[1].Init( "models/portals/portalstaticoverlay_2", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_Portal_Stencil_Hole.Init( "models/portals/portal_stencil_hole", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_Portal_Refract[0].Init( "models/portals/portal_refract_1", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_Portal_Refract[1].Init( "models/portals/portal_refract_2", TEXTURE_GROUP_CLIENT_EFFECTS );
+ //m_Materials.m_PortalLightTransfer_ShadowTexture.Init( "effects/flashlight001", TEXTURE_GROUP_OTHER ); //light transfers disabled indefinitely
+
+ m_Materials.m_pDepthDoubleViewMatrixVar = m_Materials.m_PortalDepthDoubler->FindVar( "$alternateviewmatrix", NULL, false );
+ Assert( m_Materials.m_pDepthDoubleViewMatrixVar != NULL );
+ }
+};
+static CAutoInitFlatBasicPortalDrawingMaterials s_FlatBasicPortalDrawingMaterials;
+
+const FlatBasicPortalRenderingMaterials_t& CPortalRenderable_FlatBasic::m_Materials = s_FlatBasicPortalDrawingMaterials.m_Materials;
+
+
+LINK_ENTITY_TO_CLASS( prop_portal_flatbasic, CPortalRenderable_FlatBasic );
+
+
+CPortalRenderable_FlatBasic::CPortalRenderable_FlatBasic( void )
+: m_pLinkedPortal( NULL ),
+ m_ptOrigin( 0.0f, 0.0f, 0.0f ),
+ m_vForward( 1.0f, 0.0f, 0.0f ),
+ m_vUp( 0.0f, 0.0f, 1.0f ),
+ m_vRight( 0.0f, 1.0f, 0.0f ),
+ m_fStaticAmount( 0.0f ),
+ m_fSecondaryStaticAmount( 0.0f ),
+ m_fOpenAmount( 0.0f ),
+ m_bIsPortal2( false )
+{
+ m_InternallyMaintainedData.m_VisData.m_fDistToAreaPortalTolerance = 64.0f;
+ m_InternallyMaintainedData.m_VisData.m_vecVisOrigin = Vector(0,0,0);
+ m_InternallyMaintainedData.m_iViewLeaf = -1;
+
+ m_InternallyMaintainedData.m_DepthDoublerTextureView.Identity();
+ m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration = false;
+ m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = SKYBOX_NOT_VISIBLE;
+
+ m_InternallyMaintainedData.m_ptForwardOrigin.Init( 1.0f, 0.0f, 0.0f );
+ m_InternallyMaintainedData.m_ptCorners[0] =
+ m_InternallyMaintainedData.m_ptCorners[1] =
+ m_InternallyMaintainedData.m_ptCorners[2] =
+ m_InternallyMaintainedData.m_ptCorners[3] =
+ Vector( 0.0f, 0.0f, 0.0f );
+}
+
+void CPortalRenderable_FlatBasic::GetToolRecordingState( bool bActive, KeyValues *msg )
+{
+ if ( !ToolsEnabled() )
+ return;
+
+ VPROF_BUDGET( "CPortalRenderable_FlatBasic::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
+
+ BaseClass::GetToolRecordingState( msg );
+ CPortalRenderable::GetToolRecordingState( bActive, msg );
+
+ C_Prop_Portal *pLinkedPortal = static_cast<C_Prop_Portal*>( m_pLinkedPortal );
+
+ static PortalRecordingState_t state;
+ state.m_nPortalId = static_cast<C_Prop_Portal*>( this )->index;
+ state.m_nLinkedPortalId = pLinkedPortal ? pLinkedPortal->index : -1;
+ state.m_fStaticAmount = m_fStaticAmount;
+ state.m_fSecondaryStaticAmount = m_fSecondaryStaticAmount;
+ state.m_fOpenAmount = m_fOpenAmount;
+ state.m_bIsPortal2 = m_bIsPortal2;
+ msg->SetPtr( "portal", &state );
+}
+
+void CPortalRenderable_FlatBasic::PortalMoved( void )
+{
+ m_InternallyMaintainedData.m_ptForwardOrigin = m_ptOrigin + m_vForward;
+ m_InternallyMaintainedData.m_fPlaneDist = m_vForward.Dot( m_ptOrigin );
+
+ // Update the points on the portal which we add to PVS
+ {
+ Vector vScaledRight = m_vRight * PORTAL_HALF_WIDTH;
+ Vector vScaledUp = m_vUp * PORTAL_HALF_HEIGHT;
+
+ m_InternallyMaintainedData.m_ptCorners[0] = (m_InternallyMaintainedData.m_ptForwardOrigin + vScaledRight) + vScaledUp;
+ m_InternallyMaintainedData.m_ptCorners[1] = (m_InternallyMaintainedData.m_ptForwardOrigin - vScaledRight) + vScaledUp;
+ m_InternallyMaintainedData.m_ptCorners[2] = (m_InternallyMaintainedData.m_ptForwardOrigin - vScaledRight) - vScaledUp;
+ m_InternallyMaintainedData.m_ptCorners[3] = (m_InternallyMaintainedData.m_ptForwardOrigin + vScaledRight) - vScaledUp;
+
+
+ m_InternallyMaintainedData.m_VisData.m_vecVisOrigin = m_InternallyMaintainedData.m_ptForwardOrigin;
+ m_InternallyMaintainedData.m_VisData.m_fDistToAreaPortalTolerance = 64.0f;
+ m_InternallyMaintainedData.m_iViewLeaf = enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptForwardOrigin );
+ }
+
+ m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = engine->IsSkyboxVisibleFromPoint( m_InternallyMaintainedData.m_ptForwardOrigin );
+ for( int i = 0; i < 4 && ( m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners != SKYBOX_3DSKYBOX_VISIBLE ); ++i )
+ {
+ SkyboxVisibility_t nCornerVis = engine->IsSkyboxVisibleFromPoint( m_InternallyMaintainedData.m_ptCorners[i] );
+ if ( ( m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners == SKYBOX_NOT_VISIBLE ) || ( nCornerVis != SKYBOX_NOT_VISIBLE ) )
+ {
+ m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = nCornerVis;
+ }
+ }
+
+ //render fix bounding planes
+ {
+ for( int i = 0; i != PORTALRENDERFIXMESH_OUTERBOUNDPLANES; ++i )
+ {
+ float fCirclePos = ((float)(i)) * ((M_PI * 2.0f) / (float)PORTALRENDERFIXMESH_OUTERBOUNDPLANES);
+ float fUpBlend = cosf( fCirclePos );
+ float fRightBlend = sinf( fCirclePos );
+
+ Vector vNormal = -fUpBlend * m_vUp - fRightBlend * m_vRight;
+ Vector ptOnPlane = m_ptOrigin + (m_vUp * (fUpBlend * PORTAL_HALF_HEIGHT * 1.1f)) + (m_vRight * (fRightBlend * PORTAL_HALF_WIDTH * 1.1f));
+
+ m_InternallyMaintainedData.m_BoundingPlanes[i].Init( vNormal, vNormal.Dot( ptOnPlane ) );
+ }
+
+ m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].Init( -m_vForward, (-m_vForward).Dot( m_ptOrigin ) );
+ m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 1].Init( m_vForward, m_vForward.Dot( m_ptOrigin - (m_vForward * 5.0f) ) );
+ }
+
+ //update depth doubler usability flag
+ m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration =
+ ( m_pLinkedPortal && //linked to another portal
+ ( m_vForward.Dot( m_pLinkedPortal->m_ptOrigin - m_ptOrigin ) > 0.0f ) && //this portal looking in the general direction of the other portal
+ ( m_vForward.Dot( m_pLinkedPortal->m_vForward ) < -0.7071f ) ); //within 45 degrees of facing directly at each other
+
+ if( m_pLinkedPortal )
+ m_pLinkedPortal->m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration = true;
+
+
+
+ //lastly, update link matrix
+ if ( m_pLinkedPortal != NULL )
+ {
+ matrix3x4_t localToWorld( m_vForward, -m_vRight, m_vUp, m_ptOrigin );
+ matrix3x4_t remoteToWorld( m_pLinkedPortal->m_vForward, -m_pLinkedPortal->m_vRight, m_pLinkedPortal->m_vUp, m_pLinkedPortal->m_ptOrigin );
+ CProp_Portal_Shared::UpdatePortalTransformationMatrix( localToWorld, remoteToWorld, &m_matrixThisToLinked );
+
+ // update the remote portal
+ MatrixInverseTR( m_matrixThisToLinked, m_pLinkedPortal->m_matrixThisToLinked );
+ }
+ else
+ {
+ m_matrixThisToLinked.Identity(); // don't accidentally teleport objects to zero space
+ }
+}
+
+
+
+
+bool CPortalRenderable_FlatBasic::WillUseDepthDoublerThisDraw( void ) const
+{
+ return g_pPortalRender->ShouldUseStencilsToRenderPortals() &&
+ m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration &&
+ (g_pPortalRender->GetRemainingPortalViewDepth() == 0) &&
+ (g_pPortalRender->GetViewRecursionLevel() > 1) &&
+ (g_pPortalRender->GetCurrentViewExitPortal() != this);
+}
+
+
+
+ConVar r_portal_use_complex_frustums( "r_portal_use_complex_frustums", "1", FCVAR_CLIENTDLL, "View optimization, turn this off if you get odd visual bugs." );
+
+bool CPortalRenderable_FlatBasic::CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum )
+{
+ if( r_portal_use_complex_frustums.GetBool() == false )
+ return false;
+
+ int i;
+
+ int iViewRecursionLevel = g_pPortalRender->GetViewRecursionLevel();
+ int iNextViewRecursionLevel = iViewRecursionLevel + 1;
+
+ if( (iViewRecursionLevel == 0) &&
+ ( (ptCurrentViewOrigin - m_ptOrigin).LengthSqr() < (PORTAL_HALF_HEIGHT * PORTAL_HALF_HEIGHT) ) )//FIXME: Player closeness check might need reimplementation
+ {
+ //calculations are most likely going to be completely useless, return nothing
+ return false;
+ }
+
+ if( m_pLinkedPortal == NULL )
+ return false;
+
+ if( m_vForward.Dot( ptCurrentViewOrigin ) <= m_InternallyMaintainedData.m_fPlaneDist )
+ return false; //looking at portal backface
+
+ //VPlane *pInputFrustum = view->GetFrustum(); //g_pPortalRender->m_RecursiveViewComplexFrustums[iViewRecursionLevel].Base();
+ //int iInputFrustumPlaneCount = 6; //g_pPortalRender->m_RecursiveViewComplexFrustums[iViewRecursionLevel].Count();
+ VPlane *pInputFrustum = g_pPortalRender->m_RecursiveViewComplexFrustums[iViewRecursionLevel].Base();
+ int iInputFrustumPlaneCount = g_pPortalRender->m_RecursiveViewComplexFrustums[iViewRecursionLevel].Count();
+ Assert( iInputFrustumPlaneCount > 0 );
+
+ Vector ptTempWork[2];
+ int iAllocSize = 4 + iInputFrustumPlaneCount;
+
+ Vector *pInVerts = (Vector *)stackalloc( sizeof( Vector ) * iAllocSize * 2 ); //possible to add 1 point per cut, 4 starting points, iInputFrustumPlaneCount cuts
+ Vector *pOutVerts = pInVerts + iAllocSize;
+ Vector *pTempVerts;
+
+ //clip by first plane and put output into pInVerts
+ int iVertCount = ClipPolyToPlane( m_InternallyMaintainedData.m_ptCorners, 4, pInVerts, pInputFrustum[0].m_Normal, pInputFrustum[0].m_Dist, 0.01f );
+
+ //clip by other planes and flipflop in and out pointers
+ for( i = 1; i != iInputFrustumPlaneCount; ++i )
+ {
+ if( iVertCount < 3 )
+ return false; //nothing left in the frustum
+
+ iVertCount = ClipPolyToPlane( pInVerts, iVertCount, pOutVerts, pInputFrustum[i].m_Normal, pInputFrustum[i].m_Dist, 0.01f );
+ pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
+ }
+
+ if( iVertCount < 3 )
+ return false; //nothing left in the frustum
+
+ g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].SetCount( iVertCount + 2 ); //+2 for near and far z planes
+
+ Vector ptTransformedCamera = m_matrixThisToLinked * ptCurrentViewOrigin;
+
+ //generate planes defined by each line around the convex and the camera origin
+ for( i = 0; i != iVertCount; ++i )
+ {
+ Vector *p1, *p2;
+ p1 = &pInVerts[i];
+ p2 = &pInVerts[(i+1)%iVertCount];
+
+ Vector vLine1 = *p1 - ptCurrentViewOrigin;
+ Vector vLine2 = *p2 - ptCurrentViewOrigin;
+ Vector vNormal = vLine1.Cross( vLine2 );
+ vNormal.NormalizeInPlace();
+
+ vNormal = m_matrixThisToLinked.ApplyRotation( vNormal );
+ g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Element(i).Init( vNormal, vNormal.Dot( ptTransformedCamera ) );
+ }
+
+ //Near Z
+ g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Element(i).Init( m_pLinkedPortal->m_vForward, m_pLinkedPortal->m_InternallyMaintainedData.m_fPlaneDist );
+
+ //Far Z
+ ++i;
+ {
+ Vector vNormal = m_matrixThisToLinked.ApplyRotation( pInputFrustum[iInputFrustumPlaneCount - 1].m_Normal );
+ Vector ptOnPlane = pInputFrustum[iInputFrustumPlaneCount - 1].m_Dist * pInputFrustum[iInputFrustumPlaneCount - 1].m_Normal;
+ g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Element(i).Init( vNormal, vNormal.Dot( m_matrixThisToLinked * ptOnPlane ) );
+ }
+
+
+
+
+ if( iVertCount > 4 )
+ {
+ float *fLineLengthSqr = (float *)stackalloc( sizeof( float ) * iVertCount );
+ VPlane *Planes = (VPlane *)stackalloc( sizeof( VPlane ) * iVertCount );
+
+ memcpy( Planes, g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Base(), sizeof( VPlane ) * iVertCount );
+
+ for( i = 0; i != (iVertCount - 1); ++i )
+ {
+ fLineLengthSqr[i] = (pInVerts[i + 1] - pInVerts[i]).LengthSqr();
+ }
+ fLineLengthSqr[i] = (pInVerts[0] - pInVerts[i]).LengthSqr(); //wrap around
+
+
+ while( iVertCount > 4 )
+ {
+ //we have too many verts to represent this accurately as a frustum,
+ //so, we're going to eliminate the smallest sides and bridge the surrounding sides until we're down to 4
+
+ float fMinSide = fLineLengthSqr[0];
+ int iMinSideFirstPoint = 0;
+ int iOldVertCount = iVertCount;
+ --iVertCount; //we're going to decrement this sometime in this block, it makes math easier to do it now
+
+ for( i = 1; i != iOldVertCount; ++i )
+ {
+ if( fLineLengthSqr[i] < fMinSide )
+ {
+ fMinSide = fLineLengthSqr[i];
+ iMinSideFirstPoint = i;
+ }
+ }
+
+ int i1, i2, i3, i4;
+ i1 = (iMinSideFirstPoint + iVertCount)%(iOldVertCount);
+ i2 = iMinSideFirstPoint;
+ i3 = (iMinSideFirstPoint + 1)%(iOldVertCount);
+ i4 = (iMinSideFirstPoint + 2)%(iOldVertCount);
+
+ Vector *p1, *p2, *p3, *p4;
+ p1 = &pInVerts[i1];
+ p2 = &pInVerts[i2];
+ p3 = &pInVerts[i3];
+ p4 = &pInVerts[i4];
+
+
+ //now we know the two points that we have to merge to one, project and make a merged point from the surrounding lines
+ if( fMinSide >= 0.1f ) //only worth doing the math if it's actually going to be accurate and make a difference
+ {
+ Vector vLine1 = *p2 - *p1;
+ Vector vLine2 = *p3 - *p4;
+
+ Vector vLine1Normal = vLine1.Cross( m_vForward );
+ vLine1Normal.NormalizeInPlace();
+
+ float fNormalDot = vLine1Normal.Dot( vLine2 );
+
+ AssertMsgOnce( fNormalDot != 0.0f, "Tell Dave Kircher if this pops up. It won't interfere with gameplay though" );
+ if( fNormalDot == 0.0f )
+ {
+ return false; //something went horribly wrong, bail and just suffer a slight framerate penalty for now
+ }
+
+ float fDist = vLine1Normal.Dot(*p1 - *p4);
+ *p2 = *p4 + (vLine2 * (fDist/fNormalDot));
+ }
+
+ fLineLengthSqr[i1] = (*p2 - *p1).LengthSqr();
+ fLineLengthSqr[i2] = (*p4 - *p2).LengthSqr(); //must do this BEFORE possibly shifting points p4+ left
+
+ if( i2 < i3 )
+ {
+ VPlane *v2 = &Planes[iMinSideFirstPoint];
+ memcpy( v2, v2 + 1, sizeof( VPlane ) * (iVertCount - iMinSideFirstPoint) );
+ }
+
+ if( i3 < i4 ) //not the last point in the array
+ {
+ int iElementShift = (iOldVertCount - i4);
+
+ //eliminate p3, we merged p2+p3 and already stored the result in p2
+ memcpy( p3, p3 + 1, sizeof( Vector ) * iElementShift );
+
+ float *l3 = &fLineLengthSqr[i3];
+ memcpy( l3, l3 + 1, sizeof( float ) * iElementShift );
+ }
+ }
+
+ for( i = 0; i != 4; ++i )
+ {
+ OutputFrustum[i] = Planes[i];
+ }
+ }
+ else
+ {
+ for( i = 0; i != iVertCount; ++i )
+ {
+ OutputFrustum[i] = g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Element(i);
+ }
+
+ for( ; i != 4; ++i )
+ {
+ //we had less than 4 planes for the sides, just copy from the last valid plane
+ OutputFrustum[i] = OutputFrustum[iVertCount-1];
+ }
+ }
+
+ //copy near/far planes
+ int iComplexCount = g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Count();
+ OutputFrustum[FRUSTUM_NEARZ] = g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Element(iComplexCount-2);
+ OutputFrustum[FRUSTUM_FARZ] = g_pPortalRender->m_RecursiveViewComplexFrustums[iNextViewRecursionLevel].Element(iComplexCount-1);
+
+ return true;
+}
+
+
+
+void CPortalRenderable_FlatBasic::RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView )
+{
+ VPROF( "CPortalRenderable_FlatBasic::RenderPortalViewToBackBuffer" );
+
+ if( m_fStaticAmount == 1.0f )
+ return; //not going to see anything anyways
+
+ if( m_pLinkedPortal == NULL ) //not linked to any portal, so we'll be all static anyways
+ return;
+
+ Frustum FrustumBackup;
+ memcpy( FrustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
+
+ Frustum seeThroughFrustum;
+ bool bUseSeeThroughFrustum;
+
+ bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
+
+ Vector vCameraForward;
+ AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
+
+ // Setup fog state for the camera.
+ Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
+ Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
+
+ Vector ptRemotePortalPosition = m_pLinkedPortal->m_ptOrigin;
+ Vector vRemotePortalForward = m_pLinkedPortal->m_vForward;
+
+ CViewSetup portalView = cameraView;
+
+ if( portalView.zNear < 1.0f )
+ portalView.zNear = 1.0f;
+
+ QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
+
+ portalView.width = cameraView.width;
+ portalView.height = cameraView.height;
+ portalView.x = cameraView.x;
+ portalView.y = cameraView.y;
+ portalView.origin = ptPOVOrigin;
+ portalView.angles = qPOVAngles;
+ portalView.fov = cameraView.fov;
+ portalView.m_bOrtho = false;
+ portalView.m_flAspectRatio = cameraView.m_flAspectRatio;
+
+ CopyToCurrentView( pViewRender, portalView );
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ {
+ float fCustomClipPlane[4];
+ fCustomClipPlane[0] = vRemotePortalForward.x;
+ fCustomClipPlane[1] = vRemotePortalForward.y;
+ fCustomClipPlane[2] = vRemotePortalForward.z;
+ fCustomClipPlane[3] = vRemotePortalForward.Dot( ptRemotePortalPosition - (vRemotePortalForward * 0.5f) ); //moving it back a smidge to eliminate visual artifacts for half-in objects
+
+ pRenderContext->PushCustomClipPlane( fCustomClipPlane ); //this is technically the same plane within recursive views, but pushing it anyway in-case something else has been added to the stack
+ }
+
+
+ {
+ ViewCustomVisibility_t customVisibility;
+ m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
+ render->Push3DView( portalView, 0, NULL, pViewRender->GetFrustum() );
+ {
+ if( bUseSeeThroughFrustum)
+ memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
+
+ render->OverrideViewFrustum( pViewRender->GetFrustum() );
+ SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() + 1 );
+
+ CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
+ CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
+ SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
+
+ //DRAW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ ViewDrawScene_PortalStencil( pViewRender, portalView, &customVisibility );
+
+ SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
+
+ if( m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration && (g_pPortalRender->GetRemainingPortalViewDepth() == 1) )
+ {
+ //save the view matrix for usage with the depth doubler.
+ //It's important that we do this AFTER using the depth doubler this frame to compensate for the fact that the front buffer is 1 frame behind the current view matrix
+ //otherwise we get a lag effect when the player changes their viewing angles
+ pRenderContext->GetMatrix( MATERIAL_VIEW, &m_InternallyMaintainedData.m_DepthDoublerTextureView );
+ }
+
+ SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() - 1 );
+ }
+ render->PopView( pViewRender->GetFrustum() );
+
+ //restore old frustum
+ memcpy( pViewRender->GetFrustum(), FrustumBackup, sizeof( Frustum ) );
+ render->OverrideViewFrustum( FrustumBackup );
+ }
+
+ pRenderContext->PopCustomClipPlane();
+
+ //restore old vis data
+ CopyToCurrentView( pViewRender, cameraView );
+}
+
+
+void CPortalRenderable_FlatBasic::RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView )
+{
+ if( m_fStaticAmount == 1.0f )
+ return; //not going to see anything anyways
+
+ if( m_pLinkedPortal == NULL ) //not linked to any portal, so we'll be all static anyways
+ return;
+
+ float fPixelVisibilty = g_pPortalRender->GetPixelVisilityForPortalSurface( this );
+ if( (fPixelVisibilty >= 0.0f) && (fPixelVisibilty <= PORTALRENDERABLE_FLATBASIC_MINPIXELVIS) )
+ return;
+
+ ITexture *pRenderTarget;
+ if( m_bIsPortal2 )
+ pRenderTarget = portalrendertargets->GetPortal2Texture();
+ else
+ pRenderTarget = portalrendertargets->GetPortal1Texture();
+
+ // Require that we have render textures for drawing
+ AssertMsg( pRenderTarget, "Portal render targets not initialized properly" );
+
+ // We're about to dereference this, so just bail if we can't
+ if ( !pRenderTarget )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ Vector vCameraForward;
+ AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
+
+ Frustum seeThroughFrustum;
+ bool bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
+
+ // Setup fog state for the camera.
+ Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
+ Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
+
+ Vector vCameraToPortal = m_ptOrigin - cameraView.origin;
+
+ CViewSetup portalView = cameraView;
+ Frustum frustumBackup;
+ memcpy( frustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
+
+ QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
+
+ portalView.width = pRenderTarget->GetActualWidth();
+ portalView.height = pRenderTarget->GetActualHeight();
+ portalView.x = 0;
+ portalView.y = 0;
+ portalView.origin = ptPOVOrigin;
+ portalView.angles = qPOVAngles;
+ portalView.fov = cameraView.fov;
+ portalView.m_bOrtho = false;
+ portalView.m_flAspectRatio = (float)cameraView.width / (float)cameraView.height; //use the screen aspect ratio, 0.0f doesn't work as advertised
+
+ //pRenderContext->Flush( false );
+
+ float fCustomClipPlane[4];
+ fCustomClipPlane[0] = m_pLinkedPortal->m_vForward.x;
+ fCustomClipPlane[1] = m_pLinkedPortal->m_vForward.y;
+ fCustomClipPlane[2] = m_pLinkedPortal->m_vForward.z;
+ fCustomClipPlane[3] = m_pLinkedPortal->m_vForward.Dot( m_pLinkedPortal->m_ptOrigin - (m_pLinkedPortal->m_vForward * 0.5f) ); //moving it back a smidge to eliminate visual artifacts for half-in objects
+
+ pRenderContext->PushCustomClipPlane( fCustomClipPlane );
+
+ {
+ render->Push3DView( portalView, VIEW_CLEAR_DEPTH, pRenderTarget, pViewRender->GetFrustum() );
+
+ {
+ ViewCustomVisibility_t customVisibility;
+ m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
+
+ SetRemainingViewDepth( 0 );
+ SetViewRecursionLevel( 1 );
+
+ CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
+ CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
+ SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
+
+ bool bDrew3dSkybox = false;
+ SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE;
+
+ // if the 3d skybox world is drawn, then don't draw the normal skybox
+ int nClearFlags = 0;
+ Draw3dSkyboxworld_Portal( pViewRender, portalView, nClearFlags, bDrew3dSkybox, nSkyboxVisible, pRenderTarget );
+
+ if( bUseSeeThroughFrustum )
+ {
+ memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
+ }
+
+ render->OverrideViewFrustum( pViewRender->GetFrustum() );
+
+ pRenderContext->EnableUserClipTransformOverride( false );
+
+ ViewDrawScene( pViewRender, bDrew3dSkybox, nSkyboxVisible, portalView, nClearFlags, (view_id_t)g_pPortalRender->GetCurrentViewId(), false, 0, &customVisibility );
+
+ SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
+
+ SetRemainingViewDepth( 1 );
+ SetViewRecursionLevel( 0 );
+
+ memcpy( pViewRender->GetFrustum(), frustumBackup, sizeof( Frustum ) );
+ render->OverrideViewFrustum( pViewRender->GetFrustum() );
+ }
+
+ render->PopView( pViewRender->GetFrustum() );
+ }
+
+ pRenderContext->PopCustomClipPlane();
+
+ //pRenderContext->Flush( false );
+
+ CopyToCurrentView( pViewRender, cameraView );
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// AddToVisAsExitPortal
+// input - pViewRender: pointer to the CViewRender class used to render this scene.
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void CPortalRenderable_FlatBasic::AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility )
+{
+ if ( !pCustomVisibility )
+ return;
+
+ // Add four corners of the portal to the renderer as visibility origins
+ for ( int i = 0; i < 4; ++i )
+ {
+ if( enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptCorners[i] ) != -1 )
+ pCustomVisibility->AddVisOrigin( m_InternallyMaintainedData.m_ptCorners[i] );
+ }
+
+ // Specify which leaf to use for area portal culling
+ pCustomVisibility->ForceVisOverride( m_InternallyMaintainedData.m_VisData );
+ pCustomVisibility->ForceViewLeaf( m_InternallyMaintainedData.m_iViewLeaf );
+}
+
+void CPortalRenderable_FlatBasic::DrawPreStencilMask( void )
+{
+ if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
+ {
+ DrawSimplePortalMesh( m_Materials.m_Portal_Refract[ ( ( m_bIsPortal2 ) ? ( 1 ) : ( 0 ) ) ] );
+ }
+}
+
+void CPortalRenderable_FlatBasic::DrawStencilMask( void )
+{
+ DrawSimplePortalMesh( m_Materials.m_Portal_Stencil_Hole );
+ DrawRenderFixMesh( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
+}
+
+void CPortalRenderable_FlatBasic::DrawPostStencilFixes( void )
+{
+ CMatRenderContextPtr pRenderContext( materials );
+
+ //fast clipping may have hosed depth, reset it
+ pRenderContext->ClearBuffersObeyStencil( false, true );
+
+ //replace the fog we overwrote
+ RenderFogQuad();
+
+ //replace depth
+ DrawSimplePortalMesh( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model, 0.0f );
+ DrawRenderFixMesh( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model, 0.0f );
+}
+
+bool CPortalRenderable_FlatBasic::ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup )
+{
+ return ( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration) &&
+ (m_pLinkedPortal != NULL) &&
+ (m_fStaticAmount < 1.0f) );
+}
+
+void CPortalRenderable_FlatBasic::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
+{
+ int nLinkedPortalId = pKeyValues->GetInt( "linkedPortalId" );
+ m_fOpenAmount = pKeyValues->GetFloat( "openAmount" );
+ m_fStaticAmount = pKeyValues->GetFloat( "staticAmount" );
+ m_fSecondaryStaticAmount = pKeyValues->GetFloat( "secondaryStaticAmount" );
+ m_bIsPortal2 = pKeyValues->GetInt( "isPortal2" ) != 0;
+ m_pLinkedPortal = nLinkedPortalId >= 0 ? (CPortalRenderable_FlatBasic *)FindRecordedPortal( nLinkedPortalId ) : NULL;
+ matrix3x4_t *pMat = (matrix3x4_t*)pKeyValues->GetPtr( "portalToWorld" );
+
+ MatrixGetColumn( *pMat, 3, m_ptOrigin );
+ MatrixGetColumn( *pMat, 0, m_vForward );
+ MatrixGetColumn( *pMat, 1, m_vRight );
+ MatrixGetColumn( *pMat, 2, m_vUp );
+ m_vRight *= -1.0f;
+
+ PortalMoved();
+}
+
+extern ConVar mat_wireframe;
+static void DrawComplexPortalMesh_SubQuad( Vector &ptBottomLeft, Vector &vUp, Vector &vRight, float *fSubQuadRect, void *pBindEnt, const IMaterial *pMaterial, const VMatrix *pReplacementViewMatrixForTexCoords = NULL )
+{
+ PortalMeshPoint_t Vertices[4];
+
+ Vertices[0].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[2]) + (vUp * fSubQuadRect[3]);
+ Vertices[0].texCoord.x = fSubQuadRect[2];
+ Vertices[0].texCoord.y = fSubQuadRect[3];
+
+ Vertices[1].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[2]) + (vUp * fSubQuadRect[1]);
+ Vertices[1].texCoord.x = fSubQuadRect[2];
+ Vertices[1].texCoord.y = fSubQuadRect[1];
+
+ Vertices[2].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[0]) + (vUp * fSubQuadRect[1]);
+ Vertices[2].texCoord.x = fSubQuadRect[0];
+ Vertices[2].texCoord.y = fSubQuadRect[1];
+
+ Vertices[3].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[0]) + (vUp * fSubQuadRect[3]);
+ Vertices[3].texCoord.x = fSubQuadRect[0];
+ Vertices[3].texCoord.y = fSubQuadRect[3];
+
+ Clip_And_Render_Convex_Polygon( Vertices, 4, pMaterial, pBindEnt );
+}
+
+#define PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS 8
+#define PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS 6
+
+void CPortalRenderable_FlatBasic::DrawComplexPortalMesh( const IMaterial *pMaterialOverride, float fForwardOffsetModifier ) //generates and draws the portal mesh (Needed for compatibility with fixed function rendering)
+{
+ PortalMeshPoint_t BaseVertices[4];
+
+ Vector ptBottomLeft = m_ptOrigin + (m_vForward * (fForwardOffsetModifier)) - (m_vRight * PORTAL_HALF_WIDTH) - (m_vUp * PORTAL_HALF_HEIGHT);
+ Vector vScaledUp = m_vUp * (2.0f * PORTAL_HALF_HEIGHT);
+ Vector vScaledRight = m_vRight * (2.0f * PORTAL_HALF_WIDTH);
+
+ CMatRenderContextPtr pRenderContext( materials );
+ VMatrix matView;
+ pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
+
+ const IMaterial *pMaterial;
+ if( pMaterialOverride )
+ {
+ pMaterial = pMaterialOverride;
+ }
+ else
+ {
+ pMaterial = m_Materials.m_PortalMaterials[(m_bIsPortal2)?1:0];
+ }
+
+
+ float fSubQuadRect[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
+
+ float fHeightBegin = 0.0f;
+ for( int i = 0; i != PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS; ++i )
+ {
+ float fHeightEnd = fHeightBegin + (1.0f / ((float)PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS));
+
+ fSubQuadRect[1] = fHeightBegin;
+ fSubQuadRect[3] = fHeightEnd;
+
+ float fWidthBegin = 0.0f;
+ for( int j = 0; j != PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS; ++j )
+ {
+ float fWidthEnd = fWidthBegin + (1.0f / ((float)PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS));
+
+ fSubQuadRect[0] = fWidthBegin;
+ fSubQuadRect[2] = fWidthEnd;
+
+ DrawComplexPortalMesh_SubQuad( ptBottomLeft, vScaledUp, vScaledRight, fSubQuadRect, GetClientRenderable(), pMaterial );
+
+ fWidthBegin = fWidthEnd;
+ }
+ fHeightBegin = fHeightEnd;
+ }
+
+ //pRenderContext->Flush( false );
+}
+
+void CPortalRenderable_FlatBasic::DrawDepthDoublerMesh( float fForwardOffsetModifier )
+{
+ IMaterialVar *pDepthDoubleViewMatrixVar = s_FlatBasicPortalDrawingMaterials.m_Materials.m_PortalDepthDoubler->FindVar( "$alternateviewmatrix", NULL, false );
+ if ( pDepthDoubleViewMatrixVar )
+ pDepthDoubleViewMatrixVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView );
+ DrawSimplePortalMesh( m_Materials.m_PortalDepthDoubler, fForwardOffsetModifier );
+}
+
+
+
+void CPortalRenderable_FlatBasic::DrawSimplePortalMesh( const IMaterial *pMaterialOverride, float fForwardOffsetModifier )
+{
+ const IMaterial *pMaterial;
+ if( pMaterialOverride )
+ pMaterial = pMaterialOverride;
+ else
+ pMaterial = m_Materials.m_PortalMaterials[m_bIsPortal2?1:0];
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( (IMaterial *)pMaterial, GetClientRenderable() );
+
+ // This can depend on the Bind command above, so keep this after!
+ UpdateFrontBufferTexturesForMaterial( (IMaterial *)pMaterial );
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL ); //just in case
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ Vector ptCenter = m_ptOrigin + (m_vForward * fForwardOffsetModifier);
+
+ Vector verts[4];
+ verts[0] = ptCenter + (m_vRight * PORTAL_HALF_WIDTH) - (m_vUp * PORTAL_HALF_HEIGHT);
+ verts[1] = ptCenter + (m_vRight * PORTAL_HALF_WIDTH) + (m_vUp * PORTAL_HALF_HEIGHT);
+ verts[2] = ptCenter - (m_vRight * PORTAL_HALF_WIDTH) - (m_vUp * PORTAL_HALF_HEIGHT);
+ verts[3] = ptCenter - (m_vRight * PORTAL_HALF_WIDTH) + (m_vUp * PORTAL_HALF_HEIGHT);
+
+ float vTangent[4] = { -m_vRight.x, -m_vRight.y, -m_vRight.z, 1.0f };
+
+ CMeshBuilder meshBuilder;
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3fv( &verts[0].x );
+ meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 1, 0.0f, 1.0f );
+ meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
+ meshBuilder.UserData( vTangent );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( &verts[1].x );
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
+ meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
+ meshBuilder.UserData( vTangent );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( &verts[2].x );
+ meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
+ meshBuilder.TexCoord2f( 1, 1.0f, 1.0f );
+ meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
+ meshBuilder.UserData( vTangent );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( &verts[3].x );
+ meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
+ meshBuilder.TexCoord2f( 1, 1.0f, 0.0f );
+ meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
+ meshBuilder.UserData( vTangent );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ if( mat_wireframe.GetBool() )
+ {
+ pRenderContext->Bind( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_Wireframe, (CPortalRenderable*)this );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3fv( &verts[0].x );
+ meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 1, 0.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( &verts[1].x );
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( &verts[2].x );
+ meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
+ meshBuilder.TexCoord2f( 1, 1.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( &verts[3].x );
+ meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
+ meshBuilder.TexCoord2f( 1, 1.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+}
+
+
+
+void CPortalRenderable_FlatBasic::DrawRenderFixMesh( const IMaterial *pMaterialOverride, float fFrontClipDistance )
+{
+ const IMaterial *pMaterial;
+ if( pMaterialOverride )
+ pMaterial = pMaterialOverride;
+ else
+ pMaterial = m_Materials.m_PortalRenderFixMaterials[(m_bIsPortal2)?1:0];
+
+ if( g_pPortalRender->GetViewRecursionLevel() != 0 )
+ return; //a render fix should only ever be necessary in the primary view
+
+ Vector ptCameraOrigin = CurrentViewOrigin();
+
+ Vector vPortalCenterToCamera = ptCameraOrigin - m_ptOrigin;
+ if( (vPortalCenterToCamera.Dot( m_vForward ) < -1.0f) ) //camera coplanar (to 1.0 units) or in front of portal plane
+ return;
+
+ if( vPortalCenterToCamera.LengthSqr() < (PORTAL_HALF_HEIGHT * PORTAL_HALF_HEIGHT) ) //FIXME: Player closeness check might need reimplementation
+ {
+ //if the player is this close to the portal, immediately get rid of any static it has as well as draw the fix
+ m_fStaticAmount = 0.0f;
+ //m_fSecondaryStaticAmount = 0.0f;
+
+ float fOldDist = m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist;
+
+
+ float fCameraDist = m_vForward.Dot(ptCameraOrigin - m_ptOrigin);
+
+ if( fFrontClipDistance > fCameraDist ) //never clip further out than the camera, we can see into the garbage space of the portal view's texture
+ fFrontClipDistance = fCameraDist;
+
+ m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist -= fFrontClipDistance;
+ Internal_DrawRenderFixMesh( pMaterial );
+ m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist = fOldDist;
+ }
+
+
+}
+
+void CPortalRenderable_FlatBasic::Internal_DrawRenderFixMesh( const IMaterial *pMaterial )
+{
+ PortalMeshPoint_t WorkVertices[4];
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL ); //just in case
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+
+ //view->GetViewSetup()->zNear;
+ Vector vForward, vUp, vRight, vOrigin;
+ vForward = CurrentViewForward();
+ vUp = CurrentViewUp();
+ vRight = CurrentViewRight();
+
+ vOrigin = CurrentViewOrigin() + vForward * (view->GetViewSetup()->zNear + 0.05f);
+
+ for( int i = 0; i != 4; ++i )
+ {
+ WorkVertices[i].texCoord.x = WorkVertices[i].texCoord.y = 0.0f;
+ }
+
+ WorkVertices[0].vWorldSpacePosition = vOrigin + (vRight * 40.0f) + (vUp * -40.0f);
+ WorkVertices[1].vWorldSpacePosition = vOrigin + (vRight * 40.0f) + (vUp * 40.0f);
+ WorkVertices[2].vWorldSpacePosition = vOrigin + (vRight * -40.0f) + (vUp * 40.0f);
+ WorkVertices[3].vWorldSpacePosition = vOrigin + (vRight * -40.0f) + (vUp * -40.0f);
+
+ ClipFixToBoundingAreaAndDraw( WorkVertices, pMaterial);
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+}
+
+void CPortalRenderable_FlatBasic::ClipFixToBoundingAreaAndDraw( PortalMeshPoint_t *pVerts, const IMaterial *pMaterial )
+{
+ PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( 4 * (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2) * 16 * sizeof( PortalMeshPoint_t ) ); //really only should need 4x points, but I'm paranoid
+ PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( 4 * (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2) * 16 * sizeof( PortalMeshPoint_t ) );
+ PortalMeshPoint_t *pTempVerts;
+
+ memcpy( pInVerts, pVerts, sizeof( PortalMeshPoint_t ) * 4 );
+ int iVertCount = 4;
+
+ //clip by bounding area planes
+ {
+ for( int i = 0; i != (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 1); ++i )
+ {
+ if( iVertCount < 3 )
+ return; //nothing to draw
+
+ iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, m_InternallyMaintainedData.m_BoundingPlanes[i].m_Normal, m_InternallyMaintainedData.m_BoundingPlanes[i].m_Dist, 0.01f );
+ pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
+ }
+
+ if( iVertCount < 3 )
+ return; //nothing to draw
+ }
+
+
+ //clip by the viewing frustum
+ {
+ VPlane *pFrustum = view->GetFrustum();
+
+ for( int i = 0; i != FRUSTUM_NUMPLANES; ++i )
+ {
+ if( iVertCount < 3 )
+ return; //nothing to draw
+
+ iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, pFrustum[i].m_Normal, pFrustum[i].m_Dist, 0.01f );
+ pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
+ }
+
+ if( iVertCount < 3 )
+ return; //nothing to draw
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ //project the points so we can fudge the numbers a bit and move them to exactly 0.0f depth
+ {
+ VMatrix matProj, matView, matViewProj;
+ pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
+ pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
+ MatrixMultiply( matProj, matView, matViewProj );
+
+ for( int i = 0; i != iVertCount; ++i )
+ {
+ float W, inverseW;
+
+ W = matViewProj.m[3][0] * pInVerts[i].vWorldSpacePosition.x;
+ W += matViewProj.m[3][1] * pInVerts[i].vWorldSpacePosition.y;
+ W += matViewProj.m[3][2] * pInVerts[i].vWorldSpacePosition.z;
+ W += matViewProj.m[3][3];
+
+ inverseW = 1.0f / W;
+
+
+ pInVerts[i].vWorldSpacePosition = matViewProj * pInVerts[i].vWorldSpacePosition;
+ pInVerts[i].vWorldSpacePosition *= inverseW;
+ pInVerts[i].vWorldSpacePosition.z = 0.00001f; //the primary reason we're projecting on the CPU to begin with
+ }
+ }
+
+ //render with identity transforms and clipping disabled
+ {
+ bool bClippingEnabled = pRenderContext->EnableClipping( false );
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ RenderPortalMeshConvexPolygon( pInVerts, iVertCount, pMaterial, this );
+ if( mat_wireframe.GetBool() )
+ RenderPortalMeshConvexPolygon( pInVerts, iVertCount, materials->FindMaterial( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS, false ), this );
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+
+ pRenderContext->EnableClipping( bClippingEnabled );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// renders a quad that simulates fog as an overlay for something else (most notably the hole we create for stencil mode portals)
+//-----------------------------------------------------------------------------
+void CPortalRenderable_FlatBasic::RenderFogQuad( void )
+{
+ CMatRenderContextPtr pRenderContext( materials );
+ if( pRenderContext->GetFogMode() == MATERIAL_FOG_NONE )
+ return;
+
+ float fFogStart, fFogEnd;
+ pRenderContext->GetFogDistances( &fFogStart, &fFogEnd, NULL );
+ float vertexColor[4];
+ unsigned char fogColor[3];
+ pRenderContext->GetFogColor( fogColor );
+
+ /*float fColorScale = LinearToGammaFullRange( pRenderContext->GetToneMappingScaleLinear().x );
+
+ fogColor[0] *= fColorScale;
+ fogColor[1] *= fColorScale;
+ fogColor[2] *= fColorScale;*/
+
+ vertexColor[0] = fogColor[0] * (1.0f / 255.0f);
+ vertexColor[1] = fogColor[1] * (1.0f / 255.0f);
+ vertexColor[2] = fogColor[2] * (1.0f / 255.0f);
+
+ float ooFogRange = 1.0f;
+ if ( fFogEnd != fFogStart )
+ {
+ ooFogRange = 1.0f / (fFogEnd - fFogStart);
+ }
+
+ float FogEndOverFogRange = ooFogRange * fFogEnd;
+
+ VMatrix matView, matViewProj, matProj;
+ pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
+ pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
+ MatrixMultiply( matProj, matView, matViewProj );
+
+ Vector vUp = m_vUp * (PORTAL_HALF_HEIGHT * 2.0f);
+ Vector vRight = m_vRight * (PORTAL_HALF_WIDTH * 2.0f);
+
+ Vector ptCorners[4];
+ ptCorners[0] = (m_ptOrigin + vUp) + vRight;
+ ptCorners[1] = (m_ptOrigin + vUp) - vRight;
+ ptCorners[2] = (m_ptOrigin - vUp) + vRight;
+ ptCorners[3] = (m_ptOrigin - vUp) - vRight;
+
+
+ pRenderContext->Bind( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_TranslucentVertexColor );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ for( int i = 0; i != 4; ++i )
+ {
+ float projZ;
+ projZ = matViewProj.m[2][0] * ptCorners[i].x;
+ projZ += matViewProj.m[2][1] * ptCorners[i].y;
+ projZ += matViewProj.m[2][2] * ptCorners[i].z;
+ projZ += matViewProj.m[2][3];
+
+ float fFogAmount = ((-projZ * ooFogRange) + FogEndOverFogRange); //projZ should be negative
+
+ //range fix
+ if( fFogAmount >= 1.0f )
+ fFogAmount = 1.0f;
+
+ if( fFogAmount <= 0.0f )
+ fFogAmount = 0.0f;
+
+ vertexColor[3] = 1.0f - fFogAmount; //alpha is inverse fog
+
+ meshBuilder.Position3fv( &ptCorners[i].x );
+ meshBuilder.Color4fv( vertexColor );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+void CPortalRenderable_FlatBasic::DrawPortal( void )
+{
+ if( (view->GetDrawFlags() & DF_RENDER_REFLECTION) != 0 )
+ return;
+
+ if ( g_pPortalRender->ShouldUseStencilsToRenderPortals() )
+ {
+ //stencil-based rendering
+ if( g_pPortalRender->IsRenderingPortal() == false ) //main view
+ {
+ if( m_pLinkedPortal == NULL ) //didn't pass through pre-stencil mask
+ {
+ if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
+ {
+ DrawSimplePortalMesh( m_Materials.m_Portal_Refract[ ( ( m_bIsPortal2 ) ? ( 1 ) : ( 0 ) ) ] );
+ }
+ }
+
+ DrawSimplePortalMesh( m_Materials.m_PortalStaticOverlay[((m_bIsPortal2)?(1):(0))] );
+ DrawRenderFixMesh( g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
+ }
+ else if( g_pPortalRender->GetCurrentViewExitPortal() != this )
+ {
+ if( m_pLinkedPortal == NULL ) //didn't pass through pre-stencil mask
+ {
+ if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
+ {
+ DrawSimplePortalMesh( m_Materials.m_Portal_Refract[ ( ( m_bIsPortal2 ) ? ( 1 ) : ( 0 ) ) ] );
+ }
+ }
+
+ if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
+ && (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
+ && (g_pPortalRender->GetViewRecursionLevel() > 1) )
+ {
+ DrawDepthDoublerMesh();
+ }
+ else
+ {
+ DrawSimplePortalMesh( m_Materials.m_PortalStaticOverlay[((m_bIsPortal2)?(1):(0))] );
+ }
+ }
+ }
+ else
+ {
+ BeginPortalPixelVisibilityQuery();
+
+ //texture-based rendering
+ if( g_pPortalRender->IsRenderingPortal() == false )
+ {
+ if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
+ {
+ DrawSimplePortalMesh( m_Materials.m_Portal_Refract[ ( ( m_bIsPortal2 ) ? ( 1 ) : ( 0 ) ) ] );
+ }
+
+ DrawComplexPortalMesh();
+ DrawRenderFixMesh();
+ }
+ else if( g_pPortalRender->GetCurrentViewExitPortal() != this ) //don't render portals that our view is exiting from
+ {
+ if ( ( m_fOpenAmount > 0.0f ) && ( m_fOpenAmount < 1.0f ) )
+ {
+ DrawSimplePortalMesh( m_Materials.m_Portal_Refract[ ( ( m_bIsPortal2 ) ? ( 1 ) : ( 0 ) ) ] );
+ }
+ DrawSimplePortalMesh( m_Materials.m_PortalStaticOverlay[((m_bIsPortal2)?(1):(0))] ); //FIXME: find out why the projection mesh screws up at the second level of rendering in -nouserclip situations
+ }
+
+ EndPortalPixelVisibilityQuery();
+ }
+}
+
+bool CPortalRenderable_FlatBasic::DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
+{
+ bool bAboveWater = false, bBelowWater = false;
+ for( int i = 0; i != 4; ++i )
+ {
+ bAboveWater |= (m_InternallyMaintainedData.m_ptCorners[i].z >= waterZ);
+ bBelowWater |= (m_InternallyMaintainedData.m_ptCorners[i].z <= waterZ);
+ }
+
+ return (bAboveWater && bBelowWater);
+}
+
+bool CPortalRenderable_FlatBasic::ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, CUtlVector<VPlane> &currentComplexFrustum )
+{
+ if( m_pLinkedPortal == NULL )
+ return false;
+
+ if( m_fStaticAmount == 1.0f )
+ return false;
+
+ Vector vCameraPos = currentView.origin;
+
+ if( (g_pPortalRender->GetViewRecursionLevel() == 0) &&
+ ((m_ptOrigin - vCameraPos).LengthSqr() < (PORTAL_HALF_HEIGHT * PORTAL_HALF_HEIGHT)) ) //FIXME: Player closeness check might need reimplementation
+ {
+ return true; //fudgery time. The player might not be able to see the surface, but they can probably see the render fix
+ }
+
+ if( m_vForward.Dot( vCameraPos ) <= m_InternallyMaintainedData.m_fPlaneDist )
+ return false; //looking at portal backface
+
+ VPlane *currentFrustum = currentComplexFrustum.Base();
+ int iCurrentFrustmPlanes = currentComplexFrustum.Count();
+
+ //now slice up the portal quad and see if any is visible within the frustum
+ int allocSize = (6 + currentComplexFrustum.Count()); //possible to add 1 point per cut, 4 starting points, N plane cuts, 2 extra because I'm paranoid
+ Vector *pInVerts = (Vector *)stackalloc( sizeof( Vector ) * allocSize * 2 );
+ Vector *pOutVerts = pInVerts + allocSize;
+ Vector *pTempVerts;
+
+ //clip by first plane and put output into pInVerts
+ int iVertCount = ClipPolyToPlane( m_InternallyMaintainedData.m_ptCorners, 4, pInVerts, currentFrustum[0].m_Normal, currentFrustum[0].m_Dist, 0.01f );
+
+ //clip by other planes and flipflop in and out pointers
+ for( int i = 1; i != iCurrentFrustmPlanes; ++i )
+ {
+ if( iVertCount < 3 )
+ return false; //nothing left in the frustum
+
+ iVertCount = ClipPolyToPlane( pInVerts, iVertCount, pOutVerts, currentFrustum[i].m_Normal, currentFrustum[i].m_Dist, 0.01f );
+ pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
+ }
+
+ if( iVertCount < 3 )
+ return false; //nothing left in the frustum
+
+ return true;
+}
+
+bool CPortalRenderable_FlatBasic::ShouldUpdatePortalView_BasedOnPixelVisibility( float fScreenFilledByStencilMaskLastFrame_Normalized )
+{
+ return (fScreenFilledByStencilMaskLastFrame_Normalized < 0.0f) || // < 0 is an error value
+ (fScreenFilledByStencilMaskLastFrame_Normalized > PORTALRENDERABLE_FLATBASIC_MINPIXELVIS );
+}
+
+CPortalRenderable *CreatePortal_FlatBasic_Fn( void )
+{
+ return new CPortalRenderable_FlatBasic;
+}
+
+static CPortalRenderableCreator CreatePortal_FlatBasic( "flatBasic", CreatePortal_FlatBasic_Fn );
diff --git a/game/client/portal/portalrenderable_flatbasic.h b/game/client/portal/portalrenderable_flatbasic.h
new file mode 100644
index 0000000..69c6b86
--- /dev/null
+++ b/game/client/portal/portalrenderable_flatbasic.h
@@ -0,0 +1,121 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#ifndef PORTALRENDERABLE_FLATBASIC_H
+#define PORTALRENDERABLE_FLATBASIC_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "PortalRender.h"
+
+struct PortalMeshPoint_t;
+#define PORTALRENDERFIXMESH_OUTERBOUNDPLANES 12
+
+
+struct FlatBasicPortalRenderingMaterials_t
+{
+ CMaterialReference m_PortalMaterials[2];
+ CMaterialReference m_PortalRenderFixMaterials[2];
+ CMaterialReference m_PortalDepthDoubler;
+ CMaterialReference m_PortalStaticOverlay[2];
+ CMaterialReference m_Portal_Stencil_Hole;
+ CMaterialReference m_Portal_Refract[2];
+ //CTextureReference m_PortalLightTransfer_ShadowTexture; //light transfers disabled indefinitely
+
+ IMaterialVar *m_pDepthDoubleViewMatrixVar;
+};
+
+//As seen in "Portal"
+class CPortalRenderable_FlatBasic : public C_BaseAnimating, public CPortalRenderable
+{
+ DECLARE_CLASS( CPortalRenderable_FlatBasic, C_BaseAnimating );
+
+public:
+ CPortalRenderable_FlatBasic( void );
+
+ //generates a 8x6 tiled set of quads, each clipped to the view frustum. Helps vs11/ps11 portal shaders interpolate correctly. Not necessary for vs20/ps20 portal shaders or stencil mode.
+ virtual void DrawComplexPortalMesh( const IMaterial *pMaterialOverride = NULL, float fForwardOffsetModifier = 0.25f );
+
+ //generates a single quad
+ virtual void DrawSimplePortalMesh( const IMaterial *pMaterialOverride = NULL, float fForwardOffsetModifier = 0.25f );
+
+ //draws a screenspace mesh to replace missing pixels caused by the camera near plane intersecting the portal mesh
+ virtual void DrawRenderFixMesh( const IMaterial *pMaterialOverride = NULL, float fFrontClipDistance = 0.3f );
+
+ //When we're in a configuration that sees through recursive portal views to a depth of 2, we should be able to cheaply approximate even further depth using pixels from previous frames
+ virtual void DrawDepthDoublerMesh( float fForwardOffsetModifier = 0.25f );
+
+ virtual void DrawPortal( void );
+
+ virtual void DrawPreStencilMask( void ); //Draw to wherever you can see through the portal. The mask will later be filled with the portal view.
+ virtual void DrawStencilMask( void ); //Draw to wherever you can see through the portal. The mask will later be filled with the portal view.
+ virtual void DrawPostStencilFixes( void ); //After done drawing to the portal mask, we need to fix the depth buffer as well as fog. So draw your mesh again, writing to z and with the fog color alpha'd in by distance
+
+ virtual void RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView );
+ virtual void RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView );
+
+ void AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility );
+
+ virtual bool ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, CUtlVector<VPlane> &currentComplexFrustum ); //portal is both visible, and will display at least some portion of a remote view
+ virtual bool ShouldUpdatePortalView_BasedOnPixelVisibility( float fScreenFilledByStencilMaskLastFrame_Normalized );
+ virtual bool ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup );
+
+ virtual void GetToolRecordingState( bool bActive, KeyValues *msg );
+ virtual void HandlePortalPlaybackMessage( KeyValues *pKeyValues );
+
+ virtual const Vector &GetFogOrigin( void ) const { return m_ptOrigin; };
+ virtual SkyboxVisibility_t SkyBoxVisibleFromPortal( void ) { return m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners; };
+ virtual bool DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const;
+
+ bool WillUseDepthDoublerThisDraw( void ) const; //returns true if the DrawPortal() would draw a depth doubler mesh if you were to call it right now
+
+ virtual CPortalRenderable *GetLinkedPortal() const { return m_pLinkedPortal; };
+ bool CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum );
+
+protected:
+ void ClipFixToBoundingAreaAndDraw( PortalMeshPoint_t *pVerts, const IMaterial *pMaterial );
+ void Internal_DrawRenderFixMesh( const IMaterial *pMaterial );
+
+ // renders a quad that simulates fog as an overlay for something else (most notably the hole we create for stencil mode portals)
+ void RenderFogQuad( void );
+
+ void PortalMoved( void ); // call this if you've moved the portal around so we can update internally maintained data
+
+
+ static const FlatBasicPortalRenderingMaterials_t& m_Materials;
+ struct FlatBasicPortal_InternalData_t
+ {
+ VPlane m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2]; // +2 for front and back
+
+ VisOverrideData_t m_VisData; // a data to use for visibility calculations (to override area portal culling)
+ int m_iViewLeaf; // leaf to start in for area portal flowing through calculations
+
+ VMatrix m_DepthDoublerTextureView; //cached version of view matrix at depth 1 for use when drawing the depth doubler mesh
+ bool m_bUsableDepthDoublerConfiguration; //every time a portal moves we re-evaluate whether the depth doubler will reasonably approximate more views
+ SkyboxVisibility_t m_nSkyboxVisibleFromCorners;
+
+ Vector m_ptForwardOrigin;
+ Vector m_ptCorners[4];
+
+ float m_fPlaneDist; //combines with m_vForward to make a plane
+ };
+
+ FlatBasicPortal_InternalData_t m_InternallyMaintainedData;
+
+public:
+ CPortalRenderable_FlatBasic *m_pLinkedPortal;
+ Vector m_ptOrigin;
+ Vector m_vForward, m_vUp, m_vRight;
+ float m_fStaticAmount;
+ float m_fSecondaryStaticAmount; // used to help kludge the end of our recursive rendering chain
+ float m_fOpenAmount;
+ bool m_bIsPortal2; //for any set of portals, one must be portal 1, and the other portal 2. Uses different render targets
+};
+
+#endif //#ifndef PORTALRENDERABLE_FLATBASIC_H \ No newline at end of file
diff --git a/game/client/portal/vgui_neurotoxin_countdown_screen.cpp b/game/client/portal/vgui_neurotoxin_countdown_screen.cpp
new file mode 100644
index 0000000..75a26bc
--- /dev/null
+++ b/game/client/portal/vgui_neurotoxin_countdown_screen.cpp
@@ -0,0 +1,186 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "c_vguiscreen.h"
+#include "vgui_controls/Label.h"
+#include "vgui_bitmappanel.h"
+#include <vgui/IVGui.h>
+#include "c_neurotoxin_countdown.h"
+#include "ienginevgui.h"
+#include "fmtstr.h"
+#include "vgui_controls/ImagePanel.h"
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Control screen
+//-----------------------------------------------------------------------------
+class CNeurotoxinCountdownScreen : public CVGuiScreenPanel
+{
+ DECLARE_CLASS( CNeurotoxinCountdownScreen, CVGuiScreenPanel );
+
+public:
+ CNeurotoxinCountdownScreen( vgui::Panel *parent, const char *panelName );
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+
+ virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData );
+ virtual void OnTick();
+
+private:
+ void Update( C_NeurotoxinCountdown *pNeurotoxinCountdown );
+
+private:
+ vgui::Label *m_pDisplayTextLabel;
+
+ int iLastSlideIndex;
+
+ Color m_cDefault;
+ Color m_cInvisible;
+
+ bool bIsAlreadyVisible;
+
+ //ImagePanel *(m_pNumberImages[ 6 ][ 10 ]);
+};
+
+
+DECLARE_VGUI_SCREEN_FACTORY( CNeurotoxinCountdownScreen, "neurotoxin_countdown_screen" );
+
+//-----------------------------------------------------------------------------
+// Constructor:
+//-----------------------------------------------------------------------------
+CNeurotoxinCountdownScreen::CNeurotoxinCountdownScreen( vgui::Panel *parent, const char *panelName )
+ : BaseClass( parent, "CNeurotoxinCountdownScreen", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/NeurotoxinCountdownScreen.res", "NeuroToxinScreen" ) )
+{
+ m_pDisplayTextLabel = new vgui::Label( this, "NumberDisplay", "x" );
+ iLastSlideIndex = 0;
+}
+
+void CNeurotoxinCountdownScreen::ApplySchemeSettings( IScheme *pScheme )
+{
+ assert( pScheme );
+
+ m_cDefault = pScheme->GetColor( "CNeurotoxinCountdownScreen_Default", GetFgColor() );
+ m_cInvisible = Color( 0, 0, 0, 0 );
+
+ m_pDisplayTextLabel->SetFgColor( m_cDefault );
+}
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+bool CNeurotoxinCountdownScreen::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
+{
+ // Make sure we get ticked...
+ vgui::ivgui()->AddTickSignal( GetVPanel() );
+
+ if (!BaseClass::Init(pKeyValues, pInitData))
+ return false;
+
+ //for ( int iDigit = 0; iDigit < 6; ++iDigit )
+ //{
+ // for ( int iNumber = 0; iNumber < 6; ++iNumber )
+ // {
+ // m_pNumberImages[ iDigit ][ iNumber ] = SETUP_PANEL( new ImagePanel( this, "SlideshowImage" ) );
+ // m_pNumberImages[ iDigit ][ iNumber ]->SetImage( "" );
+ // m_pNumberImages[ iDigit ][ iNumber ]->SetZPos( -2 );
+ // m_pNumberImages[ iDigit ][ iNumber ]->SetVisible( false );
+ // }
+ //}
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Update the display string
+//-----------------------------------------------------------------------------
+void CNeurotoxinCountdownScreen::OnTick()
+{
+ BaseClass::OnTick();
+
+ if ( g_NeurotoxinCountdowns.Count() <= 0 )
+ return;
+
+ C_NeurotoxinCountdown *pNeurotoxinCountdown = NULL;
+
+ for ( int iDisplayScreens = 0; iDisplayScreens < g_NeurotoxinCountdowns.Count(); ++iDisplayScreens )
+ {
+ C_NeurotoxinCountdown *pNeurotoxinCountdownTemp = g_NeurotoxinCountdowns[ iDisplayScreens ];
+ if ( pNeurotoxinCountdownTemp && pNeurotoxinCountdownTemp->IsEnabled() )
+ {
+ pNeurotoxinCountdown = pNeurotoxinCountdownTemp;
+ break;
+ }
+ }
+
+ if( !pNeurotoxinCountdown )
+ {
+ // All display screens are disabled
+ if ( bIsAlreadyVisible )
+ {
+ SetVisible( false );
+ bIsAlreadyVisible = false;
+ }
+ return;
+ }
+
+ if ( !bIsAlreadyVisible )
+ {
+ SetVisible( true );
+ bIsAlreadyVisible = true;
+ }
+
+ Update( pNeurotoxinCountdown );
+}
+
+void CNeurotoxinCountdownScreen::Update( C_NeurotoxinCountdown *pNeurotoxinCountdown )
+{
+ char szBuff[ 256 ];
+
+ char szMinutesBuff[ 4 ];
+ char szSecondsBuff[ 4 ];
+ char szMillisecondsBuff[ 4 ];
+
+ int iMinutes = pNeurotoxinCountdown->GetMinutes();
+ int iSeconds = pNeurotoxinCountdown->GetSeconds();
+ int iMilliseconds;
+
+ if ( iMinutes <= 0 && iSeconds <= 0 )
+ {
+ iMinutes = 0;
+ iSeconds = 0;
+ iMilliseconds = 0;
+
+ // Blink!
+ m_pDisplayTextLabel->SetVisible( static_cast<int>( gpGlobals->curtime * 10.0f ) % 2 == 0 );
+ }
+ else
+ {
+ iMilliseconds = pNeurotoxinCountdown->GetMilliseconds();
+ m_pDisplayTextLabel->SetVisible( true );
+ }
+
+ if ( iMinutes < 10 )
+ sprintf( szMinutesBuff, "0%i", iMinutes );
+ else
+ sprintf( szMinutesBuff, "%i", iMinutes );
+
+ if ( iSeconds < 10 )
+ sprintf( szSecondsBuff, "0%i", iSeconds );
+ else
+ sprintf( szSecondsBuff, "%i", iSeconds );
+
+ if ( iMilliseconds < 10 )
+ sprintf( szMillisecondsBuff, "0%i", iMilliseconds );
+ else
+ sprintf( szMillisecondsBuff, "%i", iMilliseconds );
+
+ sprintf( szBuff, "%s:%s:%s", szMinutesBuff, szSecondsBuff, szMillisecondsBuff );
+ m_pDisplayTextLabel->SetText( szBuff );
+}
diff --git a/game/client/portal/vgui_portal_stats_display_screen.cpp b/game/client/portal/vgui_portal_stats_display_screen.cpp
new file mode 100644
index 0000000..9b62be9
--- /dev/null
+++ b/game/client/portal/vgui_portal_stats_display_screen.cpp
@@ -0,0 +1,305 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "c_vguiscreen.h"
+#include "vgui_controls/Label.h"
+#include "vgui_bitmappanel.h"
+#include <vgui/IVGui.h>
+#include "c_prop_portal_stats_display.h"
+#include "ienginevgui.h"
+#include "fmtstr.h"
+#include "portal_shareddefs.h"
+
+using namespace vgui;
+
+
+#define TESTCHAMBER_STATS_FIRST_MEDAL "MedalPortalGold"
+#define TESTCHAMBER_STATS_FIRST_MEDAL_POINTER "MedalPointer00"
+#define TESTCHAMBER_STATS_FIRST_SUBJECT_REACTION "SubjectReactionIdle"
+
+
+//-----------------------------------------------------------------------------
+// Control screen
+//-----------------------------------------------------------------------------
+class CPortalStatsDisplayScreen : public CVGuiScreenPanel
+{
+ DECLARE_CLASS( CPortalStatsDisplayScreen, CVGuiScreenPanel );
+
+public:
+ CPortalStatsDisplayScreen( vgui::Panel *parent, const char *panelName );
+ ~CPortalStatsDisplayScreen();
+
+ virtual void ApplySchemeSettings( IScheme *pScheme );
+
+ virtual bool Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData );
+ virtual void OnTick();
+
+private:
+ void UpdateTextFields( C_PropPortalStatsDisplay *pPropPortalStatsDisplay );
+ void UpdateMedals( C_PropPortalStatsDisplay *pPropPortalStatsDisplay );
+
+private:
+ vgui::Label *m_pNumPlayerLabel;
+ vgui::Label *m_pNumGoalLabel;
+ vgui::Label *m_pCheatedLabel;
+
+ float m_flNextDigitRandomizeTime; //next time to grab a new digit while scrolling random digits
+ //in un-decoded digits
+ int m_iLastRandomInt; //store the random digit between new rand calls
+
+ bool m_bInitLabelColor;
+
+ Color m_cPass;
+ Color m_cFail;
+ Color m_cUnknown;
+ Color m_cInvisible;
+};
+
+
+DECLARE_VGUI_SCREEN_FACTORY( CPortalStatsDisplayScreen, "portal_stats_display_screen" );
+
+//-----------------------------------------------------------------------------
+// Constructor:
+//-----------------------------------------------------------------------------
+CPortalStatsDisplayScreen::CPortalStatsDisplayScreen( vgui::Panel *parent, const char *panelName )
+ : BaseClass( parent, "CPortalStatsDisplayScreen", vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/PortalStatsDisplayScreen.res", "PortalStatsDisplayScreens" ) )
+{
+ m_pNumPlayerLabel = new vgui::Label( this, "NumPlayer", "" );
+ m_pNumGoalLabel = new vgui::Label( this, "NumGoal", "" );
+ m_pCheatedLabel = new vgui::Label( this, "CheatedLabel", "" );
+
+ m_flNextDigitRandomizeTime = 0;
+ m_iLastRandomInt = 0;
+
+ m_bInitLabelColor = true;
+}
+
+CPortalStatsDisplayScreen::~CPortalStatsDisplayScreen()
+{
+}
+
+void CPortalStatsDisplayScreen::ApplySchemeSettings( IScheme *pScheme )
+{
+ assert( pScheme );
+
+ m_cPass = pScheme->GetColor( "CPortalStatsDisplayScreen_Pass", GetFgColor() );
+ m_cFail = pScheme->GetColor( "CPortalStatsDisplayScreen_Fail", GetFgColor() );
+ m_cUnknown = pScheme->GetColor( "CPortalStatsDisplayScreen_Unknown", GetFgColor() );
+ m_cInvisible = Color( 0, 0, 0, 0 );
+
+ if( m_bInitLabelColor )
+ {
+ m_pNumPlayerLabel->SetFgColor( m_cUnknown );
+ m_pNumGoalLabel->SetFgColor( m_cUnknown );
+
+ m_bInitLabelColor = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+bool CPortalStatsDisplayScreen::Init( KeyValues* pKeyValues, VGuiScreenInitData_t* pInitData )
+{
+ // Make sure we get ticked...
+ vgui::ivgui()->AddTickSignal( GetVPanel() );
+
+ if (!BaseClass::Init(pKeyValues, pInitData))
+ return false;
+
+ return true;
+}
+
+void MakeTimeString( CFmtStr &str, int iHours, int iMinutes, int iSeconds )
+{
+ if ( iHours )
+ {
+ if ( iMinutes < 10 )
+ {
+ if ( iSeconds < 10 )
+ str.sprintf( "%d:0%d:0%d", iHours, iMinutes, iSeconds );
+ else
+ str.sprintf( "%d:0%d:%d", iHours, iMinutes, iSeconds );
+ }
+ else
+ {
+ if ( iSeconds < 10 )
+ str.sprintf( "%d:%d:0%d", iHours, iMinutes, iSeconds );
+ else
+ str.sprintf( "%d:%d:%d", iHours, iMinutes, iSeconds );
+ }
+ }
+ else
+ {
+ if ( iSeconds < 10 )
+ str.sprintf( "%d:0%d", iMinutes, iSeconds );
+ else
+ str.sprintf( "%d:%d", iMinutes, iSeconds );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Update the display string
+//-----------------------------------------------------------------------------
+void CPortalStatsDisplayScreen::OnTick()
+{
+ BaseClass::OnTick();
+
+ if ( g_PropPortalStatsDisplays.Count() <= 0 )
+ return;
+
+ C_PropPortalStatsDisplay *pPropPortalStatsDisplay = NULL;
+
+ for ( int iDisplayScreens = 0; iDisplayScreens < g_PropPortalStatsDisplays.Count(); ++iDisplayScreens )
+ {
+ C_PropPortalStatsDisplay *pPropPortalStatsDisplayTemp = g_PropPortalStatsDisplays[ iDisplayScreens ];
+ if ( pPropPortalStatsDisplayTemp && pPropPortalStatsDisplayTemp->IsEnabled() )
+ {
+ pPropPortalStatsDisplay = pPropPortalStatsDisplayTemp;
+ break;
+ }
+ }
+
+ if( !pPropPortalStatsDisplay )
+ {
+ // All display screens are disabled
+ SetVisible( false );
+ return;
+ }
+
+ SetVisible( true );
+
+ UpdateTextFields( pPropPortalStatsDisplay );
+ UpdateMedals( pPropPortalStatsDisplay );
+}
+
+void CPortalStatsDisplayScreen::UpdateTextFields( C_PropPortalStatsDisplay *pPropPortalStatsDisplay )
+{
+ bool bIsTime = pPropPortalStatsDisplay->IsTime();
+ float fNumPlayer = pPropPortalStatsDisplay->GetNumPlayerDisplay();
+ float fNumGoal = pPropPortalStatsDisplay->GetNumGoalDisplay();
+
+ // Figure out colors
+
+ Color *( pColors[ 3 ] ) = { &m_cUnknown, &m_cFail, &m_cPass };
+
+ int iColor = pPropPortalStatsDisplay->GetGoalSuccess() + 1;
+
+ m_pNumPlayerLabel->SetFgColor( ( fNumPlayer != 0.0f ) ? ( *pColors[ iColor ] ) : ( m_cInvisible ) );
+ m_pNumGoalLabel->SetFgColor( ( pPropPortalStatsDisplay->GetGoalVisible() ) ? ( *pColors[ iColor ] ) : ( m_cInvisible ) );
+
+ // Fill in labels
+
+ CFmtStr str;
+
+ if ( !bIsTime )
+ {
+ str.sprintf( "%.0f", fNumPlayer );
+ m_pNumPlayerLabel->SetText( str );
+
+ str.sprintf( "%.0f", fNumGoal );
+ m_pNumGoalLabel->SetText( str );
+ }
+ else
+ {
+ // break seconds into mnutes and seconds
+ int iMinutes = static_cast<int>( fNumPlayer / 60.0f );
+ int iHours = iMinutes / 60;
+ iMinutes %= 60;
+ int iSeconds = static_cast<int>( fNumPlayer ) % 60;
+ MakeTimeString( str, iHours, iMinutes, iSeconds );
+ m_pNumPlayerLabel->SetText( str );
+
+ // break seconds into mnutes and seconds
+ iMinutes = static_cast<int>( fNumGoal / 60.0f );
+ iHours = iMinutes / 60;
+ iMinutes %= 60;
+ iSeconds = static_cast<int>( fNumGoal ) % 60;
+ MakeTimeString( str, iHours, iMinutes, iSeconds );
+ m_pNumGoalLabel->SetText( str );
+ }
+
+ // Draw cheated label when needed
+ m_pCheatedLabel->SetVisible( pPropPortalStatsDisplay->HasCheated() );
+}
+
+int GetMedalOffset( int iObjective, int iLevel )
+{
+ return iObjective * 6 + ( 2 - iLevel ) * 2;
+}
+
+void CPortalStatsDisplayScreen::UpdateMedals( C_PropPortalStatsDisplay *pPropPortalStatsDisplay )
+{
+ // Get the index of the first medal image
+ int iFirstChildIndex = FindChildIndexByName( TESTCHAMBER_STATS_FIRST_MEDAL );
+
+ CBitmapPanel *pBitmapPanel;
+
+ // Loop through all the goals
+ for ( int iGoal = 0; iGoal < PORTAL_LEVEL_STAT_TOTAL; ++iGoal )
+ {
+ int iMedalComplete = -1;
+
+ // Loop through bronze, silver, and gold
+ for ( int iLevel = 0; iLevel < 3; ++iLevel )
+ {
+ if ( pPropPortalStatsDisplay->IsMedalCompleted( iLevel ) )
+ iMedalComplete = iLevel;
+ }
+
+ // Loop through bronze, silver, and gold
+ for ( int iLevel = 0; iLevel < 3; ++iLevel )
+ {
+ int iMedalIndex = iFirstChildIndex + GetMedalOffset( iGoal, iLevel );
+
+ if ( iGoal != pPropPortalStatsDisplay->GetDisplayObjective() )
+ {
+ // Set both invisible
+ pBitmapPanel = dynamic_cast<CBitmapPanel*>( GetChild( iMedalIndex ) );
+ pBitmapPanel->SetVisible( false );
+
+ // Set if "no medal" is visible
+ pBitmapPanel = dynamic_cast<CBitmapPanel*>( GetChild( iMedalIndex + 1 ) );
+ pBitmapPanel->SetVisible( false );
+
+ }
+ else
+ {
+ // Set if medal is visible
+ pBitmapPanel = dynamic_cast<CBitmapPanel*>( GetChild( iMedalIndex ) );
+ pBitmapPanel->SetVisible( ( iLevel == iMedalComplete ) ? ( true ) : ( false ) );
+
+ // Set if "no medal" is visible
+ pBitmapPanel = dynamic_cast<CBitmapPanel*>( GetChild( iMedalIndex + 1 ) );
+ pBitmapPanel->SetVisible( ( iMedalComplete == -1 && iLevel == 0 ) ? ( true ) : ( false ) );
+ }
+ }
+ }
+
+ // Get the index of the first pointer
+ iFirstChildIndex = FindChildIndexByName( TESTCHAMBER_STATS_FIRST_MEDAL_POINTER );
+
+ // Loop through bronze, silver, and gold
+ for ( int iLevel = 0; iLevel < 3; ++iLevel )
+ {
+ pBitmapPanel = dynamic_cast<CBitmapPanel*>( GetChild( iFirstChildIndex + iLevel ) );
+ pBitmapPanel->SetVisible( ( iLevel == pPropPortalStatsDisplay->GetGoalLevelDisplay() ) ? ( true ) : ( false ) );
+ }
+
+ int iSubjectReaction = pPropPortalStatsDisplay->GetGoalSuccess() + 1;
+
+ // Get the index of the first subject reaction
+ iFirstChildIndex = FindChildIndexByName( TESTCHAMBER_STATS_FIRST_SUBJECT_REACTION );
+
+ // Loop through idle, happy, sad
+ for ( int iReaction = 0; iReaction < 3; ++iReaction )
+ {
+ pBitmapPanel = dynamic_cast<CBitmapPanel*>( GetChild( iFirstChildIndex + iReaction ) );
+ pBitmapPanel->SetVisible( ( iReaction == iSubjectReaction ) ? ( true ) : ( false ) );
+ }
+} \ No newline at end of file