2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
20 // world.c -- world query functions
26 entities never clip against themselves, or their owner
28 line of sight checks trace->crosscontent, but bullets don't
35 vec3_t boxmins
, boxmaxs
;// enclose the test object along entire move
36 float *mins
, *maxs
; // size of the moving object
37 vec3_t mins2
, maxs2
; // size when clipping against mosnters
45 int SV_HullPointContents (hull_t
*hull
, int num
, vec3_t p
);
48 ===============================================================================
52 ===============================================================================
56 static hull_t box_hull
;
57 static dclipnode_t box_clipnodes
[6];
58 static mplane_t box_planes
[6];
64 Set up the planes and clipnodes so that the six floats of a bounding box
65 can just be stored out and get a proper hull_t structure.
68 void SV_InitBoxHull (void)
73 box_hull
.clipnodes
= box_clipnodes
;
74 box_hull
.planes
= box_planes
;
75 box_hull
.firstclipnode
= 0;
76 box_hull
.lastclipnode
= 5;
80 box_clipnodes
[i
].planenum
= i
;
84 box_clipnodes
[i
].children
[side
] = CONTENTS_EMPTY
;
86 box_clipnodes
[i
].children
[side
^1] = i
+ 1;
88 box_clipnodes
[i
].children
[side
^1] = CONTENTS_SOLID
;
90 box_planes
[i
].type
= i
>>1;
91 box_planes
[i
].normal
[i
>>1] = 1;
101 To keep everything totally uniform, bounding boxes are turned into small
102 BSP trees instead of being compared directly.
105 hull_t
*SV_HullForBox (vec3_t mins
, vec3_t maxs
)
107 box_planes
[0].dist
= maxs
[0];
108 box_planes
[1].dist
= mins
[0];
109 box_planes
[2].dist
= maxs
[1];
110 box_planes
[3].dist
= mins
[1];
111 box_planes
[4].dist
= maxs
[2];
112 box_planes
[5].dist
= mins
[2];
123 Returns a hull that can be used for testing or clipping an object of mins/maxs
125 Offset is filled in to contain the adjustment that must be added to the
126 testing object's origin to get a point to use with the returned hull.
129 hull_t
*SV_HullForEntity (edict_t
*ent
, vec3_t mins
, vec3_t maxs
, vec3_t offset
)
133 vec3_t hullmins
, hullmaxs
;
136 // decide which clipping hull to use, based on the size
137 if (ent
->v
.solid
== SOLID_BSP
)
138 { // explicit hulls in the BSP model
139 if (ent
->v
.movetype
!= MOVETYPE_PUSH
)
140 Sys_Error ("SOLID_BSP without MOVETYPE_PUSH");
142 model
= sv
.models
[ (int)ent
->v
.modelindex
];
144 if (!model
|| model
->type
!= mod_brush
)
145 Sys_Error ("MOVETYPE_PUSH with a non bsp model");
147 VectorSubtract (maxs
, mins
, size
);
149 hull
= &model
->hulls
[0];
150 else if (size
[0] <= 32)
151 hull
= &model
->hulls
[1];
153 hull
= &model
->hulls
[2];
155 // calculate an offset value to center the origin
156 VectorSubtract (hull
->clip_mins
, mins
, offset
);
157 VectorAdd (offset
, ent
->v
.origin
, offset
);
160 { // create a temp hull from bounding box sizes
162 VectorSubtract (ent
->v
.mins
, maxs
, hullmins
);
163 VectorSubtract (ent
->v
.maxs
, mins
, hullmaxs
);
164 hull
= SV_HullForBox (hullmins
, hullmaxs
);
166 VectorCopy (ent
->v
.origin
, offset
);
174 ===============================================================================
178 ===============================================================================
181 typedef struct areanode_s
183 int axis
; // -1 = leaf node
185 struct areanode_s
*children
[2];
186 link_t trigger_edicts
;
191 #define AREA_NODES 32
193 static areanode_t sv_areanodes
[AREA_NODES
];
194 static int sv_numareanodes
;
202 areanode_t
*SV_CreateAreaNode (int depth
, vec3_t mins
, vec3_t maxs
)
206 vec3_t mins1
, maxs1
, mins2
, maxs2
;
208 anode
= &sv_areanodes
[sv_numareanodes
];
211 ClearLink (&anode
->trigger_edicts
);
212 ClearLink (&anode
->solid_edicts
);
214 if (depth
== AREA_DEPTH
)
217 anode
->children
[0] = anode
->children
[1] = NULL
;
221 VectorSubtract (maxs
, mins
, size
);
222 if (size
[0] > size
[1])
227 anode
->dist
= 0.5 * (maxs
[anode
->axis
] + mins
[anode
->axis
]);
228 VectorCopy (mins
, mins1
);
229 VectorCopy (mins
, mins2
);
230 VectorCopy (maxs
, maxs1
);
231 VectorCopy (maxs
, maxs2
);
233 maxs1
[anode
->axis
] = mins2
[anode
->axis
] = anode
->dist
;
235 anode
->children
[0] = SV_CreateAreaNode (depth
+1, mins2
, maxs2
);
236 anode
->children
[1] = SV_CreateAreaNode (depth
+1, mins1
, maxs1
);
247 void SV_ClearWorld (void)
251 memset (sv_areanodes
, 0, sizeof(sv_areanodes
));
253 SV_CreateAreaNode (0, sv
.worldmodel
->mins
, sv
.worldmodel
->maxs
);
263 void SV_UnlinkEdict (edict_t
*ent
)
266 return; // not linked in anywhere
267 RemoveLink (&ent
->area
);
268 ent
->area
.prev
= ent
->area
.next
= NULL
;
277 void SV_TouchLinks ( edict_t
*ent
, areanode_t
*node
)
281 int old_self
, old_other
;
283 // touch linked edicts
284 for (l
= node
->trigger_edicts
.next
; l
!= &node
->trigger_edicts
; l
= next
)
287 touch
= EDICT_FROM_AREA(l
);
290 if (!touch
->v
.touch
|| touch
->v
.solid
!= SOLID_TRIGGER
)
292 if (ent
->v
.absmin
[0] > touch
->v
.absmax
[0]
293 || ent
->v
.absmin
[1] > touch
->v
.absmax
[1]
294 || ent
->v
.absmin
[2] > touch
->v
.absmax
[2]
295 || ent
->v
.absmax
[0] < touch
->v
.absmin
[0]
296 || ent
->v
.absmax
[1] < touch
->v
.absmin
[1]
297 || ent
->v
.absmax
[2] < touch
->v
.absmin
[2] )
299 old_self
= pr_global_struct
->self
;
300 old_other
= pr_global_struct
->other
;
302 pr_global_struct
->self
= EDICT_TO_PROG(touch
);
303 pr_global_struct
->other
= EDICT_TO_PROG(ent
);
304 pr_global_struct
->time
= sv
.time
;
305 PR_ExecuteProgram (touch
->v
.touch
);
307 pr_global_struct
->self
= old_self
;
308 pr_global_struct
->other
= old_other
;
311 // recurse down both sides
312 if (node
->axis
== -1)
315 if ( ent
->v
.absmax
[node
->axis
] > node
->dist
)
316 SV_TouchLinks ( ent
, node
->children
[0] );
317 if ( ent
->v
.absmin
[node
->axis
] < node
->dist
)
318 SV_TouchLinks ( ent
, node
->children
[1] );
328 void SV_FindTouchedLeafs (edict_t
*ent
, mnode_t
*node
)
330 mplane_t
*splitplane
;
335 if (node
->contents
== CONTENTS_SOLID
)
338 // add an efrag if the node is a leaf
340 if ( node
->contents
< 0)
342 if (ent
->num_leafs
== MAX_ENT_LEAFS
)
345 leaf
= (mleaf_t
*)node
;
346 leafnum
= leaf
- sv
.worldmodel
->leafs
- 1;
348 ent
->leafnums
[ent
->num_leafs
] = leafnum
;
355 splitplane
= node
->plane
;
356 sides
= BOX_ON_PLANE_SIDE(ent
->v
.absmin
, ent
->v
.absmax
, splitplane
);
358 // recurse down the contacted sides
360 SV_FindTouchedLeafs (ent
, node
->children
[0]);
363 SV_FindTouchedLeafs (ent
, node
->children
[1]);
372 void SV_LinkEdict (edict_t
*ent
, qboolean touch_triggers
)
377 SV_UnlinkEdict (ent
); // unlink from old position
379 if (ent
== sv
.edicts
)
380 return; // don't add the world
388 if (ent
->v
.solid
== SOLID_BSP
&&
389 (ent
->v
.angles
[0] || ent
->v
.angles
[1] || ent
->v
.angles
[2]) )
390 { // expand for rotation
395 for (i
=0 ; i
<3 ; i
++)
397 v
=fabsf( ent
->v
.mins
[i
]);
400 v
=fabsf( ent
->v
.maxs
[i
]);
404 for (i
=0 ; i
<3 ; i
++)
406 ent
->v
.absmin
[i
] = ent
->v
.origin
[i
] - max
;
407 ent
->v
.absmax
[i
] = ent
->v
.origin
[i
] + max
;
413 VectorAdd (ent
->v
.origin
, ent
->v
.mins
, ent
->v
.absmin
);
414 VectorAdd (ent
->v
.origin
, ent
->v
.maxs
, ent
->v
.absmax
);
418 // to make items easier to pick up and allow them to be grabbed off
419 // of shelves, the abs sizes are expanded
421 if ((int)ent
->v
.flags
& FL_ITEM
)
423 ent
->v
.absmin
[0] -= 15;
424 ent
->v
.absmin
[1] -= 15;
425 ent
->v
.absmax
[0] += 15;
426 ent
->v
.absmax
[1] += 15;
429 { // because movement is clipped an epsilon away from an actual edge,
430 // we must fully check even when bounding boxes don't quite touch
431 ent
->v
.absmin
[0] -= 1;
432 ent
->v
.absmin
[1] -= 1;
433 ent
->v
.absmin
[2] -= 1;
434 ent
->v
.absmax
[0] += 1;
435 ent
->v
.absmax
[1] += 1;
436 ent
->v
.absmax
[2] += 1;
441 if (ent
->v
.modelindex
)
442 SV_FindTouchedLeafs (ent
, sv
.worldmodel
->nodes
);
444 if (ent
->v
.solid
== SOLID_NOT
)
447 // find the first node that the ent's box crosses
451 if (node
->axis
== -1)
453 if (ent
->v
.absmin
[node
->axis
] > node
->dist
)
454 node
= node
->children
[0];
455 else if (ent
->v
.absmax
[node
->axis
] < node
->dist
)
456 node
= node
->children
[1];
458 break; // crosses the node
463 if (ent
->v
.solid
== SOLID_TRIGGER
)
464 InsertLinkBefore (&ent
->area
, &node
->trigger_edicts
);
466 InsertLinkBefore (&ent
->area
, &node
->solid_edicts
);
468 // if touch_triggers, touch all entities at this node and decend for more
470 SV_TouchLinks ( ent
, sv_areanodes
);
476 ===============================================================================
478 POINT TESTING IN HULLS
480 ===============================================================================
491 int SV_HullPointContents (hull_t
*hull
, int num
, vec3_t p
)
499 if (num
< hull
->firstclipnode
|| num
> hull
->lastclipnode
)
500 Sys_Error ("SV_HullPointContents: bad node number");
502 node
= hull
->clipnodes
+ num
;
503 plane
= hull
->planes
+ node
->planenum
;
506 d
= p
[plane
->type
] - plane
->dist
;
508 d
= DotProduct (plane
->normal
, p
) - plane
->dist
;
510 num
= node
->children
[1];
512 num
= node
->children
[0];
527 int SV_PointContents (vec3_t p
)
531 cont
= SV_HullPointContents (&sv
.worldmodel
->hulls
[0], 0, p
);
532 if (cont
<= CONTENTS_CURRENT_0
&& cont
>= CONTENTS_CURRENT_DOWN
)
533 cont
= CONTENTS_WATER
;
537 int SV_TruePointContents (vec3_t p
)
539 return SV_HullPointContents (&sv
.worldmodel
->hulls
[0], 0, p
);
542 //===========================================================================
546 SV_TestEntityPosition
548 This could be a lot more efficient...
551 edict_t
*SV_TestEntityPosition (edict_t
*ent
)
555 trace
= SV_Move (ent
->v
.origin
, ent
->v
.mins
, ent
->v
.maxs
, ent
->v
.origin
, 0, ent
);
557 if (trace
.startsolid
)
565 ===============================================================================
567 LINE TESTING IN HULLS
569 ===============================================================================
572 // 1/32 epsilon to keep floating point happy
573 #define DIST_EPSILON (0.03125)
577 SV_RecursiveHullCheck
581 qboolean
SV_RecursiveHullCheck (hull_t
*hull
, int num
, float p1f
, float p2f
, vec3_t p1
, vec3_t p2
, trace_t
*trace
)
595 if (num
!= CONTENTS_SOLID
)
597 trace
->allsolid
= false;
598 if (num
== CONTENTS_EMPTY
)
599 trace
->inopen
= true;
601 trace
->inwater
= true;
604 trace
->startsolid
= true;
605 return true; // empty
608 if (num
< hull
->firstclipnode
|| num
> hull
->lastclipnode
)
609 Sys_Error ("SV_RecursiveHullCheck: bad node number");
612 // find the point distances
614 node
= hull
->clipnodes
+ num
;
615 plane
= hull
->planes
+ node
->planenum
;
619 t1
= p1
[plane
->type
] - plane
->dist
;
620 t2
= p2
[plane
->type
] - plane
->dist
;
624 t1
= DotProduct (plane
->normal
, p1
) - plane
->dist
;
625 t2
= DotProduct (plane
->normal
, p2
) - plane
->dist
;
629 if (t1
>= 0 && t2
>= 0)
630 return SV_RecursiveHullCheck (hull
, node
->children
[0], p1f
, p2f
, p1
, p2
, trace
);
631 if (t1
< 0 && t2
< 0)
632 return SV_RecursiveHullCheck (hull
, node
->children
[1], p1f
, p2f
, p1
, p2
, trace
);
634 if ( (t1
>= DIST_EPSILON
&& t2
>= DIST_EPSILON
) || (t2
> t1
&& t1
>= 0) )
635 return SV_RecursiveHullCheck (hull
, node
->children
[0], p1f
, p2f
, p1
, p2
, trace
);
636 if ( (t1
<= -DIST_EPSILON
&& t2
<= -DIST_EPSILON
) || (t2
< t1
&& t1
<= 0) )
637 return SV_RecursiveHullCheck (hull
, node
->children
[1], p1f
, p2f
, p1
, p2
, trace
);
640 // put the crosspoint DIST_EPSILON pixels on the near side
642 frac
= (t1
+ DIST_EPSILON
)/(t1
-t2
);
644 frac
= (t1
- DIST_EPSILON
)/(t1
-t2
);
650 midf
= p1f
+ (p2f
- p1f
)*frac
;
651 for (i
=0 ; i
<3 ; i
++)
652 mid
[i
] = p1
[i
] + frac
*(p2
[i
] - p1
[i
]);
656 // move up to the node
657 if (!SV_RecursiveHullCheck (hull
, node
->children
[side
], p1f
, midf
, p1
, mid
, trace
) )
661 if (SV_HullPointContents (sv_hullmodel
, mid
, node
->children
[side
])
664 Con_Printf ("mid PointInHullSolid\n");
669 if (SV_HullPointContents (hull
, node
->children
[side
^1], mid
)
672 return SV_RecursiveHullCheck (hull
, node
->children
[side
^1], midf
, p2f
, mid
, p2
, trace
);
675 return false; // never got out of the solid area
678 // the other side of the node is solid, this is the impact point
682 VectorCopy (plane
->normal
, trace
->plane
.normal
);
683 trace
->plane
.dist
= plane
->dist
;
687 VectorSubtract (vec3_origin
, plane
->normal
, trace
->plane
.normal
);
688 trace
->plane
.dist
= -plane
->dist
;
691 while (SV_HullPointContents (hull
, hull
->firstclipnode
, mid
)
693 { // shouldn't really happen, but does occasionally
697 trace
->fraction
= midf
;
698 VectorCopy (mid
, trace
->endpos
);
699 Con_DPrintf ("backup past 0\n");
702 midf
= p1f
+ (p2f
- p1f
)*frac
;
703 for (i
=0 ; i
<3 ; i
++)
704 mid
[i
] = p1
[i
] + frac
*(p2
[i
] - p1
[i
]);
707 trace
->fraction
= midf
;
708 VectorCopy (mid
, trace
->endpos
);
718 Handles selection or creation of a clipping hull, and offseting (and
719 eventually rotation) of the end points
722 trace_t
SV_ClipMoveToEntity (edict_t
*ent
, vec3_t start
, vec3_t mins
, vec3_t maxs
, vec3_t end
)
726 vec3_t start_l
, end_l
;
729 // fill in a default trace
730 memset (&trace
, 0, sizeof(trace_t
));
732 trace
.allsolid
= true;
733 VectorCopy (end
, trace
.endpos
);
735 // get the clipping hull
736 hull
= SV_HullForEntity (ent
, mins
, maxs
, offset
);
738 VectorSubtract (start
, offset
, start_l
);
739 VectorSubtract (end
, offset
, end_l
);
742 // rotate start and end into the models frame of reference
743 if (ent
->v
.solid
== SOLID_BSP
&&
744 (ent
->v
.angles
[0] || ent
->v
.angles
[1] || ent
->v
.angles
[2]) )
747 vec3_t forward
, right
, up
;
750 AngleVectors (ent
->v
.angles
, forward
, right
, up
);
752 VectorCopy (start_l
, temp
);
753 start_l
[0] = DotProduct (temp
, forward
);
754 start_l
[1] = -DotProduct (temp
, right
);
755 start_l
[2] = DotProduct (temp
, up
);
757 VectorCopy (end_l
, temp
);
758 end_l
[0] = DotProduct (temp
, forward
);
759 end_l
[1] = -DotProduct (temp
, right
);
760 end_l
[2] = DotProduct (temp
, up
);
764 // trace a line through the apropriate clipping hull
765 SV_RecursiveHullCheck (hull
, hull
->firstclipnode
, 0, 1, start_l
, end_l
, &trace
);
768 // rotate endpos back to world frame of reference
769 if (ent
->v
.solid
== SOLID_BSP
&&
770 (ent
->v
.angles
[0] || ent
->v
.angles
[1] || ent
->v
.angles
[2]) )
773 vec3_t forward
, right
, up
;
776 if (trace
.fraction
!= 1)
778 VectorSubtract (vec3_origin
, ent
->v
.angles
, a
);
779 AngleVectors (a
, forward
, right
, up
);
781 VectorCopy (trace
.endpos
, temp
);
782 trace
.endpos
[0] = DotProduct (temp
, forward
);
783 trace
.endpos
[1] = -DotProduct (temp
, right
);
784 trace
.endpos
[2] = DotProduct (temp
, up
);
786 VectorCopy (trace
.plane
.normal
, temp
);
787 trace
.plane
.normal
[0] = DotProduct (temp
, forward
);
788 trace
.plane
.normal
[1] = -DotProduct (temp
, right
);
789 trace
.plane
.normal
[2] = DotProduct (temp
, up
);
794 // fix trace up by the offset
795 if (trace
.fraction
!= 1)
796 VectorAdd (trace
.endpos
, offset
, trace
.endpos
);
798 // did we clip the move?
799 if (trace
.fraction
< 1 || trace
.startsolid
)
805 //===========================================================================
811 Mins and maxs enclose the entire area swept by the move
814 void SV_ClipToLinks ( areanode_t
*node
, moveclip_t
*clip
)
820 // touch linked edicts
821 for (l
= node
->solid_edicts
.next
; l
!= &node
->solid_edicts
; l
= next
)
824 touch
= EDICT_FROM_AREA(l
);
825 if (touch
->v
.solid
== SOLID_NOT
)
827 if (touch
== clip
->passedict
)
829 if (touch
->v
.solid
== SOLID_TRIGGER
)
830 Sys_Error ("Trigger in clipping list");
832 if (clip
->type
== MOVE_NOMONSTERS
&& touch
->v
.solid
!= SOLID_BSP
)
835 if (clip
->boxmins
[0] > touch
->v
.absmax
[0]
836 || clip
->boxmins
[1] > touch
->v
.absmax
[1]
837 || clip
->boxmins
[2] > touch
->v
.absmax
[2]
838 || clip
->boxmaxs
[0] < touch
->v
.absmin
[0]
839 || clip
->boxmaxs
[1] < touch
->v
.absmin
[1]
840 || clip
->boxmaxs
[2] < touch
->v
.absmin
[2] )
843 if (clip
->passedict
&& clip
->passedict
->v
.size
[0] && !touch
->v
.size
[0])
844 continue; // points never interact
846 // might intersect, so do an exact clip
847 if (clip
->trace
.allsolid
)
851 if (PROG_TO_EDICT(touch
->v
.owner
) == clip
->passedict
)
852 continue; // don't clip against own missiles
853 if (PROG_TO_EDICT(clip
->passedict
->v
.owner
) == touch
)
854 continue; // don't clip against owner
857 if ((int)touch
->v
.flags
& FL_MONSTER
)
858 trace
= SV_ClipMoveToEntity (touch
, clip
->start
, clip
->mins2
, clip
->maxs2
, clip
->end
);
860 trace
= SV_ClipMoveToEntity (touch
, clip
->start
, clip
->mins
, clip
->maxs
, clip
->end
);
861 if (trace
.allsolid
|| trace
.startsolid
||
862 trace
.fraction
< clip
->trace
.fraction
)
865 if (clip
->trace
.startsolid
)
868 clip
->trace
.startsolid
= true;
873 else if (trace
.startsolid
)
874 clip
->trace
.startsolid
= true;
877 // recurse down both sides
878 if (node
->axis
== -1)
881 if ( clip
->boxmaxs
[node
->axis
] > node
->dist
)
882 SV_ClipToLinks ( node
->children
[0], clip
);
883 if ( clip
->boxmins
[node
->axis
] < node
->dist
)
884 SV_ClipToLinks ( node
->children
[1], clip
);
893 void SV_MoveBounds (vec3_t start
, vec3_t mins
, vec3_t maxs
, vec3_t end
, vec3_t boxmins
, vec3_t boxmaxs
)
896 // debug to test against everything
897 boxmins
[0] = boxmins
[1] = boxmins
[2] = -9999;
898 boxmaxs
[0] = boxmaxs
[1] = boxmaxs
[2] = 9999;
902 for (i
=0 ; i
<3 ; i
++)
904 if (end
[i
] > start
[i
])
906 boxmins
[i
] = start
[i
] + mins
[i
] - 1;
907 boxmaxs
[i
] = end
[i
] + maxs
[i
] + 1;
911 boxmins
[i
] = end
[i
] + mins
[i
] - 1;
912 boxmaxs
[i
] = start
[i
] + maxs
[i
] + 1;
923 trace_t
SV_Move (vec3_t start
, vec3_t mins
, vec3_t maxs
, vec3_t end
, int type
, edict_t
*passedict
)
928 memset ( &clip
, 0, sizeof ( moveclip_t
) );
931 clip
.trace
= SV_ClipMoveToEntity ( sv
.edicts
, start
, mins
, maxs
, end
);
938 clip
.passedict
= passedict
;
940 if (type
== MOVE_MISSILE
)
942 for (i
=0 ; i
<3 ; i
++)
950 VectorCopy (mins
, clip
.mins2
);
951 VectorCopy (maxs
, clip
.maxs2
);
954 // create the bounding box of the entire move
955 SV_MoveBounds ( start
, clip
.mins2
, clip
.maxs2
, end
, clip
.boxmins
, clip
.boxmaxs
);
958 SV_ClipToLinks ( sv_areanodes
, &clip
);