]> icculus.org git repositories - divverent/darkplaces.git/blob - world.c
removed steporigin etc, they aren't used anymore
[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 /*
33 typedef struct link_s
34 {
35         struct link_s   *prev, *next;
36 } link_t;
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 // (type *)STRUCT_FROM_LINK(link_t *link, type, member)
46 // ent = STRUCT_FROM_LINK(link,entity_t,order)
47 // FIXME: remove this mess!
48 //#define       STRUCT_FROM_LINK(l,t,m) ((t *)((byte *)l - (int)&(((t *)0)->m)))
49
50 #define EDICT_FROM_AREA(l) ((edict_t *)((byte *)l - (int)&(((edict_t *)0)->area)))
51
52 //============================================================================
53
54 // ClearLink is used for new headnodes
55 void ClearLink (link_t *l)
56 {
57         l->prev = l->next = l;
58 }
59
60 void RemoveLink (link_t *l)
61 {
62         l->next->prev = l->prev;
63         l->prev->next = l->next;
64 }
65
66 void InsertLinkBefore (link_t *l, link_t *before)
67 {
68         l->next = before;
69         l->prev = before->prev;
70         l->prev->next = l;
71         l->next->prev = l;
72 }
73 void InsertLinkAfter (link_t *l, link_t *after)
74 {
75         l->next = after->next;
76         l->prev = after;
77         l->prev->next = l;
78         l->next->prev = l;
79 }
80
81
82 typedef struct
83 {
84         vec3_t          boxmins, boxmaxs;// enclose the test object along entire move
85         float           *mins, *maxs;   // size of the moving object
86         vec3_t          mins2, maxs2;   // size when clipping against mosnters
87         float           *start, *end;
88         trace_t         trace;
89         int                     type;
90         edict_t         *passedict;
91 } moveclip_t;
92
93
94 /*
95 ===============================================================================
96
97 HULL BOXES
98
99 ===============================================================================
100 */
101
102
103 static  hull_t          box_hull;
104 static  dclipnode_t     box_clipnodes[6];
105 static  mplane_t        box_planes[6];
106
107 /*
108 ===================
109 SV_InitBoxHull
110
111 Set up the planes and clipnodes so that the six floats of a bounding box
112 can just be stored out and get a proper hull_t structure.
113 ===================
114 */
115 void SV_InitBoxHull (void)
116 {
117         int             i;
118         int             side;
119
120         box_hull.clipnodes = box_clipnodes;
121         box_hull.planes = box_planes;
122         box_hull.firstclipnode = 0;
123         box_hull.lastclipnode = 5;
124
125         for (i=0 ; i<6 ; i++)
126         {
127                 box_clipnodes[i].planenum = i;
128                 
129                 side = i&1;
130                 
131                 box_clipnodes[i].children[side] = CONTENTS_EMPTY;
132                 if (i != 5)
133                         box_clipnodes[i].children[side^1] = i + 1;
134                 else
135                         box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
136                 
137                 box_planes[i].type = i>>1;
138                 box_planes[i].normal[i>>1] = 1;
139         }
140         
141 }
142
143
144 /*
145 ===================
146 SV_HullForBox
147
148 To keep everything totally uniform, bounding boxes are turned into small
149 BSP trees instead of being compared directly.
150 ===================
151 */
152 hull_t  *SV_HullForBox (vec3_t mins, vec3_t maxs)
153 {
154         box_planes[0].dist = maxs[0];
155         box_planes[1].dist = mins[0];
156         box_planes[2].dist = maxs[1];
157         box_planes[3].dist = mins[1];
158         box_planes[4].dist = maxs[2];
159         box_planes[5].dist = mins[2];
160
161         return &box_hull;
162 }
163
164
165
166 /*
167 ================
168 SV_HullForEntity
169
170 Returns a hull that can be used for testing or clipping an object of mins/maxs
171 size.
172 Offset is filled in to contain the adjustment that must be added to the
173 testing object's origin to get a point to use with the returned hull.
174 ================
175 */
176 hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset)
177 {
178         model_t         *model;
179         vec3_t          size;
180         vec3_t          hullmins, hullmaxs;
181         hull_t          *hull;
182
183 // decide which clipping hull to use, based on the size
184         if (ent->v.solid == SOLID_BSP)
185         {       // explicit hulls in the BSP model
186                 if (ent->v.movetype != MOVETYPE_PUSH)
187                         Host_Error ("SOLID_BSP without MOVETYPE_PUSH");
188
189                 model = sv.models[ (int)ent->v.modelindex ];
190                 Mod_CheckLoaded(model);
191
192                 // LordHavoc: fixed SOLID_BSP error message
193                 if (!model || model->type != mod_brush)
194                 {
195                         Con_Printf ("SOLID_BSP with a non bsp model, entity dump:\n");
196                         ED_Print (ent);
197                         Host_Error ("SOLID_BSP with a non bsp model\n");
198                 }
199
200                 VectorSubtract (maxs, mins, size);
201                 // LordHavoc: FIXME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
202                 if (sv.worldmodel->ishlbsp)
203                 {
204                         if (size[0] < 3)
205                                 hull = &model->hulls[0]; // 0x0x0
206                         else if (size[0] <= 32)
207                         {
208                                 if (size[2] < 54) // pick the nearest of 36 or 72
209                                         hull = &model->hulls[3]; // 32x32x36
210                                 else
211                                         hull = &model->hulls[1]; // 32x32x72
212                         }
213                         else
214                                 hull = &model->hulls[2]; // 64x64x64
215                 }
216                 else
217                 {
218                         if (size[0] < 3)
219                                 hull = &model->hulls[0]; // 0x0x0
220                         else if (size[0] <= 32)
221                                 hull = &model->hulls[1]; // 32x32x56
222                         else
223                                 hull = &model->hulls[2]; // 64x64x88
224                 }
225
226 // calculate an offset value to center the origin
227                 VectorSubtract (hull->clip_mins, mins, offset);
228                 VectorAdd (offset, ent->v.origin, offset);
229         }
230         else
231         {       // create a temp hull from bounding box sizes
232
233                 VectorSubtract (ent->v.mins, maxs, hullmins);
234                 VectorSubtract (ent->v.maxs, mins, hullmaxs);
235                 hull = SV_HullForBox (hullmins, hullmaxs);
236
237                 VectorCopy (ent->v.origin, offset);
238         }
239
240
241         return hull;
242 }
243
244 /*
245 ===============================================================================
246
247 ENTITY AREA CHECKING
248
249 ===============================================================================
250 */
251
252 typedef struct areanode_s
253 {
254         int             axis;           // -1 = leaf node
255         float   dist;
256         struct areanode_s       *children[2];
257         link_t  trigger_edicts;
258         link_t  solid_edicts;
259 } areanode_t;
260
261 #define AREA_DEPTH      4
262 #define AREA_NODES      32
263
264 static  areanode_t      sv_areanodes[AREA_NODES];
265 static  int                     sv_numareanodes;
266
267 /*
268 ===============
269 SV_CreateAreaNode
270
271 ===============
272 */
273 areanode_t *SV_CreateAreaNode (int depth, vec3_t mins, vec3_t maxs)
274 {
275         areanode_t      *anode;
276         vec3_t          size;
277         vec3_t          mins1, maxs1, mins2, maxs2;
278
279         anode = &sv_areanodes[sv_numareanodes];
280         sv_numareanodes++;
281
282         ClearLink (&anode->trigger_edicts);
283         ClearLink (&anode->solid_edicts);
284
285         if (depth == AREA_DEPTH)
286         {
287                 anode->axis = -1;
288                 anode->children[0] = anode->children[1] = NULL;
289                 return anode;
290         }
291
292         VectorSubtract (maxs, mins, size);
293         if (size[0] > size[1])
294                 anode->axis = 0;
295         else
296                 anode->axis = 1;
297
298         anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
299         VectorCopy (mins, mins1);
300         VectorCopy (mins, mins2);
301         VectorCopy (maxs, maxs1);
302         VectorCopy (maxs, maxs2);
303
304         maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
305
306         anode->children[0] = SV_CreateAreaNode (depth+1, mins2, maxs2);
307         anode->children[1] = SV_CreateAreaNode (depth+1, mins1, maxs1);
308
309         return anode;
310 }
311
312 /*
313 ===============
314 SV_ClearWorld
315
316 ===============
317 */
318 void SV_ClearWorld (void)
319 {
320         SV_InitBoxHull ();
321
322         memset (sv_areanodes, 0, sizeof(sv_areanodes));
323         sv_numareanodes = 0;
324         Mod_CheckLoaded(sv.worldmodel);
325         SV_CreateAreaNode (0, sv.worldmodel->normalmins, sv.worldmodel->normalmaxs);
326 }
327
328
329 /*
330 ===============
331 SV_UnlinkEdict
332
333 ===============
334 */
335 void SV_UnlinkEdict (edict_t *ent)
336 {
337         if (!ent->area.prev)
338                 return;         // not linked in anywhere
339         RemoveLink (&ent->area);
340         ent->area.prev = ent->area.next = NULL;
341 }
342
343
344 /*
345 ====================
346 SV_TouchLinks
347 ====================
348 */
349 void SV_TouchLinks ( edict_t *ent, areanode_t *node )
350 {
351         link_t          *l, *next;
352         edict_t         *touch;
353         int                     old_self, old_other;
354
355 loc0:
356 // touch linked edicts
357         for (l = node->trigger_edicts.next ; l != &node->trigger_edicts ; l = next)
358         {
359                 next = l->next;
360                 touch = EDICT_FROM_AREA(l);
361                 if (touch == ent)
362                         continue;
363                 if (!touch->v.touch || touch->v.solid != SOLID_TRIGGER)
364                         continue;
365                 if (ent->v.absmin[0] > touch->v.absmax[0]
366                  || ent->v.absmin[1] > touch->v.absmax[1]
367                  || ent->v.absmin[2] > touch->v.absmax[2]
368                  || ent->v.absmax[0] < touch->v.absmin[0]
369                  || ent->v.absmax[1] < touch->v.absmin[1]
370                  || ent->v.absmax[2] < touch->v.absmin[2])
371                         continue;
372                 old_self = pr_global_struct->self;
373                 old_other = pr_global_struct->other;
374
375                 pr_global_struct->self = EDICT_TO_PROG(touch);
376                 pr_global_struct->other = EDICT_TO_PROG(ent);
377                 pr_global_struct->time = sv.time;
378                 PR_ExecuteProgram (touch->v.touch, "");
379
380                 pr_global_struct->self = old_self;
381                 pr_global_struct->other = old_other;
382         }
383
384 // recurse down both sides
385         if (node->axis == -1)
386                 return;
387
388         // LordHavoc: optimized recursion
389 //      if (ent->v.absmax[node->axis] > node->dist) SV_TouchLinks (ent, node->children[0]);
390 //      if (ent->v.absmin[node->axis] < node->dist) SV_TouchLinks (ent, node->children[1]);
391         if (ent->v.absmax[node->axis] > node->dist)
392         {
393                 if (ent->v.absmin[node->axis] < node->dist)
394                         SV_TouchLinks(ent, node->children[1]); // order reversed to reduce code
395                 node = node->children[0];
396                 goto loc0;
397         }
398         else
399         {
400                 if (ent->v.absmin[node->axis] < node->dist)
401                 {
402                         node = node->children[1];
403                         goto loc0;
404                 }
405         }
406 }
407
408
409 /*
410 ===============
411 SV_LinkEdict
412
413 ===============
414 */
415 void SV_LinkEdict (edict_t *ent, qboolean touch_triggers)
416 {
417         areanode_t      *node;
418
419         if (ent->area.prev)
420                 SV_UnlinkEdict (ent);   // unlink from old position
421                 
422         if (ent == sv.edicts)
423                 return;         // don't add the world
424
425         if (ent->free)
426                 return;
427
428 // set the abs box
429
430 // LordHavoc: enabling rotating bmodels
431         if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]))
432         {
433                 // expand for rotation
434                 float           max, v;
435                 int                     i;
436
437                 max = DotProduct(ent->v.mins, ent->v.mins);
438                 v = DotProduct(ent->v.maxs, ent->v.maxs);
439                 if (max < v)
440                         max = v;
441                 max = sqrt(max);
442                 /*
443                 max = 0;
444                 for (i=0 ; i<3 ; i++)
445                 {
446                         v = fabs(ent->v.mins[i]);
447                         if (max < v)
448                                 max = v;
449                         v = fabs(ent->v.maxs[i]);
450                         if (max < v)
451                                 max = v;
452                 }
453                 */
454                 for (i=0 ; i<3 ; i++)
455                 {
456                         ent->v.absmin[i] = ent->v.origin[i] - max;
457                         ent->v.absmax[i] = ent->v.origin[i] + max;
458                 }
459         }
460         else
461         {
462                 VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
463                 VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
464         }
465
466 //
467 // to make items easier to pick up and allow them to be grabbed off
468 // of shelves, the abs sizes are expanded
469 //
470         if ((int)ent->v.flags & FL_ITEM)
471         {
472                 ent->v.absmin[0] -= 15;
473                 ent->v.absmin[1] -= 15;
474                 ent->v.absmax[0] += 15;
475                 ent->v.absmax[1] += 15;
476         }
477         else
478         {
479                 // because movement is clipped an epsilon away from an actual edge,
480                 // we must fully check even when bounding boxes don't quite touch
481                 ent->v.absmin[0] -= 1;
482                 ent->v.absmin[1] -= 1;
483                 ent->v.absmin[2] -= 1;
484                 ent->v.absmax[0] += 1;
485                 ent->v.absmax[1] += 1;
486                 ent->v.absmax[2] += 1;
487         }
488
489         if (ent->v.solid == SOLID_NOT)
490                 return;
491
492 // find the first node that the ent's box crosses
493         node = sv_areanodes;
494         while (1)
495         {
496                 if (node->axis == -1)
497                         break;
498                 if (ent->v.absmin[node->axis] > node->dist)
499                         node = node->children[0];
500                 else if (ent->v.absmax[node->axis] < node->dist)
501                         node = node->children[1];
502                 else
503                         break;          // crosses the node
504         }
505
506 // link it in
507
508         if (ent->v.solid == SOLID_TRIGGER)
509                 InsertLinkBefore (&ent->area, &node->trigger_edicts);
510         else
511                 InsertLinkBefore (&ent->area, &node->solid_edicts);
512
513 // if touch_triggers, touch all entities at this node and descend for more
514         if (touch_triggers)
515                 SV_TouchLinks ( ent, sv_areanodes );
516 }
517
518
519
520 /*
521 ===============================================================================
522
523 POINT TESTING IN HULLS
524
525 ===============================================================================
526 */
527
528 /*
529 ==================
530 SV_HullPointContents
531
532 ==================
533 */
534 int SV_HullPointContents (hull_t *hull, int num, vec3_t p)
535 {
536         while (num >= 0)
537                 num = hull->clipnodes[num].children[(hull->planes[hull->clipnodes[num].planenum].type < 3 ? p[hull->planes[hull->clipnodes[num].planenum].type] : DotProduct (hull->planes[hull->clipnodes[num].planenum].normal, p)) < hull->planes[hull->clipnodes[num].planenum].dist];
538
539         return num;
540 }
541
542 /*
543 ============
544 SV_TestEntityPosition
545
546 This could be a lot more efficient...
547 ============
548 */
549 edict_t *SV_TestEntityPosition (edict_t *ent)
550 {
551         trace_t trace;
552
553         trace = SV_Move (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent);
554
555         if (trace.startsolid)
556                 return sv.edicts;
557                 
558         return NULL;
559 }
560
561
562 /*
563 ===============================================================================
564
565 LINE TESTING IN HULLS
566
567 ===============================================================================
568 */
569
570 // 1/32 epsilon to keep floating point happy
571 //#define       DIST_EPSILON    (0.03125)
572 #define DIST_EPSILON (0.125)
573
574 #define HULLCHECKSTATE_EMPTY 0
575 #define HULLCHECKSTATE_SOLID 1
576 #define HULLCHECKSTATE_DONE 2
577
578 // LordHavoc: FIXME: this is not thread safe, if threading matters here, pass
579 // this as a struct to RecursiveHullCheck, RecursiveHullCheck_Impact, etc...
580 RecursiveHullCheckTraceInfo_t RecursiveHullCheckInfo;
581 #define RHC RecursiveHullCheckInfo
582
583 void SV_RecursiveHullCheck_Impact (mplane_t *plane, int side)
584 {
585         // LordHavoc: using doubles for extra accuracy
586         double t1, t2, frac;
587
588         // LordHavoc: now that we have found the impact, recalculate the impact
589         // point from scratch for maximum accuracy, with an epsilon bias on the
590         // surface distance
591         frac = plane->dist;
592         if (side)
593         {
594                 frac -= DIST_EPSILON;
595                 VectorNegate (plane->normal, RHC.trace->plane.normal);
596                 RHC.trace->plane.dist = -plane->dist;
597         }
598         else
599         {
600                 frac += DIST_EPSILON;
601                 VectorCopy (plane->normal, RHC.trace->plane.normal);
602                 RHC.trace->plane.dist = plane->dist;
603         }
604
605         if (plane->type < 3)
606         {
607                 t1 = RHC.start[plane->type] - frac;
608                 t2 = RHC.start[plane->type] + RHC.dist[plane->type] - frac;
609         }
610         else
611         {
612                 t1 = plane->normal[0] * RHC.start[0] + plane->normal[1] * RHC.start[1] + plane->normal[2] * RHC.start[2] - frac;
613                 t2 = plane->normal[0] * (RHC.start[0] + RHC.dist[0]) + plane->normal[1] * (RHC.start[1] + RHC.dist[1]) + plane->normal[2] * (RHC.start[2] + RHC.dist[2]) - frac;
614         }
615
616         frac = t1 / (t1 - t2);
617         frac = bound(0.0f, frac, 1.0f);
618
619         RHC.trace->fraction = frac;
620         RHC.trace->endpos[0] = RHC.start[0] + frac * RHC.dist[0];
621         RHC.trace->endpos[1] = RHC.start[1] + frac * RHC.dist[1];
622         RHC.trace->endpos[2] = RHC.start[2] + frac * RHC.dist[2];
623 }
624
625 int SV_RecursiveHullCheck (int num, float p1f, float p2f, vec3_t p1, vec3_t p2)
626 {
627         dclipnode_t     *node;
628         vec3_t          mid;
629         int                     side;
630         float           midf;
631         // LordHavoc: FIXME: this is not thread safe...  if threading matters here,
632         // remove the static prefixes
633         static int ret;
634         static mplane_t *plane;
635         static float t1, t2, frac;
636
637         // LordHavoc: a goto!  everyone flee in terror... :)
638 loc0:
639         // check for empty
640         if (num < 0)
641         {
642                 RHC.trace->endcontents = num;
643                 if (RHC.trace->startcontents)
644                 {
645                         if (num == RHC.trace->startcontents)
646                                 RHC.trace->allsolid = false;
647                         else
648                         {
649                                 // if the first leaf is solid, set startsolid
650                                 if (RHC.trace->allsolid)
651                                         RHC.trace->startsolid = true;
652                                 return HULLCHECKSTATE_SOLID;
653                         }
654                         return HULLCHECKSTATE_EMPTY;
655                 }
656                 else
657                 {
658                         if (num != CONTENTS_SOLID)
659                         {
660                                 RHC.trace->allsolid = false;
661                                 if (num == CONTENTS_EMPTY)
662                                         RHC.trace->inopen = true;
663                                 else
664                                         RHC.trace->inwater = true;
665                         }
666                         else
667                         {
668                                 // if the first leaf is solid, set startsolid
669                                 if (RHC.trace->allsolid)
670                                         RHC.trace->startsolid = true;
671                                 return HULLCHECKSTATE_SOLID;
672                         }
673                         return HULLCHECKSTATE_EMPTY;
674                 }
675         }
676
677         // find the point distances
678         node = RHC.hull->clipnodes + num;
679
680         plane = RHC.hull->planes + node->planenum;
681         if (plane->type < 3)
682         {
683                 t1 = p1[plane->type] - plane->dist;
684                 t2 = p2[plane->type] - plane->dist;
685         }
686         else
687         {
688                 t1 = DotProduct (plane->normal, p1) - plane->dist;
689                 t2 = DotProduct (plane->normal, p2) - plane->dist;
690         }
691
692         // LordHavoc: rearranged the side/frac code
693         if (t1 >= 0)
694         {
695                 if (t2 >= 0)
696                 {
697                         num = node->children[0];
698                         goto loc0;
699                 }
700                 // put the crosspoint DIST_EPSILON pixels on the near side
701                 side = 0;
702         }
703         else
704         {
705                 if (t2 < 0)
706                 {
707                         num = node->children[1];
708                         goto loc0;
709                 }
710                 // put the crosspoint DIST_EPSILON pixels on the near side
711                 side = 1;
712         }
713
714         frac = t1 / (t1 - t2);
715         frac = bound(0.0f, frac, 1.0f);
716
717         midf = p1f + ((p2f - p1f) * frac);
718         mid[0] = RHC.start[0] + midf * RHC.dist[0];
719         mid[1] = RHC.start[1] + midf * RHC.dist[1];
720         mid[2] = RHC.start[2] + midf * RHC.dist[2];
721
722         // front side first
723         ret = SV_RecursiveHullCheck (node->children[side], p1f, midf, p1, mid);
724         if (ret != HULLCHECKSTATE_EMPTY)
725                 return ret; // solid or done
726         ret = SV_RecursiveHullCheck (node->children[!side], midf, p2f, mid, p2);
727         if (ret != HULLCHECKSTATE_SOLID)
728                 return ret; // empty or done
729
730         // front is air and back is solid, this is the impact point...
731         SV_RecursiveHullCheck_Impact(RHC.hull->planes + node->planenum, side);
732
733         return HULLCHECKSTATE_DONE;
734 }
735
736 /*
737 qboolean SV_RecursiveHullCheckContentBoundary (hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, trace_t *trace)
738 {
739         dclipnode_t     *node;
740         mplane_t        *plane;
741         float           t1, t2;
742         float           frac;
743         int                     i;
744         vec3_t          mid;
745         int                     side;
746         float           midf;
747
748         // LordHavoc: a goto!  everyone flee in terror... :)
749 loc0:
750 // check for empty
751         if (num < 0)
752         {
753                 if (num != trace->startcontents)
754                         trace->startsolid = true;
755                 else
756                         trace->allsolid = false;
757                 return true;            // empty
758         }
759
760 // find the point distances
761         node = hull->clipnodes + num;
762         plane = hull->planes + node->planenum;
763
764         if (plane->type < 3)
765         {
766                 t1 = p1[plane->type] - plane->dist;
767                 t2 = p2[plane->type] - plane->dist;
768         }
769         else
770         {
771                 t1 = DotProduct (plane->normal, p1) - plane->dist;
772                 t2 = DotProduct (plane->normal, p2) - plane->dist;
773         }
774
775         // LordHavoc: rearranged the side/frac code
776         // LordHavoc: recursion optimization
777         if (t1 >= 0)
778         {
779                 if (t2 >= 0)
780                 {
781                         num = node->children[0];
782                         goto loc0;
783                 }
784                 // put the crosspoint DIST_EPSILON pixels on the near side
785                 side = 0;
786         }
787         else
788         {
789                 if (t2 < 0)
790                 {
791                         num = node->children[1];
792                         goto loc0;
793                 }
794                 // put the crosspoint DIST_EPSILON pixels on the near side
795                 side = 1;
796         }
797
798         frac = t1 / (t1 - t2);
799         frac = bound(0.0f, frac, 1.0f);
800
801         midf = p1f + ((p2f - p1f) * frac);
802         mid[0] = p1[0] + ((p2[0] - p1[0]) * frac);
803         mid[1] = p1[1] + ((p2[1] - p1[1]) * frac);
804         mid[2] = p1[2] + ((p2[2] - p1[2]) * frac);
805
806 // move up to the node
807         if (!SV_RecursiveHullCheck (hull, node->children[side], p1f, midf, p1, mid, trace) )
808                 return false;
809
810 */
811         /*
812 #ifdef PARANOID
813         if (SV_HullPointContents (pm_hullmodel, mid, node->children[side]) != trace->startcontents)
814         {
815                 Con_Printf ("mid PointInHullSolid\n");
816                 return false;
817         }
818 #endif
819         */
820 /*
821
822         // LordHavoc: warning to the clumsy, this recursion can not be optimized because mid would need to be duplicated on a stack
823         if (SV_HullPointContents (hull, node->children[side^1], mid) == trace->startcontents)
824 // go past the node
825                 return SV_RecursiveHullCheck (hull, node->children[side^1], midf, p2f, mid, p2, trace);
826
827         if (trace->allsolid)
828                 return false;           // never got out of the solid area
829
830 //==================
831 // the other side of the node is solid, this is the impact point
832 //==================
833         if (!side)
834         {
835                 VectorCopy (plane->normal, trace->plane.normal);
836                 trace->plane.dist = plane->dist;
837         }
838         else
839         {
840                 VectorNegate (plane->normal, trace->plane.normal);
841                 trace->plane.dist = -plane->dist;
842         }
843
844 */
845         /*
846         while (SV_HullPointContents (hull, hull->firstclipnode, mid) != trace->startcontents)
847         {
848                 // shouldn't really happen, but does occasionally
849                 frac -= 0.1;
850                 if (frac < 0)
851                 {
852                         trace->fraction = midf;
853                         VectorCopy (mid, trace->endpos);
854                         Con_DPrintf ("backup past 0\n");
855                         return false;
856                 }
857                 midf = p1f + (p2f - p1f)*frac;
858                 mid[0] = p1[0] + frac*(p2[0] - p1[0]);
859                 mid[1] = p1[1] + frac*(p2[1] - p1[1]);
860                 mid[2] = p1[2] + frac*(p2[2] - p1[2]);
861         }
862         */
863 /*
864
865         frac = t1;
866         if (side)
867                 frac += DIST_EPSILON;
868         else
869                 frac -= DIST_EPSILON;
870
871         frac /= (t1 - t2);
872         frac = bound(0.0f, frac, 1.0f);
873
874         trace->fraction = p1f + (p2f - p1f)*frac;
875         trace->endpos[0] = p1[0] + frac*(p2[0] - p1[0]);
876         trace->endpos[1] = p1[1] + frac*(p2[1] - p1[1]);
877         trace->endpos[2] = p1[2] + frac*(p2[2] - p1[2]);
878
879         return false;
880 }
881 */
882
883 /*
884 // FIXME: this is broken and I'm not interested in figuring out what is broken about it right now
885 qboolean SV_TestLine (hull_t *hull, int num, vec3_t p1, vec3_t p2)
886 {
887         dclipnode_t     *node;
888         mplane_t        *plane;
889         float           t1, t2, frac;
890         vec3_t          mid;
891         int                     side;
892
893 loc0:
894 // check for empty
895         if (num < 0)
896                 return num != CONTENTS_SOLID;
897
898         if (num < hull->firstclipnode || num > hull->lastclipnode)
899                 Sys_Error ("SV_RecursiveHullCheck: bad node number");
900
901 //
902 // find the point distances
903 //
904         node = hull->clipnodes + num;
905         if (node->children[0] < 0)
906         {
907                 if (node->children[0] == CONTENTS_SOLID)
908                         return false;
909                 if (node->children[1] < 0)
910                         return node->children[1] != CONTENTS_SOLID;
911         }
912         else if (node->children[1] == CONTENTS_SOLID)
913                 return false;
914
915         plane = hull->planes + node->planenum;
916
917         if (plane->type < 3)
918         {
919                 t1 = p1[plane->type] - plane->dist;
920                 t2 = p2[plane->type] - plane->dist;
921         }
922         else
923         {
924                 t1 = DotProduct (plane->normal, p1) - plane->dist;
925                 t2 = DotProduct (plane->normal, p2) - plane->dist;
926         }
927
928         if (t1 >= 0)
929         {
930                 if (t2 >= 0)
931                 {
932                         num = node->children[0];
933                         goto loc0;
934                 }
935                 side = 0;
936         }
937         else
938         {
939                 if (t2 < 0)
940                 {
941                         num = node->children[1];
942                         goto loc0;
943                 }
944                 side = 1;
945         }
946
947         if (node->children[side] < 0)
948         {
949                 if (node->children[side] == CONTENTS_SOLID)
950                         return false;
951
952                 if (node->children[!side] < 0)
953                         return node->children[!side] != CONTENTS_SOLID;
954                 else
955                 {
956                         frac = t1 / (t1 - t2);
957                         frac = bound(0, frac, 1);
958
959                         mid[0] = p1[0] + frac*(p2[0] - p1[0]);
960                         mid[1] = p1[1] + frac*(p2[1] - p1[1]);
961                         mid[2] = p1[2] + frac*(p2[2] - p1[2]);
962
963                         return SV_TestLine(hull, node->children[!side], mid, p2);
964                 }
965         }
966         else
967         {
968                 if (node->children[!side] < 0)
969                 {
970                         if (node->children[!side] == CONTENTS_SOLID)
971                                 return false;
972
973                         frac = t1 / (t1 - t2);
974                         frac = bound(0, frac, 1);
975
976                         mid[0] = p1[0] + frac*(p2[0] - p1[0]);
977                         mid[1] = p1[1] + frac*(p2[1] - p1[1]);
978                         mid[2] = p1[2] + frac*(p2[2] - p1[2]);
979
980                         return SV_TestLine(hull, node->children[side], p1, mid);
981                 }
982                 else
983                 {
984                         frac = t1 / (t1 - t2);
985                         frac = bound(0, frac, 1);
986
987                         mid[0] = p1[0] + frac*(p2[0] - p1[0]);
988                         mid[1] = p1[1] + frac*(p2[1] - p1[1]);
989                         mid[2] = p1[2] + frac*(p2[2] - p1[2]);
990
991                         if (SV_TestLine(hull, node->children[side], p1, mid))
992                                 return SV_TestLine(hull, node->children[!side], mid, p2);
993                         else
994                                 return false;
995                 }
996         }
997 }
998 */
999
1000
1001 /*
1002 ==================
1003 SV_ClipMoveToEntity
1004
1005 Handles selection or creation of a clipping hull, and offseting (and
1006 eventually rotation) of the end points
1007 ==================
1008 */
1009 trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
1010 {
1011         trace_t         trace;
1012         vec3_t          offset;
1013         vec3_t          start_l, end_l;
1014         hull_t          *hull;
1015
1016 // fill in a default trace
1017         memset (&trace, 0, sizeof(trace_t));
1018         trace.fraction = 1;
1019         trace.allsolid = true;
1020         VectorCopy (end, trace.endpos);
1021
1022 // get the clipping hull
1023         hull = SV_HullForEntity (ent, mins, maxs, offset);
1024
1025         VectorSubtract (start, offset, start_l);
1026         VectorSubtract (end, offset, end_l);
1027
1028 // LordHavoc: enabling rotating bmodels
1029         // rotate start and end into the models frame of reference
1030         if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]))
1031         {
1032                 vec3_t  forward, right, up;
1033                 vec3_t  temp;
1034
1035                 AngleVectors (ent->v.angles, forward, right, up);
1036
1037                 VectorCopy (start_l, temp);
1038                 start_l[0] = DotProduct (temp, forward);
1039                 start_l[1] = -DotProduct (temp, right);
1040                 start_l[2] = DotProduct (temp, up);
1041
1042                 VectorCopy (end_l, temp);
1043                 end_l[0] = DotProduct (temp, forward);
1044                 end_l[1] = -DotProduct (temp, right);
1045                 end_l[2] = DotProduct (temp, up);
1046         }
1047
1048 // trace a line through the apropriate clipping hull
1049         VectorCopy(start_l, RecursiveHullCheckInfo.start);
1050         VectorSubtract(end_l, start_l, RecursiveHullCheckInfo.dist);
1051         RecursiveHullCheckInfo.hull = hull;
1052         RecursiveHullCheckInfo.trace = &trace;
1053         SV_RecursiveHullCheck (hull->firstclipnode, 0, 1, start_l, end_l);
1054
1055 // LordHavoc: enabling rotating bmodels
1056         // rotate endpos back to world frame of reference
1057         if (ent->v.solid == SOLID_BSP && (ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]))
1058         {
1059                 vec3_t  a;
1060                 vec3_t  forward, right, up;
1061                 vec3_t  temp;
1062
1063                 if (trace.fraction != 1)
1064                 {
1065                         VectorNegate (ent->v.angles, a);
1066                         AngleVectors (a, forward, right, up);
1067
1068                         VectorCopy (trace.endpos, temp);
1069                         trace.endpos[0] = DotProduct (temp, forward);
1070                         trace.endpos[1] = -DotProduct (temp, right);
1071                         trace.endpos[2] = DotProduct (temp, up);
1072
1073                         VectorCopy (trace.plane.normal, temp);
1074                         trace.plane.normal[0] = DotProduct (temp, forward);
1075                         trace.plane.normal[1] = -DotProduct (temp, right);
1076                         trace.plane.normal[2] = DotProduct (temp, up);
1077                 }
1078         }
1079
1080 // fix trace up by the offset
1081         if (trace.fraction != 1)
1082                 VectorAdd (trace.endpos, offset, trace.endpos);
1083
1084 // did we clip the move?
1085         if (trace.fraction < 1 || trace.startsolid  )
1086                 trace.ent = ent;
1087
1088         return trace;
1089 }
1090
1091 //===========================================================================
1092
1093 /*
1094 ====================
1095 SV_ClipToLinks
1096
1097 Mins and maxs enclose the entire area swept by the move
1098 ====================
1099 */
1100 void SV_ClipToLinks ( areanode_t *node, moveclip_t *clip )
1101 {
1102         link_t          *l, *next;
1103         edict_t         *touch;
1104         trace_t         trace;
1105
1106 loc0:
1107 // touch linked edicts
1108         for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next)
1109         {
1110                 next = l->next;
1111                 touch = EDICT_FROM_AREA(l);
1112                 if (touch->v.solid == SOLID_NOT)
1113                         continue;
1114                 if (touch == clip->passedict)
1115                         continue;
1116                 if (touch->v.solid == SOLID_TRIGGER)
1117                         Sys_Error ("Trigger in clipping list");
1118
1119                 if (clip->type == MOVE_NOMONSTERS && touch->v.solid != SOLID_BSP)
1120                         continue;
1121
1122                 if (clip->boxmins[0] > touch->v.absmax[0]
1123                  || clip->boxmins[1] > touch->v.absmax[1]
1124                  || clip->boxmins[2] > touch->v.absmax[2]
1125                  || clip->boxmaxs[0] < touch->v.absmin[0]
1126                  || clip->boxmaxs[1] < touch->v.absmin[1]
1127                  || clip->boxmaxs[2] < touch->v.absmin[2])
1128                         continue;
1129
1130                 if (clip->passedict != NULL && clip->passedict->v.size[0] && !touch->v.size[0])
1131                         continue;       // points never interact
1132
1133         // might intersect, so do an exact clip
1134                 if (clip->trace.allsolid)
1135                         return;
1136                 if (clip->passedict)
1137                 {
1138                         if (PROG_TO_EDICT(touch->v.owner) == clip->passedict)
1139                                 continue;       // don't clip against own missiles
1140                         if (PROG_TO_EDICT(clip->passedict->v.owner) == touch)
1141                                 continue;       // don't clip against owner
1142                         // LordHavoc: corpse code
1143                         if (clip->passedict->v.solid == SOLID_CORPSE && touch->v.solid == SOLID_SLIDEBOX)
1144                                 continue;
1145                         if (clip->passedict->v.solid == SOLID_SLIDEBOX && touch->v.solid == SOLID_CORPSE)
1146                                 continue;
1147                 }
1148
1149                 if ((int)touch->v.flags & FL_MONSTER)
1150                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end);
1151                 else
1152                         trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end);
1153                 if (trace.allsolid || trace.startsolid || trace.fraction < clip->trace.fraction)
1154                 {
1155                         trace.ent = touch;
1156                         if (clip->trace.startsolid)
1157                         {
1158                                 clip->trace = trace;
1159                                 clip->trace.startsolid = true;
1160                         }
1161                         else
1162                                 clip->trace = trace;
1163                 }
1164                 else if (trace.startsolid)
1165                         clip->trace.startsolid = true;
1166         }
1167
1168 // recurse down both sides
1169         if (node->axis == -1)
1170                 return;
1171
1172         // LordHavoc: optimized recursion
1173 //      if (clip->boxmaxs[node->axis] > node->dist) SV_ClipToLinks(node->children[0], clip);
1174 //      if (clip->boxmins[node->axis] < node->dist) SV_ClipToLinks(node->children[1], clip);
1175         if (clip->boxmaxs[node->axis] > node->dist)
1176         {
1177                 if (clip->boxmins[node->axis] < node->dist)
1178                         SV_ClipToLinks(node->children[1], clip);
1179                 node = node->children[0];
1180                 goto loc0;
1181         }
1182         else if (clip->boxmins[node->axis] < node->dist)
1183         {
1184                 node = node->children[1];
1185                 goto loc0;
1186         }
1187 }
1188
1189
1190 /*
1191 ==================
1192 SV_MoveBounds
1193 ==================
1194 */
1195 void SV_MoveBounds (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
1196 {
1197 #if 0
1198 // debug to test against everything
1199 boxmins[0] = boxmins[1] = boxmins[2] = -9999;
1200 boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;
1201 #else
1202         int             i;
1203         
1204         for (i=0 ; i<3 ; i++)
1205         {
1206                 if (end[i] > start[i])
1207                 {
1208                         boxmins[i] = start[i] + mins[i] - 1;
1209                         boxmaxs[i] = end[i] + maxs[i] + 1;
1210                 }
1211                 else
1212                 {
1213                         boxmins[i] = end[i] + mins[i] - 1;
1214                         boxmaxs[i] = start[i] + maxs[i] + 1;
1215                 }
1216         }
1217 #endif
1218 }
1219
1220 /*
1221 ==================
1222 SV_Move
1223 ==================
1224 */
1225 trace_t SV_Move (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int type, edict_t *passedict)
1226 {
1227         moveclip_t      clip;
1228         int                     i;
1229
1230         memset ( &clip, 0, sizeof ( moveclip_t ) );
1231
1232 // clip to world
1233         clip.trace = SV_ClipMoveToEntity ( sv.edicts, start, mins, maxs, end );
1234
1235         clip.start = start;
1236         clip.end = end;
1237         clip.mins = mins;
1238         clip.maxs = maxs;
1239         clip.type = type;
1240         clip.passedict = passedict;
1241
1242         if (type == MOVE_MISSILE)
1243         {
1244                 for (i=0 ; i<3 ; i++)
1245                 {
1246                         clip.mins2[i] = -15;
1247                         clip.maxs2[i] = 15;
1248                 }
1249         }
1250         else
1251         {
1252                 VectorCopy (mins, clip.mins2);
1253                 VectorCopy (maxs, clip.maxs2);
1254         }
1255
1256 // create the bounding box of the entire move
1257         SV_MoveBounds ( start, clip.mins2, clip.maxs2, end, clip.boxmins, clip.boxmaxs );
1258
1259 // clip to entities
1260         SV_ClipToLinks ( sv_areanodes, &clip );
1261
1262         return clip.trace;
1263 }