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