void SV_LinkEdict(edict_t *ent) { guard(SV_LinkEdict); int i, j, k; //!! HOOK - move outside ? if (bspfile.type != map_q2) { if (ent->s.modelindex >= 0 && ent->s.modelindex < MAX_MODELS) { // link model hook const char *modelName = sv.configstrings[CS_MODELS + ent->s.modelindex]; if (!strcmp(modelName, "models/objects/dmspot/tris.md2")) { // teleporter (source+target) was found // appPrintf(S_CYAN"teleport: %g %g %g\n", VECTOR_ARG(ent->s.origin)); return; // simply do not link model; trigger entity will be added anyway } } } if (ent->area.prev) SV_UnlinkEdict(ent); // unlink from old position (i.e. relink edict) if (ent == ge->edicts) return; // don't add the world if (!ent->inuse) return; entityHull_t &ex = ents[NUM_FOR_EDICT(ent)]; memset(&ex, 0, sizeof(entityHull_t)); ex.owner = ent; ex.axis.FromEuler(ent->s.angles); // set the size VectorSubtract(ent->bounds.maxs, ent->bounds.mins, ent->size); // encode the size into the entity_state for client prediction if (ent->solid == SOLID_BBOX) { // assume that x/y are equal and symetric i = appRound(ent->bounds.maxs[0] / 8); // z is not symetric j = appRound(-ent->bounds.mins[2] / 8); // and z maxs can be negative... k = appRound((ent->bounds.maxs[2] + 32) / 8); // original Q2 have bounded i/j/k/ with lower margin==1 (for client prediction only); this will // produce incorrect collision test when bbox mins/maxs is (0,0,0) i = bound(i, 0, 31); // mins/maxs[0,1] range is -248..0/0..248 j = bound(j, 0, 31); // mins[2] range is [-248..0] k = bound(k, 0, 63); // maxs[2] range is [-32..472] // if SVF_DEADMONSTER, s.solid should be 0 ent->s.solid = (ent->svflags & SVF_DEADMONSTER) ? 0 : (k<<10) | (j<<5) | i; i *= 8; j *= 8; k *= 8; ex.bounds.mins.Set(-i, -i, -j); ex.bounds.maxs.Set(i, i, k - 32); ex.bounds.GetCenter(ex.center); ex.center.Add(ent->s.origin); ex.model = NULL; ex.radius = VectorDistance(ex.bounds.maxs, ex.bounds.mins) / 2; } else if (ent->solid == SOLID_BSP) { ex.model = sv.models[ent->s.modelindex]; if (!ex.model) Com_DropError("MOVETYPE_PUSH with a non bsp model"); CVec3 v; ex.model->bounds.GetCenter(v); UnTransformPoint(ent->s.origin, ex.axis, v, ex.center); ex.radius = ex.model->radius; ent->s.solid = 31; // a SOLID_BBOX will never create this value (mins=(-248,-248,0) maxs=(248,248,-32)) } else if (ent->solid == SOLID_TRIGGER) { ent->s.solid = 0; // check for model link ex.model = sv.models[ent->s.modelindex]; if (!ex.model) { // model not attached by game, check entstring //?? can optimize: add 'bool spawningEnts', set to 'true' before SpawnEntities() //?? and 'false' after; skip code below when 'false' for (triggerModelLink_t *link = bspfile.modelLinks; link; link = link->next) #define CMP(n) (fabs(ent->s.origin[n] - link->origin[n]) < 0.5f) if (CMP(0) && CMP(1) && CMP(2)) { CBspModel *model = CM_InlineModel(link->modelIdx); VectorSubtract(model->bounds.maxs, ent->s.origin, ent->bounds.maxs); VectorSubtract(model->bounds.mins, ent->s.origin, ent->bounds.mins); break; } #undef CMP } } else ent->s.solid = 0; // set the abs box if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { // expand for rotation for (i = 0; i < 3 ; i++) { ent->absBounds.mins[i] = ex.center[i] - ex.radius; ent->absBounds.maxs[i] = ex.center[i] + ex.radius; } } else { // normal VectorAdd(ent->s.origin, ent->bounds.mins, ent->absBounds.mins); VectorAdd(ent->s.origin, ent->bounds.maxs, ent->absBounds.maxs); } // because movement is clipped an epsilon away from an actual edge, // we must fully check even when bounding boxes don't quite touch for (i = 0; i < 3; i++) { ent->absBounds.mins[i] -= 1; ent->absBounds.maxs[i] += 1; } // link to PVS leafs ent->num_clusters = 0; ent->zonenum = 0; ent->zonenum2 = 0; // get all leafs, including solids CBspLeaf *leafs[MAX_TOTAL_ENT_LEAFS]; int topnode; int num_leafs = CM_BoxLeafs(ent->absBounds, ARRAY_ARG(leafs), &topnode); // set zones int clusters[MAX_TOTAL_ENT_LEAFS]; for (i = 0; i < num_leafs; i++) { clusters[i] = leafs[i]->cluster; int zone = leafs[i]->zone; if (zone) { // doors may legally straggle two zones, // but nothing should evern need more than that if (ent->zonenum && ent->zonenum != zone) { if (ent->zonenum2 && ent->zonenum2 != zone && sv.state == ss_loading) Com_DPrintf("Object touching 3 zones at %g %g %g\n", VECTOR_ARG(ent->absBounds.mins)); ent->zonenum2 = zone; } else ent->zonenum = zone; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = topnode; } else { ent->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->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = topnode; break; } ent->clusternums[ent->num_clusters++] = clusters[i]; } } } // if first time, make sure old_origin is valid if (!ent->linkcount) ent->s.old_origin = ent->s.origin; ent->linkcount++; if (ent->solid == SOLID_NOT) return; // find the first node that the ent's box crosses areanode_t *node = areaNodes; while (node->axis != -1) { if (ent->absBounds.mins[node->axis] > node->dist) node = node->children[0]; else if (ent->absBounds.maxs[node->axis] < node->dist) node = node->children[1]; else break; // crosses the node } // link it in areanode_t *node2 = node; if (ent->solid == SOLID_TRIGGER) { InsertLinkBefore(ent->area, node->trigEdicts); for ( ; node2; node2 = node2->parent) node2->numTrigEdicts++; } else { InsertLinkBefore(ent->area, node->solidEdicts); for ( ; node2; node2 = node2->parent) node2->numSolidEdicts++; } ex.area = node; unguard; }
/* =============== SV_LinkEdict General purpose routine shared between game DLL and MVD code. Links entity to PVS leafs. =============== */ void SV_LinkEdict(cm_t *cm, edict_t *ent) { mleaf_t *leafs[MAX_TOTAL_ENT_LEAFS]; int clusters[MAX_TOTAL_ENT_LEAFS]; int num_leafs; int i, j; int area; mnode_t *topnode; // set the size VectorSubtract(ent->maxs, ent->mins, ent->size); // set the abs box if (ent->solid == SOLID_BSP && (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { // expand for rotation float max, v; int i; max = 0; for (i = 0; i < 3; i++) { v = Q_fabs(ent->mins[i]); if (v > max) max = v; v = Q_fabs(ent->maxs[i]); if (v > max) max = v; } for (i = 0; i < 3; i++) { ent->absmin[i] = ent->s.origin[i] - max; ent->absmax[i] = ent->s.origin[i] + max; } } else { // normal VectorAdd(ent->s.origin, ent->mins, ent->absmin); VectorAdd(ent->s.origin, ent->maxs, ent->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->absmin[0] -= 1; ent->absmin[1] -= 1; ent->absmin[2] -= 1; ent->absmax[0] += 1; ent->absmax[1] += 1; ent->absmax[2] += 1; // link to PVS leafs ent->num_clusters = 0; ent->areanum = 0; ent->areanum2 = 0; //get all leafs, including solids num_leafs = CM_BoxLeafs(cm, ent->absmin, ent->absmax, leafs, MAX_TOTAL_ENT_LEAFS, &topnode); // set areas for (i = 0; i < num_leafs; i++) { clusters[i] = CM_LeafCluster(leafs[i]); area = CM_LeafArea(leafs[i]); if (area) { // doors may legally straggle two areas, // but nothing should evern need more than that if (ent->areanum && ent->areanum != area) { if (ent->areanum2 && ent->areanum2 != area && sv.state == ss_loading) { Com_DPrintf("Object touching 3 areas at %f %f %f\n", ent->absmin[0], ent->absmin[1], ent->absmin[2]); } ent->areanum2 = area; } else ent->areanum = area; } } if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = CM_NumNode(cm, topnode); } else { ent->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->num_clusters == MAX_ENT_CLUSTERS) { // assume we missed some leafs, and mark by headnode ent->num_clusters = -1; ent->headnode = CM_NumNode(cm, topnode); break; } ent->clusternums[ent->num_clusters++] = clusters[i]; } } } }