1 /*
2 * Copyright (C) 1997-2001 Id Software, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License as published by the Free
6 * Software Foundation; either version 2 of the License, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59
17 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *
19 */
20 /* world.c -- world query functions */
21
22 #include "server.h"
23
24 /*
25 *
26 * ============================================================================
27 * ===
28 *
29 * ENTITY AREA CHECKING
30 *
31 * FIXME: this use of "area" is different from the bsp file use
32 * ===========================================================================
33 * ====
34 */
35
36 /* (type *)STRUCT_FROM_LINK(link_t *link, type, member) */
37 /* ent = STRUCT_FROM_LINK(link,entity_t,order) */
38 /* FIXME: remove this mess! */
39 #define STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m)))
40
41 #define EDICT_FROM_AREA(l) STRUCT_FROM_LINK(l,edict_t,area)
42
43 typedef struct areanode_s {
44 int axis; /* -1 = leaf node */
45 float dist;
46 struct areanode_s *children[2];
47 link_t trigger_edicts;
48 link_t solid_edicts;
49 } areanode_t;
50
51 #define AREA_DEPTH 4
52 #define AREA_NODES 32
53
54 areanode_t sv_areanodes[AREA_NODES];
55 int sv_numareanodes;
56
57 float *area_mins, *area_maxs;
58 edict_t **area_list;
59 int area_count, area_maxcount;
60 int area_type;
61
62 int SV_HullForEntity(edict_t * ent);
63
64
65 /* ClearLink is used for new headnodes */
66 void
ClearLink(link_t * l)67 ClearLink(link_t * l)
68 {
69 l->prev = l->next = l;
70 }
71
72 void
RemoveLink(link_t * l)73 RemoveLink(link_t * l)
74 {
75 l->next->prev = l->prev;
76 l->prev->next = l->next;
77 }
78
79 void
InsertLinkBefore(link_t * l,link_t * before)80 InsertLinkBefore(link_t * l, link_t * before)
81 {
82 l->next = before;
83 l->prev = before->prev;
84 l->prev->next = l;
85 l->next->prev = l;
86 }
87
88 /*
89 * =============== SV_CreateAreaNode
90 *
91 * Builds a uniformly subdivided tree for the given world size ===============
92 */
93 areanode_t *
SV_CreateAreaNode(int depth,vec3_t mins,vec3_t maxs)94 SV_CreateAreaNode(int depth, vec3_t mins, vec3_t maxs)
95 {
96 areanode_t *anode;
97 vec3_t size;
98 vec3_t mins1, maxs1, mins2, maxs2;
99
100 anode = &sv_areanodes[sv_numareanodes];
101 sv_numareanodes++;
102
103 ClearLink(&anode->trigger_edicts);
104 ClearLink(&anode->solid_edicts);
105
106 if (depth == AREA_DEPTH) {
107 anode->axis = -1;
108 anode->children[0] = anode->children[1] = NULL;
109 return anode;
110 }
111 VectorSubtract(maxs, mins, size);
112 if (size[0] > size[1])
113 anode->axis = 0;
114 else
115 anode->axis = 1;
116
117 anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
118 VectorCopy(mins, mins1);
119 VectorCopy(mins, mins2);
120 VectorCopy(maxs, maxs1);
121 VectorCopy(maxs, maxs2);
122
123 maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
124
125 anode->children[0] = SV_CreateAreaNode(depth + 1, mins2, maxs2);
126 anode->children[1] = SV_CreateAreaNode(depth + 1, mins1, maxs1);
127
128 return anode;
129 }
130
131 /*
132 * =============== SV_ClearWorld
133 *
134 * ===============
135 */
136 void
SV_ClearWorld(void)137 SV_ClearWorld(void)
138 {
139 memset(sv_areanodes, 0, sizeof(sv_areanodes));
140 sv_numareanodes = 0;
141 SV_CreateAreaNode(0, sv.models[1]->mins, sv.models[1]->maxs);
142 }
143
144
145 /*
146 * =============== SV_UnlinkEdict
147 *
148 * ===============
149 */
150 void
SV_UnlinkEdict(edict_t * ent)151 SV_UnlinkEdict(edict_t * ent)
152 {
153 if (!ent->area.prev)
154 return; /* not linked in anywhere */
155 RemoveLink(&ent->area);
156 ent->area.prev = ent->area.next = NULL;
157 }
158
159
160 /*
161 * =============== SV_LinkEdict
162 *
163 * ===============
164 */
165 #define MAX_TOTAL_ENT_LEAFS 128
166 void
SV_LinkEdict(edict_t * ent)167 SV_LinkEdict(edict_t * ent)
168 {
169 areanode_t *node;
170 int leafs[MAX_TOTAL_ENT_LEAFS];
171 int clusters[MAX_TOTAL_ENT_LEAFS];
172 int num_leafs;
173 int i, j, k;
174 int area;
175 int topnode;
176
177 if (ent->area.prev)
178 SV_UnlinkEdict(ent); /* unlink from old position */
179
180 if (ent == ge->edicts)
181 return; /* don't add the world */
182
183 if (!ent->inuse)
184 return;
185
186 /* set the size */
187 VectorSubtract(ent->maxs, ent->mins, ent->size);
188
189 /* encode the size into the entity_state for client prediction */
190 if (ent->solid == SOLID_BBOX && !(ent->svflags & SVF_DEADMONSTER)) { /* assume that x/y are
191 * equal and symetric */
192 i = ent->maxs[0] / 8;
193 if (i < 1)
194 i = 1;
195 if (i > 31)
196 i = 31;
197
198 /* z is not symetric */
199 j = (-ent->mins[2]) / 8;
200 if (j < 1)
201 j = 1;
202 if (j > 31)
203 j = 31;
204
205 /* and z maxs can be negative... */
206 k = (ent->maxs[2] + 32) / 8;
207 if (k < 1)
208 k = 1;
209 if (k > 63)
210 k = 63;
211
212 ent->s.solid = (k << 10) | (j << 5) | i;
213 } else if (ent->solid == SOLID_BSP) {
214 ent->s.solid = 31; /* a solid_bbox will never create
215 * this value */
216 } else
217 ent->s.solid = 0;
218
219 /* set the abs box */
220 if (ent->solid == SOLID_BSP &&
221 (ent->s.angles[0] || ent->s.angles[1] || ent->s.angles[2])) { /* expand for rotation */
222 float max , v;
223
224 max = 0;
225 for (i = 0; i < 3; i++) {
226 v = fabs(ent->mins[i]);
227 if (v > max)
228 max = v;
229 v = fabs(ent->maxs[i]);
230 if (v > max)
231 max = v;
232 }
233 for (i = 0; i < 3; i++) {
234 ent->absmin[i] = ent->s.origin[i] - max;
235 ent->absmax[i] = ent->s.origin[i] + max;
236 }
237 } else { /* normal */
238 VectorAdd(ent->s.origin, ent->mins, ent->absmin);
239 VectorAdd(ent->s.origin, ent->maxs, ent->absmax);
240 }
241
242 /* because movement is clipped an epsilon away from an actual edge, */
243 /* we must fully check even when bounding boxes don't quite touch */
244 ent->absmin[0] -= 1;
245 ent->absmin[1] -= 1;
246 ent->absmin[2] -= 1;
247 ent->absmax[0] += 1;
248 ent->absmax[1] += 1;
249 ent->absmax[2] += 1;
250
251 /* link to PVS leafs */
252 ent->num_clusters = 0;
253 ent->areanum = 0;
254 ent->areanum2 = 0;
255
256 /* get all leafs, including solids */
257 num_leafs = CM_BoxLeafnums(ent->absmin, ent->absmax,
258 leafs, MAX_TOTAL_ENT_LEAFS, &topnode);
259
260 /* set areas */
261 for (i = 0; i < num_leafs; i++) {
262 clusters[i] = CM_LeafCluster(leafs[i]);
263 area = CM_LeafArea(leafs[i]);
264 if (area) { /* doors may legally straggle two areas, */
265 /* but nothing should evern need more than that */
266 if (ent->areanum && ent->areanum != area) {
267 if (ent->areanum2 && ent->areanum2 != area && sv.state == ss_loading)
268 Com_DPrintf("Object touching 3 areas at %f %f %f\n",
269 ent->absmin[0], ent->absmin[1], ent->absmin[2]);
270 ent->areanum2 = area;
271 } else
272 ent->areanum = area;
273 }
274 }
275
276 if (num_leafs >= MAX_TOTAL_ENT_LEAFS) { /* assume we missed some
277 * leafs, and mark by
278 * headnode */
279 ent->num_clusters = -1;
280 ent->headnode = topnode;
281 } else {
282 ent->num_clusters = 0;
283 for (i = 0; i < num_leafs; i++) {
284 if (clusters[i] == -1)
285 continue; /* not a visible leaf */
286 for (j = 0; j < i; j++)
287 if (clusters[j] == clusters[i])
288 break;
289 if (j == i) {
290 if (ent->num_clusters == MAX_ENT_CLUSTERS) { /* assume we missed some
291 * leafs, and mark by
292 * headnode */
293 ent->num_clusters = -1;
294 ent->headnode = topnode;
295 break;
296 }
297 ent->clusternums[ent->num_clusters++] = clusters[i];
298 }
299 }
300 }
301
302 /* if first time, make sure old_origin is valid */
303 if (!ent->linkcount) {
304 VectorCopy(ent->s.origin, ent->s.old_origin);
305 }
306 ent->linkcount++;
307
308 if (ent->solid == SOLID_NOT)
309 return;
310
311 /* find the first node that the ent's box crosses */
312 node = sv_areanodes;
313 while (1) {
314 if (node->axis == -1)
315 break;
316 if (ent->absmin[node->axis] > node->dist)
317 node = node->children[0];
318 else if (ent->absmax[node->axis] < node->dist)
319 node = node->children[1];
320 else
321 break; /* crosses the node */
322 }
323
324 /* link it in */
325 if (ent->solid == SOLID_TRIGGER)
326 InsertLinkBefore(&ent->area, &node->trigger_edicts);
327 else
328 InsertLinkBefore(&ent->area, &node->solid_edicts);
329
330 }
331
332
333 /*
334 * ==================== SV_AreaEdicts_r
335 *
336 * ====================
337 */
338 void
SV_AreaEdicts_r(areanode_t * node)339 SV_AreaEdicts_r(areanode_t * node)
340 {
341 link_t *l, *next, *start;
342 edict_t *check;
343 int count;
344
345 count = 0;
346
347 /* touch linked edicts */
348 if (area_type == AREA_SOLID)
349 start = &node->solid_edicts;
350 else
351 start = &node->trigger_edicts;
352
353 for (l = start->next; l != start; l = next) {
354 next = l->next;
355 check = EDICT_FROM_AREA(l);
356
357 if (check->solid == SOLID_NOT)
358 continue; /* deactivated */
359 if (check->absmin[0] > area_maxs[0]
360 || check->absmin[1] > area_maxs[1]
361 || check->absmin[2] > area_maxs[2]
362 || check->absmax[0] < area_mins[0]
363 || check->absmax[1] < area_mins[1]
364 || check->absmax[2] < area_mins[2])
365 continue; /* not touching */
366
367 if (area_count == area_maxcount) {
368 Com_Printf("SV_AreaEdicts: MAXCOUNT\n");
369 return;
370 }
371 area_list[area_count] = check;
372 area_count++;
373 }
374
375 if (node->axis == -1)
376 return; /* terminal node */
377
378 /* recurse down both sides */
379 if (area_maxs[node->axis] > node->dist)
380 SV_AreaEdicts_r(node->children[0]);
381 if (area_mins[node->axis] < node->dist)
382 SV_AreaEdicts_r(node->children[1]);
383 }
384
385 /*
386 * ================ SV_AreaEdicts ================
387 */
388 int
SV_AreaEdicts(vec3_t mins,vec3_t maxs,edict_t ** list,int maxcount,int areatype)389 SV_AreaEdicts(vec3_t mins, vec3_t maxs, edict_t ** list,
390 int maxcount, int areatype)
391 {
392 area_mins = mins;
393 area_maxs = maxs;
394 area_list = list;
395 area_count = 0;
396 area_maxcount = maxcount;
397 area_type = areatype;
398
399 SV_AreaEdicts_r(sv_areanodes);
400
401 return area_count;
402 }
403
404
405 /*
406 * ===========================================================================
407 *
408 */
409
410 /*
411 * ============= SV_PointContents =============
412 */
413 int
SV_PointContents(vec3_t p)414 SV_PointContents(vec3_t p)
415 {
416 edict_t *touch[MAX_EDICTS], *hit;
417 int i, num;
418 int contents, c2;
419 int headnode;
420 float *angles;
421
422 /* get base contents from world */
423 contents = CM_PointContents(p, sv.models[1]->headnode);
424
425 /* or in contents from all the other entities */
426 num = SV_AreaEdicts(p, p, touch, MAX_EDICTS, AREA_SOLID);
427
428 for (i = 0; i < num; i++) {
429 hit = touch[i];
430
431 /* might intersect, so do an exact clip */
432 headnode = SV_HullForEntity(hit);
433 angles = hit->s.angles;
434 if (hit->solid != SOLID_BSP)
435 angles = vec3_origin; /* boxes don't rotate */
436
437 c2 = CM_TransformedPointContents(p, headnode, hit->s.origin, hit->s.angles);
438
439 contents |= c2;
440 }
441
442 return contents;
443 }
444
445
446
447 typedef struct {
448 vec3_t boxmins, boxmaxs; /* enclose the test object along entire move */
449 float *mins, *maxs; /* size of the moving object */
450 vec3_t mins2, maxs2; /* size when clipping against monsters */
451 float *start, *end;
452 trace_t trace;
453 edict_t *passedict;
454 int contentmask;
455 } moveclip_t;
456
457
458
459 /*
460 * ================ SV_HullForEntity
461 *
462 * Returns a headnode that can be used for testing or clipping an object of
463 * mins/maxs size. Offset is filled in to contain the adjustment that must be
464 * added to the testing object's origin to get a point to use with the
465 * returned hull. ================
466 */
467 int
SV_HullForEntity(edict_t * ent)468 SV_HullForEntity(edict_t * ent)
469 {
470 cmodel_t *model;
471
472 /* decide which clipping hull to use, based on the size */
473 if (ent->solid == SOLID_BSP) { /* explicit hulls in the BSP model */
474 model = sv.models[ent->s.modelindex];
475
476 if (!model)
477 Com_Error(ERR_FATAL, "MOVETYPE_PUSH with a non bsp model");
478
479 return model->headnode;
480 }
481 /* create a temp hull from bounding box sizes */
482
483 return CM_HeadnodeForBox(ent->mins, ent->maxs);
484 }
485
486
487 /*
488 * ===========================================================================
489 *
490 */
491
492 /*
493 * ==================== SV_ClipMoveToEntities
494 *
495 * ====================
496 */
497 void
SV_ClipMoveToEntities(moveclip_t * clip)498 SV_ClipMoveToEntities(moveclip_t * clip)
499 {
500 int i, num;
501 edict_t *touchlist[MAX_EDICTS], *touch;
502 trace_t trace;
503 int headnode;
504 float *angles;
505
506 num = SV_AreaEdicts(clip->boxmins, clip->boxmaxs, touchlist
507 ,MAX_EDICTS, AREA_SOLID);
508
509 /* be careful, it is possible to have an entity in this */
510 /* list removed before we get to it (killtriggered) */
511 for (i = 0; i < num; i++) {
512 touch = touchlist[i];
513 if (touch->solid == SOLID_NOT)
514 continue;
515 if (touch == clip->passedict)
516 continue;
517 if (clip->trace.allsolid)
518 return;
519 if (clip->passedict) {
520 if (touch->owner == clip->passedict)
521 continue; /* don't clip against own
522 * missiles */
523 if (clip->passedict->owner == touch)
524 continue; /* don't clip against owner */
525 }
526 if (!(clip->contentmask & CONTENTS_DEADMONSTER)
527 && (touch->svflags & SVF_DEADMONSTER))
528 continue;
529
530 /* might intersect, so do an exact clip */
531 headnode = SV_HullForEntity(touch);
532 angles = touch->s.angles;
533 if (touch->solid != SOLID_BSP)
534 angles = vec3_origin; /* boxes don't rotate */
535
536 if (touch->svflags & SVF_MONSTER)
537 trace = CM_TransformedBoxTrace(clip->start, clip->end,
538 clip->mins2, clip->maxs2, headnode, clip->contentmask,
539 touch->s.origin, angles);
540 else
541 trace = CM_TransformedBoxTrace(clip->start, clip->end,
542 clip->mins, clip->maxs, headnode, clip->contentmask,
543 touch->s.origin, angles);
544
545 if (trace.allsolid || trace.startsolid ||
546 trace.fraction < clip->trace.fraction) {
547 trace.ent = touch;
548 if (clip->trace.startsolid) {
549 clip->trace = trace;
550 clip->trace.startsolid = true;
551 } else
552 clip->trace = trace;
553 } else if (trace.startsolid)
554 clip->trace.startsolid = true;
555 }
556 }
557
558
559 /*
560 * ================== SV_TraceBounds ==================
561 */
562 void
SV_TraceBounds(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,vec3_t boxmins,vec3_t boxmaxs)563 SV_TraceBounds(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
564 {
565 int i;
566
567 for (i = 0; i < 3; i++) {
568 if (end[i] > start[i]) {
569 boxmins[i] = start[i] + mins[i] - 1;
570 boxmaxs[i] = end[i] + maxs[i] + 1;
571 } else {
572 boxmins[i] = end[i] + mins[i] - 1;
573 boxmaxs[i] = start[i] + maxs[i] + 1;
574 }
575 }
576 }
577
578 /*
579 * ================== SV_Trace
580 *
581 * Moves the given mins/maxs volume through the world from start to end.
582 *
583 * Passedict and edicts owned by passedict are explicitly not checked.
584 *
585 * ==================
586 */
587 trace_t
SV_Trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end,edict_t * passedict,int contentmask)588 SV_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t * passedict, int contentmask)
589 {
590 moveclip_t clip;
591
592 if (!mins)
593 mins = vec3_origin;
594 if (!maxs)
595 maxs = vec3_origin;
596
597 memset(&clip, 0, sizeof(moveclip_t));
598
599 /* clip to world */
600 clip.trace = CM_BoxTrace(start, end, mins, maxs, 0, contentmask);
601 clip.trace.ent = ge->edicts;
602 if (clip.trace.fraction == 0)
603 return clip.trace; /* blocked by the world */
604
605 clip.contentmask = contentmask;
606 clip.start = start;
607 clip.end = end;
608 clip.mins = mins;
609 clip.maxs = maxs;
610 clip.passedict = passedict;
611
612 VectorCopy(mins, clip.mins2);
613 VectorCopy(maxs, clip.maxs2);
614
615 /* create the bounding box of the entire move */
616 SV_TraceBounds(start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs);
617
618 /* clip to other solid entities */
619 SV_ClipMoveToEntities(&clip);
620
621 return clip.trace;
622 }
623