//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Mirrors the movement of a camera about a given point. // //=============================================================================// #include "cbase.h" #include "baseentity.h" #include "modelentities.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" ////////////////////////////////////////////////////////////////////////// // CLogicMirrorMovement // This will record the vector offset of an entity's center from a given reference point // (most likely the center of a mirror or portal) and place an entity (most likely a point camera) // at a the same offset, mirrored about the reference point and orientation. ////////////////////////////////////////////////////////////////////////// class CLogicMirrorMovement : public CLogicalEntity { DECLARE_DATADESC(); DECLARE_CLASS( CLogicMirrorMovement, CLogicalEntity ); private: void SetMirrorTarget( const char *pName ); // Set entity to watch and mirror (ex. the player) void SetTarget( const char *pName ); // Set entity to move based on the Mirror Target entity (ex. a point_camera) void SetMirrorRelative( const char* pName ); // Set the point about which to measure an offset to orient based upon (ex. portal 1) void SetRemoteTarget ( const char *pName ); // Entity's orientation/location from which to offset the movement target (ex. portal 2) void SetDrawingSurface ( const char *pName ); void InputSetMirrorTarget( inputdata_t &inputdata ); void InputSetTarget( inputdata_t &inputdata ); void InputSetRemoteTarget ( inputdata_t &inputdata ); void InputSetMirrorRelative ( inputdata_t &inputdata ); void Think(); virtual void Activate(); string_t m_strMirrorTarget; string_t m_strRemoteTarget; string_t m_strMirrorRelative; EHANDLE m_hRemoteTarget; EHANDLE m_hMirrorTarget; EHANDLE m_hMovementTarget; EHANDLE m_hMirrorRelative; }; LINK_ENTITY_TO_CLASS( logic_mirror_movement, CLogicMirrorMovement ); BEGIN_DATADESC( CLogicMirrorMovement ) DEFINE_KEYFIELD( m_strMirrorTarget, FIELD_STRING, "MirrorTarget" ), DEFINE_KEYFIELD( m_strRemoteTarget, FIELD_STRING, "RemoteTarget" ), DEFINE_KEYFIELD( m_strMirrorRelative, FIELD_STRING, "MirrorRelative" ), DEFINE_FIELD( m_hMirrorTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_hMovementTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_hRemoteTarget, FIELD_EHANDLE ), DEFINE_FIELD( m_hMirrorRelative, FIELD_EHANDLE ), DEFINE_INPUTFUNC( FIELD_STRING, "SetMirrorTarget", InputSetMirrorTarget ), DEFINE_INPUTFUNC( FIELD_STRING, "SetTarget", InputSetTarget ), DEFINE_INPUTFUNC( FIELD_STRING, "SetRemoteTarget", InputSetRemoteTarget ), DEFINE_INPUTFUNC( FIELD_STRING, "SetMirrorRelative", InputSetMirrorRelative ), DEFINE_THINKFUNC( Think ), END_DATADESC() void CLogicMirrorMovement::Activate() { BaseClass::Activate(); SetMirrorTarget( STRING(m_strMirrorTarget) ); SetTarget( STRING(m_target) ); SetRemoteTarget( STRING(m_strRemoteTarget ) ); SetMirrorRelative( STRING( m_strMirrorRelative) ); SetThink( &CLogicMirrorMovement::Think ); SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); } void CLogicMirrorMovement::SetMirrorTarget( const char *pName ) { m_hMirrorTarget = gEntList.FindEntityByName( NULL, pName ); if ( !m_hMirrorTarget ) { if ( Q_strnicmp( STRING(m_strMirrorTarget), "!player", 8 ) ) { Warning("logic_mirror_movement: Unable to find mirror target entity %s\n", pName ); } } } void CLogicMirrorMovement::SetTarget( const char *pName ) { m_hMovementTarget = gEntList.FindEntityByName( NULL, pName ); if ( !m_hMovementTarget ) { Warning("logic_mirror_movement: Unable to find movement target entity %s\n", pName ); } } void CLogicMirrorMovement::SetRemoteTarget(const char *pName ) { m_hRemoteTarget = gEntList.FindEntityByName( NULL, pName ); if ( !m_hRemoteTarget ) { Warning("logic_mirror_movement: Unable to find remote target entity %s\n", pName ); } } void CLogicMirrorMovement::SetMirrorRelative(const char* pName ) { m_hMirrorRelative = gEntList.FindEntityByName( NULL, pName ); if ( !m_hMirrorRelative ) { Warning("logic_mirror_movement: Unable to find mirror relative entity %s\n", pName ); } } void CLogicMirrorMovement::InputSetMirrorTarget( inputdata_t &inputdata ) { m_strMirrorTarget = AllocPooledString( inputdata.value.String() ); SetMirrorTarget( inputdata.value.String() ); } void CLogicMirrorMovement::InputSetTarget( inputdata_t &inputdata ) { m_target = AllocPooledString( inputdata.value.String() ); SetTarget( inputdata.value.String() ); } void CLogicMirrorMovement::InputSetRemoteTarget(inputdata_t &inputdata ) { m_strRemoteTarget = AllocPooledString( inputdata.value.String() ); SetRemoteTarget( inputdata.value.String() ); } void CLogicMirrorMovement::InputSetMirrorRelative(inputdata_t &inputdata ) { m_strMirrorRelative = AllocPooledString ( inputdata.value.String() ); SetMirrorRelative( inputdata.value.String() ); } void CLogicMirrorMovement::Think() { // Attempt to get the player's handle because it didn't exist at Activate time if ( !m_hMirrorTarget.Get() ) { // If we will never find a target, we don't have a use... shutdown if ( m_strMirrorTarget == NULL_STRING ) SetNextThink ( NULL ); //BUGBUG: If m_strSetMirrorTarget doesn't exist in ent list, we get per-think searches with no results ever... SetMirrorTarget ( STRING(m_strMirrorTarget) ); } // Make sure all entities are valid if ( m_hMirrorTarget.Get() && m_hMovementTarget.Get() && m_hRemoteTarget.Get() && m_hMirrorRelative.Get() ) { // Get our two portal's world transforms transforms VMatrix matPortal1ToWorldInv, matPortal2ToWorld; MatrixInverseGeneral( m_hMirrorRelative->EntityToWorldTransform(), matPortal1ToWorldInv ); matPortal2ToWorld = m_hRemoteTarget->EntityToWorldTransform(); VMatrix matTransformToRemotePortal = matPortal1ToWorldInv * matPortal2ToWorld; // Get our scene camera's current orientation Vector ptCameraPosition, vCameraLook, vCameraRight, vCameraUp; ptCameraPosition = m_hMirrorTarget->EyePosition(); m_hMirrorTarget->GetVectors ( &vCameraLook, &vCameraRight, &vCameraUp ); // map this position and orientation to the remote portal, mirrored (invert the result) Vector ptNewPosition, vNewLook; ptNewPosition = matPortal1ToWorldInv * ptCameraPosition; ptNewPosition = matPortal2ToWorld*( Vector( -ptNewPosition.x, -ptNewPosition.y, ptNewPosition.z ) ); vNewLook = matPortal1ToWorldInv.ApplyRotation( vCameraLook ); vNewLook = matPortal2ToWorld.ApplyRotation( Vector( -vNewLook.x, -vNewLook.y, vNewLook.z) ); // Set the point camera to the new location/orientation QAngle qNewAngles; VectorAngles( vNewLook, qNewAngles ); m_hMovementTarget->Teleport( &ptNewPosition, &qNewAngles, NULL ); } SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); }