text: //========= 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 );
  }
  
  
  