virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) { IClientUnknown *pUnk = (IClientUnknown*)pHandleEntity; ICollideable *pCollide = pUnk->GetCollideable(); if ( pCollide->GetSolid() != SOLID_VPHYSICS && pCollide->GetSolid() != SOLID_BSP ) return false; return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask ); }
/* =============== SV_LinkEdict =============== */ void SV_LinkEdict( edict_t *ent, qboolean touch_triggers, const Vector* pPrevAbsOrigin ) { IServerEntity *pServerEntity = ent->GetIServerEntity(); if ( !pServerEntity ) return; // Remove it from whatever lists it may be in at the moment // We'll re-add it below if we need to. if (ent->partition != PARTITION_INVALID_HANDLE) { SpatialPartition()->Remove( ent->partition ); } if (ent->free) return; if (ent == sv.edicts) return; // don't add the world pServerEntity->CalcAbsolutePosition(); // set the abs box pServerEntity->SetObjectCollisionBox(); // link to PVS leafs SV_BuildEntityClusterList( ent ); // Update KD tree information ICollideable *pCollide = pServerEntity->GetCollideable(); if (ent->partition == PARTITION_INVALID_HANDLE) { // Here, we haven't added the entity to the partition before // So we have to make a new partition handle. ent->partition = SpatialPartition()->CreateHandle( pCollide->GetEntityHandle() ); } // Here, we indicate the entity has moved. Note that this call does // some fast early-outing to prevent unnecessary work SpatialPartition()->ElementMoved( ent->partition, pServerEntity->GetAbsMins(), pServerEntity->GetAbsMaxs() ); // Make sure it's in the list of all entities SpatialPartition()->Insert( PARTITION_ENGINE_NON_STATIC_EDICTS, ent->partition ); SolidType_t iSolid = pCollide->GetSolid(); int nSolidFlags = pCollide->GetSolidFlags(); bool bIsSolid = IsSolid( iSolid, nSolidFlags ) || ((nSolidFlags & FSOLID_TRIGGER) != 0); if ( !bIsSolid ) { // If this ent's touch list isn't empty, it's transitioning to not solid if ( pServerEntity->IsCurrentlyTouching() ) { // mark ent so that at the end of frame it will check to see if it's no longer touch ents pServerEntity->SetCheckUntouch( true ); } return; } // Insert it into the appropriate lists. // We have to continually reinsert it because its solid type may have changed SpatialPartitionListMask_t mask = 0; if (( nSolidFlags & FSOLID_NOT_SOLID ) == 0) { mask |= PARTITION_ENGINE_SOLID_EDICTS; } if (( nSolidFlags & FSOLID_TRIGGER ) != 0 ) { mask |= PARTITION_ENGINE_TRIGGER_EDICTS; } Assert( mask != 0 ); SpatialPartition()->Insert( mask, ent->partition ); if ( iSolid == SOLID_BSP ) { model_t *model = sv.GetModel( pServerEntity->GetModelIndex() ); if ( !model && strlen( STRING( pServerEntity->GetModelName() ) ) == 0 ) { Con_DPrintf( "Inserted %s with no model\n", STRING( ent->classname ) ); return; } } // if touch_triggers, touch all entities at this node and descend for more if (touch_triggers) { // mark ent so that at the end of frame it will check to see if it's no longer touch ents pServerEntity->SetCheckUntouch( true ); // If this is a trigger that's moved, then query the solid list instead if ( mask == PARTITION_ENGINE_TRIGGER_EDICTS ) { CTriggerMoved triggerEnum( ent ); SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_SOLID_EDICTS, pServerEntity->GetAbsMins(), pServerEntity->GetAbsMaxs(), false, &triggerEnum ); triggerEnum.HandleTouchedEntities( ); } else { if (!pPrevAbsOrigin) { CTouchLinks touchEnumerator(ent, NULL); SpatialPartition()->EnumerateElementsInBox( PARTITION_ENGINE_TRIGGER_EDICTS, pServerEntity->GetAbsMins(), pServerEntity->GetAbsMaxs(), false, &touchEnumerator ); touchEnumerator.HandleTouchedEntities( ); } else { CTouchLinks touchEnumerator(ent, pPrevAbsOrigin); // A version that checks against an extruded ray indicating the motion SpatialPartition()->EnumerateElementsAlongRay( PARTITION_ENGINE_TRIGGER_EDICTS, touchEnumerator.m_Ray, false, &touchEnumerator ); touchEnumerator.HandleTouchedEntities( ); } } } }
//----------------------------------------------------------------------------- // 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 } }