/* * GClip_LinkEntity_AreaGrid */ static void GClip_LinkEntity_AreaGrid( areagrid_t *areagrid, edict_t *ent ) { link_t *grid; int igrid[3], igridmins[3], igridmaxs[3], gridnum, entitynumber; entitynumber = NUM_FOR_EDICT( ent ); if( entitynumber <= 0 || entitynumber >= game.maxentities || EDICT_NUM( entitynumber ) != ent ) { Com_Printf( "GClip_LinkEntity_AreaGrid: invalid edict %p " "(edicts is %p, edict compared to prog->edicts is %i)\n", (void *)ent, game.edicts, entitynumber ); return; } igridmins[0] = (int) floor( (ent->r.absmin[0] + areagrid->bias[0]) * areagrid->scale[0] ); igridmins[1] = (int) floor( (ent->r.absmin[1] + areagrid->bias[1]) * areagrid->scale[1] ); //igridmins[2] = (int) floor( (ent->r.absmin[2] + areagrid->bias[2]) * areagrid->scale[2] ); igridmaxs[0] = (int) floor( (ent->r.absmax[0] + areagrid->bias[0]) * areagrid->scale[0] ) + 1; igridmaxs[1] = (int) floor( (ent->r.absmax[1] + areagrid->bias[1]) * areagrid->scale[1] ) + 1; //igridmaxs[2] = (int) floor( (ent->r.absmax[2] + areagrid->bias[2]) * areagrid->scale[2] ) + 1; if( igridmins[0] < 0 || igridmaxs[0] > AREA_GRID || igridmins[1] < 0 || igridmaxs[1] > AREA_GRID || ((igridmaxs[0] - igridmins[0]) * (igridmaxs[1] - igridmins[1])) > MAX_ENT_AREAS ) { // wow, something outside the grid, store it as such GClip_InsertLinkBefore( &ent->areagrid[0], &areagrid->outside, entitynumber ); return; } gridnum = 0; for( igrid[1] = igridmins[1]; igrid[1] < igridmaxs[1]; igrid[1]++ ) { grid = areagrid->grid + igrid[1] * AREA_GRID + igridmins[0]; for( igrid[0] = igridmins[0]; igrid[0] < igridmaxs[0]; igrid[0]++, grid++, gridnum++ ) GClip_InsertLinkBefore( &ent->areagrid[gridnum], grid, entitynumber ); } }
void GClip_LinkEntity( edict_t *ent ) { areanode_t *node; int leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j, k; int area; int topnode; if( ent->r.area.prev ) GClip_UnlinkEntity( ent ); // unlink from old position if( ent == game.edicts ) return; // don't add the world if( !ent->r.inuse ) return; // set the size VectorSubtract( ent->r.maxs, ent->r.mins, ent->r.size ); if( ent->r.solid == SOLID_NOT || ( ent->r.svflags & SVF_PROJECTILE ) ) { ent->s.solid = 0; } else if( ISBRUSHMODEL( ent->s.modelindex ) ) { // the only predicted SOLID_TRIGGER entity is ET_PUSH_TRIGGER if( ent->r.solid != SOLID_TRIGGER || ent->s.type == ET_PUSH_TRIGGER ) ent->s.solid = SOLID_BMODEL; else ent->s.solid = 0; } else // encode the size into the entity_state for client prediction { if( ent->r.solid == SOLID_TRIGGER ) { ent->s.solid = 0; } else { // assume that x/y are equal and symetric i = ent->r.maxs[0]/8; clamp( i, 1, 31 ); // z is not symetric j = ( -ent->r.mins[2] )/8; clamp( j, 1, 31 ); // and z maxs can be negative... k = ( ent->r.maxs[2]+32 )/8; clamp( k, 1, 63 ); ent->s.solid = ( k<<10 ) | ( j<<5 ) | i; } } // set the abs box if( ISBRUSHMODEL( ent->s.modelindex ) && ( ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2] ) ) { // expand for rotation float radius; radius = RadiusFromBounds( ent->r.mins, ent->r.maxs ); for( i = 0; i < 3; i++ ) { ent->r.absmin[i] = ent->s.origin[i] - radius; ent->r.absmax[i] = ent->s.origin[i] + radius; } } else // axis aligned { VectorAdd( ent->s.origin, ent->r.mins, ent->r.absmin ); VectorAdd( ent->s.origin, ent->r.maxs, ent->r.absmax ); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch ent->r.absmin[0] -= 1; ent->r.absmin[1] -= 1; ent->r.absmin[2] -= 1; ent->r.absmax[0] += 1; ent->r.absmax[1] += 1; ent->r.absmax[2] += 1; // link to PVS leafs ent->r.num_clusters = 0; ent->r.areanum = ent->r.areanum2 = -1; // get all leafs, including solids num_leafs = trap_CM_BoxLeafnums( ent->r.absmin, ent->r.absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode ); // set areas for( i = 0; i < num_leafs; i++ ) { clusters[i] = trap_CM_LeafCluster( leafs[i] ); area = trap_CM_LeafArea( leafs[i] ); if( area > -1 ) { // doors may legally straggle two areas, // but nothing should ever need more than that if( ent->r.areanum > -1 && ent->r.areanum != area ) { if( ent->r.areanum2 > -1 && ent->r.areanum2 != area ) { if( developer->integer ) G_Printf( "Object %s touching 3 areas at %f %f %f\n", ( ent->classname ? ent->classname : "" ), ent->r.absmin[0], ent->r.absmin[1], ent->r.absmin[2] ); } ent->r.areanum2 = area; } else ent->r.areanum = area; } } if( num_leafs >= MAX_TOTAL_ENT_LEAFS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; } else { ent->r.num_clusters = 0; for( i = 0; i < num_leafs; i++ ) { if( clusters[i] == -1 ) continue; // not a visible leaf for( j = 0; j < i; j++ ) if( clusters[j] == clusters[i] ) break; if( j == i ) { if( ent->r.num_clusters == MAX_ENT_CLUSTERS ) { // assume we missed some leafs, and mark by headnode ent->r.num_clusters = -1; ent->r.headnode = topnode; break; } ent->r.clusternums[ent->r.num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if( !ent->r.linkcount && !( ent->r.svflags & SVF_TRANSMITORIGIN2 ) ) { VectorCopy( ent->s.origin, ent->s.old_origin ); ent->olds = ent->s; } ent->r.linkcount++; ent->linked = qtrue; if( ent->r.solid == SOLID_NOT ) return; // find the first node that the ent's box crosses node = sv_areanodes; while( 1 ) { if( node->axis == -1 ) break; if( ent->r.absmin[node->axis] > node->dist ) node = node->children[0]; else if( ent->r.absmax[node->axis] < node->dist ) node = node->children[1]; else break; // crosses the node } // link it in if( ent->r.solid == SOLID_TRIGGER ) GClip_InsertLinkBefore( &ent->r.area, &node->trigger_edicts, NUM_FOR_EDICT( ent ) ); else GClip_InsertLinkBefore( &ent->r.area, &node->solid_edicts, NUM_FOR_EDICT( ent ) ); }