Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

622 lines
20 KiB

  1. //========= Copyright � 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "baseprojectedentity_shared.h"
  8. #if defined( GAME_DLL )
  9. # include "baseprojector.h"
  10. # include "info_placement_helper.h"
  11. # include "portal_gamestats.h"
  12. # include "weapon_portalgun_shared.h"
  13. #else
  14. typedef C_BaseProjectedEntity CBaseProjectedEntity;
  15. #include "prediction.h"
  16. #include "c_baseprojector.h"
  17. #endif
  18. // offset away from the projector, so the trace doesn't start in solid
  19. #define PROJECTEDENTITY_TRACE_OFFSET 25.f
  20. void UTil_ProjectedEntity_Trace_Filter( CTraceFilterSimpleClassnameList *traceFilter )
  21. {
  22. traceFilter->AddClassnameToIgnore( "prop_physics" );
  23. traceFilter->AddClassnameToIgnore( "func_physbox" );
  24. traceFilter->AddClassnameToIgnore( "simple_physics_brush" );
  25. /*traceFilter->AddClassnameToIgnore( "prop_weighted_cube" );
  26. traceFilter->AddClassnameToIgnore( "npc_portal_turret_floor" );
  27. traceFilter->AddClassnameToIgnore( "prop_energy_ball" );
  28. traceFilter->AddClassnameToIgnore( "npc_security_camera" );
  29. traceFilter->AddClassnameToIgnore( "simple_physics_prop" );
  30. traceFilter->AddClassnameToIgnore( "prop_ragdoll" );
  31. traceFilter->AddClassnameToIgnore( "prop_glados_core" );
  32. traceFilter->AddClassnameToIgnore( "player" );
  33. traceFilter->AddClassnameToIgnore( "projected_wall_entity" );
  34. traceFilter->AddClassnameToIgnore( "prop_paint_bomb" );
  35. traceFilter->AddClassnameToIgnore( "prop_exploding_futbol" );
  36. traceFilter->AddClassnameToIgnore( "prop_wall_projector" );
  37. traceFilter->AddClassnameToIgnore( "projected_wall_entity" );
  38. traceFilter->AddClassnameToIgnore( "projected_tractor_beam_entity" );
  39. traceFilter->AddClassnameToIgnore( "trigger_tractorbeam" );
  40. traceFilter->AddClassnameToIgnore( "physicsshadowclone" );
  41. traceFilter->AddClassnameToIgnore( "prop_floor_button" );*/
  42. }
  43. //--------------------------------------------------------------------------------------------------
  44. //
  45. //--------------------------------------------------------------------------------------------------
  46. bool CBaseProjectedEntity::IsHittingPortal( Vector* pOutOrigin, QAngle* pOutAngles, CPortal_Base2D** pOutPortal )
  47. {
  48. #if defined( GAME_DLL )
  49. QAngle qAngles = GetLocalAngles();
  50. #else
  51. QAngle qAngles = GetNetworkAngles();
  52. #endif
  53. // Get current orientation
  54. Vector vForward, vecRight, vecUp;
  55. AngleVectors( qAngles, &vForward );
  56. Vector mins, maxs;
  57. GetProjectionExtents( mins, maxs );
  58. #if defined( GAME_DLL )
  59. Vector vStart = GetLocalOrigin();
  60. #else
  61. Vector vStart = GetNetworkOrigin();
  62. #endif
  63. Ray_t ray;
  64. // Projected ents keep themselves off the walls slightly, so move this up double that ammount to make
  65. // sure we hit any potential portal.
  66. Vector rayPos = vStart + PROJECTEDENTITY_TRACE_OFFSET * vForward;// GetEndPoint() - 5.f * vForward; // back up the start pos of the ray a bit to make sure that we miss anything at the endpoint
  67. ray.Init( rayPos, rayPos + ( vForward * MAX_TRACE_LENGTH ), mins, maxs );
  68. float flPortalTraceFraction = 1.0f;
  69. trace_t worldTrace;
  70. CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
  71. UTil_ProjectedEntity_Trace_Filter( &traceFilter );
  72. UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &worldTrace );
  73. CPortal_Base2D* pHitPortal = UTIL_Portal_FirstAlongRay( ray, flPortalTraceFraction );
  74. if ( pOutOrigin )
  75. {
  76. *pOutOrigin = worldTrace.endpos;
  77. }
  78. if ( pOutAngles )
  79. {
  80. *pOutAngles = qAngles;
  81. }
  82. // trace hit world brush before hitting portal
  83. // we need the threshold because the difference of the two fractions is very small in a valid case
  84. float flTraceThreshold = 0.0001f;
  85. if ( flPortalTraceFraction - worldTrace.fraction > flTraceThreshold )
  86. {
  87. return false;
  88. }
  89. if ( pOutPortal )
  90. {
  91. *pOutPortal = pHitPortal;
  92. }
  93. if ( !pHitPortal )
  94. {
  95. return false;
  96. }
  97. // We only care about portals that are currently linked (we don't project through anything else)
  98. if ( pHitPortal->IsActivedAndLinked() == false )
  99. return false;
  100. float flIntersectionFraction = UTIL_IntersectRayWithPortal( ray, pHitPortal );
  101. Vector vHitPoint = ray.m_Start + ray.m_Delta*flIntersectionFraction;
  102. // Wall hit a portal, reorient and project on the other side
  103. VMatrix matToLinked = pHitPortal->MatrixThisToLinked();
  104. Vector vNewWallOrigin;
  105. UTIL_Portal_PointTransform( matToLinked, vHitPoint, vNewWallOrigin );
  106. QAngle vNewAngles;
  107. UTIL_Portal_AngleTransform( matToLinked, qAngles, vNewAngles );
  108. Vector vNewForward;
  109. AngleVectors( vNewAngles, &vNewForward, NULL, NULL );
  110. // Move far enough in front of the portal not to be co-planar
  111. // (will cause traces to start solid and have z fighting issues on the renderable)
  112. vNewWallOrigin += vNewForward * PROJECTION_END_POINT_EPSILON;
  113. if ( pOutAngles )
  114. {
  115. *pOutAngles = vNewAngles;
  116. }
  117. if ( pOutOrigin )
  118. {
  119. *pOutOrigin = vNewWallOrigin;
  120. }
  121. return true;
  122. }
  123. #if defined( CLIENT_DLL )
  124. ConVar cl_predict_projected_entities( "cl_predict_projected_entities", "1" );
  125. #endif
  126. //--------------------------------------------------------------------------------------------------
  127. //
  128. //--------------------------------------------------------------------------------------------------
  129. void CBaseProjectedEntity::RecursiveProjection( bool bShouldSpawn, CBaseProjector *pParentProjector, CPortal_Base2D *pExitPortal, const Vector &vProjectOrigin, const QAngle &qProjectAngles, int iRemainingProjections, bool bDisablePlacementHelper )
  130. {
  131. #if defined( CLIENT_DLL )
  132. if( !prediction->InPrediction() || !GetPredictable() )
  133. return;
  134. #endif
  135. AddEffects( EF_NOINTERP );
  136. #if 0
  137. Vector vFlooredPosition; //HACKHACK: the inputs vary just ever so slightly from client/server. Hopefully flooring them will keep them in sync
  138. vFlooredPosition.x = floor( vProjectOrigin.x * 512.0f ) / 512.0f;
  139. vFlooredPosition.y = floor( vProjectOrigin.y * 512.0f ) / 512.0f;
  140. vFlooredPosition.z = floor( vProjectOrigin.z * 512.0f ) / 512.0f;
  141. #else
  142. Vector vFlooredPosition = vProjectOrigin;
  143. #endif
  144. #if defined( GAME_DLL )
  145. OnPreProjected();
  146. #endif
  147. Vector vOldOrigin = GetAbsOrigin();
  148. QAngle qModAngles; //SendProxy_Angles will perform this operation on the angles, making them differ by either extremely small values, or 360 degrees (-90 == 270 angularly, but not from a precision standpoint)
  149. qModAngles.x = anglemod( qProjectAngles.x );
  150. qModAngles.y = anglemod( qProjectAngles.y );
  151. qModAngles.z = anglemod( qProjectAngles.z );
  152. #if defined( CLIENT_DLL )
  153. Assert( bShouldSpawn == false );
  154. SetNetworkOrigin( vFlooredPosition );
  155. SetNetworkAngles( qModAngles );
  156. #else
  157. SetOwnerEntity( pParentProjector );
  158. SetLocalOrigin( vFlooredPosition );
  159. SetLocalAngles( qModAngles );
  160. #endif
  161. //EASY_DIFFPRINT( this, "CBaseProjectedEntity::RecursiveProjection() %f %f %f\n", XYZ( vFlooredPosition ) );
  162. m_iMaxRemainingRecursions = iRemainingProjections;
  163. #if defined( GAME_DLL )
  164. if( bShouldSpawn )
  165. {
  166. DispatchSpawn( this );
  167. }
  168. else
  169. #endif
  170. {
  171. FindProjectedEndpoints();
  172. }
  173. if( pExitPortal )
  174. {
  175. SetSourcePortal( pExitPortal );
  176. #if defined( GAME_DLL ) && !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  177. CWeaponPortalgun *pPortalGun = dynamic_cast<CWeaponPortalgun*>( pExitPortal->m_hPlacedBy.Get() );
  178. if ( pPortalGun != NULL )
  179. {
  180. Vector vecForward, vecRight, vecUp;
  181. AngleVectors( qModAngles, &vecForward, &vecRight, &vecUp );
  182. g_PortalGameStats.Event_TractorBeam_Project( pExitPortal->m_ptOrigin, vecForward , ToPortalPlayer( pPortalGun->GetOwner() ) );
  183. }
  184. #endif
  185. }
  186. OnProjected();
  187. #if defined( CLIENT_DLL )
  188. if( cl_predict_projected_entities.GetBool() == false )
  189. return;
  190. #endif
  191. // If this hits a portal, reorient through it.
  192. // We create a new ent to do this.
  193. if ( iRemainingProjections > 1 )
  194. {
  195. // If there is a portal within a small distance of our end point, reorient
  196. CPortal_Base2D* pHitPortal = NULL;
  197. Vector vNewProjectedEntityOrigin;
  198. QAngle qNewProjectedEntityAngles;
  199. bool bIsHittingPortal = IsHittingPortal( &vNewProjectedEntityOrigin, &qNewProjectedEntityAngles, &pHitPortal );
  200. SetHitPortal( pHitPortal );
  201. if ( bIsHittingPortal && pHitPortal && pHitPortal->IsActivedAndLinked() )
  202. {
  203. CPortal_Base2D *pNewExitPortal = pHitPortal->m_hLinkedPortal.Get();
  204. bool bCreateNew = (m_hChildSegment.Get() == NULL);
  205. #if defined( GAME_DLL ) //TODO: Set dormant on the client
  206. if( bCreateNew )
  207. {
  208. m_hChildSegment = CreateNewProjectedEntity();
  209. }
  210. #else
  211. if( !bCreateNew )
  212. #endif
  213. {
  214. m_hChildSegment.Get()->RecursiveProjection( bCreateNew, pParentProjector, pNewExitPortal, vNewProjectedEntityOrigin, qNewProjectedEntityAngles, iRemainingProjections - 1, bDisablePlacementHelper );
  215. }
  216. }
  217. // FIXME: Bring this back for DLC2
  218. //else if ( engine->HasPaintmap() )
  219. //{
  220. // //TestForReflectPaint();
  221. //}
  222. else if( m_hChildSegment.Get() != NULL )
  223. {
  224. #if defined( GAME_DLL ) //TODO: Set dormant on the client
  225. UTIL_Remove( m_hChildSegment.Get() );
  226. m_hChildSegment = NULL;
  227. #endif
  228. }
  229. }
  230. #if defined( GAME_DLL )
  231. if( !bDisablePlacementHelper )
  232. {
  233. m_bCreatePlacementHelper = true;
  234. bool bCreatePlacement = m_hPlacementHelper.Get() == NULL;
  235. if( bCreatePlacement )
  236. {
  237. m_hPlacementHelper = (CInfoPlacementHelper *) CreateEntityByName( "info_placement_helper" );
  238. }
  239. PlacePlacementHelper( m_hPlacementHelper );
  240. if( bCreatePlacement )
  241. {
  242. DispatchSpawn( m_hPlacementHelper );
  243. }
  244. }
  245. else
  246. {
  247. m_bCreatePlacementHelper = false;
  248. }
  249. PhysicsTouchTriggers( NULL );
  250. #endif
  251. // Projected entities should probably reflect in water because they are noticeable
  252. AddEffects( EF_MARKED_FOR_FAST_REFLECTION );
  253. }
  254. //--------------------------------------------------------------------------------------------------
  255. //
  256. //--------------------------------------------------------------------------------------------------
  257. void CBaseProjectedEntity::TestForProjectionChanges( void )
  258. {
  259. #if defined( CLIENT_DLL )
  260. if( cl_predict_projected_entities.GetBool() == false )
  261. return;
  262. #endif
  263. Vector vNewPosition;
  264. QAngle qNewAngles;
  265. CPortal_Base2D* pHitPortal = NULL;
  266. bool bIsHittingPortal = IsHittingPortal( &vNewPosition, &qNewAngles, &pHitPortal );
  267. CBaseProjectedEntity *pChild = m_hChildSegment.Get();
  268. //if( pChild )
  269. //{
  270. // EASY_DIFFPRINT( pChild, "CBaseProjectedEntity::TestForProjectionChanges() %i %s %i", entindex(), bIsHittingPortal ? "true" : "false", pHitPortal ? pHitPortal->entindex() : -1 );
  271. //}
  272. // Lost the portal we were hitting: Fizzle all children
  273. if ( !bIsHittingPortal || (pHitPortal && !pHitPortal->IsActivedAndLinked()) )
  274. {
  275. SetHitPortal( NULL );
  276. #if defined( GAME_DLL ) //TODO: Set dormant on the client
  277. float flDistSqr = GetEndPoint().DistToSqr( vNewPosition );
  278. if ( flDistSqr > 0.1f )
  279. {
  280. FindProjectedEndpoints();
  281. OnProjected();
  282. }
  283. #endif
  284. // FIXME: Bring this back for DLC2
  285. // check for reflect paint
  286. /*if ( engine->HasPaintmap() )
  287. {
  288. TestForReflectPaint();
  289. }*/
  290. #if defined( GAME_DLL )
  291. /*else*/ if( pChild )
  292. {
  293. UTIL_Remove( pChild );
  294. m_hChildSegment = NULL;
  295. }
  296. #endif
  297. }
  298. #if defined( GAME_DLL )
  299. else if( pHitPortal->IsActivedAndLinked() && (DidRedirectionPortalMove( pHitPortal ) || ((pChild == NULL) && (m_iMaxRemainingRecursions > 0))) )
  300. #else
  301. else if( pHitPortal->IsActivedAndLinked() && DidRedirectionPortalMove( pHitPortal ) && (pChild != NULL) )
  302. #endif
  303. {
  304. #if defined( CLIENT_DLL )
  305. if( GetPredictable() )
  306. #endif
  307. {
  308. Vector vPrevStart = GetStartPoint();
  309. Vector vPrevEnd = GetEndPoint();
  310. FindProjectedEndpoints();
  311. if( (vPrevStart != GetStartPoint()) || (vPrevEnd != GetEndPoint()) )
  312. {
  313. OnProjected();
  314. }
  315. SetHitPortal( pHitPortal );
  316. }
  317. //reproject child portal
  318. bool bCreateNew = (pChild == NULL);
  319. #if defined( GAME_DLL )
  320. if( bCreateNew )
  321. {
  322. m_hChildSegment = pChild = CreateNewProjectedEntity();
  323. }
  324. #else
  325. if( !bCreateNew && pChild->GetPredictable() )
  326. #endif
  327. {
  328. pChild->RecursiveProjection( bCreateNew, (CBaseProjector *)GetOwnerEntity(), pHitPortal->m_hLinkedPortal.Get(), vNewPosition, qNewAngles, m_iMaxRemainingRecursions - 1, m_bCreatePlacementHelper );
  329. }
  330. }
  331. #if defined( GAME_DLL ) //server propogates down the chain. Client evaluates each entity separately since it's not guaranteed to know about them all
  332. else if( pChild )
  333. {
  334. pChild->TestForProjectionChanges();
  335. }
  336. #endif
  337. }
  338. //--------------------------------------------------------------------------------------------------
  339. // Test for perturbation of the portal this projected entity is redirecting through
  340. // if true, this entity's owning projector will rebuild all projected ents after this one
  341. //--------------------------------------------------------------------------------------------------
  342. bool CBaseProjectedEntity::DidRedirectionPortalMove( CPortal_Base2D* pPortal )
  343. {
  344. if ( !pPortal )
  345. return true;
  346. // remote portal must exist to project through
  347. if ( pPortal->IsActivedAndLinked() == false )
  348. return true;
  349. if ( pPortal != m_hHitPortal.Get() )
  350. return true;
  351. CBaseProjectedEntity *pChild = m_hChildSegment;
  352. if( !pChild )
  353. return true;
  354. // close portal moved
  355. if ( VectorsAreEqual( pChild->m_vecSourcePortalRemoteCenter, pPortal->m_ptOrigin ) == false )
  356. {
  357. //EASY_DIFFPRINT( pChild, "CBaseProjectedEntity::DidRedirectionPortalMove() hit portal moved" );
  358. return true;
  359. }
  360. // close portal rotated
  361. if ( QAnglesAreEqual( pChild->m_vecSourcePortalRemoteAngle, pPortal->m_qAbsAngle ) == false )
  362. {
  363. return true;
  364. }
  365. //EASY_DIFFPRINT( pChild, "%f %f %f %f %f %f", XYZ( (Vector)pChild->m_vecSourcePortalCenter ), XYZ( pPortal->m_hLinkedPortal->m_ptOrigin ) );
  366. // remote portal moved
  367. if ( VectorsAreEqual( pChild->m_vecSourcePortalCenter, pPortal->m_hLinkedPortal->m_ptOrigin ) == false )
  368. {
  369. //EASY_DIFFPRINT( pChild, "CBaseProjectedEntity::DidRedirectionPortalMove() remote portal moved" );
  370. return true;
  371. }
  372. // remote portal rotated
  373. if ( QAnglesAreEqual( pChild->m_vecSourcePortalAngle, pPortal->m_hLinkedPortal->m_qAbsAngle ) == false )
  374. {
  375. return true;
  376. }
  377. return false;
  378. }
  379. //--------------------------------------------------------------------------------------------------
  380. // Project from origin to solid in the direction of our forward
  381. //--------------------------------------------------------------------------------------------------
  382. void CBaseProjectedEntity::FindProjectedEndpoints( void )
  383. {
  384. #if defined( GAME_DLL )
  385. QAngle qAngles = GetLocalAngles();
  386. #else
  387. QAngle qAngles = GetNetworkAngles();
  388. #endif
  389. // Get current orientation
  390. Vector vecForward, vecRight, vecUp;
  391. AngleVectors( qAngles, &vecForward, &vecRight, &vecUp );
  392. Vector mins, maxs;
  393. GetProjectionExtents( mins, maxs );
  394. #if defined( GAME_DLL )
  395. Vector vStart = GetLocalOrigin();
  396. #else
  397. Vector vStart = GetNetworkOrigin();
  398. #endif
  399. Vector vRayPos = vStart + PROJECTEDENTITY_TRACE_OFFSET * vecForward;
  400. Ray_t ray;
  401. ray.Init( vRayPos, vRayPos + vecForward*PROJECTOR_MAX_LENGTH, mins, maxs );
  402. trace_t tr;
  403. CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
  404. UTil_ProjectedEntity_Trace_Filter( &traceFilter );
  405. UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
  406. // Should up the max trace dist if this hits
  407. Assert ( tr.DidHit() );
  408. m_vecStartPoint = vStart;
  409. m_vecEndPoint = tr.endpos + tr.plane.normal * (PROJECTION_END_POINT_EPSILON); // Move a tiny bit off the hit surface so there's no physical overlap
  410. }
  411. //--------------------------------------------------------------------------------------------------
  412. //
  413. //--------------------------------------------------------------------------------------------------
  414. void CBaseProjectedEntity::SetHitPortal( CPortal_Base2D* pPortal )
  415. {
  416. m_hHitPortal = pPortal;
  417. if ( pPortal )
  418. {
  419. Assert( pPortal->IsActivedAndLinked() );
  420. if ( pPortal->IsActivedAndLinked() )
  421. {
  422. #if defined( GAME_DLL )
  423. // Listen for this portal to move
  424. //pPortal->AddPortalEventListener( this );
  425. #endif
  426. }
  427. }
  428. }
  429. CPortal_Base2D* CBaseProjectedEntity::GetHitPortal( void )
  430. {
  431. return m_hHitPortal.Get();
  432. }
  433. //--------------------------------------------------------------------------------------------------
  434. //
  435. //--------------------------------------------------------------------------------------------------
  436. void CBaseProjectedEntity::SetSourcePortal( CPortal_Base2D* pPortal )
  437. {
  438. Assert( pPortal && pPortal->IsActivedAndLinked() );
  439. m_hSourcePortal.Set( pPortal );
  440. #if defined( CLIENT_DLL )
  441. SetPredictionEligible( pPortal != NULL );
  442. #endif
  443. if( pPortal )
  444. {
  445. m_vecSourcePortalCenter = pPortal->m_ptOrigin;
  446. m_vecSourcePortalRemoteCenter = pPortal->m_hLinkedPortal->m_ptOrigin;
  447. m_vecSourcePortalAngle = pPortal->m_qAbsAngle;
  448. m_vecSourcePortalRemoteAngle = pPortal->m_hLinkedPortal->m_qAbsAngle;
  449. }
  450. else
  451. {
  452. m_vecSourcePortalCenter = vec3_origin;
  453. m_vecSourcePortalRemoteCenter = vec3_origin;
  454. m_vecSourcePortalAngle = vec3_angle;
  455. m_vecSourcePortalRemoteAngle = vec3_angle;
  456. }
  457. if( pPortal && pPortal->GetSimulatingPlayer() )
  458. {
  459. SetPlayerSimulated( pPortal->GetSimulatingPlayer() );
  460. }
  461. else
  462. {
  463. UnsetPlayerSimulated();
  464. }
  465. }
  466. CPortal_Base2D* CBaseProjectedEntity::GetSourcePortal( void )
  467. {
  468. return m_hSourcePortal.Get();
  469. }
  470. //--------------------------------------------------------------------------------------------------
  471. // Specify the extents to use for the projection trace
  472. //--------------------------------------------------------------------------------------------------
  473. void CBaseProjectedEntity::GetProjectionExtents( Vector &outMins, Vector &outMaxs )
  474. {
  475. outMins = outMaxs = vec3_origin;
  476. }
  477. void CBaseProjectedEntity::OnProjected( void )
  478. {
  479. AddEffects( EF_NOINTERP );
  480. #if defined( CLIENT_DLL )
  481. SetNetworkOrigin( GetStartPoint() );
  482. PreDataChanged.vStartPoint = GetStartPoint();
  483. PreDataChanged.vEndPoint = GetEndPoint();
  484. PreDataChanged.qAngles = GetNetworkAngles();
  485. #endif
  486. }
  487. void CBaseProjectedEntity::TestForReflectPaint( void )
  488. {
  489. Ray_t ray;
  490. // make ray twice longer than the projected length, so the trace will actually hit something
  491. ray.Init( GetStartPoint(), GetStartPoint() + 2.f * ( GetEndPoint() - GetStartPoint() ) );
  492. CTraceFilterSimpleClassnameList traceFilter( this, COLLISION_GROUP_NONE );
  493. UTil_ProjectedEntity_Trace_Filter( &traceFilter );
  494. trace_t tr;
  495. UTIL_ClearTrace( tr );
  496. UTIL_TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
  497. Vector vDir, vNewProjectedEntityOrigin;
  498. if ( UTIL_Paint_Reflect( tr, vNewProjectedEntityOrigin, vDir ) )
  499. {
  500. // rotate psuedo up vector
  501. Vector vOldDir, vOldUp;
  502. GetVectors( &vOldDir, NULL, &vOldUp );
  503. Vector vRotAxis = CrossProduct( vOldDir, vDir );
  504. float flAngleBetween = RAD2DEG( acos( clamp( DotProduct( vOldDir, vDir ), -1.f, 1.f ) ) );
  505. matrix3x4_t matRotation;
  506. MatrixBuildRotationAboutAxis( vRotAxis, flAngleBetween, matRotation );
  507. Vector vNewUp;
  508. VectorRotate( vOldUp, matRotation, vNewUp );
  509. QAngle qNewProjectedEntityAngles;
  510. VectorAngles( vDir, vNewUp, qNewProjectedEntityAngles );
  511. //reproject child portal
  512. bool bCreateNew = (m_hChildSegment.Get() == NULL);
  513. #if defined( GAME_DLL )
  514. if( bCreateNew )
  515. {
  516. m_hChildSegment = CreateNewProjectedEntity();
  517. }
  518. #else
  519. if( !bCreateNew )
  520. #endif
  521. {
  522. m_hChildSegment.Get()->RecursiveProjection( bCreateNew, (CBaseProjector *)GetOwnerEntity(), NULL, vNewProjectedEntityOrigin, qNewProjectedEntityAngles, m_iMaxRemainingRecursions - 1, m_bCreatePlacementHelper );
  523. }
  524. }
  525. #if defined( GAME_DLL )
  526. else if ( m_hChildSegment.Get() != NULL )
  527. {
  528. UTIL_Remove( m_hChildSegment.Get() );
  529. m_hChildSegment = NULL;
  530. }
  531. #endif
  532. }