/* ================== S_AddLoopingSound Called during entity generation for a frame Include velocity in case I get around to doing doppler... ================== */ void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { sfx_t *sfx; if ( !s_soundStarted || s_soundMuted ) { return; } if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Printf( S_COLOR_YELLOW, "S_AddLoopingSound: handle %i out of range\n", sfxHandle ); return; } sfx = &s_knownSfx[ sfxHandle ]; if (sfx->inMemory == qfalse) { S_memoryLoad(sfx); } if ( !sfx->soundLength ) { Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); } VectorCopy( origin, loopSounds[entityNum].origin ); VectorCopy( velocity, loopSounds[entityNum].velocity ); loopSounds[entityNum].active = qtrue; loopSounds[entityNum].kill = qtrue; loopSounds[entityNum].doppler = qfalse; loopSounds[entityNum].oldDopplerScale = 1.0; loopSounds[entityNum].dopplerScale = 1.0; loopSounds[entityNum].sfx = sfx; if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) { vec3_t out; float lena, lenb; loopSounds[entityNum].doppler = qtrue; lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin); VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out); lenb = DistanceSquared(loopSounds[listener_number].origin, out); if ((loopSounds[entityNum].framenum+1) != cls.framecount) { loopSounds[entityNum].oldDopplerScale = 1.0; } else { loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale; } loopSounds[entityNum].dopplerScale = lenb/(lena*100); if (loopSounds[entityNum].dopplerScale<=1.0) { loopSounds[entityNum].doppler = qfalse; // don't bother doing the math } } loopSounds[entityNum].framenum = cls.framecount; }
static void CM_TraceThroughLeaf( traceWork_t* tw, const cLeaf_t* leaf ) { int k; int brushnum; cbrush_t *b; cPatch_t *patch; // trace line against all brushes in the leaf for ( k = 0 ; k < leaf->numLeafBrushes ; k++ ) { brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; b = &cm.brushes[brushnum]; if ( b->checkcount == cm.checkcount ) { continue; // already checked this brush in another leaf } b->checkcount = cm.checkcount; if ( !(b->contents & tw->contents) ) { continue; } if (!CM_BoundsIntersect( tw->bounds[0], tw->bounds[1], b->bounds[0], b->bounds[1] )) continue; CM_TraceThroughBrush( tw, b ); if ( !tw->trace.fraction ) { return; } } // trace line against all patches in the leaf #ifdef BSPC if (1) { #else if ( !cm_noCurves->integer ) { #endif for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ]; if ( !patch ) { continue; } if ( patch->checkcount == cm.checkcount ) { continue; // already checked this patch in another leaf } patch->checkcount = cm.checkcount; if ( !(patch->contents & tw->contents) ) { continue; } CM_TraceThroughPatch( tw, patch ); if ( !tw->trace.fraction ) { return; } } } } #define RADIUS_EPSILON 1.0f /* ================ CM_TraceThroughSphere get the first intersection of the ray with the sphere ================ */ void CM_TraceThroughSphere( traceWork_t *tw, vec3_t origin, float radius, vec3_t start, vec3_t end ) { float l1, l2, length, scale, fraction; float a, b, c, d, sqrtd; vec3_t v1, dir, intersection; // if inside the sphere VectorSubtract(start, origin, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.fraction = 0; tw->trace.startsolid = qtrue; // test for allsolid VectorSubtract(end, origin, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.allsolid = qtrue; } return; } // VectorSubtract(end, start, dir); length = VectorNormalize(dir); // l1 = CM_DistanceFromLineSquared(origin, start, end, dir); VectorSubtract(end, origin, v1); l2 = VectorLengthSquared(v1); // if no intersection with the sphere and the end point is at least an epsilon away if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { return; } // // | origin - (start + t * dir) | = radius // a = dir[0]^2 + dir[1]^2 + dir[2]^2; // b = 2 * (dir[0] * (start[0] - origin[0]) + dir[1] * (start[1] - origin[1]) + dir[2] * (start[2] - origin[2])); // c = (start[0] - origin[0])^2 + (start[1] - origin[1])^2 + (start[2] - origin[2])^2 - radius^2; // VectorSubtract(start, origin, v1); // dir is normalized so a = 1 a = 1.0f;//dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]; b = 2.0f * (dir[0] * v1[0] + dir[1] * v1[1] + dir[2] * v1[2]); c = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); d = b * b - 4.0f * c;// * a; if (d > 0) { sqrtd = SquareRootFloat(d); // = (- b + sqrtd) * 0.5f; // / (2.0f * a); fraction = (- b - sqrtd) * 0.5f; // / (2.0f * a); // if (fraction < 0) { fraction = 0; } else { fraction /= length; } if ( fraction < tw->trace.fraction ) { tw->trace.fraction = fraction; VectorSubtract(end, start, dir); VectorMA(start, fraction, dir, intersection); VectorSubtract(intersection, origin, dir); #ifdef CAPSULE_DEBUG l2 = VectorLength(dir); if (l2 < radius) { int bah = 1; } #endif scale = 1 / (radius+RADIUS_EPSILON); VectorScale(dir, scale, dir); VectorCopy(dir, tw->trace.plane.normal); VectorAdd( tw->modelOrigin, intersection, intersection); tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection); tw->trace.contents = CONTENTS_BODY; } } else if (d == 0) { //t1 = (- b ) / 2; // slide along the sphere } // no intersection at all }
void VEH_TurretThink( Vehicle_t *pVeh, gentity_t *parent, int turretNum ) //----------------------------------------------------- { qboolean doAim = qfalse; float enemyDist, rangeSq; vec3_t enemyDir; turretStats_t *turretStats = &pVeh->m_pVehicleInfo->turret[turretNum]; vehWeaponInfo_t *vehWeapon = NULL; gentity_t *turretEnemy = NULL; int curMuzzle = 0;//? if ( !turretStats || !turretStats->iAmmoMax ) {//not a valid turret return; } if ( turretStats->passengerNum && pVeh->m_iNumPassengers >= turretStats->passengerNum ) {//the passenger that has control of this turret is on the ship VEH_TurretObeyPassengerControl( pVeh, parent, turretNum ); return; } else if ( !turretStats->bAI )//try AI {//this turret does not think on its own. return; } vehWeapon = &g_vehWeaponInfo[turretStats->iWeapon]; rangeSq = (turretStats->fAIRange*turretStats->fAIRange); curMuzzle = pVeh->turretStatus[turretNum].nextMuzzle; if ( pVeh->turretStatus[turretNum].enemyEntNum < ENTITYNUM_WORLD ) { turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum]; if ( turretEnemy->health < 0 || !turretEnemy->inuse || turretEnemy == ((gentity_t*)pVeh->m_pPilot)//enemy became my pilot///? || turretEnemy == parent || turretEnemy->r.ownerNum == parent->s.number // a passenger? || ( turretEnemy->client && turretEnemy->client->sess.sessionTeam == TEAM_SPECTATOR ) || ( turretEnemy->client && turretEnemy->client->tempSpectate >= level.time ) ) {//don't keep going after spectators, pilot, self, dead people, etc. turretEnemy = NULL; pVeh->turretStatus[turretNum].enemyEntNum = ENTITYNUM_NONE; } } if ( pVeh->turretStatus[turretNum].enemyHoldTime < level.time ) { if ( VEH_TurretFindEnemies( pVeh, parent, turretStats, turretNum, curMuzzle ) ) { turretEnemy = &g_entities[pVeh->turretStatus[turretNum].enemyEntNum]; doAim = qtrue; } else if ( parent->enemy && parent->enemy->s.number < ENTITYNUM_WORLD ) { turretEnemy = parent->enemy; doAim = qtrue; } if ( turretEnemy ) {//found one if ( turretEnemy->client ) {//hold on to clients for a min of 3 seconds pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 3000; } else {//hold less pVeh->turretStatus[turretNum].enemyHoldTime = level.time + 500; } } } if ( turretEnemy != NULL ) { if ( turretEnemy->health > 0 ) { // enemy is alive WP_CalcVehMuzzle( parent, curMuzzle ); VectorSubtract( turretEnemy->r.currentOrigin, pVeh->m_vMuzzlePos[curMuzzle], enemyDir ); enemyDist = VectorLengthSquared( enemyDir ); if ( enemyDist < rangeSq ) { // was in valid radius if ( trap_InPVS( pVeh->m_vMuzzlePos[curMuzzle], turretEnemy->r.currentOrigin ) ) { // Every now and again, check to see if we can even trace to the enemy trace_t tr; vec3_t start, end; VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], start ); VectorCopy( turretEnemy->r.currentOrigin, end ); trap_Trace( &tr, start, NULL, NULL, end, parent->s.number, MASK_SHOT ); if ( tr.entityNum == turretEnemy->s.number || (!tr.allsolid && !tr.startsolid ) ) { doAim = qtrue; // Can see our enemy } } } } } if ( doAim ) { vec3_t aimAngles; if ( VEH_TurretAim( pVeh, parent, turretEnemy, turretStats, vehWeapon, turretNum, curMuzzle, aimAngles ) ) { VEH_TurretCheckFire( pVeh, parent, /*turretEnemy,*/ turretStats, vehWeapon, turretNum, curMuzzle ); } } }
void CalcEntitySpot ( const gentity_t *ent, const spot_t spot, vec3_t point ) { vec3_t forward, up, right; vec3_t start, end; trace_t tr; if ( !ent ) { return; } ViewHeightFix(ent); switch ( spot ) { case SPOT_ORIGIN: if(VectorCompare(ent->currentOrigin, vec3_origin)) {//brush VectorSubtract(ent->absmax, ent->absmin, point);//size VectorMA(ent->absmin, 0.5, point, point); } else { VectorCopy ( ent->currentOrigin, point ); } break; case SPOT_CHEST: case SPOT_HEAD: if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) && (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD) ) {//Actual tag_head eyespot! //FIXME: Stasis aliens may have a problem here... VectorCopy( ent->client->renderInfo.eyePoint, point ); if ( ent->client->NPC_class == CLASS_ATST ) {//adjust up some point[2] += 28;//magic number :) } if ( ent->NPC ) {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards point[0] = ent->currentOrigin[0]; point[1] = ent->currentOrigin[1]; } else if ( !ent->s.number ) { SubtractLeanOfs( ent, point ); } } else { VectorCopy ( ent->currentOrigin, point ); if ( ent->client ) { point[2] += ent->client->ps.viewheight; } } if ( spot == SPOT_CHEST && ent->client ) { if ( ent->client->NPC_class != CLASS_ATST ) {//adjust up some point[2] -= ent->maxs[2]*0.2f; } } break; case SPOT_HEAD_LEAN: if ( ent->client && VectorLengthSquared( ent->client->renderInfo.eyePoint ) && (ent->client->ps.viewEntity <= 0 || ent->client->ps.viewEntity >= ENTITYNUM_WORLD) ) {//Actual tag_head eyespot! //FIXME: Stasis aliens may have a problem here... VectorCopy( ent->client->renderInfo.eyePoint, point ); if ( ent->client->NPC_class == CLASS_ATST ) {//adjust up some point[2] += 28;//magic number :) } if ( ent->NPC ) {//always aim from the center of my bbox, so we don't wiggle when we lean forward or backwards point[0] = ent->currentOrigin[0]; point[1] = ent->currentOrigin[1]; } else if ( !ent->s.number ) { SubtractLeanOfs( ent, point ); } //NOTE: automatically takes leaning into account! } else { VectorCopy ( ent->currentOrigin, point ); if ( ent->client ) { point[2] += ent->client->ps.viewheight; } //AddLeanOfs ( ent, point ); } break; //FIXME: implement... //case SPOT_CHEST: //Returns point 3/4 from tag_torso to tag_head? //break; case SPOT_LEGS: VectorCopy ( ent->currentOrigin, point ); point[2] += (ent->mins[2] * 0.5); break; case SPOT_WEAPON: if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles )) { AngleVectors( ent->NPC->shootAngles, forward, right, up ); } else { AngleVectors( ent->client->ps.viewangles, forward, right, up ); } CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point, 0 ); //NOTE: automatically takes leaning into account! break; case SPOT_GROUND: // if entity is on the ground, just use it's absmin if ( ent->s.groundEntityNum != -1 ) { VectorCopy( ent->currentOrigin, point ); point[2] = ent->absmin[2]; break; } // if it is reasonably close to the ground, give the point underneath of it VectorCopy( ent->currentOrigin, start ); start[2] = ent->absmin[2]; VectorCopy( start, end ); end[2] -= 64; gi.trace( &tr, start, ent->mins, ent->maxs, end, ent->s.number, MASK_PLAYERSOLID, (EG2_Collision)0, 0); if ( tr.fraction < 1.0 ) { VectorCopy( tr.endpos, point); break; } // otherwise just use the origin VectorCopy( ent->currentOrigin, point ); break; default: VectorCopy ( ent->currentOrigin, point ); break; } }
/* ================ CM_VectorDistanceSquared ================ */ float CM_VectorDistanceSquared(vec3_t p1, vec3_t p2) { vec3_t dir; VectorSubtract(p2, p1, dir); return VectorLengthSquared(dir); }
void S_Base_AddLoopingSound(const vec3_t origin, const vec3_t velocity, int range, sfxHandle_t sfxHandle, int volume, int soundTime) { sfx_t *sfx; if (!s_soundStarted || s_soundMuted || !volume) { return; } if (numLoopSounds >= MAX_LOOP_SOUNDS) { return; } if (sfxHandle < 0 || sfxHandle >= numSfx) { Com_Printf(S_COLOR_YELLOW "S_AddLoopingSound: handle %i out of range\n", sfxHandle); return; } sfx = &knownSfx[sfxHandle]; if (sfx->inMemory == qfalse) { S_memoryLoad(sfx); } if (!sfx->soundLength) { Com_Error(ERR_DROP, "%s has length 0", sfx->soundName); } VectorCopy(origin, loopSounds[numLoopSounds].origin); VectorCopy(velocity, loopSounds[numLoopSounds].velocity); loopSounds[numLoopSounds].startSample = soundTime % sfx->soundLength; loopSounds[numLoopSounds].active = qtrue; loopSounds[numLoopSounds].kill = qtrue; loopSounds[numLoopSounds].doppler = qfalse; loopSounds[numLoopSounds].oldDopplerScale = 1; loopSounds[numLoopSounds].dopplerScale = 1; loopSounds[numLoopSounds].sfx = sfx; loopSounds[numLoopSounds].range = range ? range : SOUND_RANGE_DEFAULT; loopSounds[numLoopSounds].loudUnderWater = (volume & 1 << UNDERWATER_BIT) != 0; if (volume > 65535) { volume = 65535; } else if (volume < 0) { volume = 0; } loopSounds[numLoopSounds].volume = (int)((float)volume * s_volCurrent); if (s_doppler->integer && VectorLengthSquared(velocity) > 0) { vec3_t out; float lena, lenb; // don't do the doppler effect when trumpets of train and station are at the same position if (entityPositions[listener_number] != entityPositions[numLoopSounds]) { loopSounds[numLoopSounds].doppler = qtrue; } else { loopSounds[numLoopSounds].doppler = qfalse; } lena = DistanceSquared(entityPositions[listener_number], entityPositions[numLoopSounds]); VectorAdd(entityPositions[numLoopSounds], loopSounds[numLoopSounds].velocity, out); lenb = DistanceSquared(entityPositions[listener_number], out); if ((loopSounds[numLoopSounds].framenum + 1) != cls.framecount) { loopSounds[numLoopSounds].oldDopplerScale = 1; } else { loopSounds[numLoopSounds].oldDopplerScale = loopSounds[numLoopSounds].dopplerScale; } if (lena == 0) // div/0 { loopSounds[numLoopSounds].dopplerScale = 1; // no doppler } else { loopSounds[numLoopSounds].dopplerScale = lenb / (lena * 100); } if (loopSounds[numLoopSounds].dopplerScale <= 1) { loopSounds[numLoopSounds].doppler = qfalse; // don't bother doing the math } else if (loopSounds[numLoopSounds].dopplerScale > MAX_DOPPLER_SCALE) { loopSounds[numLoopSounds].dopplerScale = MAX_DOPPLER_SCALE; } } loopSounds[numLoopSounds].framenum = cls.framecount; numLoopSounds++; }
/* ================= MakeMeshNormals Handles all the complicated wrapping and degenerate cases ================= */ static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { int i, j, k, dist; vec3_t normal; vec3_t sum; int count; vec3_t base; vec3_t delta; int x, y; drawVert_t *dv; vec3_t around[8], temp; qboolean good[8]; qboolean wrapWidth, wrapHeight; float len; static int neighbors[8][2] = { {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} }; wrapWidth = qfalse; for ( i = 0 ; i < height ; i++ ) { VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == height ) { wrapWidth = qtrue; } wrapHeight = qfalse; for ( i = 0 ; i < width ; i++ ) { VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta ); len = VectorLengthSquared( delta ); if ( len > 1.0 ) { break; } } if ( i == width) { wrapHeight = qtrue; } for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { count = 0; dv = &ctrl[j][i]; VectorCopy( dv->xyz, base ); for ( k = 0 ; k < 8 ; k++ ) { VectorClear( around[k] ); good[k] = qfalse; for ( dist = 1 ; dist <= 3 ; dist++ ) { x = i + neighbors[k][0] * dist; y = j + neighbors[k][1] * dist; if ( wrapWidth ) { if ( x < 0 ) { x = width - 1 + x; } else if ( x >= width ) { x = 1 + x - width; } } if ( wrapHeight ) { if ( y < 0 ) { y = height - 1 + y; } else if ( y >= height ) { y = 1 + y - height; } } if ( x < 0 || x >= width || y < 0 || y >= height ) { break; // edge of patch } VectorSubtract( ctrl[y][x].xyz, base, temp ); if ( VectorNormalize2( temp, temp ) == 0 ) { continue; // degenerate edge, get more dist } else { good[k] = qtrue; VectorCopy( temp, around[k] ); break; // good edge } } } VectorClear( sum ); for ( k = 0 ; k < 8 ; k++ ) { if ( !good[k] || !good[(k+1)&7] ) { continue; // didn't get two points } CrossProduct( around[(k+1)&7], around[k], normal ); if ( VectorNormalize2( normal, normal ) == 0 ) { continue; } VectorAdd( normal, sum, sum ); count++; } if ( count == 0 ) { //printf("bad normal\n"); count = 1; } VectorNormalize2( sum, dv->normal ); } } }
// now we're at poly level, check each model space transformed poly against the model world transfomed ray static bool G2_RadiusTracePolys( const mdxmSurface_t *surface, CTraceSurface &TS ) { int i,j; vec3_t basis1; vec3_t basis2; vec3_t taxis; vec3_t saxis; basis2[0]=0.0f; basis2[1]=0.0f; basis2[2]=1.0f; vec3_t v3RayDir; VectorSubtract(TS.rayEnd, TS.rayStart, v3RayDir); CrossProduct(v3RayDir,basis2,basis1); if (DotProduct(basis1,basis1)<.1f) { basis2[0]=0.0f; basis2[1]=1.0f; basis2[2]=0.0f; CrossProduct(v3RayDir,basis2,basis1); } CrossProduct(v3RayDir,basis1,basis2); // Give me a shot direction not a bunch of zeros :) -Gil // assert(DotProduct(basis1,basis1)>.0001f); // assert(DotProduct(basis2,basis2)>.0001f); VectorNormalize(basis1); VectorNormalize(basis2); const float c=cosf(0);//theta const float s=sinf(0);//theta VectorScale(basis1, 0.5f * c / TS.m_fRadius,taxis); VectorMA(taxis, 0.5f * s / TS.m_fRadius,basis2,taxis); VectorScale(basis1,-0.5f * s /TS.m_fRadius,saxis); VectorMA( saxis, 0.5f * c /TS.m_fRadius,basis2,saxis); const float * const verts = (float *)TS.TransformedVertsArray[surface->thisSurfaceIndex]; const int numVerts = surface->numVerts; int flags=63; //rayDir/=lengthSquared(raydir); const float f = VectorLengthSquared(v3RayDir); v3RayDir[0]/=f; v3RayDir[1]/=f; v3RayDir[2]/=f; for ( j = 0; j < numVerts; j++ ) { const int pos=j*5; vec3_t delta; delta[0]=verts[pos+0]-TS.rayStart[0]; delta[1]=verts[pos+1]-TS.rayStart[1]; delta[2]=verts[pos+2]-TS.rayStart[2]; const float s=DotProduct(delta,saxis)+0.5f; const float t=DotProduct(delta,taxis)+0.5f; const float u=DotProduct(delta,v3RayDir); int vflags=0; if (s>0) { vflags|=1; } if (s<1) { vflags|=2; } if (t>0) { vflags|=4; } if (t<1) { vflags|=8; } if (u>0) { vflags|=16; } if (u<1) { vflags|=32; } vflags=(~vflags); flags&=vflags; GoreVerts[j].flags=vflags; } if (flags) { return false; // completely off the gore splotch (so presumably hit nothing? -Ste) } const int numTris = surface->numTriangles; const mdxmTriangle_t * const tris = (mdxmTriangle_t *) ((byte *)surface + surface->ofsTriangles); for ( j = 0; j < numTris; j++ ) { assert(tris[j].indexes[0]>=0&&tris[j].indexes[0]<numVerts); assert(tris[j].indexes[1]>=0&&tris[j].indexes[1]<numVerts); assert(tris[j].indexes[2]>=0&&tris[j].indexes[2]<numVerts); flags=63& GoreVerts[tris[j].indexes[0]].flags& GoreVerts[tris[j].indexes[1]].flags& GoreVerts[tris[j].indexes[2]].flags; if (flags) { continue; } else { // we hit a triangle, so init a collision record... // for (i=0; i<MAX_G2_COLLISIONS;i++) { if (TS.collRecMap[i].mEntityNum == -1) { CCollisionRecord &newCol = TS.collRecMap[i]; newCol.mPolyIndex = j; newCol.mEntityNum = TS.entNum; newCol.mSurfaceIndex = surface->thisSurfaceIndex; newCol.mModelIndex = TS.modelIndex; // if (face>0) // { newCol.mFlags = G2_FRONTFACE; // } // else // { // newCol.mFlags = G2_BACKFACE; // } //get normal from triangle const float *A = &verts[(tris[j].indexes[0] * 5)]; const float *B = &verts[(tris[j].indexes[1] * 5)]; const float *C = &verts[(tris[j].indexes[2] * 5)]; vec3_t normal; vec3_t edgeAC, edgeBA; VectorSubtract(C, A, edgeAC); VectorSubtract(B, A, edgeBA); CrossProduct(edgeBA, edgeAC, normal); // transform normal (but don't translate) into world angles TransformPoint(normal, newCol.mCollisionNormal, &worldMatrix); VectorNormalize(newCol.mCollisionNormal); newCol.mMaterial = newCol.mLocation = 0; // exit now if we should if (TS.eG2TraceType == G2_RETURNONHIT) { TS.hitOne = true; return true; } //i don't know the hitPoint, but let's just assume it's the first vert for now... const float *hitPoint = A; vec3_t distVect; VectorSubtract(hitPoint, TS.rayStart, distVect); newCol.mDistance = VectorLength(distVect); // put the hit point back into world space TransformAndTranslatePoint(hitPoint, newCol.mCollisionPosition, &worldMatrix); newCol.mBarycentricI = newCol.mBarycentricJ = 0.0f; break; } } if (i==MAX_G2_COLLISIONS) { //assert(i!=MAX_G2_COLLISIONS); // run out of collision record space - happens OFTEN TS.hitOne = true; //force stop recursion return true; // return true to avoid wasting further time, but no hit will result without a record } } } return false; }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( float gravMod ) { vec3_t start_o, start_v; vec3_t down_o, down_v; vec3_t slideMove, stepUpMove; trace_t trace; vec3_t up, down; qboolean cantStepUpFwd, isGiant = qfalse;; int stepSize = STEPSIZE; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_InReboundHold( pm->ps->legsAnim ) ) { gravMod = 0.0f; } if ( PM_SlideMove( gravMod ) == 0 ) { return; // we got exactly where we wanted to go first try }//else Bumped into something, see if we can step over it if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 ) {//Hovering vehicles don't do steps //FIXME: maybe make hovering vehicles go up steps, but not down them? return; } if ( pm->gent && pm->gent->client && (pm->gent->client->NPC_class == CLASS_ATST||pm->gent->client->NPC_class == CLASS_RANCOR) ) { isGiant = qtrue; if ( pm->gent->client->NPC_class == CLASS_RANCOR ) { if ( (pm->gent->spawnflags&1) ) { stepSize = 64;//hack for Mutant Rancor stepping } else { stepSize = 48;//hack for Rancor stepping } } else { stepSize = 70;//hack for AT-ST stepping, slightly taller than a standing stormtrooper } } else if ( pm->maxs[2] <= 0 ) {//short little guys can't go up steps... FIXME: just make this a flag for certain NPCs- especially ones that roll? stepSize = 4; } //Q3Final addition... VectorCopy(start_o, down); down[2] -= stepSize; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {//All our velocity was cancelled sliding return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += stepSize; // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask); if ( trace.allsolid || trace.startsolid || trace.fraction == 0) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } if ( pm->debugLevel ) { G_DebugLine(start_o,trace.endpos,2000,0xffffff,qtrue); } //===Another slidemove forward================================================================================ // try slidemove from this position VectorCopy( trace.endpos, pm->ps->origin ); VectorCopy( start_v, pm->ps->velocity ); cantStepUpFwd = PM_SlideMove( gravMod ); //===Another slidemove forward================================================================================ if ( pm->debugLevel ) { G_DebugLine(trace.endpos,pm->ps->origin,2000,0xffffff,qtrue); } //compare the initial slidemove and this slidemove from a step up position VectorSubtract( down_o, start_o, slideMove ); VectorSubtract( trace.endpos, pm->ps->origin, stepUpMove ); if ( fabs(stepUpMove[0]) < 0.1 && fabs(stepUpMove[1]) < 0.1 && VectorLengthSquared( slideMove ) > VectorLengthSquared( stepUpMove ) ) { //slideMove was better, use it VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { qboolean skipStep = qfalse; // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask); if ( pm->debugLevel ) { G_DebugLine(pm->ps->origin,trace.endpos,2000,0xffffff,qtrue); } if ( g_stepSlideFix->integer ) { if ( pm->ps->clientNum < MAX_CLIENTS && trace.plane.normal[2] < MIN_WALK_NORMAL ) {//normal players cannot step up slopes that are too steep to walk on! vec3_t stepVec; //okay, the step up ends on a slope that it too steep to step up onto, //BUT: //If the step looks like this: // (B)\__ // \_____(A) //Then it might still be okay, so we figure out the slope of the entire move //from (A) to (B) and if that slope is walk-upabble, then it's okay VectorSubtract( trace.endpos, down_o, stepVec ); VectorNormalize( stepVec ); if ( stepVec[2] > (1.0f-MIN_WALK_NORMAL) ) { if ( pm->debugLevel ) { G_DebugLine(down_o,trace.endpos,2000,0x0000ff,qtrue); } skipStep = qtrue; } } } if ( !trace.allsolid && !skipStep ) //normal players cannot step up slopes that are too steep to walk on! { if ( pm->ps->clientNum && isGiant && g_entities[trace.entityNum].client && pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) {//Rancor don't step on clients if ( g_stepSlideFix->integer ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } else if ( pm->ps->clientNum && isGiant && g_entities[trace.entityNum].client && g_entities[trace.entityNum].client->playerTeam == pm->gent->client->playerTeam ) {//AT-ST's don't step up on allies if ( g_stepSlideFix->integer ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { VectorCopy (start_o, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); } } else { VectorCopy( trace.endpos, pm->ps->origin ); if ( g_stepSlideFix->integer ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } } else { if ( g_stepSlideFix->integer ) { VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } } if ( !g_stepSlideFix->integer ) { if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } } /* if(cantStepUpFwd && pm->ps->origin[2] < start_o[2] + stepSize && pm->ps->origin[2] >= start_o[2]) {//We bumped into something we could not step up pm->ps->pm_flags |= PMF_BLOCKED; } else {//We did step up, clear the bumped flag } */ #if 0 // if the down trace can trace back to the original position directly, don't step pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { // use the original move VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); if ( pm->debugLevel ) { Com_Printf("%i:bend\n", c_pmove); } } else #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
void CGCam_FollowUpdate ( void ) { vec3_t center, dir, cameraAngles, vec, focus[MAX_CAMERA_GROUP_SUBJECTS];//No more than 16 subjects in a cameraGroup gentity_t *from = NULL; centity_t *fromCent = NULL; int num_subjects = 0, i; qboolean focused = qfalse; if ( client_camera.cameraGroup && client_camera.cameraGroup[0] ) { //Stay centered in my cameraGroup, if I have one while( NULL != (from = G_Find(from, FOFS(cameraGroup), client_camera.cameraGroup))) { /* if ( from->s.number == client_camera.aimEntNum ) {//This is the misc_camera_focus, we'll be removing this ent altogether eventually continue; } */ if ( num_subjects >= MAX_CAMERA_GROUP_SUBJECTS ) { gi.Printf(S_COLOR_RED"ERROR: Too many subjects in shot composition %s", client_camera.cameraGroup); break; } fromCent = &cg_entities[from->s.number]; if ( !fromCent ) { continue; } focused = qfalse; if ( from->client && client_camera.cameraGroupTag && client_camera.cameraGroupTag[0] && fromCent->gent->ghoul2.size() ) { int newBolt = gi.G2API_AddBolt( &fromCent->gent->ghoul2[from->playerModel], client_camera.cameraGroupTag ); if ( newBolt != -1 ) { mdxaBone_t boltMatrix; vec3_t fromAngles = {0,from->client->ps.legsYaw,0}; gi.G2API_GetBoltMatrix( fromCent->gent->ghoul2, from->playerModel, newBolt, &boltMatrix, fromAngles, fromCent->lerpOrigin, cg.time, cgs.model_draw, fromCent->currentState.modelScale ); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, focus[num_subjects] ); focused = qtrue; } } if ( !focused ) { if ( from->s.pos.trType != TR_STATIONARY ) // if ( from->s.pos.trType == TR_INTERPOLATE ) {//use interpolated origin? if ( !VectorCompare( vec3_origin, fromCent->lerpOrigin ) ) {//hunh? Somehow we've never seen this gentity on the client, so there is no lerpOrigin, so cheat over to the game and use the currentOrigin VectorCopy( from->currentOrigin, focus[num_subjects] ); } else { VectorCopy( fromCent->lerpOrigin, focus[num_subjects] ); } } else { VectorCopy(from->currentOrigin, focus[num_subjects]); } //FIXME: make a list here of their s.numbers instead so we can do other stuff with the list below if ( from->client ) {//Track to their eyes - FIXME: maybe go off a tag? //FIXME: //Based on FOV and distance to subject from camera, pick the point that //keeps eyes 3/4 up from bottom of screen... what about bars? focus[num_subjects][2] += from->client->ps.viewheight; } } if ( client_camera.cameraGroupZOfs ) { focus[num_subjects][2] += client_camera.cameraGroupZOfs; } num_subjects++; } if ( !num_subjects ) // Bad cameragroup { #ifndef FINAL_BUILD gi.Printf(S_COLOR_RED"ERROR: Camera Focus unable to locate cameragroup: %s\n", client_camera.cameraGroup); #endif return; } //Now average all points VectorCopy( focus[0], center ); for( i = 1; i < num_subjects; i++ ) { VectorAdd( focus[i], center, center ); } VectorScale( center, 1.0f/((float)num_subjects), center ); } else { return; } //Need to set a speed to keep a distance from //the subject- fixme: only do this if have a distance //set VectorSubtract( client_camera.subjectPos, center, vec ); client_camera.subjectSpeed = VectorLengthSquared( vec ) * 100.0f / cg.frametime; /* if ( !cg_skippingcin.integer ) { Com_Printf( S_COLOR_RED"org: %s\n", vtos(center) ); } */ VectorCopy( center, client_camera.subjectPos ); VectorSubtract( center, cg.refdef.vieworg, dir );//can't use client_camera.origin because it's not updated until the end of the move. //Get desired angle vectoangles(dir, cameraAngles); if ( client_camera.followInitLerp ) {//Lerping float frac = cg.frametime/100.0f * client_camera.followSpeed/100.f; for( i = 0; i < 3; i++ ) { cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); cameraAngles[i] = AngleNormalize180( client_camera.angles[i] + frac * AngleNormalize180(cameraAngles[i] - client_camera.angles[i]) ); cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } #if 0 Com_Printf( "%s\n", vtos(cameraAngles) ); #endif } else {//Snapping, should do this first time if follow_lerp_to_start_duration is zero //will lerp from this point on client_camera.followInitLerp = qtrue; for( i = 0; i < 3; i++ ) {//normalize so that when we start lerping, it doesn't freak out cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } //So tracker doesn't move right away thinking the first angle change //is the subject moving... FIXME: shouldn't set this until lerp done OR snapped? client_camera.subjectSpeed = 0; } //Point camera to lerp angles /* if ( !cg_skippingcin.integer ) { Com_Printf( "ang: %s\n", vtos(cameraAngles) ); } */ VectorCopy( cameraAngles, client_camera.angles ); }
void CGCam_TrackEntUpdate ( void ) {//FIXME: only do every 100 ms gentity_t *trackEnt = NULL; gentity_t *newTrackEnt = NULL; qboolean reached = qfalse; vec3_t vec; float dist; if ( client_camera.trackEntNum >= 0 && client_camera.trackEntNum < ENTITYNUM_WORLD ) {//We're already heading to a path_corner trackEnt = &g_entities[client_camera.trackEntNum]; VectorSubtract( trackEnt->currentOrigin, client_camera.origin, vec ); dist = VectorLengthSquared( vec ); if ( dist < 256 )//16 squared {//FIXME: who should be doing the using here? G_UseTargets( trackEnt, trackEnt ); reached = qtrue; } } if ( trackEnt && reached ) { if ( trackEnt->target && trackEnt->target[0] ) {//Find our next path_corner newTrackEnt = G_Find( NULL, FOFS(targetname), trackEnt->target ); if ( newTrackEnt ) { if ( newTrackEnt->radius < 0 ) {//Don't bother trying to maintain a radius client_camera.distance = 0; client_camera.speed = client_camera.initSpeed; } else if ( newTrackEnt->radius > 0 ) { client_camera.distance = newTrackEnt->radius; } if ( newTrackEnt->speed < 0 ) {//go back to our default speed client_camera.speed = client_camera.initSpeed; } else if ( newTrackEnt->speed > 0 ) { client_camera.speed = newTrackEnt->speed/10.0f; } } } else {//stop thinking if this is the last one CGCam_TrackDisable(); } } if ( newTrackEnt ) {//Update will lerp this client_camera.info_state |= CAMERA_TRACKING; client_camera.trackEntNum = newTrackEnt->s.number; VectorCopy( newTrackEnt->currentOrigin, client_camera.trackToOrg ); } client_camera.nextTrackEntUpdateTime = cg.time + 100; }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( int psIndex, int clientNum, vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes[psIndex] = CM_WriteAreaBits( frame->areabits[psIndex], clientarea ); clientpvs = CM_ClusterPVS (clientcluster); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to a given mask of clients if ( ent->r.svFlags & SVF_CLIENTMASK ) { if ( !Com_ClientListContains( &ent->r.sendClients, clientNum ) ) continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // limit based on distance if ( ent->r.cullDistance ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->r.cullDistance * ent->r.cullDistance ) { continue; } } // broadcast entities are always sent if ( ent->r.svFlags & SVF_BROADCAST ) { SV_AddEntToSnapshot( frame, svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // visibility dummies if ( ent->r.svFlags & SVF_VISDUMMY ) { sharedEntity_t *ment = NULL; // find master ment = SV_GentityNum( ent->r.visDummyNum ); if ( ment ) { svEntity_t *master = NULL; master = SV_SvEntityForGentity( ment ); if ( master->snapshotCounter == sv.snapshotCounter || !ment->r.linked ) { continue; } SV_AddEntToSnapshot( frame, master, ment, eNums ); } // master needs to be added, but not this dummy ent continue; } else if ( ent->r.svFlags & SVF_VISDUMMY_MULTIPLE ) { int h; sharedEntity_t *ment = NULL; svEntity_t *master = NULL; for ( h = 0; h < sv.num_entities; h++ ) { ment = SV_GentityNum( h ); if ( ment == ent ) { continue; } if ( ment ) { master = SV_SvEntityForGentity( ment ); } else { continue; } if ( !ment->r.linked ) { continue; } if ( ment->s.number != h ) { Com_DPrintf( "FIXING vis dummy multiple ment->S.NUMBER!!!\n" ); ment->s.number = h; } if ( ment->r.svFlags & SVF_NOCLIENT ) { continue; } if ( master->snapshotCounter == sv.snapshotCounter ) { continue; } if ( ment->r.visDummyNum == ent->s.number ) { SV_AddEntToSnapshot( frame, master, ment, eNums ); } } // masters need to be added, but not this dummy ent continue; } // add it SV_AddEntToSnapshot( frame, svEnt, ent, eNums ); // if it's a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->r.portalCullDistance ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->r.portalCullDistance * ent->r.portalCullDistance ) { continue; } } SV_AddEntitiesVisibleFromPoint( psIndex, clientNum, ent->s.origin2, frame, eNums, qtrue ); } }
/* ================== S_Base_AddLoopingSound Called during entity generation for a frame Include velocity in case I get around to doing doppler... ================== */ static void S_Base_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfxHandle ) { sfx_t *sfx; if ( !s_soundStarted || s_soundMuted ) { return; } if (entityNum >= MAX_LOOP_SOUNDS || entityNum < 0) { Com_Printf(S_COLOR_YELLOW "%s() invalid entity number %d\n", __FUNCTION__, entityNum); return; } if ( sfxHandle < 0 || sfxHandle >= s_numSfx ) { Com_Printf( S_COLOR_YELLOW "%s() handle %i out of range\n", __FUNCTION__, sfxHandle ); return; } sfx = &s_knownSfx[ sfxHandle ]; //Com_Printf("S_Base_AddLoopingSound %d : %s velocity %f\n", sfxHandle, sfx->soundName, VectorLengthSquared(velocity)); if (sfx->inMemory == qfalse) { S_memoryLoad(sfx); } if ( !sfx->soundLength ) { Com_Error( ERR_DROP, "%s has length 0", sfx->soundName ); } VectorCopy( origin, loopSounds[entityNum].origin ); VectorCopy( velocity, loopSounds[entityNum].velocity ); loopSounds[entityNum].active = qtrue; loopSounds[entityNum].kill = qtrue; loopSounds[entityNum].doppler = qfalse; loopSounds[entityNum].oldDopplerScale = 1.0; loopSounds[entityNum].dopplerScale = 1.0; loopSounds[entityNum].sfx = sfx; if (s_doppler->integer && VectorLengthSquared(velocity)>0.0) { vec3_t out; float lena, lenb; //Com_Printf("distance: %f\n", Distance(loopSounds[listener_number].origin, loopSounds[entityNum].origin)); //Com_Printf("distance: %f\n", Distance(listener_origin, loopSounds[entityNum].origin)); loopSounds[entityNum].doppler = qtrue; //lena = DistanceSquared(loopSounds[listener_number].origin, loopSounds[entityNum].origin); lena = DistanceSquared(listener_origin, loopSounds[entityNum].origin); VectorAdd(loopSounds[entityNum].origin, loopSounds[entityNum].velocity, out); //lenb = DistanceSquared(loopSounds[listener_number].origin, out); lenb = DistanceSquared(listener_origin, out); if ((loopSounds[entityNum].framenum+1) != cls.framecount) { loopSounds[entityNum].oldDopplerScale = 1.0; } else { loopSounds[entityNum].oldDopplerScale = loopSounds[entityNum].dopplerScale; } loopSounds[entityNum].dopplerScale = lenb/(lena*100); if (loopSounds[entityNum].dopplerScale<=1.0) { loopSounds[entityNum].doppler = qfalse; // don't bother doing the math } else if (loopSounds[entityNum].dopplerScale>MAX_DOPPLER_SCALE) { //} else if (loopSounds[entityNum].dopplerScale > 2.0) { loopSounds[entityNum].dopplerScale = MAX_DOPPLER_SCALE; //loopSounds[entityNum].dopplerScale = 2.0; } //if (loopSounds[entityNum].doppler) { //Com_Printf("dopplerscale %f\n", loopSounds[entityNum].dopplerScale); //} //if (loopSounds[entityNum].doppler) { //Com_Printf("doppler set: %d\n", loopSounds[entityNum].doppler); //} } loopSounds[entityNum].framenum = cls.framecount; }
void GCam_FollowUpdate ( void ) { vec3_t center, dir, cameraAngles, vec, focus[MAX_CAMERA_GROUP_SUBJECTS];//No more than 16 subjects in a cameraGroup gentity_t *from = NULL; //centity_t *fromCent = NULL; int num_subjects = 0, i; qboolean focused = qfalse; if ( client_camera.cameraGroup[0] == -1 ) {//follow disabled return; } for( i = 0; i < MAX_CAMERA_GROUP_SUBJECTS; i++ ) { //fromCent = &cg_entities[client_camera.cameraGroup[i]]; from = &g_entities[client_camera.cameraGroup[i]]; if ( !from ) { continue; } focused = qfalse; if ( (from->s.eType == ET_PLAYER || from->s.eType == ET_NPC || from->s.number < MAX_CLIENTS) && client_camera.cameraGroupTag && client_camera.cameraGroupTag[0] ) { int newBolt = trap_G2API_AddBolt( &from->ghoul2, 0, client_camera.cameraGroupTag ); if ( newBolt != -1 ) { mdxaBone_t boltMatrix; vec3_t angle; VectorSet(angle, 0, from->client->ps.viewangles[YAW], 0); trap_G2API_GetBoltMatrix( &from->ghoul2, 0, newBolt, &boltMatrix, angle, from->client->ps.origin, level.time, NULL, from->modelScale ); BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, focus[num_subjects] ); focused = qtrue; } } if ( !focused ) { VectorCopy(from->r.currentOrigin, focus[num_subjects]); if ( from->s.eType == ET_PLAYER || from->s.eType == ET_NPC || from->s.number < MAX_CLIENTS ) {//Track to their eyes - FIXME: maybe go off a tag? focus[num_subjects][2] += from->client->ps.viewheight; } } if ( client_camera.cameraGroupZOfs ) { focus[num_subjects][2] += client_camera.cameraGroupZOfs; } num_subjects++; } if ( !num_subjects ) // Bad cameragroup { #ifndef FINAL_BUILD G_Printf(S_COLOR_RED"ERROR: Camera Focus unable to locate cameragroup: %s\n", client_camera.cameraGroup); #endif return; } //Now average all points VectorCopy( focus[0], center ); for( i = 1; i < num_subjects; i++ ) { VectorAdd( focus[i], center, center ); } VectorScale( center, 1.0f/((float)num_subjects), center ); //Need to set a speed to keep a distance from //the subject- fixme: only do this if have a distance //set VectorSubtract( client_camera.subjectPos, center, vec ); client_camera.subjectSpeed = VectorLengthSquared( vec ) * 100.0f / g_TimeSinceLastFrame; VectorCopy( center, client_camera.subjectPos ); VectorSubtract( center, camerapos, dir );//can't use client_camera.origin because it's not updated until the end of the move. //Get desired angle vectoangles(dir, cameraAngles); if ( client_camera.followInitLerp ) {//Lerping float frac = g_TimeSinceLastFrame/100.0f * client_camera.followSpeed/100.f; for( i = 0; i < 3; i++ ) { cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); cameraAngles[i] = AngleNormalize180( client_camera.angles[i] + frac * AngleNormalize180(cameraAngles[i] - client_camera.angles[i]) ); cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } } else {//Snapping, should do this first time if follow_lerp_to_start_duration is zero //will lerp from this point on client_camera.followInitLerp = qtrue; for( i = 0; i < 3; i++ ) {//normalize so that when we start lerping, it doesn't freak out cameraAngles[i] = AngleNormalize180( cameraAngles[i] ); } //So tracker doesn't move right away thinking the first angle change //is the subject moving... FIXME: shouldn't set this until lerp done OR snapped? client_camera.subjectSpeed = 0; } VectorCopy( cameraAngles, client_camera.angles ); }
/* ** SurfIsOffscreen ** ** Determines if a surface is completely offscreen. */ static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) { float shortest = 100000000; int entityNum; int numTriangles; shader_t *shader; int fogNum; int dlighted; vec4_t clip, eye; int i; unsigned int pointOr = 0; unsigned int pointAnd = (unsigned int)~0; if ( glConfig.smpActive ) { // FIXME! we can't do RB_BeginSurface/RB_EndSurface stuff with smp! return qfalse; } R_RotateForViewer(); R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); RB_BeginSurface( shader, fogNum ); rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); assert( tess.numVertexes < 128 ); for ( i = 0; i < tess.numVertexes; i++ ) { int j; unsigned int pointFlags = 0; R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); for ( j = 0; j < 3; j++ ) { if ( clip[j] >= clip[3] ) { pointFlags |= (1 << (j*2)); } else if ( clip[j] <= -clip[3] ) { pointFlags |= ( 1 << (j*2+1)); } } pointAnd &= pointFlags; pointOr |= pointFlags; } // trivially reject if ( pointAnd ) { return qtrue; } // determine if this surface is backfaced and also determine the distance // to the nearest vertex so we can cull based on portal range. Culling // based on vertex distance isn't 100% correct (we should be checking for // range to the surface), but it's good enough for the types of portals // we have in the game right now. numTriangles = tess.numIndexes / 3; for ( i = 0; i < tess.numIndexes; i += 3 ) { vec3_t normal; float len; VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); len = VectorLengthSquared( normal ); // lose the sqrt if ( len < shortest ) { shortest = len; } if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) { numTriangles--; } } if ( !numTriangles ) { return qtrue; } // mirrors can early out at this point, since we don't do a fade over distance // with them (although we could) if ( IsMirror( drawSurf, entityNum ) ) { return qfalse; } if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) { return qtrue; } return qfalse; }
static void SVT3_AddEntitiesVisibleFromPoint( int clientNum, const vec3_t origin, q3clientSnapshot_t* frame, snapshotEntityNumbers_t* eNums, bool portal, bool localClient ) { // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } int leafnum = CM_PointLeafnum( origin ); int clientarea = CM_LeafArea( leafnum ); int clientcluster = CM_LeafCluster( leafnum ); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); byte* clientpvs = CM_ClusterPVS( clientcluster ); idEntity3* playerEnt = SVT3_EntityNum( clientNum ); if ( playerEnt->GetSvFlagSelfPortal() ) { SVT3_AddEntitiesVisibleFromPoint( clientNum, playerEnt->GetOrigin2(), frame, eNums, portal, localClient ); } int l; for ( int e = 0; e < sv.q3_num_entities; e++ ) { idEntity3* ent = SVT3_EntityNum( e ); // never send entities that aren't linked in if ( !ent->GetLinked() ) { continue; } if ( ent->GetNumber() != e ) { common->DPrintf( "FIXING ENT->S.NUMBER!!!\n" ); ent->SetNumber( e ); } // entities can be flagged to explicitly not be sent to the client if ( ent->GetSvFlags() & Q3SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->GetSvFlagSingleClient() ) { if ( ent->GetSingleClient() != clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->GetSvFlagNotSingleClient() ) { if ( ent->GetSingleClient() == clientNum ) { continue; } } // entities can be flagged to be sent to a given mask of clients if ( ent->GetSvFlagClientMask() ) { if ( clientNum >= 32 ) { common->Error( "Q3SVF_CLIENTMASK: cientNum > 32\n" ); } if ( ~ent->GetSingleClient() & ( 1 << clientNum ) ) { continue; } } q3svEntity_t* svEnt = &sv.q3_svEntities[ e ]; // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.q3_snapshotCounter ) { continue; } // if this client is viewing from a camera, only add ents visible from portal ents if ( playerEnt->GetEFlagViewingCamera() && !portal ) { if ( ent->GetSvFlags() & Q3SVF_PORTAL ) { SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); SVT3_AddEntitiesVisibleFromPoint( clientNum, ent->GetOrigin2(), frame, eNums, true, localClient ); } continue; } // broadcast entities are always sent if ( ent->GetSvFlags() & Q3SVF_BROADCAST ) { SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); continue; } byte* bitvector = clientpvs; // Gordon: just check origin for being in pvs, ignore bmodel extents if ( ent->GetSvFlagIgnoreBModelExtents() ) { if ( bitvector[ svEnt->originCluster >> 3 ] & ( 1 << ( svEnt->originCluster & 7 ) ) ) { SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); } continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { goto notVisible; // blocked by a door } } // check individual leafs if ( !svEnt->numClusters ) { goto notVisible; } l = 0; int i; for ( i = 0; i < svEnt->numClusters; i++ ) { l = svEnt->clusternums[ i ]; if ( bitvector[ l >> 3 ] & ( 1 << ( l & 7 ) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for (; l <= svEnt->lastCluster; l++ ) { if ( bitvector[ l >> 3 ] & ( 1 << ( l & 7 ) ) ) { break; } } if ( l == svEnt->lastCluster ) { goto notVisible; // not visible } } else { goto notVisible; } } //----(SA) added "visibility dummies" if ( ent->GetSvFlags() & WOLFSVF_VISDUMMY ) { //find master; idEntity3* ment = SVT3_EntityNum( ent->GetOtherEntityNum() ); q3svEntity_t* master = &sv.q3_svEntities[ ent->GetOtherEntityNum() ]; if ( master->snapshotCounter == sv.q3_snapshotCounter || !ment->GetLinked() ) { goto notVisible; } SVT3_AddEntToSnapshot( clientNum, master, ment, eNums ); // master needs to be added, but not this dummy ent goto notVisible; } else if ( ent->GetSvFlags() & WOLFSVF_VISDUMMY_MULTIPLE ) { { for ( int h = 0; h < sv.q3_num_entities; h++ ) { idEntity3* ment = SVT3_EntityNum( h ); if ( ment == ent ) { continue; } q3svEntity_t* master = &sv.q3_svEntities[ h ]; if ( !ment->GetLinked() ) { continue; } if ( ment->GetNumber() != h ) { common->DPrintf( "FIXING vis dummy multiple ment->S.NUMBER!!!\n" ); ment->SetNumber( h ); } if ( ment->GetSvFlags() & Q3SVF_NOCLIENT ) { continue; } if ( master->snapshotCounter == sv.q3_snapshotCounter ) { continue; } if ( ment->GetOtherEntityNum() == ent->GetNumber() ) { SVT3_AddEntToSnapshot( clientNum, master, ment, eNums ); } } goto notVisible; } } // add it SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->GetSvFlags() & Q3SVF_PORTAL ) { if ( ent->GetGeneric1() ) { vec3_t dir; VectorSubtract( ent->GetOrigin(), origin, dir ); if ( VectorLengthSquared( dir ) > ( float )ent->GetGeneric1() * ent->GetGeneric1() ) { continue; } } SVT3_AddEntitiesVisibleFromPoint( clientNum, ent->GetOrigin2(), frame, eNums, true, localClient ); } continue; notVisible: // Ridah, if this entity has changed events, then send it regardless of whether we can see it or not // DHM - Nerve :: not in multiplayer please if ( GGameType & ( GAME_WolfSP | GAME_WolfMP ) && svt3_gametype->integer == Q3GT_SINGLE_PLAYER && localClient ) { if ( ent->GetEventTime() == svs.q3_time ) { ent->SetEFlagNoDraw(); // don't draw, just process event SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); } else if ( ent->GetEType() == Q3ET_PLAYER ) { // keep players around if they are alive and active (so sounds dont get messed up) if ( !ent->GetEFlagDead() ) { ent->SetEFlagNoDraw(); // don't draw, just process events and sounds SVT3_AddEntToSnapshot( clientNum, svEnt, ent, eNums ); } } } }
/* ================== PM_StepSlideMove ================== */ void PM_StepSlideMove( float gravMod ) { vec3_t start_o, start_v; vec3_t down_o, down_v; vec3_t slideMove, stepUpMove; trace_t trace; vec3_t up, down; qboolean cantStepUpFwd, isATST = qfalse;; int stepSize = STEPSIZE; VectorCopy (pm->ps->origin, start_o); VectorCopy (pm->ps->velocity, start_v); if ( PM_SlideMove( gravMod ) == 0 ) { return; // we got exactly where we wanted to go first try }//else Bumped into something, see if we can step over it if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST) { isATST = qtrue; stepSize = 66;//hack for AT-ST stepping, slightly taller than a standing stormtrooper } else if ( pm->maxs[2] <= 0 ) {//short little guys can't go up steps... FIXME: just make this a flag for certain NPCs- especially ones that roll? stepSize = 4; } //Q3Final addition... VectorCopy(start_o, down); down[2] -= stepSize; pm->trace (&trace, start_o, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, G2_NOCOLLIDE, 0); VectorSet(up, 0, 0, 1); // never step up when you still have up velocity if ( pm->ps->velocity[2] > 0 && (trace.fraction == 1.0 || DotProduct(trace.plane.normal, up) < 0.7)) { return; } if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) {//All our velocity was cancelled sliding return; } VectorCopy (pm->ps->origin, down_o); VectorCopy (pm->ps->velocity, down_v); VectorCopy (start_o, up); up[2] += stepSize; // test the player position if they were a stepheight higher pm->trace (&trace, start_o, pm->mins, pm->maxs, up, pm->ps->clientNum, pm->tracemask, G2_NOCOLLIDE, 0); if ( trace.allsolid || trace.startsolid || trace.fraction == 0) { if ( pm->debugLevel ) { Com_Printf("%i:bend can't step\n", c_pmove); } return; // can't step up } // try slidemove from this position VectorCopy (trace.endpos, pm->ps->origin); VectorCopy (start_v, pm->ps->velocity); cantStepUpFwd = PM_SlideMove( gravMod ); //compare the initial slidemove and this slidemove from a step up position VectorSubtract( down_o, start_o, slideMove ); VectorSubtract( trace.endpos, pm->ps->origin, stepUpMove ); if ( fabs(stepUpMove[0]) < 0.1 && fabs(stepUpMove[1]) < 0.1 && VectorLengthSquared( slideMove ) > VectorLengthSquared( stepUpMove ) ) { //slideMove was better, use it VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); } else { // push down the final amount VectorCopy (pm->ps->origin, down); down[2] -= stepSize; pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, pm->tracemask, G2_NOCOLLIDE, 0); if ( !trace.allsolid ) { if ( pm->ps->clientNum && isATST && g_entities[trace.entityNum].client && g_entities[trace.entityNum].client->playerTeam == pm->gent->client->playerTeam ) {//AT-ST's don't step up on allies } else { VectorCopy( trace.endpos, pm->ps->origin ); } } if ( trace.fraction < 1.0 ) { PM_ClipVelocity( pm->ps->velocity, trace.plane.normal, pm->ps->velocity, OVERCLIP ); } } /* if(cantStepUpFwd && pm->ps->origin[2] < start_o[2] + stepSize && pm->ps->origin[2] >= start_o[2]) {//We bumped into something we could not step up pm->ps->pm_flags |= PMF_BLOCKED; } else {//We did step up, clear the bumped flag pm->ps->pm_flags &= ~PMF_BUMPED; } */ #if 0 // if the down trace can trace back to the original position directly, don't step pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, start_o, pm->ps->clientNum, pm->tracemask); if ( trace.fraction == 1.0 ) { // use the original move VectorCopy (down_o, pm->ps->origin); VectorCopy (down_v, pm->ps->velocity); if ( pm->debugLevel ) { Com_Printf("%i:bend\n", c_pmove); } } else #endif { // use the step move float delta; delta = pm->ps->origin[2] - start_o[2]; if ( delta > 2 ) { if ( delta < 7 ) { PM_AddEvent( EV_STEP_4 ); } else if ( delta < 11 ) { PM_AddEvent( EV_STEP_8 ); } else if ( delta < 15 ) { PM_AddEvent( EV_STEP_12 ); } else { PM_AddEvent( EV_STEP_16 ); } } if ( pm->debugLevel ) { Com_Printf("%i:stepped\n", c_pmove); } } }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); c_fullsend = 0; for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } svEnt = SV_SvEntityForGentity( ent ); if ( sv.gentitiesMV != NULL && sv.gentitySizeMV > 0 ) { mvsharedEntity_t *mvEnt = MV_EntityNum(e); if ( VM_MVAPILevel( gvm ) >= 2 ) { // MV entities can be flagged to be sent only to // spectators or non-spectators if ( frame->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR || (frame->ps.pm_flags & PMF_FOLLOW) ) { if ( mvEnt->mvFlags & MVF_NOSPEC ) continue; } else { if ( mvEnt->mvFlags & MVF_SPECONLY ) continue; } } // MV entities can be flagged to be sent only to specific // clients (can't filter following spectators this way) if ( mvEnt->snapshotIgnore[frame->ps.clientNum] ) continue; else if ( mvEnt->snapshotEnforce[frame->ps.clientNum] ) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->r.svFlags & SVF_BROADCAST || (e == frame->ps.clientNum) || (ent->r.broadcastClients[frame->ps.clientNum/32] & (1<<(frame->ps.clientNum%32)))) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } }
/* =============== SV_AddEntitiesVisibleFromPoint =============== */ static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to a given mask of clients if ( ent->r.svFlags & SVF_CLIENTMASK ) { if (frame->ps.clientNum >= 32) Com_Error( ERR_DROP, "SVF_CLIENTMASK: clientNum >= 32" ); if (~ent->r.singleClient & (1 << frame->ps.clientNum)) continue; } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent if ( ent->r.svFlags & SVF_BROADCAST ) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if it's a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } }
//----------------------------------------------------- static qboolean turretG2_find_enemies( gentity_t *self ) //----------------------------------------------------- { qboolean found = qfalse; int i, count; float bestDist = self->radius * self->radius; float enemyDist; vec3_t enemyDir, org, org2; qboolean foundClient = qfalse; gentity_t *entity_list[MAX_GENTITIES], *target, *bestTarget = NULL; if ( self->aimDebounceTime > level.time ) // time since we've been shut off { // We were active and alert, i.e. had an enemy in the last 3 secs if ( self->painDebounceTime < level.time ) { if ( !(self->spawnflags&SPF_TURRETG2_TURBO) ) { G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" )); } self->painDebounceTime = level.time + 1000; } } VectorCopy( self->r.currentOrigin, org2 ); if ( self->spawnflags & 2 ) { org2[2] += 20; } else { org2[2] -= 20; } count = G_RadiusList( org2, self->radius, self, qtrue, entity_list ); for ( i = 0; i < count; i++ ) { trace_t tr; target = entity_list[i]; if ( !target->client ) { // only attack clients if ( !(target->flags&FL_BBRUSH)//not a breakable brush || !target->takedamage//is a bbrush, but invincible || (target->NPC_targetname&&self->targetname&&Q_stricmp(target->NPC_targetname,self->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me { continue; } //else: we will shoot at bbrushes! } if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET )) { continue; } if ( target->client && target->client->sess.sessionTeam == TEAM_SPECTATOR ) { continue; } if ( self->alliedTeam ) { if ( target->client ) { if ( target->client->sess.sessionTeam == self->alliedTeam ) { // A bot/client/NPC we don't want to shoot continue; } } else if ( target->teamnodmg == self->alliedTeam ) { // An ent we don't want to shoot continue; } } if ( !trap_InPVS( org2, target->r.currentOrigin )) { continue; } if ( target->client ) { VectorCopy( target->client->renderInfo.eyePoint, org ); } else { VectorCopy( target->r.currentOrigin, org ); } if ( self->spawnflags & 2 ) { org[2] -= 15; } else { org[2] += 5; } trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT ); if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number )) { // Only acquire if have a clear shot, Is it in range and closer than our best? VectorSubtract( target->r.currentOrigin, self->r.currentOrigin, enemyDir ); enemyDist = VectorLengthSquared( enemyDir ); if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current { if ( self->attackDebounceTime < level.time ) { // We haven't fired or acquired an enemy in the last 2 seconds-start-up sound if ( !(self->spawnflags&SPF_TURRETG2_TURBO) ) { G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" )); } // Wind up turrets for a bit self->attackDebounceTime = level.time + 1400; } bestTarget = target; bestDist = enemyDist; found = qtrue; if ( target->client ) { //prefer clients over non-clients foundClient = qtrue; } } } } if ( found ) { /* if ( !self->enemy ) {//just aquired one AddSoundEvent( bestTarget, self->r.currentOrigin, 256, AEL_DISCOVERED ); AddSightEvent( bestTarget, self->r.currentOrigin, 512, AEL_DISCOVERED, 20 ); } */ G_SetEnemy( self, bestTarget ); if ( VALIDSTRING( self->target2 )) { G_UseTargets2( self, self, self->target2 ); } } return found; }
/* ================= R_SubdividePatchToGrid ================= */ srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { int i, j, k, l; drawVert_t prev, next, mid; float len, maxLen; int dir; int t; MAC_STATIC drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; float errorTable[2][MAX_GRID_SIZE]; for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { ctrl[j][i] = points[j*width+i]; } } for ( dir = 0 ; dir < 2 ; dir++ ) { for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) { errorTable[dir][j] = 0; } // horizontal subdivisions for ( j = 0 ; j + 2 < width ; j += 2 ) { // check subdivided midpoints against control points // FIXME: also check midpoints of adjacent patches against the control points // this would basically stitch all patches in the same LOD group together. maxLen = 0; for ( i = 0 ; i < height ; i++ ) { vec3_t midxyz; vec3_t dir; vec3_t projected; float d; // calculate the point on the curve for ( l = 0 ; l < 3 ; l++ ) { midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2 + ctrl[i][j+2].xyz[l] ) * 0.25f; } // see how far off the line it is // using dist-from-line will not account for internal // texture warping, but it gives a lot less polygons than // dist-from-midpoint VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz ); VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir ); VectorNormalize( dir ); d = DotProduct( midxyz, dir ); VectorScale( dir, d, projected ); VectorSubtract( midxyz, projected, midxyz); len = VectorLengthSquared( midxyz ); // we will do the sqrt later if ( len > maxLen ) { maxLen = len; } } maxLen = sqrt(maxLen); // if all the points are on the lines, remove the entire columns if ( maxLen < 0.1f ) { errorTable[dir][j+1] = 999; continue; } // see if we want to insert subdivided columns if ( width + 2 > MAX_GRID_SIZE ) { errorTable[dir][j+1] = 1.0f/maxLen; continue; // can't subdivide any more } if ( maxLen <= r_subdivisions->value ) { errorTable[dir][j+1] = 1.0f/maxLen; continue; // didn't need subdivision } errorTable[dir][j+2] = 1.0f/maxLen; // insert two columns and replace the peak width += 2; for ( i = 0 ; i < height ; i++ ) { LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev ); LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next ); LerpDrawVert( &prev, &next, &mid ); for ( k = width - 1 ; k > j + 3 ; k-- ) { ctrl[i][k] = ctrl[i][k-2]; } ctrl[i][j + 1] = prev; ctrl[i][j + 2] = mid; ctrl[i][j + 3] = next; } // back up and recheck this set again, it may need more subdivision j -= 2; } Transpose( width, height, ctrl ); t = width; width = height; height = t; } // put all the aproximating points on the curve PutPointsOnCurve( ctrl, width, height ); // cull out any rows or columns that are colinear for ( i = 1 ; i < width-1 ; i++ ) { if ( errorTable[0][i] != 999 ) { continue; } for ( j = i+1 ; j < width ; j++ ) { for ( k = 0 ; k < height ; k++ ) { ctrl[k][j-1] = ctrl[k][j]; } errorTable[0][j-1] = errorTable[0][j]; } width--; } for ( i = 1 ; i < height-1 ; i++ ) { if ( errorTable[1][i] != 999 ) { continue; } for ( j = i+1 ; j < height ; j++ ) { for ( k = 0 ; k < width ; k++ ) { ctrl[j-1][k] = ctrl[j][k]; } errorTable[1][j-1] = errorTable[1][j]; } height--; } #if 1 // flip for longest tristrips as an optimization // the results should be visually identical with or // without this step if ( height > width ) { Transpose( width, height, ctrl ); InvertErrorTable( errorTable, width, height ); t = width; width = height; height = t; InvertCtrl( width, height, ctrl ); } #endif // calculate normals MakeMeshNormals( width, height, ctrl ); return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); }
//----------------------------------------------------- void turretG2_base_think( gentity_t *self ) //----------------------------------------------------- { qboolean turnOff = qtrue; float enemyDist; vec3_t enemyDir, org, org2; self->nextthink = level.time + FRAMETIME; if ( self->health <= 0 ) { //dead if (self->spawnflags & SPF_TURRETG2_CANRESPAWN) { //can respawn if ( self->genericValue5 && self->genericValue5 < level.time ) { //we are dead, see if it's time to respawn turretG2_respawn( self ); } } return; } else if ( self->spawnflags & 1 ) { // not turned on turretG2_turnoff( self ); turretG2_aim( self ); // No target self->flags |= FL_NOTARGET; return; } else { // I'm all hot and bothered self->flags &= ~FL_NOTARGET; } if ( self->enemy ) { if ( self->enemy->health < 0 || !self->enemy->inuse ) { self->enemy = NULL; } } if ( self->last_move_time < level.time ) { //MISNOMER: used a enemy recalcing debouncer if ( turretG2_find_enemies( self ) ) { //found one turnOff = qfalse; if ( self->enemy->client ) { //hold on to clients for a min of 3 seconds self->last_move_time = level.time + 3000; } else { //hold less self->last_move_time = level.time + 500; } } } if ( self->enemy != NULL ) { if ( self->enemy->client && self->enemy->client->sess.sessionTeam == TEAM_SPECTATOR ) { //don't keep going after spectators self->enemy = NULL; } else { //FIXME: remain single-minded or look for a new enemy every now and then? // enemy is alive VectorSubtract( self->enemy->r.currentOrigin, self->r.currentOrigin, enemyDir ); enemyDist = VectorLengthSquared( enemyDir ); if ( enemyDist < self->radius * self->radius ) { // was in valid radius if ( trap_InPVS( self->r.currentOrigin, self->enemy->r.currentOrigin ) ) { // Every now and again, check to see if we can even trace to the enemy trace_t tr; if ( self->enemy->client ) { VectorCopy( self->enemy->client->renderInfo.eyePoint, org ); } else { VectorCopy( self->enemy->r.currentOrigin, org ); } VectorCopy( self->r.currentOrigin, org2 ); if ( self->spawnflags & 2 ) { org2[2] += 10; } else { org2[2] -= 10; } trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT ); if ( !tr.allsolid && !tr.startsolid && tr.entityNum == self->enemy->s.number ) { turnOff = qfalse; // Can see our enemy } } } } } if ( turnOff ) { if ( self->bounceCount < level.time ) // bounceCount is used to keep the thing from ping-ponging from on to off { turretG2_turnoff( self ); } } else { // keep our enemy for a minimum of 2 seconds from now self->bounceCount = level.time + 2000 + random() * 150; } turretG2_aim( self ); if ( !turnOff ) { turretG2_head_think( self ); } }
/* ================== CM_Trace ================== */ void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, const vec3_t mins, const vec3_t maxs, clipHandle_t model, const vec3_t origin, int brushmask, int capsule, sphere_t *sphere ) { int i; traceWork_t tw; vec3_t offset; const cmodel_t* cmod = CM_ClipHandleToModel( model ); cm.checkcount++; // for multi-check avoidance c_traces++; // for statistics, may be zeroed // fill in a default trace Com_Memset( &tw, 0, sizeof(tw) ); tw.trace.fraction = 1; // assume it goes the entire distance until shown otherwise VectorCopy(origin, tw.modelOrigin); if (!cm.numNodes) { *results = tw.trace; return; // map not loaded, shouldn't happen } // allow NULL to be passed in for 0,0,0 if ( !mins ) { mins = vec3_origin; } if ( !maxs ) { maxs = vec3_origin; } // set basic parms tw.contents = brushmask; // adjust so that mins and maxs are always symetric, which // avoids some complications with plane expanding of rotated // bmodels for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; tw.size[0][i] = mins[i] - offset[i]; tw.size[1][i] = maxs[i] - offset[i]; tw.start[i] = start[i] + offset[i]; tw.end[i] = end[i] + offset[i]; } // if a sphere is already specified if ( sphere ) { tw.sphere = *sphere; } else { tw.sphere.use = capsule; tw.sphere.radius = ( tw.size[1][0] > tw.size[1][2] ) ? tw.size[1][2]: tw.size[1][0]; tw.sphere.halfheight = tw.size[1][2]; VectorSet( tw.sphere.offset, 0, 0, tw.size[1][2] - tw.sphere.radius ); } tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2]; // tw.offsets[signbits] = vector to apropriate corner from origin tw.offsets[0][0] = tw.size[0][0]; tw.offsets[0][1] = tw.size[0][1]; tw.offsets[0][2] = tw.size[0][2]; tw.offsets[1][0] = tw.size[1][0]; tw.offsets[1][1] = tw.size[0][1]; tw.offsets[1][2] = tw.size[0][2]; tw.offsets[2][0] = tw.size[0][0]; tw.offsets[2][1] = tw.size[1][1]; tw.offsets[2][2] = tw.size[0][2]; tw.offsets[3][0] = tw.size[1][0]; tw.offsets[3][1] = tw.size[1][1]; tw.offsets[3][2] = tw.size[0][2]; tw.offsets[4][0] = tw.size[0][0]; tw.offsets[4][1] = tw.size[0][1]; tw.offsets[4][2] = tw.size[1][2]; tw.offsets[5][0] = tw.size[1][0]; tw.offsets[5][1] = tw.size[0][1]; tw.offsets[5][2] = tw.size[1][2]; tw.offsets[6][0] = tw.size[0][0]; tw.offsets[6][1] = tw.size[1][1]; tw.offsets[6][2] = tw.size[1][2]; tw.offsets[7][0] = tw.size[1][0]; tw.offsets[7][1] = tw.size[1][1]; tw.offsets[7][2] = tw.size[1][2]; // // calculate bounds // if ( tw.sphere.use ) { for ( i = 0 ; i < 3 ; i++ ) { if ( tw.start[i] < tw.end[i] ) { tw.bounds[0][i] = tw.start[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius; tw.bounds[1][i] = tw.end[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius; } else { tw.bounds[0][i] = tw.end[i] - fabs(tw.sphere.offset[i]) - tw.sphere.radius; tw.bounds[1][i] = tw.start[i] + fabs(tw.sphere.offset[i]) + tw.sphere.radius; } } } else { for ( i = 0 ; i < 3 ; i++ ) { if ( tw.start[i] < tw.end[i] ) { tw.bounds[0][i] = tw.start[i] + tw.size[0][i]; tw.bounds[1][i] = tw.end[i] + tw.size[1][i]; } else { tw.bounds[0][i] = tw.end[i] + tw.size[0][i]; tw.bounds[1][i] = tw.start[i] + tw.size[1][i]; } } } // // check for position test special case // if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) { if ( model ) { #ifdef ALWAYS_BBOX_VS_BBOX // bk010201 - FIXME - compile time flag? if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { tw.sphere.use = qfalse; CM_TestInLeaf( &tw, &cmod->leaf ); } else #elif defined(ALWAYS_CAPSULE_VS_CAPSULE) if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { CM_TestCapsuleInCapsule( &tw, model ); } else #endif if ( model == CAPSULE_MODEL_HANDLE ) { if ( tw.sphere.use ) { CM_TestCapsuleInCapsule( &tw, model ); } else { CM_TestBoundingBoxInCapsule( &tw, model ); } } else { CM_TestInLeaf( &tw, &cmod->leaf ); } } else { CM_PositionTest( &tw ); } } else { // // check for point special case // if ( tw.size[0][0] == 0 && tw.size[0][1] == 0 && tw.size[0][2] == 0 ) { tw.isPoint = qtrue; VectorClear( tw.extents ); } else { tw.isPoint = qfalse; tw.extents[0] = tw.size[1][0]; tw.extents[1] = tw.size[1][1]; tw.extents[2] = tw.size[1][2]; } // // general sweeping through world // if ( model ) { #ifdef ALWAYS_BBOX_VS_BBOX if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { tw.sphere.use = qfalse; CM_TraceThroughLeaf( &tw, &cmod->leaf ); } else #elif defined(ALWAYS_CAPSULE_VS_CAPSULE) if ( model == BOX_MODEL_HANDLE || model == CAPSULE_MODEL_HANDLE) { CM_TraceCapsuleThroughCapsule( &tw, model ); } else #endif if ( model == CAPSULE_MODEL_HANDLE ) { if ( tw.sphere.use ) { CM_TraceCapsuleThroughCapsule( &tw, model ); } else { CM_TraceBoundingBoxThroughCapsule( &tw, model ); } } else { CM_TraceThroughLeaf( &tw, &cmod->leaf ); } } else { CM_TraceThroughTree( &tw, 0, 0, 1, tw.start, tw.end ); } } // generate endpos from the original, unmodified start/end if ( tw.trace.fraction == 1 ) { VectorCopy (end, tw.trace.endpos); } else { for ( i=0 ; i<3 ; i++ ) { tw.trace.endpos[i] = start[i] + tw.trace.fraction * (end[i] - start[i]); } } // If allsolid is set (was entirely inside something solid), the plane is not valid. // If fraction == 1.0, we never hit anything, and thus the plane is not valid. // Otherwise, the normal on the plane should have unit length assert(tw.trace.allsolid || tw.trace.fraction == 1.0 || VectorLengthSquared(tw.trace.plane.normal) > 0.9999); *results = tw.trace; }
static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; byte *clientpvs; byte *bitvector; vec3_t difference; float length, radius; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.eFlags & EF_PERMANENT) { // he's permanent, so don't send him down! continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->r.svFlags & SVF_BROADCAST || (e == frame->ps.clientNum) || (ent->r.broadcastClients[frame->ps.clientNum/32] & (1<<(frame->ps.clientNum%32)))) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if (ent->s.isPortalEnt) { //rww - portal entities are always sent as well SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } if (g_svCullDist != -1.0f) { //do a distance cull check VectorAdd(ent->r.absmax, ent->r.absmin, difference); VectorScale(difference, 0.5f, difference); VectorSubtract(origin, difference, difference); length = VectorLength(difference); // calculate the diameter VectorSubtract(ent->r.absmax, ent->r.absmin, difference); radius = VectorLength(difference); if (length-radius >= g_svCullDist) { //then don't add it continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } }
static void CM_TestInLeaf( traceWork_t* tw, const cLeaf_t* leaf ) { int k; int brushnum; cbrush_t *b; cPatch_t *patch; // test box position against all brushes in the leaf for (k=0 ; k<leaf->numLeafBrushes ; k++) { brushnum = cm.leafbrushes[leaf->firstLeafBrush+k]; b = &cm.brushes[brushnum]; if (b->checkcount == cm.checkcount) { continue; // already checked this brush in another leaf } b->checkcount = cm.checkcount; if ( !(b->contents & tw->contents)) { continue; } CM_TestBoxInBrush( tw, b ); if ( tw->trace.allsolid ) { return; } } // test against all patches #ifdef BSPC if (1) { #else if ( !cm_noCurves->integer ) { #endif //BSPC for ( k = 0 ; k < leaf->numLeafSurfaces ; k++ ) { patch = cm.surfaces[ cm.leafsurfaces[ leaf->firstLeafSurface + k ] ]; if ( !patch ) { continue; } if ( patch->checkcount == cm.checkcount ) { continue; // already checked this brush in another leaf } patch->checkcount = cm.checkcount; if ( !(patch->contents & tw->contents)) { continue; } if ( CM_PositionTestInPatchCollide( tw, patch->pc ) ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; tw->trace.contents = patch->contents; return; } } } } /* ================== CM_TestCapsuleInCapsule capsule inside capsule check ================== */ void CM_TestCapsuleInCapsule( traceWork_t *tw, clipHandle_t model ) { int i; vec3_t mins, maxs; vec3_t top, bottom; vec3_t p1, p2, tmp; vec3_t offset, symetricSize[2]; float radius, halfwidth, halfheight, offs, r; CM_ModelBounds(model, mins, maxs); VectorAdd(tw->start, tw->sphere.offset, top); VectorSubtract(tw->start, tw->sphere.offset, bottom); for ( i = 0 ; i < 3 ; i++ ) { offset[i] = ( mins[i] + maxs[i] ) * 0.5; symetricSize[0][i] = mins[i] - offset[i]; symetricSize[1][i] = maxs[i] - offset[i]; } halfwidth = symetricSize[ 1 ][ 0 ]; halfheight = symetricSize[ 1 ][ 2 ]; radius = ( halfwidth > halfheight ) ? halfheight : halfwidth; offs = halfheight - radius; r = Square(tw->sphere.radius + radius); // check if any of the spheres overlap VectorCopy(offset, p1); p1[2] += offs; VectorSubtract(p1, top, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } VectorSubtract(p1, bottom, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } VectorCopy(offset, p2); p2[2] -= offs; VectorSubtract(p2, top, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } VectorSubtract(p2, bottom, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } // if between cylinder up and lower bounds if ( (top[2] >= p1[2] && top[2] <= p2[2]) || (bottom[2] >= p1[2] && bottom[2] <= p2[2]) ) { // 2d coordinates top[2] = p1[2] = 0; // if the cylinders overlap VectorSubtract(top, p1, tmp); if ( VectorLengthSquared(tmp) < r ) { tw->trace.startsolid = tw->trace.allsolid = qtrue; tw->trace.fraction = 0; } } }
void trigger_push_touch (gentity_t *self, gentity_t *other, trace_t *trace ) { if ( self->svFlags & SVF_INACTIVE ) {//set by target_deactivate return; } if( level.time < self->painDebounceTime + self->wait ) // normal 'wait' check { if( self->spawnflags & 2048 ) // MULTIPLE - allow multiple entities to touch this trigger in one frame { if ( self->painDebounceTime && level.time > self->painDebounceTime ) // if we haven't reached the next frame continue to let ents touch the trigger { return; } } else // only allowing one ent per frame to touch trigger { return; } } // if the player has already activated this trigger this frame if( other && !other->s.number && self->aimDebounceTime == level.time ) { return; } if( self->spawnflags & PUSH_CONVEYOR ) { // only push player if he's on the ground if( other->s.groundEntityNum == ENTITYNUM_NONE ) { return; } } if ( self->spawnflags & 1 ) {//PLAYERONLY if ( other->s.number != 0 ) { return; } } else { if ( self->spawnflags & 8 ) {//NPCONLY if ( other->NPC == NULL ) { return; } } } if ( !other->client ) { if ( other->s.pos.trType != TR_STATIONARY && other->s.pos.trType != TR_LINEAR_STOP && other->s.pos.trType != TR_NONLINEAR_STOP && VectorLengthSquared( other->s.pos.trDelta ) ) {//already moving VectorCopy( other->currentOrigin, other->s.pos.trBase ); VectorCopy( self->s.origin2, other->s.pos.trDelta ); other->s.pos.trTime = level.time; } return; } if ( other->client->ps.pm_type != PM_NORMAL ) { return; } if ( (self->spawnflags&16) ) {//relative, dir to it * speed vec3_t dir; VectorSubtract( self->s.origin2, other->currentOrigin, dir ); if ( self->speed ) { VectorNormalize( dir ); VectorScale( dir, self->speed, dir ); } VectorCopy( dir, other->client->ps.velocity ); } else if ( (self->spawnflags&4) ) {//linear dir * speed VectorScale( self->s.origin2, self->speed, other->client->ps.velocity ); } else { VectorCopy( self->s.origin2, other->client->ps.velocity ); } //so we don't take damage unless we land lower than we start here... other->client->ps.forceJumpZStart = 0; other->client->ps.pm_flags |= PMF_TRIGGER_PUSHED;//pushed by a trigger other->client->ps.jumpZStart = other->client->ps.origin[2]; if ( self->wait == -1 ) { self->e_TouchFunc = touchF_NULL; } else if ( self->wait > 0 ) { self->painDebounceTime = level.time; } if( other && !other->s.number ) { // mark that the player has activated this trigger this frame self->aimDebounceTime =level.time; } }
/* ================ CM_TraceThroughVerticalCylinder get the first intersection of the ray with the cylinder the cylinder extends halfheight above and below the origin ================ */ void CM_TraceThroughVerticalCylinder( traceWork_t *tw, vec3_t origin, float radius, float halfheight, vec3_t start, vec3_t end) { float length, scale, fraction, l1, l2; float a, b, c, d, sqrtd; vec3_t v1, dir, start2d, end2d, org2d, intersection; // 2d coordinates VectorSet(start2d, start[0], start[1], 0); VectorSet(end2d, end[0], end[1], 0); VectorSet(org2d, origin[0], origin[1], 0); // if between lower and upper cylinder bounds if (start[2] <= origin[2] + halfheight && start[2] >= origin[2] - halfheight) { // if inside the cylinder VectorSubtract(start2d, org2d, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.fraction = 0; tw->trace.startsolid = qtrue; VectorSubtract(end2d, org2d, dir); l1 = VectorLengthSquared(dir); if (l1 < Square(radius)) { tw->trace.allsolid = qtrue; } return; } } // VectorSubtract(end2d, start2d, dir); length = VectorNormalize(dir); // l1 = CM_DistanceFromLineSquared(org2d, start2d, end2d, dir); VectorSubtract(end2d, org2d, v1); l2 = VectorLengthSquared(v1); // if no intersection with the cylinder and the end point is at least an epsilon away if (l1 >= Square(radius) && l2 > Square(radius+SURFACE_CLIP_EPSILON)) { return; } // // // (start[0] - origin[0] - t * dir[0]) ^ 2 + (start[1] - origin[1] - t * dir[1]) ^ 2 = radius ^ 2 // (v1[0] + t * dir[0]) ^ 2 + (v1[1] + t * dir[1]) ^ 2 = radius ^ 2; // v1[0] ^ 2 + 2 * v1[0] * t * dir[0] + (t * dir[0]) ^ 2 + // v1[1] ^ 2 + 2 * v1[1] * t * dir[1] + (t * dir[1]) ^ 2 = radius ^ 2 // t ^ 2 * (dir[0] ^ 2 + dir[1] ^ 2) + t * (2 * v1[0] * dir[0] + 2 * v1[1] * dir[1]) + // v1[0] ^ 2 + v1[1] ^ 2 - radius ^ 2 = 0 // VectorSubtract(start, origin, v1); // dir is normalized so we can use a = 1 a = 1.0f;// * (dir[0] * dir[0] + dir[1] * dir[1]); b = 2.0f * (v1[0] * dir[0] + v1[1] * dir[1]); c = v1[0] * v1[0] + v1[1] * v1[1] - (radius+RADIUS_EPSILON) * (radius+RADIUS_EPSILON); d = b * b - 4.0f * c;// * a; if (d > 0) { sqrtd = SquareRootFloat(d); // = (- b + sqrtd) * 0.5f;// / (2.0f * a); fraction = (- b - sqrtd) * 0.5f;// / (2.0f * a); // if (fraction < 0) { fraction = 0; } else { fraction /= length; } if ( fraction < tw->trace.fraction ) { VectorSubtract(end, start, dir); VectorMA(start, fraction, dir, intersection); // if the intersection is between the cylinder lower and upper bound if (intersection[2] <= origin[2] + halfheight && intersection[2] >= origin[2] - halfheight) { // tw->trace.fraction = fraction; VectorSubtract(intersection, origin, dir); dir[2] = 0; #ifdef CAPSULE_DEBUG l2 = VectorLength(dir); if (l2 <= radius) { int bah = 1; } #endif scale = 1 / (radius+RADIUS_EPSILON); VectorScale(dir, scale, dir); VectorCopy(dir, tw->trace.plane.normal); VectorAdd( tw->modelOrigin, intersection, intersection); tw->trace.plane.dist = DotProduct(tw->trace.plane.normal, intersection); tw->trace.contents = CONTENTS_BODY; } } } else if (d == 0) { //t[0] = (- b ) / 2 * a; // slide along the cylinder } // no intersection at all }
void trigger_teleporter_touch (gentity_t *self, gentity_t *other, trace_t *trace ) { gentity_t *dest; if ( self->svFlags & SVF_INACTIVE ) {//set by target_deactivate return; } dest = G_PickTarget( self->target ); if (!dest) { gi.Printf ("Couldn't find teleporter destination\n"); return; } if ( other->client ) { if ( other->client->ps.pm_type == PM_DEAD ) { if ( !(self->spawnflags&TTSF_DEAD_OK) ) {//dead men can't teleport return; } } if ( other->NPC ) { if ( self->spawnflags & NO_NPCS ) { return; } } if ( other->client->playerTeam != TEAM_FREE && SpotWouldTelefrag2( other, dest->currentOrigin ) )//SpotWouldTelefrag( dest, other->client->playerTeam ) ) {//Don't go through if something blocking on the other side return; } TeleportPlayer( other, dest->s.origin, dest->s.angles ); } //FIXME: check for SVF_NO_TELEPORT else if ( !(self->svFlags & SVF_NO_TELEPORT) && !(self->spawnflags & NO_MISSILES) && VectorLengthSquared( other->s.pos.trDelta ) ) {//It's a mover of some sort and is currently moving vec3_t diffAngles = {0, 0, 0}; qboolean snap = qfalse; if ( self->lastEnemy ) { VectorSubtract( dest->s.angles, self->lastEnemy->s.angles, diffAngles ); } else {//snaps to angle VectorSubtract( dest->s.angles, other->currentAngles, diffAngles ); snap = qtrue; } TeleportMover( other, dest->s.origin, diffAngles, snap ); } }
//----------------------------------------------------- static qboolean VEH_TurretFindEnemies( Vehicle_t *pVeh, gentity_t *parent, turretStats_t *turretStats, int turretNum, int curMuzzle ) //----------------------------------------------------- { qboolean found = qfalse; int i, count; float bestDist = turretStats->fAIRange * turretStats->fAIRange; float enemyDist; vec3_t enemyDir, org, org2; qboolean foundClient = qfalse; gentity_t *entity_list[MAX_GENTITIES], *target, *bestTarget = NULL; WP_CalcVehMuzzle( parent, curMuzzle ); VectorCopy( pVeh->m_vMuzzlePos[curMuzzle], org2 ); count = G_RadiusList( org2, turretStats->fAIRange, parent, qtrue, entity_list ); for ( i = 0; i < count; i++ ) { trace_t tr; target = entity_list[i]; if ( target == parent || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET )) { continue; } if ( !target->client ) {// only attack clients if ( !(target->flags&FL_BBRUSH)//not a breakable brush || !target->takedamage//is a bbrush, but invincible || (target->NPC_targetname&&parent->targetname&&Q_stricmp(target->NPC_targetname,parent->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me { if ( target->s.weapon == WP_TURRET && target->classname && Q_strncmp( "misc_turret", target->classname, 11 ) == 0 ) {//these guys we want to shoot at } else { continue; } } //else: we will shoot at bbrushes! } else if ( target->client->sess.sessionTeam == TEAM_SPECTATOR ) { continue; } else if ( target->client->tempSpectate >= level.time ) { continue; } if ( target == ((gentity_t*)pVeh->m_pPilot) || target->r.ownerNum == parent->s.number ) {//don't get angry at my pilot or passengers? continue; } if ( parent->client && parent->client->sess.sessionTeam ) { if ( target->client ) { if ( target->client->sess.sessionTeam == parent->client->sess.sessionTeam ) { // A bot/client/NPC we don't want to shoot continue; } } else if ( target->teamnodmg == parent->client->sess.sessionTeam ) {//some other entity that's allied with us continue; } } if ( !trap->InPVS( org2, target->r.currentOrigin )) { continue; } VectorCopy( target->r.currentOrigin, org ); trap->Trace( &tr, org2, NULL, NULL, org, parent->s.number, MASK_SHOT, qfalse, 0, 0 ); if ( tr.entityNum == target->s.number || (!tr.allsolid && !tr.startsolid && tr.fraction == 1.0 ) ) { // Only acquire if have a clear shot, Is it in range and closer than our best? VectorSubtract( target->r.currentOrigin, org2, enemyDir ); enemyDist = VectorLengthSquared( enemyDir ); if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current { bestTarget = target; bestDist = enemyDist; found = qtrue; if ( target->client ) {//prefer clients over non-clients foundClient = qtrue; } } } } if ( found ) { pVeh->turretStatus[turretNum].enemyEntNum = bestTarget->s.number; } return found; }
/* ================== CM_BiSphereTrace ================== */ void CM_BiSphereTrace( trace_t *results, const vec3_t start, const vec3_t end, float startRad, float endRad, clipHandle_t model, int mask ) { int i; traceWork_t tw; float largestRadius = startRad > endRad ? startRad : endRad; cmodel_t *cmod; cmod = CM_ClipHandleToModel( model ); cm.checkcount++; // for multi-check avoidance c_traces++; // for statistics, may be zeroed // fill in a default trace Com_Memset( &tw, 0, sizeof( tw ) ); tw.trace.fraction = 1.0f; // assume it goes the entire distance until shown otherwise VectorCopy( vec3_origin, tw.modelOrigin ); tw.type = TT_BISPHERE; tw.testLateralCollision = qtrue; tw.trace.lateralFraction = 1.0f; if( !cm.numNodes ) { *results = tw.trace; return; // map not loaded, shouldn't happen } // set basic parms tw.contents = mask; VectorCopy( start, tw.start ); VectorCopy( end, tw.end ); tw.biSphere.startRadius = startRad; tw.biSphere.endRadius = endRad; // // calculate bounds // for( i = 0 ; i < 3 ; i++ ) { if( tw.start[ i ] < tw.end[ i ] ) { tw.bounds[ 0 ][ i ] = tw.start[ i ] - tw.biSphere.startRadius; tw.bounds[ 1 ][ i ] = tw.end[ i ] + tw.biSphere.endRadius; } else { tw.bounds[ 0 ][ i ] = tw.end[ i ] + tw.biSphere.endRadius; tw.bounds[ 1 ][ i ] = tw.start[ i ] - tw.biSphere.startRadius; } } tw.isPoint = qfalse; tw.extents[ 0 ] = largestRadius; tw.extents[ 1 ] = largestRadius; tw.extents[ 2 ] = largestRadius; // // general sweeping through world // if( model ) CM_TraceThroughLeaf( &tw, &cmod->leaf ); else CM_TraceThroughTree( &tw, 0, 0.0f, 1.0f, tw.start, tw.end ); // generate endpos from the original, unmodified start/end if( tw.trace.fraction == 1.0f ) { VectorCopy( end, tw.trace.endpos ); } else { for( i = 0; i < 3; i++ ) tw.trace.endpos[ i ] = start[ i ] + tw.trace.fraction * ( end[ i ] - start[ i ] ); } // If allsolid is set (was entirely inside something solid), the plane is not valid. // If fraction == 1.0, we never hit anything, and thus the plane is not valid. // Otherwise, the normal on the plane should have unit length assert( tw.trace.allsolid || tw.trace.fraction == 1.0 || VectorLengthSquared(tw.trace.plane.normal ) > 0.9999 ); *results = tw.trace; }