// Look at the given point in space for the given duration (-1 means forever) void CCSBot::SetLookAt(const char *desc, const Vector *pos, PriorityType pri, float duration, bool clearIfClose, float angleTolerance) { if (pos == NULL) return; // if currently looking at a point in space with higher priority, ignore this request if (m_lookAtSpotState != NOT_LOOKING_AT_SPOT && m_lookAtSpotPriority > pri) return; // if already looking at this spot, just extend the time const float tolerance = 10.0f; if (m_lookAtSpotState != NOT_LOOKING_AT_SPOT && VectorsAreEqual(pos, &m_lookAtSpot, tolerance)) { m_lookAtSpotDuration = duration; if (m_lookAtSpotPriority < pri) m_lookAtSpotPriority = pri; } else { // look at new spot m_lookAtSpot = *pos; m_lookAtSpotState = LOOK_TOWARDS_SPOT; m_lookAtSpotDuration = duration; m_lookAtSpotPriority = pri; } m_lookAtSpotAngleTolerance = angleTolerance; m_lookAtSpotClearIfClose = clearIfClose; m_lookAtDesc = desc; }
void C_SteamJet::UpdateLightingRamp() { if( VectorsAreEqual( m_vLastRampUpdatePos, GetAbsOrigin(), 0.1 ) && QAnglesAreEqual( m_vLastRampUpdateAngles, GetAbsAngles(), 0.1 ) ) { return; } m_vLastRampUpdatePos = GetAbsOrigin(); m_vLastRampUpdateAngles = GetAbsAngles(); // Sample the world lighting where we think the particles will be. Vector forward, right, up; AngleVectors(GetAbsAngles(), &forward, &right, &up); // Legacy env_steamjet entities faced left instead of forward. if (m_bFaceLeft) { Vector temp = forward; forward = -right; right = temp; } Vector startPos = GetAbsOrigin(); Vector endPos = GetAbsOrigin() + forward * (m_Speed * m_Lifetime); for(int iRamp=0; iRamp < STEAMJET_NUMRAMPS; iRamp++) { float t = (float)iRamp / (STEAMJET_NUMRAMPS-1); Vector vTestPos = startPos + (endPos - startPos) * t; Vector *pRamp = &m_Ramps[iRamp]; *pRamp = WorldGetLightForPoint(vTestPos, false); if ( IsEmissive() ) { pRamp->x += (m_clrRender->r/255.0f); pRamp->y += (m_clrRender->g/255.0f); pRamp->z += (m_clrRender->b/255.0f); pRamp->x = clamp( pRamp->x, 0.0f, 1.0f ); pRamp->y = clamp( pRamp->y, 0.0f, 1.0f ); pRamp->z = clamp( pRamp->z, 0.0f, 1.0f ); } else { pRamp->x *= (m_clrRender->r/255.0f); pRamp->y *= (m_clrRender->g/255.0f); pRamp->z *= (m_clrRender->b/255.0f); } // Renormalize? float maxVal = max(pRamp->x, max(pRamp->y, pRamp->z)); if(maxVal > 1) { *pRamp = *pRamp / maxVal; } } }
static bool FindEdge( CCoreDispInfo *pInfo, Vector const &vPoint1, Vector const &vPoint2, int &iEdge ) { CCoreDispSurface *pSurface = pInfo->GetSurface(); for( iEdge=0; iEdge < 4; iEdge++ ) { if( VectorsAreEqual( vPoint1, pSurface->GetPoint( iEdge ), 0.01f ) && VectorsAreEqual( vPoint2, pSurface->GetPoint( (iEdge+1) & 3), 0.01f ) ) { return true; } } return false; }
static void SetupCornerNeighbors( const CCoreDispInfo *pListBase, CCoreDispInfo *pMain, CCoreDispInfo *pOther, int *nOverflows ) { if ( HasEdgeNeighbor( pMain, pOther-pListBase ) ) return; // Do these two share a vertex? int nShared = 0; int iMainSharedCorner = -1; int iOtherSharedCorner = -1; for ( int iMainCorner=0; iMainCorner < 4; iMainCorner++ ) { Vector const &vMainCorner = pMain->GetCornerPoint( iMainCorner ); for ( int iOtherCorner=0; iOtherCorner < 4; iOtherCorner++ ) { Vector const &vOtherCorner = pOther->GetCornerPoint( iOtherCorner ); if ( VectorsAreEqual( vMainCorner, vOtherCorner, 0.001f ) ) { iMainSharedCorner = iMainCorner; iOtherSharedCorner = iOtherCorner; ++nShared; } } } if ( nShared == 1 ) { CDispCornerNeighbors *pMainCorner = pMain->GetCornerNeighbors( iMainSharedCorner ); CDispCornerNeighbors *pOtherCorner = pOther->GetCornerNeighbors( iOtherSharedCorner ); if ( pMainCorner->m_nNeighbors < MAX_DISP_CORNER_NEIGHBORS && pOtherCorner->m_nNeighbors < MAX_DISP_CORNER_NEIGHBORS ) { pMainCorner->m_Neighbors[pMainCorner->m_nNeighbors++] = pOther - pListBase; pOtherCorner->m_Neighbors[pOtherCorner->m_nNeighbors++] = pMain - pListBase; } else { ++(*nOverflows); } } }
static void PerformNewCustomEffects( const Vector &vecOrigin, trace_t &tr, const Vector &shotDir, int iMaterial, int iScale, int nFlags ) { bool bNoFlecks = !r_drawflecks.GetBool(); if ( !bNoFlecks ) { bNoFlecks = ( ( nFlags & FLAGS_CUSTIOM_EFFECTS_NOFLECKS ) != 0 ); } // Compute the impact effect name const ImpactEffect_t &effect = s_pImpactEffect[ iMaterial - 'A' ]; const char *pImpactName = effect.m_pName; if ( bNoFlecks && effect.m_pNameNoFlecks ) { pImpactName = effect.m_pNameNoFlecks; } if ( !pImpactName ) return; CSmartPtr<CNewParticleEffect> pEffect = CNewParticleEffect::Create( NULL, pImpactName ); if ( !pEffect->IsValid() ) return; Vector vecReflect; float flDot = DotProduct( shotDir, tr.plane.normal ); VectorMA( shotDir, -2.0f * flDot, tr.plane.normal, vecReflect ); Vector vecShotBackward; VectorMultiply( shotDir, -1.0f, vecShotBackward ); Vector vecImpactPoint = ( tr.fraction != 1.0f ) ? tr.endpos : vecOrigin; // Other games round m_vOrigin to nearest integer, so I guess we can afford skipping this check. #ifdef HL2_CLIENT_DLL Assert( VectorsAreEqual( vecOrigin, tr.endpos, 1e-1 ) ); #endif SetImpactControlPoint( pEffect.GetObject(), 0, vecImpactPoint, tr.plane.normal, tr.m_pEnt ); SetImpactControlPoint( pEffect.GetObject(), 1, vecImpactPoint, vecReflect, tr.m_pEnt ); SetImpactControlPoint( pEffect.GetObject(), 2, vecImpactPoint, vecShotBackward, tr.m_pEnt ); pEffect->SetControlPoint( 3, Vector( iScale, iScale, iScale ) ); if ( pEffect->m_pDef->ReadsControlPoint( 4 ) ) { Vector vecColor; GetColorForSurface( &tr, &vecColor ); pEffect->SetControlPoint( 4, vecColor ); } }
bool fogparams_t::operator !=( const fogparams_t& other ) const { if ( this->enable != other.enable || this->blend != other.blend || !VectorsAreEqual(this->dirPrimary, other.dirPrimary, 0.01f ) || this->colorPrimary.Get() != other.colorPrimary.Get() || this->colorSecondary.Get() != other.colorSecondary.Get() || this->start != other.start || this->end != other.end || this->farz != other.farz || this->maxdensity != other.maxdensity || this->colorPrimaryLerpTo.Get() != other.colorPrimaryLerpTo.Get() || this->colorSecondaryLerpTo.Get() != other.colorSecondaryLerpTo.Get() || this->startLerpTo != other.startLerpTo || this->endLerpTo != other.endLerpTo || this->lerptime != other.lerptime || this->duration != other.duration ) return true; return false; }
//----------------------------------------------------------------------------- // Implement this if you want to know when the player collides during OnPlayerMove //----------------------------------------------------------------------------- void CTFGameMovementRecon::OnTryPlayerMoveCollision( trace_t &tr ) { if ( !m_bPerformingAirMove ) return; // Only keep track of world collisions if ( tr.DidHitWorld() ) { CTFMoveData *pTFMove = TFMove(); if ( pTFMove ) { if ( ( pTFMove->ReconData().m_flSuppressionJumpTime == TIME_WALL_INVALID ) && ( pTFMove->ReconData().m_flSuppressionImpactTime == TIME_WALL_INVALID ) ) { // No walljumps off of mostly horizontal surfaces... if ( fabs( tr.plane.normal.z ) > 0.9f ) return; // No walljumps off of the same plane as the last one... if ( (pTFMove->ReconData().m_flImpactDist == tr.plane.dist) && (VectorsAreEqual(pTFMove->ReconData().m_vecImpactNormal, tr.plane.normal, 1e-2) ) ) { return; } // If you hit a wall, no double jumps for you pTFMove->ReconData().m_nJumpCount = 2; // Play an impact sound MoveHelper()->StartSound( pTFMove->m_vecAbsOrigin, "Recon.WallJump" ); pTFMove->ReconData().m_vecImpactNormal = tr.plane.normal; pTFMove->ReconData().m_flImpactDist = tr.plane.dist; pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_ACTIVATE_JUMP; pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_SUPPRESSION_IMPACT; } } } }
bool CAI_TacticalServices::FindLateralLos( const Vector &vecThreat, Vector *pResult ) { AI_PROFILE_SCOPE( CAI_TacticalServices_FindLateralLos ); if( !m_bAllowFindLateralLos ) { return false; } MARK_TASK_EXPENSIVE(); Vector vecLeftTest; Vector vecRightTest; Vector vecStepRight; Vector vecCheckStart; bool bLookingForEnemy = GetEnemy() && VectorsAreEqual(vecThreat, GetEnemy()->EyePosition(), 0.1f); int i; if( !bLookingForEnemy || GetOuter()->HasCondition(COND_SEE_ENEMY) || GetOuter()->HasCondition(COND_HAVE_ENEMY_LOS) || GetOuter()->GetTimeScheduleStarted() == gpGlobals->curtime ) // Conditions get nuked before tasks run, assume should try { // My current position might already be valid. if ( TestLateralLos(vecThreat, GetLocalOrigin()) ) { *pResult = GetLocalOrigin(); return true; } } if( !ai_find_lateral_los.GetBool() ) { // Allows us to turn off lateral LOS at the console. Allow the above code to run // just in case the NPC has line of sight to begin with. return false; } int iChecks = COVER_CHECKS; int iDelta = COVER_DELTA; // If we're limited in how far we're allowed to move laterally, don't bother checking past it int iMaxLateralDelta = GetOuter()->GetMaxTacticalLateralMovement(); if ( iMaxLateralDelta != MAXTACLAT_IGNORE && iMaxLateralDelta < iDelta ) { iChecks = 1; iDelta = iMaxLateralDelta; } Vector right; AngleVectors( GetLocalAngles(), NULL, &right, NULL ); vecStepRight = right * iDelta; vecStepRight.z = 0; vecLeftTest = vecRightTest = GetLocalOrigin(); vecCheckStart = vecThreat; for ( i = 0 ; i < iChecks; i++ ) { vecLeftTest = vecLeftTest - vecStepRight; vecRightTest = vecRightTest + vecStepRight; if (TestLateralLos( vecCheckStart, vecLeftTest )) { *pResult = vecLeftTest; return true; } if (TestLateralLos( vecCheckStart, vecRightTest )) { *pResult = vecRightTest; return true; } } return false; }
bool CAI_LocalNavigator::MoveCalcDirect( AILocalMoveGoal_t *pMoveGoal, bool bOnlyCurThink, float *pDistClear, AIMoveResult_t *pResult ) { AI_PROFILE_SCOPE(CAI_LocalNavigator_MoveCalcDirect); bool bRetVal = false; if ( pMoveGoal->speed ) { CAI_Motor *pMotor = GetOuter()->GetMotor(); float minCheckDist = pMotor->MinCheckDist(); float probeDist = m_pPlaneSolver->CalcProbeDist( pMoveGoal->speed ); // having this match steering allows one fewer traces float checkDist = MAX( minCheckDist, probeDist ); float checkStepDist = MAX( 16.0, probeDist * 0.5 ); if ( pMoveGoal->flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) ) { // clamp checkDist to be no farther than MAX distance to goal checkDist = MIN( checkDist, pMoveGoal->maxDist ); } if ( checkDist <= 0.0 ) { *pResult = AIMR_OK; return true; } float moveThisInterval = pMotor->CalcIntervalMove(); bool bExpectingArrival = (moveThisInterval >= checkDist); if ( !m_FullDirectTimer.Expired() ) { if ( !m_fLastWasClear || ( !VectorsAreEqual(pMoveGoal->target, m_LastMoveGoal.target, 0.1) || !VectorsAreEqual(pMoveGoal->dir, m_LastMoveGoal.dir, 0.1) ) || bExpectingArrival ) { m_FullDirectTimer.Force(); } } if ( bOnlyCurThink ) // Outer code claims to have done a validation (probably a simplify operation) { m_FullDirectTimer.Set( TIME_DELAY_FULL_DIRECT_PROBE[AIStrongOpt()] ); } // First, check the probable move for this cycle bool bTraceClear = true; Vector testPos; if ( !bExpectingArrival ) { testPos = GetLocalOrigin() + pMoveGoal->dir * moveThisInterval; bTraceClear = GetMoveProbe()->MoveLimit( pMoveGoal->navType, GetLocalOrigin(), testPos, GetOuter()->GetAITraceMask(), pMoveGoal->pMoveTarget, 100.0, ( pMoveGoal->navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT, &pMoveGoal->directTrace ); if ( !bTraceClear ) { // Adjust probe top match expected probe dist (relied on later in process) pMoveGoal->directTrace.flDistObstructed = (checkDist - moveThisInterval) + pMoveGoal->directTrace.flDistObstructed; } if ( !IsRetail() && ai_debug_directnavprobe.GetBool() ) { if ( !bTraceClear ) { DevMsg( GetOuter(), "Close obstruction %f\n", checkDist - pMoveGoal->directTrace.flDistObstructed ); NDebugOverlay::Line( WorldSpaceCenter(), Vector( testPos.x, testPos.y, WorldSpaceCenter().z ), 255, 0, 0, false, 0.1 ); if ( pMoveGoal->directTrace.pObstruction ) NDebugOverlay::Line( WorldSpaceCenter(), pMoveGoal->directTrace.pObstruction->WorldSpaceCenter(), 255, 0, 255, false, 0.1 ); } else { NDebugOverlay::Line( WorldSpaceCenter(), Vector( testPos.x, testPos.y, WorldSpaceCenter().z ), 0, 255, 0, false, 0.1 ); } } pMoveGoal->thinkTrace = pMoveGoal->directTrace; } // Now project out for future obstructions if ( bTraceClear ) { if ( m_FullDirectTimer.Expired() ) { testPos = GetLocalOrigin() + pMoveGoal->dir * checkDist; float checkStepPct = (checkStepDist / checkDist) * 100.0; if ( checkStepPct > 100.0 ) checkStepPct = 100.0; bTraceClear = GetMoveProbe()->MoveLimit( pMoveGoal->navType, GetLocalOrigin(), testPos, GetOuter()->GetAITraceMask(), pMoveGoal->pMoveTarget, checkStepPct, ( pMoveGoal->navType == NAV_GROUND ) ? AIMLF_2D : AIMLF_DEFAULT, &pMoveGoal->directTrace ); if ( bExpectingArrival ) pMoveGoal->thinkTrace = pMoveGoal->directTrace; if (ai_debug_directnavprobe.GetBool() ) { if ( !bTraceClear ) { NDebugOverlay::Line( GetOuter()->EyePosition(), Vector( testPos.x, testPos.y, GetOuter()->EyePosition().z ), 255, 0, 0, false, 0.1 ); DevMsg( GetOuter(), "Obstruction %f\n", checkDist - pMoveGoal->directTrace.flDistObstructed ); } else { NDebugOverlay::Line( GetOuter()->EyePosition(), Vector( testPos.x, testPos.y, GetOuter()->EyePosition().z ), 0, 255, 0, false, 0.1 ); DevMsg( GetOuter(), "No obstruction\n" ); } } } else { if ( ai_debug_directnavprobe.GetBool() ) DevMsg( GetOuter(), "No obstruction (Near probe only)\n" ); } } pMoveGoal->bHasTraced = true; float distClear = checkDist - pMoveGoal->directTrace.flDistObstructed; if (distClear < 0.001) distClear = 0; if ( bTraceClear ) { *pResult = AIMR_OK; bRetVal = true; m_fLastWasClear = true; } else if ( ( pMoveGoal->flags & ( AILMG_TARGET_IS_TRANSITION | AILMG_TARGET_IS_GOAL ) ) && pMoveGoal->maxDist < distClear ) { *pResult = AIMR_OK; bRetVal = true; m_fLastWasClear = true; } else { *pDistClear = distClear; m_fLastWasClear = false; } } else { // Should never end up in this function with speed of zero. Probably an activity problem. *pResult = AIMR_ILLEGAL; bRetVal = true; } m_LastMoveGoal = *pMoveGoal; if ( bRetVal && m_FullDirectTimer.Expired() ) m_FullDirectTimer.Set( TIME_DELAY_FULL_DIRECT_PROBE[AIStrongOpt()] ); return bRetVal; }
void C_FuncSmokeVolume::Update( float fTimeDelta ) { // Update our world space bbox if we've moved at all. // We do this manually because sometimes people make HUGE bboxes, and if they're constantly changing because their // particles wander outside the current bounds sometimes, it'll be linking them into all the leaves repeatedly. const Vector &curOrigin = GetAbsOrigin(); const QAngle &curAngles = GetAbsAngles(); if ( !VectorsAreEqual( curOrigin, m_vLastOrigin, 0.1 ) || fabs( curAngles.x - m_vLastAngles.x ) > 0.1 || fabs( curAngles.y - m_vLastAngles.y ) > 0.1 || fabs( curAngles.z - m_vLastAngles.z ) > 0.1 || m_bFirstUpdate ) { m_bFirstUpdate = false; m_vLastAngles = curAngles; m_vLastOrigin = curOrigin; Vector vWorldMins, vWorldMaxs; CollisionProp()->WorldSpaceAABB( &vWorldMins, &vWorldMaxs ); vWorldMins -= Vector( m_ParticleRadius, m_ParticleRadius, m_ParticleRadius ); vWorldMaxs += Vector( m_ParticleRadius, m_ParticleRadius, m_ParticleRadius ); m_ParticleEffect.SetBBox( vWorldMins, vWorldMaxs ); } // lerp m_CurrentDensity towards m_Density at a rate of m_DensityRampSpeed if( m_CurrentDensity < m_Density ) { m_CurrentDensity += m_DensityRampSpeed * fTimeDelta; if( m_CurrentDensity > m_Density ) { m_CurrentDensity = m_Density; } } else if( m_CurrentDensity > m_Density ) { m_CurrentDensity -= m_DensityRampSpeed * fTimeDelta; if( m_CurrentDensity < m_Density ) { m_CurrentDensity = m_Density; } } if( m_CurrentDensity == 0.0f ) { return; } // This is used to randomize the direction it chooses to move a particle in. int offsetLookup[3] = {-1,0,1}; float tradeDurationMax = m_ParticleSpacingDistance / ( m_MovementSpeed + 0.1f ); float tradeDurationMin = tradeDurationMax * 0.5f; if ( IS_NAN( tradeDurationMax ) || IS_NAN( tradeDurationMin ) ) return; // Warning( "tradeDuration: [%f,%f]\n", tradeDurationMin, tradeDurationMax ); // Update all the moving traders and establish new ones. int nTotal = m_xCount * m_yCount * m_zCount; for( int i=0; i < nTotal; i++ ) { SmokeParticleInfo *pInfo = &m_pSmokeParticleInfos[i]; if(!pInfo->m_pParticle) continue; if(pInfo->m_TradeIndex == -1) { pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha; pInfo->m_pParticle->m_Color[0] = pInfo->m_Color[0]; pInfo->m_pParticle->m_Color[1] = pInfo->m_Color[1]; pInfo->m_pParticle->m_Color[2] = pInfo->m_Color[2]; // Is there an adjacent one that's not trading? int x, y, z; GetParticleInfoXYZ(i, x, y, z); int xCountOffset = rand(); int yCountOffset = rand(); int zCountOffset = rand(); bool bFound = false; for(int xCount=0; xCount < 3 && !bFound; xCount++) { for(int yCount=0; yCount < 3 && !bFound; yCount++) { for(int zCount=0; zCount < 3; zCount++) { int testX = x + offsetLookup[(xCount+xCountOffset) % 3]; int testY = y + offsetLookup[(yCount+yCountOffset) % 3]; int testZ = z + offsetLookup[(zCount+zCountOffset) % 3]; if(testX == x && testY == y && testZ == z) continue; if(IsValidXYZCoords(testX, testY, testZ)) { SmokeParticleInfo *pOther = GetSmokeParticleInfo(testX, testY, testZ); if(pOther->m_pParticle && pOther->m_TradeIndex == -1) { // Ok, this one is looking to trade also. pInfo->m_TradeIndex = GetSmokeParticleIndex(testX, testY, testZ); pOther->m_TradeIndex = i; pInfo->m_TradeClock = pOther->m_TradeClock = 0; pOther->m_TradeDuration = pInfo->m_TradeDuration = FRand( tradeDurationMin, tradeDurationMax ); bFound = true; break; } } } } } } else { SmokeParticleInfo *pOther = &m_pSmokeParticleInfos[pInfo->m_TradeIndex]; assert(pOther->m_TradeIndex == i); // This makes sure the trade only gets updated once per frame. if(pInfo < pOther) { // Increment the trade clock.. pInfo->m_TradeClock = (pOther->m_TradeClock += fTimeDelta); int x, y, z; GetParticleInfoXYZ(i, x, y, z); Vector myPos = GetSmokeParticlePos(x, y, z); int otherX, otherY, otherZ; GetParticleInfoXYZ(pInfo->m_TradeIndex, otherX, otherY, otherZ); Vector otherPos = GetSmokeParticlePos(otherX, otherY, otherZ); // Is the trade finished? if(pInfo->m_TradeClock >= pInfo->m_TradeDuration) { pInfo->m_TradeIndex = pOther->m_TradeIndex = -1; pInfo->m_pParticle->m_Pos = otherPos; pOther->m_pParticle->m_Pos = myPos; SmokeGrenadeParticle *temp = pInfo->m_pParticle; pInfo->m_pParticle = pOther->m_pParticle; pOther->m_pParticle = temp; } else { // Ok, move them closer. float percent = (float)cos(pInfo->m_TradeClock * 2 * 1.57079632f / pInfo->m_TradeDuration); percent = percent * 0.5 + 0.5; pInfo->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * (1 - percent); pOther->m_pParticle->m_FadeAlpha = pInfo->m_FadeAlpha + (pOther->m_FadeAlpha - pInfo->m_FadeAlpha) * percent; InterpColor(pInfo->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, 1-percent); InterpColor(pOther->m_pParticle->m_Color, pInfo->m_Color, pOther->m_Color, percent); pInfo->m_pParticle->m_Pos = myPos + (otherPos - myPos) * (1 - percent); pOther->m_pParticle->m_Pos = myPos + (otherPos - myPos) * percent; } } } } }
//----------------------------------------------------------------------------- // A version that simply accepts a ray (can work as a traceline or tracehull) //----------------------------------------------------------------------------- void CEngineTrace::TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace ) { CTraceFilterHitAll traceFilter; if ( !pTraceFilter ) { pTraceFilter = &traceFilter; } // Gather statistics. g_EngineStats.IncrementCountedStat( ENGINE_STATS_NUM_TRACE_LINES, 1 ); MEASURE_TIMED_STAT( ENGINE_STATS_TRACE_LINE_TIME ); CM_ClearTrace( pTrace ); // Collide with the world. if ( pTraceFilter->GetTraceType() != TRACE_ENTITIES_ONLY ) { ICollideable *pCollide = GetWorldCollideable(); // Make sure the world entity is unrotated // FIXME: BAH! The !pCollide test here is because of // CStaticProp::PrecacheLighting.. it's occurring too early // need to fix that later Assert(!pCollide || pCollide->GetCollisionOrigin() == vec3_origin ); Assert(!pCollide || pCollide->GetCollisionAngles() == vec3_angle ); CM_BoxTrace( ray, 0, fMask, true, *pTrace ); SetTraceEntity( pCollide, pTrace ); // Blocked by the world. if ( pTrace->fraction == 0 ) return; // Early out if we only trace against the world if ( pTraceFilter->GetTraceType() == TRACE_WORLD_ONLY ) return; } // Save the world collision fraction. float flWorldFraction = pTrace->fraction; // Create a ray that extends only until we hit the world // and adjust the trace accordingly Ray_t entityRay = ray; entityRay.m_Delta *= pTrace->fraction; // We know this is safe because if pTrace->fraction == 0 // we would have exited above pTrace->fractionleftsolid /= pTrace->fraction; pTrace->fraction = 1.0; // Collide with entities along the ray // FIXME: Hitbox code causes this to be re-entrant for the IK stuff. // If we could eliminate that, this could be static and therefore // not have to reallocate memory all the time CEntitiesAlongRay enumerator; enumerator.Reset(); SpatialPartition()->EnumerateElementsAlongRay( SpatialPartitionMask(), entityRay, false, &enumerator ); bool bNoStaticProps = pTraceFilter->GetTraceType() == TRACE_ENTITIES_ONLY; bool bFilterStaticProps = pTraceFilter->GetTraceType() == TRACE_EVERYTHING_FILTER_PROPS; trace_t tr; ICollideable *pCollideable; const char *pDebugName; int nCount = enumerator.m_EntityHandles.Count(); for ( int i = 0; i < nCount; ++i ) { // Generate a collideable IHandleEntity *pHandleEntity = enumerator.m_EntityHandles[i]; HandleEntityToCollideable( pHandleEntity, &pCollideable, &pDebugName ); // Check for error condition if ( !IsSolid( pCollideable->GetSolid(), pCollideable->GetSolidFlags() ) ) { char temp[1024]; Q_snprintf(temp, sizeof( temp ), "%s in solid list (not solid)\n", pDebugName ); Sys_Error (temp); } if ( !StaticPropMgr()->IsStaticProp( pHandleEntity ) ) { if ( !pTraceFilter->ShouldHitEntity( pHandleEntity, fMask ) ) continue; } else { // FIXME: Could remove this check here by // using a different spatial partition mask. Look into it // if we want more speedups here. if ( bNoStaticProps ) continue; if ( bFilterStaticProps ) { if ( !pTraceFilter->ShouldHitEntity( pHandleEntity, fMask ) ) continue; } } ClipRayToCollideable( entityRay, fMask, pCollideable, &tr ); // Make sure the ray is always shorter than it currently is ClipTraceToTrace( tr, pTrace ); // Stop if we're in allsolid if (pTrace->allsolid) break; } // Fix up the fractions so they are appropriate given the original // unclipped-to-world ray pTrace->fraction *= flWorldFraction; pTrace->fractionleftsolid *= flWorldFraction; #ifdef _DEBUG Vector vecOffset, vecEndTest; VectorAdd( ray.m_Start, ray.m_StartOffset, vecOffset ); VectorMA( vecOffset, pTrace->fractionleftsolid, ray.m_Delta, vecEndTest ); Assert( VectorsAreEqual( vecEndTest, pTrace->startpos, 0.1f ) ); VectorMA( vecOffset, pTrace->fraction, ray.m_Delta, vecEndTest ); Assert( VectorsAreEqual( vecEndTest, pTrace->endpos, 0.1f ) ); // Assert( !ray.m_IsRay || pTrace->allsolid || pTrace->fraction >= pTrace->fractionleftsolid ); #endif if ( !ray.m_IsRay ) { // Make sure no fractionleftsolid can be used with box sweeps VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos ); pTrace->fractionleftsolid = 0; #ifdef _DEBUG pTrace->fractionleftsolid = VEC_T_NAN; #endif } }
//----------------------------------------------------------------------------- // Traces a ray against a particular edict //----------------------------------------------------------------------------- void CEngineTrace::ClipRayToCollideable( const Ray_t &ray, unsigned int fMask, ICollideable *pEntity, trace_t *pTrace ) { CM_ClearTrace( pTrace ); VectorAdd( ray.m_Start, ray.m_StartOffset, pTrace->startpos ); VectorAdd( pTrace->startpos, ray.m_Delta, pTrace->endpos ); const model_t *pModel = pEntity->GetCollisionModel(); bool bIsStudioModel = pModel && pModel->type == mod_studio; // Cull if the collision mask isn't set + we're not testing hitboxes. if ( bIsStudioModel && (( fMask & CONTENTS_HITBOX ) == 0) ) { studiohdr_t *pStudioHdr = ( studiohdr_t * )modelloader->GetExtraData( (model_t*)pModel ); if ( ( fMask & pStudioHdr->contents ) == 0) return; } bool bTraced = false; bool bCustomPerformed = false; if ( ShouldPerformCustomRayTest( ray, pEntity ) ) { ClipRayToCustom( ray, fMask, pEntity, pTrace ); bTraced = true; bCustomPerformed = true; } else { bTraced = ClipRayToVPhysics( ray, fMask, pEntity, pTrace ); } if ( !bTraced ) { bTraced = ClipRayToBSP( ray, fMask, pEntity, pTrace ); } // Hitboxes.. bool bTracedHitboxes = false; if ( bIsStudioModel && (fMask & CONTENTS_HITBOX) ) { // Until hitboxes are no longer implemented as custom raytests, // don't bother to do the work twice if (!bCustomPerformed) { bTraced = ClipRayToHitboxes( ray, fMask, pEntity, pTrace ); if ( bTraced ) { // Hitboxes will set the surface properties bTracedHitboxes = true; } } } if ( !bTraced ) { ClipRayToBBox( ray, fMask, pEntity, pTrace ); } if ( bIsStudioModel && !bTracedHitboxes && pTrace->DidHit() ) { studiohdr_t *pStudioHdr = ( studiohdr_t * )modelloader->GetExtraData( (model_t*)pModel ); pTrace->contents = pStudioHdr->contents; // use the default surface properties pTrace->surface.name = "**studio**"; pTrace->surface.flags = 0; pTrace->surface.surfaceProps = physprop->GetSurfaceIndex( pStudioHdr->pszSurfaceProp() ); } if (pTrace->DidHit()) { SetTraceEntity( pEntity, pTrace ); } #ifdef _DEBUG Vector vecOffset, vecEndTest; VectorAdd( ray.m_Start, ray.m_StartOffset, vecOffset ); VectorMA( vecOffset, pTrace->fractionleftsolid, ray.m_Delta, vecEndTest ); Assert( VectorsAreEqual( vecEndTest, pTrace->startpos, 0.1f ) ); VectorMA( vecOffset, pTrace->fraction, ray.m_Delta, vecEndTest ); Assert( VectorsAreEqual( vecEndTest, pTrace->endpos, 0.1f ) ); #endif }