]> icculus.org git repositories - divverent/darkplaces.git/blob - world.c
disabled RecursiveHullCheckPoint because it probably isn't much of a speed gain reall...
[divverent/darkplaces.git] / world.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or 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
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // world.c -- world query functions
21
22 #include "quakedef.h"
23
24 /*
25
26 entities never clip against themselves, or their owner
27
28 line of sight checks trace->crosscontent, but bullets don't
29
30 */
31
32 cvar_t sv_useareanodes = {CVAR_NOTIFY, "sv_useareanodes", "1"};
33
34 void SV_World_Init(void)
35 {
36         Cvar_RegisterVariable(&sv_useareanodes);
37 }
38
39
40 void ClearLink (link_t *l);
41 void RemoveLink (link_t *l);
42 void InsertLinkBefore (link_t *l, link_t *before);
43 void InsertLinkAfter (link_t *l, link_t *after);
44
45 #define EDICT_FROM_AREA(l) ((edict_t *)((qbyte *)l - (int)&(((edict_t *)0)->area)))
46
47 //============================================================================
48
49 // ClearLink is used for new headnodes
50 void ClearLink (link_t *l)
51 {
52         l->prev = l->next = l;
53 }
54
55 void RemoveLink (link_t *l)
56 {
57         l->next->prev = l->prev;
58         l->prev->next = l->next;
59 }
60
61 void InsertLinkBefore (link_t *l, link_t *before)
62 {
63         l->next = before;
64         l->prev = before->prev;
65         l->prev->next = l;
66         l->next->prev = l;
67 }
68 void InsertLinkAfter (link_t *l, link_t *after)
69 {
70         l->next = after->next;
71         l->prev = after;
72         l->prev->next = l;
73         l->next->prev = l;
74 }
75
76
77 typedef struct
78 {
79         // bounding box of entire move area
80         vec3_t          boxmins, boxmaxs;
81
82         // size of the moving object
83         vec3_t          mins, maxs;
84
85         // size when clipping against monsters
86         vec3_t          mins2, maxs2;
87
88         // size when clipping against brush models
89         vec3_t          hullmins, hullmaxs;
90
91         // start and end origin of move
92         vec3_t          start, end;
93
94         // trace results
95         trace_t         trace;
96
97         // type of move (like ignoring monsters, or similar)
98         int                     type;
99
100         // the edict that is moving (if any)
101         edict_t         *passedict;
102 }
103 moveclip_t;
104
105
106 /*
107 ===============================================================================
108
109 ENTITY AREA CHECKING
110
111 ===============================================================================
112 */
113
114 typedef struct areanode_s
115 {
116         int             axis;           // -1 = leaf node
117         float   dist;
118         struct areanode_s       *children[2];
119         link_t  trigger_edicts;
120         link_t  solid_edicts;
121 } areanode_t;
122
123 #define AREA_DEPTH      4
124 #define AREA_NODES      32
125
126 static  areanode_t      sv_areanodes[AREA_NODES];
127 static  int                     sv_numareanodes;
128
129 /*
130 ===============
131 SV_CreateAreaNode
132
133 ===============
134 */
135 areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs)
136 {
137         areanode_t      *anode;
138         vec3_t          size;
139         vec3_t          mins1, maxs1, mins2, maxs2;
140
141         anode = &sv_areanodes[sv_numareanodes];
142         sv_numareanodes++;
143
144         ClearLink (&anode->trigger_edicts);
145         ClearLink (&anode->solid_edicts);
146
147         if (depth == AREA_DEPTH)
148         {
149                 anode->axis = -1;
150                 anode->children[0] = anode->children[1] = NULL;
151                 return anode;
152         }
153
154         VectorSubtract (maxs, mins, size);
155         if (size[0] > size[1])
156                 anode->axis = 0;
157         else
158                 anode->axis = 1;
159
160         anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
161         VectorCopy (mins, mins1);
162         VectorCopy (mins, mins2);
163         VectorCopy (maxs, maxs1);
164         VectorCopy (maxs, maxs2);
165
166         maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
167
168         anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2);
169         anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1);
170
171         return anode;
172 }
173
174 /*
175 ===============
176 SV_ClearWorld
177
178 ===============
179 */
180 void SV_ClearWorld (void)
181 {
182         Collision_Init ();
183
184         memset (sv_areanodes, 0, sizeof(sv_areanodes));
185         sv_numareanodes = 0;
186         Mod_CheckLoaded(sv.worldmodel);
187         SV_CreateAreaNode (0, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs);
188 }
189
190
191 /*
192 ===============
193 SV_UnlinkEdict
194
195 ===============
196 */
197 void SV_UnlinkEdict (edict_t *ent)
198 {
199         if (!ent->area.prev)
200                 return;         // not linked in anywhere
201         RemoveLink (&ent->area);
202         ent->area.prev = ent->area.next = NULL;
203 }
204
205
206 /*
207 ====================
208 SV_TouchLinks
209 ====================
210 */
211 void SV_TouchLinks ( edict_t *ent, areanode_t *node )
212 {
213         link_t          *l, *next;
214         edict_t         *touch;
215         int                     old_self, old_other;
216
217 loc0:
218 // touch linked edicts
219         for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next)
220         {
221                 next = l->next;
222                 touch = EDICT_FROM_AREA(l);
223                 if (touch == ent)
224                         continue;
225                 if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER)
226                         continue;
227                 if (ent->v.absmin[0] > touch->v.absmax[0]
228                  || ent->v.absmin[1] > touch->v.absmax[1]
229                  || ent->v.absmin[2] > touch->v.absmax[2]
230                  || ent->v.absmax[0] < touch->v.absmin[0]
231                  || ent->v.absmax[1] < touch->v.absmin[1]
232                  || ent->v.absmax[2] < touch->v.absmin[2])
233                         continue;
234                 old_self = pr_global_struct->self;
235                 old_other = pr_global_struct->other;
236
237                 pr_global_struct->self = EDICT_TO_PROG(touch);
238                 pr_global_struct->other = EDICT_TO_PROG(ent);
239                 pr_global_struct->time = sv.time;
240                 PR_ExecuteProgram (touch->v.touch, "");
241
242                 pr_global_struct->self = old_self;
243                 pr_global_struct->other = old_other;
244         }
245
246 // recurse down both sides
247         if (node->axis == -1)
248                 return;
249
250         if (ent->v.absmax[node->axis] > node->dist)
251         {
252                 if (ent->v.absmin[node->axis] < node->dist)
253                         SV_TouchLinks(ent, node->children[1]); // order reversed to reduce code
254                 node = node->children[0];
255                 goto loc0;
256         }
257         else
258         {
259                 if (ent->v.absmin[node->axis] < node->dist)
260                 {
261                         node = node->children[1];
262                         goto loc0;
263                 }
264         }
265 }
266
267
268 /*
269 ===============
270 SV_LinkEdict
271
272 ===============
273 */
274 void SV_LinkEdict (edict_t *ent, qboolean touch_triggers)
275 {
276         model_t         *model;
277         areanode_t      *node;
278
279         if (ent->area.prev)
280                 SV_UnlinkEdict (ent);   // unlink from old position
281
282         if (ent == sv.edicts)
283                 return;         // don't add the world
284
285         if (ent->free)
286                 return;
287
288 // set the abs box
289
290         if (ent->v.solid == SOLID_BSP)
291         {
292                 if (ent->v.modelindex < 0 || ent->v.modelindex > MAX_MODELS)
293                         PR_RunError("SOLID_BSP with invalid modelindex!\n");
294                 model = sv.models[(int) ent->v.modelindex];
295                 if (model != NULL)
296                 {
297                         if (model->type != mod_brush)
298                                 PR_RunError("SOLID_BSP with non-BSP model\n");
299
300                         if (ent->v.angles[0] || ent->v.angles[2] || ent->v.avelocity[0] || ent->v.avelocity[2])
301                         {
302                                 VectorAdd(ent->v.origin, model->rotatedmins, ent->v.absmin);
303                                 VectorAdd(ent->v.origin, model->rotatedmaxs, ent->v.absmax);
304                         }
305                         else if (ent->v.angles[1] || ent->v.avelocity[1])
306                         {
307                                 VectorAdd(ent->v.origin, model->yawmins, ent->v.absmin);
308                                 VectorAdd(ent->v.origin, model->yawmaxs, ent->v.absmax);
309                         }
310                         else
311                         {
312                                 VectorAdd(ent->v.origin, model->normalmins, ent->v.absmin);
313                                 VectorAdd(ent->v.origin, model->normalmaxs, ent->v.absmax);
314                         }
315                 }
316                 else
317                 {
318                         // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily
319                         VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
320                         VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
321                 }
322         }
323         else
324         {
325                 VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
326                 VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
327         }
328
329 //
330 // to make items easier to pick up and allow them to be grabbed off
331 // of shelves, the abs sizes are expanded
332 //
333         if ((int)ent->v.flags & FL_ITEM)
334         {
335                 ent->v.absmin[0] -= 15;
336                 ent->v.absmin[1] -= 15;
337                 ent->v.absmin[2] -= 1;
338                 ent->v.absmax[0] += 15;
339                 ent->v.absmax[1] += 15;
340                 ent->v.absmax[2] += 1;
341         }
342         else
343         {
344                 // because movement is clipped an epsilon away from an actual edge,
345                 // we must fully check even when bounding boxes don't quite touch
346                 ent->v.absmin[0] -= 1;
347                 ent->v.absmin[1] -= 1;
348                 ent->v.absmin[2] -= 1;
349                 ent->v.absmax[0] += 1;
350                 ent->v.absmax[1] += 1;
351                 ent->v.absmax[2] += 1;
352         }
353
354         if (ent->v.solid == SOLID_NOT)
355                 return;
356
357 // find the first node that the ent's box crosses
358         node = sv_areanodes;
359         while (1)
360         {
361                 if (node->axis == -1)
362                         break;
363                 if (ent->v.absmin[node->axis] > node->dist)
364                         node = node->children[0];
365                 else if (ent->v.absmax[node->axis] < node->dist)
366                         node = node->children[1];
367                 else
368                         break;          // crosses the node
369         }
370
371 // link it in
372
373         if (ent->v.solid == SOLID_TRIGGER)
374                 InsertLinkBefore (&ent->area, &node->trigger_edicts);
375         else
376                 InsertLinkBefore (&ent->area, &node->solid_edicts);
377
378 // if touch_triggers, touch all entities at this node and descend for more
379         if (touch_triggers)
380                 SV_TouchLinks ( ent, sv_areanodes );
381 }
382
383
384
385 /*
386 ===============================================================================
387
388 POINT TESTING IN HULLS
389
390 ===============================================================================
391 */
392
393 /*
394 ============
395 SV_TestEntityPosition
396
397 This could be a lot more efficient...
398 ============
399 */
400 int SV_TestEntityPosition (edict_t *ent)
401 {
402         return SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent).startsolid;
403 }
404
405
406 /*
407 ===============================================================================
408
409 LINE TESTING IN HULLS
410
411 ===============================================================================
412 */
413
414 /*
415 ==================
416 SV_ClipMoveToEntity
417
418 Handles selection or creation of a clipping hull, and offseting (and
419 eventually rotation) of the end points
420 ==================
421 */
422 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
423 {
424         int i;
425         trace_t trace;
426         model_t *model;
427
428         i = ent->v.modelindex;
429         if ((unsigned int) i >= MAX_MODELS)
430                 PR_RunError("SV_ClipMoveToEntity: invalid modelindex\n");
431         model = sv.models[i];
432         if (i != 0 && model == NULL)
433                 PR_RunError("SV_ClipMoveToEntity: invalid modelindex\n");
434
435         if ((int) ent->v.solid == SOLID_BSP)
436         {
437                 Mod_CheckLoaded(model);
438                 if (model->type != mod_brush)
439                 {
440                         Con_Printf ("SV_ClipMoveToEntity: SOLID_BSP with a non bsp model, entity dump:\n");
441                         ED_Print (ent);
442                         Host_Error ("SV_ClipMoveToEntity: SOLID_BSP with a non bsp model\n");
443                 }
444                 if (ent->v.movetype != MOVETYPE_PUSH)
445                         Host_Error ("SV_ClipMoveToEntity: SOLID_BSP without MOVETYPE_PUSH");
446         }
447
448         Collision_ClipTrace(&trace, ent, model, ent->v.origin, ent->v.angles, ent->v.mins, ent->v.maxs, start, mins, maxs, end);
449
450         return trace;
451 }
452
453 //===========================================================================
454
455 /*
456 ====================
457 SV_ClipToLinks
458
459 Mins and maxs enclose the entire area swept by the move
460 ====================
461 */
462 void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip )
463 {
464         link_t          *l, *next;
465         edict_t         *touch;
466         trace_t         trace;
467
468 loc0:
469         if (clip->trace.allsolid)
470                 return;
471 // touch linked edicts
472         for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
473         {
474                 next = l->next;
475                 touch = EDICT_FROM_AREA(l);
476                 if (touch->v.solid == SOLID_NOT)
477                         continue;
478                 if (touch == clip->passedict)
479                         continue;
480                 if (touch->v.solid == SOLID_TRIGGER)
481                         Host_Error ("Trigger in clipping list");
482
483                 if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
484                         continue;
485
486                 if (clip->boxmins[0] > touch->v.absmax[0]
487                  || clip->boxmaxs[0] < touch->v.absmin[0]
488                  || clip->boxmins[1] > touch->v.absmax[1]
489                  || clip->boxmaxs[1] < touch->v.absmin[1]
490                  || clip->boxmins[2] > touch->v.absmax[2]
491                  || clip->boxmaxs[2] < touch->v.absmin[2])
492                         continue;
493
494                 if (clip->passedict)
495                 {
496                         if (clip->passedict->v.size[0] && !touch->v.size[0])
497                                 continue;       // points never interact
498                         if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
499                                 continue;       // don't clip against own missiles
500                         if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
501                                 continue;       // don't clip against owner
502                         // LordHavoc: corpse code
503                         if (clip->passedict->v.solid == SOLID_CORPSE && (touch->v.solid == SOLID_SLIDEBOX || touch->v.solid == SOLID_CORPSE))
504                                 continue;
505                         if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)
506                                 continue;
507                 }
508
509                 // might interact, so do an exact clip
510                 if (touch->v.solid == SOLID_BSP)
511                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->hullmins, clip->hullmaxs, clip->end);
512                 else if ((int)touch->v.flags & FL_MONSTER)
513                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end);
514                 else
515                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end);
516                 // LordHavoc: take the 'best' answers from the new trace and combine with existing data
517                 if (trace.allsolid)
518                         clip->trace.allsolid = true;
519                 if (trace.startsolid)
520                 {
521                         clip->trace.startsolid = true;
522                         if (!clip->trace.ent)
523                                 clip->trace.ent = trace.ent;
524                 }
525                 if (trace.inopen)
526                         clip->trace.inopen = true;
527                 if (trace.inwater)
528                         clip->trace.inwater = true;
529                 if (trace.fraction < clip->trace.fraction)
530                 {
531                         clip->trace.fraction = trace.fraction;
532                         VectorCopy(trace.endpos, clip->trace.endpos);
533                         clip->trace.plane = trace.plane;
534                         clip->trace.endcontents = trace.endcontents;
535                         clip->trace.ent = trace.ent;
536                 }
537         }
538
539 // recurse down both sides
540         if (node->axis == -1)
541                 return;
542
543         if (clip->boxmaxs[node->axis] > node->dist)
544         {
545                 if (clip->boxmins[node->axis] < node->dist)
546                         SV_ClipToLinks(node->children[1], clip);
547                 node = node->children[0];
548                 goto loc0;
549         }
550         else if (clip->boxmins[node->axis] < node->dist)
551         {
552                 node = node->children[1];
553                 goto loc0;
554         }
555 }
556
557
558 /*
559 ==================
560 SV_MoveBounds
561 ==================
562 */
563 void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
564 {
565         if (sv_useareanodes.integer)
566         {
567                 int i;
568
569                 for (i=0 ; i<3 ; i++)
570                 {
571                         if (end[i] > start[i])
572                         {
573                                 boxmins[i] = start[i] + mins[i] - 1;
574                                 boxmaxs[i] = end[i] + maxs[i] + 1;
575                         }
576                         else
577                         {
578                                 boxmins[i] = end[i] + mins[i] - 1;
579                                 boxmaxs[i] = start[i] + maxs[i] + 1;
580                         }
581                 }
582         }
583         else
584         {
585                 // debug to test against everything
586                 boxmins[0] = boxmins[1] = boxmins[2] = -999999999;
587                 boxmaxs[0] = boxmaxs[1] = boxmaxs[2] =  999999999;
588         }
589 }
590
591 /*
592 ==================
593 SV_Move
594 ==================
595 */
596 trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict)
597 {
598         moveclip_t      clip;
599         vec3_t          bigmins, bigmaxs;
600         int                     i;
601
602         memset ( &clip, 0, sizeof ( moveclip_t ) );
603
604         VectorCopy(start, clip.start);
605         VectorCopy(end, clip.end);
606         VectorCopy(mins, clip.mins);
607         VectorCopy(maxs, clip.maxs);
608         clip.type = type;
609         clip.passedict = passedict;
610
611         Collision_RoundUpToHullSize(sv.worldmodel, clip.mins, clip.maxs, clip.hullmins, clip.hullmaxs);
612
613         if (type == MOVE_MISSILE)
614         {
615                 // LordHavoc: modified this, was = -15, now = clip.mins[i] - 15
616                 for (i=0 ; i<3 ; i++)
617                 {
618                         clip.mins2[i] = clip.mins[i] - 15;
619                         clip.maxs2[i] = clip.maxs[i] + 15;
620                 }
621         }
622         else
623         {
624                 VectorCopy (clip.mins, clip.mins2);
625                 VectorCopy (clip.maxs, clip.maxs2);
626         }
627
628         bigmins[0] = min(clip.mins2[0], clip.hullmins[0]);
629         bigmaxs[0] = max(clip.maxs2[0], clip.hullmaxs[0]);
630         bigmins[1] = min(clip.mins2[1], clip.hullmins[1]);
631         bigmaxs[1] = max(clip.maxs2[1], clip.hullmaxs[1]);
632         bigmins[2] = min(clip.mins2[2], clip.hullmins[2]);
633         bigmaxs[2] = max(clip.maxs2[2], clip.hullmaxs[2]);
634
635         // clip to world
636         clip.trace = SV_ClipMoveToEntity (sv.edicts, start, mins, maxs, end);
637
638         // clip to entities
639         // create the bounding box of the entire move
640         SV_MoveBounds ( start, bigmins, bigmaxs, end, clip.boxmins, clip.boxmaxs );
641
642         SV_ClipToLinks ( sv_areanodes, &clip );
643
644         return clip.trace;
645 }
646