]> icculus.org git repositories - divverent/darkplaces.git/blob - collision.c
VorteX has fixed gettaginfo to work as advertised (properly handling of attached...
[divverent/darkplaces.git] / collision.c
1
2 #include "quakedef.h"
3 #include "polygon.h"
4
5 #define COLLISION_SNAPSCALE (32.0f)
6 #define COLLISION_SNAP (1.0f / COLLISION_SNAPSCALE)
7
8 cvar_t collision_impactnudge = {0, "collision_impactnudge", "0.03125"};
9 cvar_t collision_startnudge = {0, "collision_startnudge", "0"};
10 cvar_t collision_endnudge = {0, "collision_endnudge", "0"};
11 cvar_t collision_enternudge = {0, "collision_enternudge", "0"};
12 cvar_t collision_leavenudge = {0, "collision_leavenudge", "0"};
13
14 #if 0
15 typedef struct
16 {
17         // the hull we're tracing through
18         const hull_t *hull;
19
20         // the trace structure to fill in
21         trace_t *trace;
22
23         // start and end of the trace (in model space)
24         double start[3];
25         double end[3];
26
27         // end - start
28         double dist[3];
29
30         // overrides the CONTENTS_SOLID in the box bsp tree
31         int boxsupercontents;
32 }
33 RecursiveHullCheckTraceInfo_t;
34
35 #define HULLCHECKSTATE_EMPTY 0
36 #define HULLCHECKSTATE_SOLID 1
37 #define HULLCHECKSTATE_DONE 2
38
39 static int RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
40 {
41         // status variables, these don't need to be saved on the stack when
42         // recursing...  but are because this should be thread-safe
43         // (note: tracing against a bbox is not thread-safe, yet)
44         int ret;
45         mplane_t *plane;
46         double t1, t2;
47
48         // variables that need to be stored on the stack when recursing
49         dclipnode_t *node;
50         int side;
51         double midf, mid[3];
52
53         // LordHavoc: a goto!  everyone flee in terror... :)
54 loc0:
55         // check for empty
56         if (num < 0)
57         {
58                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
59                 if (!t->trace->startfound)
60                 {
61                         t->trace->startfound = true;
62                         t->trace->startsupercontents |= num;
63                 }
64                 if (num & SUPERCONTENTS_LIQUIDSMASK)
65                         t->trace->inwater = true;
66                 if (num == 0)
67                         t->trace->inopen = true;
68                 if (num & t->trace->hitsupercontentsmask)
69                 {
70                         // if the first leaf is solid, set startsolid
71                         if (t->trace->allsolid)
72                                 t->trace->startsolid = true;
73 #if COLLISIONPARANOID >= 3
74                         Con_Print("S");
75 #endif
76                         return HULLCHECKSTATE_SOLID;
77                 }
78                 else
79                 {
80                         t->trace->allsolid = false;
81 #if COLLISIONPARANOID >= 3
82                         Con_Print("E");
83 #endif
84                         return HULLCHECKSTATE_EMPTY;
85                 }
86         }
87
88         // find the point distances
89         node = t->hull->clipnodes + num;
90
91         plane = t->hull->planes + node->planenum;
92         if (plane->type < 3)
93         {
94                 t1 = p1[plane->type] - plane->dist;
95                 t2 = p2[plane->type] - plane->dist;
96         }
97         else
98         {
99                 t1 = DotProduct (plane->normal, p1) - plane->dist;
100                 t2 = DotProduct (plane->normal, p2) - plane->dist;
101         }
102
103         if (t1 < 0)
104         {
105                 if (t2 < 0)
106                 {
107 #if COLLISIONPARANOID >= 3
108                         Con_Print("<");
109 #endif
110                         num = node->children[1];
111                         goto loc0;
112                 }
113                 side = 1;
114         }
115         else
116         {
117                 if (t2 >= 0)
118                 {
119 #if COLLISIONPARANOID >= 3
120                         Con_Print(">");
121 #endif
122                         num = node->children[0];
123                         goto loc0;
124                 }
125                 side = 0;
126         }
127
128         // the line intersects, find intersection point
129         // LordHavoc: this uses the original trace for maximum accuracy
130 #if COLLISIONPARANOID >= 3
131         Con_Print("M");
132 #endif
133         if (plane->type < 3)
134         {
135                 t1 = t->start[plane->type] - plane->dist;
136                 t2 = t->end[plane->type] - plane->dist;
137         }
138         else
139         {
140                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
141                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
142         }
143
144         midf = t1 / (t1 - t2);
145         midf = bound(p1f, midf, p2f);
146         VectorMA(t->start, midf, t->dist, mid);
147
148         // recurse both sides, front side first
149         ret = RecursiveHullCheck (t, node->children[side], p1f, midf, p1, mid);
150         // if this side is not empty, return what it is (solid or done)
151         if (ret != HULLCHECKSTATE_EMPTY)
152                 return ret;
153
154         ret = RecursiveHullCheck (t, node->children[side ^ 1], midf, p2f, mid, p2);
155         // if other side is not solid, return what it is (empty or done)
156         if (ret != HULLCHECKSTATE_SOLID)
157                 return ret;
158
159         // front is air and back is solid, this is the impact point...
160         if (side)
161         {
162                 t->trace->plane.dist = -plane->dist;
163                 VectorNegate (plane->normal, t->trace->plane.normal);
164         }
165         else
166         {
167                 t->trace->plane.dist = plane->dist;
168                 VectorCopy (plane->normal, t->trace->plane.normal);
169         }
170
171         // calculate the true fraction
172         t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist - collision_startnudge.value;
173         t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist - collision_endnudge.value;
174         midf = t1 / (t1 - t2);
175         t->trace->realfraction = bound(0, midf, 1);
176
177         // calculate the return fraction which is nudged off the surface a bit
178         midf = (t1 - collision_impactnudge.value) / (t1 - t2);
179         t->trace->fraction = bound(0, midf, 1);
180
181 #if COLLISIONPARANOID >= 3
182         Con_Print("D");
183 #endif
184         return HULLCHECKSTATE_DONE;
185 }
186
187 #if 0
188 // used if start and end are the same
189 static void RecursiveHullCheckPoint (RecursiveHullCheckTraceInfo_t *t, int num)
190 {
191         // If you can read this, you understand BSP trees
192         while (num >= 0)
193                 num = t->hull->clipnodes[num].children[((t->hull->planes[t->hull->clipnodes[num].planenum].type < 3) ? (t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type]) : (DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start))) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
194
195         // check for empty
196         t->trace->endcontents = num;
197         if (t->trace->thiscontents)
198         {
199                 if (num == t->trace->thiscontents)
200                         t->trace->allsolid = false;
201                 else
202                 {
203                         // if the first leaf is solid, set startsolid
204                         if (t->trace->allsolid)
205                                 t->trace->startsolid = true;
206                 }
207         }
208         else
209         {
210                 if (num != CONTENTS_SOLID)
211                 {
212                         t->trace->allsolid = false;
213                         if (num == CONTENTS_EMPTY)
214                                 t->trace->inopen = true;
215                         else
216                                 t->trace->inwater = true;
217                 }
218                 else
219                 {
220                         // if the first leaf is solid, set startsolid
221                         if (t->trace->allsolid)
222                                 t->trace->startsolid = true;
223                 }
224         }
225 }
226 #endif
227
228 static hull_t box_hull;
229 static dclipnode_t box_clipnodes[6];
230 static mplane_t box_planes[6];
231
232 void Mod_Q1BSP_Collision_Init (void)
233 {
234         int             i;
235         int             side;
236
237         //Set up the planes and clipnodes so that the six floats of a bounding box
238         //can just be stored out and get a proper hull_t structure.
239
240         box_hull.clipnodes = box_clipnodes;
241         box_hull.planes = box_planes;
242         box_hull.firstclipnode = 0;
243         box_hull.lastclipnode = 5;
244
245         for (i = 0;i < 6;i++)
246         {
247                 box_clipnodes[i].planenum = i;
248
249                 side = i&1;
250
251                 box_clipnodes[i].children[side] = CONTENTS_EMPTY;
252                 if (i != 5)
253                         box_clipnodes[i].children[side^1] = i + 1;
254                 else
255                         box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
256
257                 box_planes[i].type = i>>1;
258                 box_planes[i].normal[i>>1] = 1;
259         }
260 }
261
262 void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int boxsupercontents)
263 {
264         RecursiveHullCheckTraceInfo_t rhc;
265         // fill in a default trace
266         memset(&rhc, 0, sizeof(rhc));
267         memset(trace, 0, sizeof(trace_t));
268         //To keep everything totally uniform, bounding boxes are turned into small
269         //BSP trees instead of being compared directly.
270         // create a temp hull from bounding box sizes
271         box_planes[0].dist = cmaxs[0] - mins[0];
272         box_planes[1].dist = cmins[0] - maxs[0];
273         box_planes[2].dist = cmaxs[1] - mins[1];
274         box_planes[3].dist = cmins[1] - maxs[1];
275         box_planes[4].dist = cmaxs[2] - mins[2];
276         box_planes[5].dist = cmins[2] - maxs[2];
277         // trace a line through the generated clipping hull
278         rhc.boxsupercontents = boxsupercontents;
279         rhc.hull = &box_hull;
280         rhc.trace = trace;
281         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
282         rhc.trace->fraction = 1;
283         rhc.trace->realfraction = 1;
284         rhc.trace->allsolid = true;
285         VectorCopy(start, rhc.start);
286         VectorCopy(end, rhc.end);
287         VectorSubtract(rhc.end, rhc.start, rhc.dist);
288         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
289         VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
290         if (rhc.trace->startsupercontents)
291                 rhc.trace->startsupercontents = boxsupercontents;
292 }
293 #endif
294
295 void Collision_Init (void)
296 {
297         Cvar_RegisterVariable(&collision_impactnudge);
298         Cvar_RegisterVariable(&collision_startnudge);
299         Cvar_RegisterVariable(&collision_endnudge);
300         Cvar_RegisterVariable(&collision_enternudge);
301         Cvar_RegisterVariable(&collision_leavenudge);
302 }
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317 void Collision_PrintBrushAsQHull(colbrushf_t *brush, const char *name)
318 {
319         int i;
320         Con_Printf("3 %s\n%i\n", name, brush->numpoints);
321         for (i = 0;i < brush->numpoints;i++)
322                 Con_Printf("%f %f %f\n", brush->points[i].v[0], brush->points[i].v[1], brush->points[i].v[2]);
323         // FIXME: optimize!
324         Con_Printf("4\n%i\n", brush->numplanes);
325         for (i = 0;i < brush->numplanes;i++)
326                 Con_Printf("%f %f %f %f\n", brush->planes[i].normal[0], brush->planes[i].normal[1], brush->planes[i].normal[2], brush->planes[i].dist);
327 }
328
329 void Collision_ValidateBrush(colbrushf_t *brush)
330 {
331         int j, k, pointsoffplanes, pointonplanes, pointswithinsufficientplanes, printbrush;
332         float d;
333         printbrush = false;
334         if (!brush->numpoints)
335         {
336                 Con_Print("Collision_ValidateBrush: brush with no points!\n");
337                 printbrush = true;
338         }
339 #if 0
340         // it's ok for a brush to have one point and no planes...
341         if (brush->numplanes == 0 && brush->numpoints != 1)
342         {
343                 Con_Print("Collision_ValidateBrush: brush with no planes and more than one point!\n");
344                 printbrush = true;
345         }
346 #endif
347         if (brush->numplanes)
348         {
349                 pointsoffplanes = 0;
350                 pointswithinsufficientplanes = 0;
351                 for (k = 0;k < brush->numplanes;k++)
352                         if (DotProduct(brush->planes[k].normal, brush->planes[k].normal) < 0.0001f)
353                                 Con_Printf("Collision_ValidateBrush: plane #%i (%f %f %f %f) is degenerate\n", k, brush->planes[k].normal[0], brush->planes[k].normal[1], brush->planes[k].normal[2], brush->planes[k].dist);
354                 for (j = 0;j < brush->numpoints;j++)
355                 {
356                         pointonplanes = 0;
357                         for (k = 0;k < brush->numplanes;k++)
358                         {
359                                 d = DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist;
360                                 if (d > (1.0f / 8.0f))
361                                 {
362                                         Con_Printf("Collision_ValidateBrush: point #%i (%f %f %f) infront of plane #%i (%f %f %f %f)\n", j, brush->points[j].v[0], brush->points[j].v[1], brush->points[j].v[2], k, brush->planes[k].normal[0], brush->planes[k].normal[1], brush->planes[k].normal[2], brush->planes[k].dist);
363                                         printbrush = true;
364                                 }
365                                 if (fabs(d) > 0.125f)
366                                         pointsoffplanes++;
367                                 else
368                                         pointonplanes++;
369                         }
370                         if (pointonplanes < 3)
371                                 pointswithinsufficientplanes++;
372                 }
373                 if (pointswithinsufficientplanes)
374                 {
375                         Con_Print("Collision_ValidateBrush: some points have insufficient planes, every point must be on at least 3 planes to form a corner.\n");
376                         printbrush = true;
377                 }
378                 if (pointsoffplanes == 0) // all points are on all planes
379                 {
380                         Con_Print("Collision_ValidateBrush: all points lie on all planes (degenerate, no brush volume!)\n");
381                         printbrush = true;
382                 }
383         }
384         if (printbrush)
385                 Collision_PrintBrushAsQHull(brush, "unnamed");
386 }
387
388 float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
389 {
390         float dist, bestdist;
391         bestdist = DotProduct(points->v, normal);
392         points++;
393         while(--numpoints)
394         {
395                 dist = DotProduct(points->v, normal);
396                 bestdist = min(bestdist, dist);
397                 points++;
398         }
399         return bestdist;
400 }
401
402 float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
403 {
404         float dist, bestdist;
405         bestdist = DotProduct(points->v, normal);
406         points++;
407         while(--numpoints)
408         {
409                 dist = DotProduct(points->v, normal);
410                 bestdist = max(bestdist, dist);
411                 points++;
412         }
413         return bestdist;
414 }
415
416
417 colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const mplane_t *originalplanes, int supercontents)
418 {
419         int j, k, m, w;
420         int numpointsbuf = 0, maxpointsbuf = 256, numplanesbuf = 0, maxplanesbuf = 256, numelementsbuf = 0, maxelementsbuf = 256;
421         colbrushf_t *brush;
422         colpointf_t pointsbuf[256];
423         colplanef_t planesbuf[256];
424         int elementsbuf[1024];
425         int polypointbuf[256];
426         int pmaxpoints = 64;
427         int pnumpoints;
428         double p[2][3*64];
429 #if 0
430         // enable these if debugging to avoid seeing garbage in unused data
431         memset(pointsbuf, 0, sizeof(pointsbuf));
432         memset(planesbuf, 0, sizeof(planesbuf));
433         memset(elementsbuf, 0, sizeof(elementsbuf));
434         memset(polypointbuf, 0, sizeof(polypointbuf));
435         memset(p, 0, sizeof(p));
436 #endif
437         // construct a collision brush (points, planes, and renderable mesh) from
438         // a set of planes, this also optimizes out any unnecessary planes (ones
439         // whose polygon is clipped away by the other planes)
440         for (j = 0;j < numoriginalplanes;j++)
441         {
442                 // add the plane uniquely (no duplicates)
443                 for (k = 0;k < numplanesbuf;k++)
444                         if (VectorCompare(planesbuf[k].normal, originalplanes[j].normal) && planesbuf[k].dist == originalplanes[j].dist)
445                                 break;
446                 // if the plane is a duplicate, skip it
447                 if (k < numplanesbuf)
448                         continue;
449                 // check if there are too many and skip the brush
450                 if (numplanesbuf >= maxplanesbuf)
451                 {
452                         Con_Print("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
453                         return NULL;
454                 }
455
456                 // create a large polygon from the plane
457                 w = 0;
458                 PolygonD_QuadForPlane(p[w], originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist, 1024.0*1024.0*1024.0);
459                 pnumpoints = 4;
460                 // clip it by all other planes
461                 for (k = 0;k < numoriginalplanes && pnumpoints && pnumpoints <= pmaxpoints;k++)
462                 {
463                         if (k != j)
464                         {
465                                 // we want to keep the inside of the brush plane so we flip
466                                 // the cutting plane
467                                 PolygonD_Divide(pnumpoints, p[w], -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, 1.0/32.0, pmaxpoints, p[!w], &pnumpoints, 0, NULL, NULL);
468                                 w = !w;
469                         }
470                 }
471                 // if nothing is left, skip it
472                 if (pnumpoints < 3)
473                 {
474                         //Con_Printf("Collision_NewBrushFromPlanes: warning: polygon for plane %f %f %f %f clipped away\n", originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist);
475                         continue;
476                 }
477
478                 for (k = 0;k < pnumpoints;k++)
479                 {
480                         int l, m;
481                         m = 0;
482                         for (l = 0;l < numoriginalplanes;l++)
483                                 if (fabs(DotProduct(&p[w][k*3], originalplanes[l].normal) - originalplanes[l].dist) < 1.0/8.0)
484                                         m++;
485                         if (m < 3)
486                                 break;
487                 }
488                 if (k < pnumpoints)
489                 {
490                         Con_Printf("Collision_NewBrushFromPlanes: warning: polygon point does not lie on at least 3 planes\n");
491                         //return NULL;
492                 }
493
494                 // check if there are too many polygon vertices for buffer
495                 if (pnumpoints > pmaxpoints)
496                 {
497                         Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
498                         return NULL;
499                 }
500
501                 // check if there are too many triangle elements for buffer
502                 if (numelementsbuf + (pnumpoints - 2) * 3 > maxelementsbuf)
503                 {
504                         Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many triangle elements for buffer\n");
505                         return NULL;
506                 }
507
508                 for (k = 0;k < pnumpoints;k++)
509                 {
510                         // check if there is already a matching point (no duplicates)
511                         for (m = 0;m < numpointsbuf;m++)
512                                 if (VectorDistance2(&p[w][k*3], pointsbuf[m].v) < COLLISION_SNAP)
513                                         break;
514
515                         // if there is no match, add a new one
516                         if (m == numpointsbuf)
517                         {
518                                 // check if there are too many and skip the brush
519                                 if (numpointsbuf >= maxpointsbuf)
520                                 {
521                                         Con_Print("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
522                                         return NULL;
523                                 }
524                                 // add the new one
525                                 VectorCopy(&p[w][k*3], pointsbuf[numpointsbuf].v);
526                                 numpointsbuf++;
527                         }
528
529                         // store the index into a buffer
530                         polypointbuf[k] = m;
531                 }
532
533                 // add the triangles for the polygon
534                 // (this particular code makes a triangle fan)
535                 for (k = 0;k < pnumpoints - 2;k++)
536                 {
537                         elementsbuf[numelementsbuf++] = polypointbuf[0];
538                         elementsbuf[numelementsbuf++] = polypointbuf[k + 1];
539                         elementsbuf[numelementsbuf++] = polypointbuf[k + 2];
540                 }
541
542                 // add the new plane
543                 VectorCopy(originalplanes[j].normal, planesbuf[numplanesbuf].normal);
544                 planesbuf[numplanesbuf].dist = originalplanes[j].dist;
545                 numplanesbuf++;
546         }
547
548         // validate plane distances
549         for (j = 0;j < numplanesbuf;j++)
550         {
551                 float d = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpointsbuf);
552                 if (fabs(planesbuf[j].dist - d) > (1.0f/32.0f))
553                         Con_Printf("plane %f %f %f %f mismatches dist %f\n", planesbuf[j].normal[0], planesbuf[j].normal[1], planesbuf[j].normal[2], planesbuf[j].dist, d);
554         }
555
556         // if nothing is left, there's nothing to allocate
557         if (numelementsbuf < 12 || numplanesbuf < 4 || numpointsbuf < 4)
558         {
559                 Con_Printf("Collision_NewBrushFromPlanes: failed to build collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
560                 return NULL;
561         }
562
563         // allocate the brush and copy to it
564         brush = Collision_AllocBrushFloat(mempool, numpointsbuf, numplanesbuf, numelementsbuf / 3, supercontents);
565         for (j = 0;j < brush->numpoints;j++)
566         {
567                 brush->points[j].v[0] = pointsbuf[j].v[0];
568                 brush->points[j].v[1] = pointsbuf[j].v[1];
569                 brush->points[j].v[2] = pointsbuf[j].v[2];
570         }
571         for (j = 0;j < brush->numplanes;j++)
572         {
573                 brush->planes[j].normal[0] = planesbuf[j].normal[0];
574                 brush->planes[j].normal[1] = planesbuf[j].normal[1];
575                 brush->planes[j].normal[2] = planesbuf[j].normal[2];
576                 brush->planes[j].dist = planesbuf[j].dist;
577         }
578         for (j = 0;j < brush->numtriangles * 3;j++)
579                 brush->elements[j] = elementsbuf[j];
580         VectorCopy(brush->points[0].v, brush->mins);
581         VectorCopy(brush->points[0].v, brush->maxs);
582         for (j = 1;j < brush->numpoints;j++)
583         {
584                 brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
585                 brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
586                 brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
587                 brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
588                 brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
589                 brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
590         }
591         brush->mins[0] -= 1;
592         brush->mins[1] -= 1;
593         brush->mins[2] -= 1;
594         brush->maxs[0] += 1;
595         brush->maxs[1] += 1;
596         brush->maxs[2] += 1;
597         Collision_ValidateBrush(brush);
598         return brush;
599 }
600
601
602
603 colbrushf_t *Collision_AllocBrushFloat(mempool_t *mempool, int numpoints, int numplanes, int numtriangles, int supercontents)
604 {
605         colbrushf_t *brush;
606         brush = Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colpointf_t) * numpoints + sizeof(colplanef_t) * numplanes + sizeof(int[3]) * numtriangles);
607         brush->supercontents = supercontents;
608         brush->numplanes = numplanes;
609         brush->numpoints = numpoints;
610         brush->numtriangles = numtriangles;
611         brush->planes = (void *)(brush + 1);
612         brush->points = (void *)(brush->planes + brush->numplanes);
613         brush->elements = (void *)(brush->points + brush->numpoints);
614         return brush;
615 }
616
617 void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush)
618 {
619         int i;
620         float edge0[3], edge1[3], edge2[3], normal[3], dist, bestdist;
621         colpointf_t *p, *p2;
622
623         if (brush->numpoints == 3)
624         {
625                 // optimized triangle case
626                 TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal);
627                 if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f)
628                 {
629                         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
630                         brush->numplanes = 0;
631                         return;
632                 }
633                 else
634                 {
635                         brush->numplanes = 5;
636                         VectorNormalize(brush->planes[0].normal);
637                         brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal);
638                         VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
639                         brush->planes[1].dist = -brush->planes[0].dist;
640                         VectorSubtract(brush->points[2].v, brush->points[0].v, edge0);
641                         VectorSubtract(brush->points[0].v, brush->points[1].v, edge1);
642                         VectorSubtract(brush->points[1].v, brush->points[2].v, edge2);
643 #if 1
644                         {
645                                 float projectionnormal[3], projectionedge0[3], projectionedge1[3], projectionedge2[3];
646                                 int i, best;
647                                 float dist, bestdist;
648                                 bestdist = fabs(brush->planes[0].normal[0]);
649                                 best = 0;
650                                 for (i = 1;i < 3;i++)
651                                 {
652                                         dist = fabs(brush->planes[0].normal[i]);
653                                         if (bestdist < dist)
654                                         {
655                                                 bestdist = dist;
656                                                 best = i;
657                                         }
658                                 }
659                                 VectorClear(projectionnormal);
660                                 if (brush->planes[0].normal[best] < 0)
661                                         projectionnormal[best] = -1;
662                                 else
663                                         projectionnormal[best] = 1;
664                                 VectorCopy(edge0, projectionedge0);
665                                 VectorCopy(edge1, projectionedge1);
666                                 VectorCopy(edge2, projectionedge2);
667                                 projectionedge0[best] = 0;
668                                 projectionedge1[best] = 0;
669                                 projectionedge2[best] = 0;
670                                 CrossProduct(projectionedge0, projectionnormal, brush->planes[2].normal);
671                                 CrossProduct(projectionedge1, projectionnormal, brush->planes[3].normal);
672                                 CrossProduct(projectionedge2, projectionnormal, brush->planes[4].normal);
673                         }
674 #else
675                         CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal);
676                         CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal);
677                         CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal);
678 #endif
679                         VectorNormalize(brush->planes[2].normal);
680                         VectorNormalize(brush->planes[3].normal);
681                         VectorNormalize(brush->planes[4].normal);
682                         brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal);
683                         brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal);
684                         brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal);
685
686                         if (developer.integer)
687                         {
688                                 // validation code
689 #if 0
690                                 float temp[3];
691
692                                 VectorSubtract(brush->points[0].v, brush->points[1].v, edge0);
693                                 VectorSubtract(brush->points[2].v, brush->points[1].v, edge1);
694                                 CrossProduct(edge0, edge1, normal);
695                                 VectorNormalize(normal);
696                                 VectorSubtract(normal, brush->planes[0].normal, temp);
697                                 if (VectorLength(temp) > 0.01f)
698                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: TriangleNormal gave wrong answer (%f %f %f != correct answer %f %f %f)\n", brush->planes->normal[0], brush->planes->normal[1], brush->planes->normal[2], normal[0], normal[1], normal[2]);
699                                 if (fabs(DotProduct(brush->planes[1].normal, brush->planes[0].normal) - -1.0f) > 0.01f || fabs(brush->planes[1].dist - -brush->planes[0].dist) > 0.01f)
700                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 1 (%f %f %f %f) is not opposite plane 0 (%f %f %f %f)\n", brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist);
701 #if 0
702                                 if (fabs(DotProduct(brush->planes[2].normal, brush->planes[0].normal)) > 0.01f)
703                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[2].dist);
704                                 if (fabs(DotProduct(brush->planes[3].normal, brush->planes[0].normal)) > 0.01f)
705                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[3].dist);
706                                 if (fabs(DotProduct(brush->planes[4].normal, brush->planes[0].normal)) > 0.01f)
707                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[4].dist);
708                                 if (fabs(DotProduct(brush->planes[2].normal, edge0)) > 0.01f)
709                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to edge 0 (%f %f %f to %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2]);
710                                 if (fabs(DotProduct(brush->planes[3].normal, edge1)) > 0.01f)
711                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to edge 1 (%f %f %f to %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2]);
712                                 if (fabs(DotProduct(brush->planes[4].normal, edge2)) > 0.01f)
713                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to edge 2 (%f %f %f to %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2]);
714 #endif
715 #endif
716                                 if (fabs(DotProduct(brush->points[0].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f)
717                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off front plane 0 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist);
718                                 if (fabs(DotProduct(brush->points[0].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f)
719                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off back plane 1 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist);
720                                 if (fabs(DotProduct(brush->points[2].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f || fabs(DotProduct(brush->points[0].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f)
721                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist);
722                                 if (fabs(DotProduct(brush->points[0].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f)
723                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist);
724                                 if (fabs(DotProduct(brush->points[1].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f)
725                                         Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist);
726                         }
727                 }
728         }
729         else
730         {
731                 // choose best surface normal for polygon's plane
732                 bestdist = 0;
733                 for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++)
734                 {
735                         VectorSubtract(p[-1].v, p[0].v, edge0);
736                         VectorSubtract(p[1].v, p[0].v, edge1);
737                         CrossProduct(edge0, edge1, normal);
738                         //TriangleNormal(p[-1].v, p[0].v, p[1].v, normal);
739                         dist = DotProduct(normal, normal);
740                         if (i == 0 || bestdist < dist)
741                         {
742                                 bestdist = dist;
743                                 VectorCopy(normal, brush->planes->normal);
744                         }
745                 }
746                 if (bestdist < 0.0001f)
747                 {
748                         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
749                         brush->numplanes = 0;
750                         return;
751                 }
752                 else
753                 {
754                         brush->numplanes = brush->numpoints + 2;
755                         VectorNormalize(brush->planes->normal);
756                         brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal);
757
758                         // negate plane to create other side
759                         VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
760                         brush->planes[1].dist = -brush->planes[0].dist;
761                         for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++)
762                         {
763                                 VectorSubtract(p->v, p2->v, edge0);
764                                 CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal);
765                                 VectorNormalize(brush->planes[i + 2].normal);
766                                 brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal);
767                         }
768                 }
769         }
770
771         if (developer.integer)
772         {
773                 // validity check - will be disabled later
774                 Collision_ValidateBrush(brush);
775                 for (i = 0;i < brush->numplanes;i++)
776                 {
777                         int j;
778                         for (j = 0, p = brush->points;j < brush->numpoints;j++, p++)
779                                 if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + (1.0 / 32.0))
780                                         Con_Printf("Error in brush plane generation, plane %i\n", i);
781                 }
782         }
783 }
784
785 colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents)
786 {
787         colbrushf_t *brush;
788         brush = Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colplanef_t) * (numpoints + 2));
789         brush->supercontents = supercontents;
790         brush->numpoints = numpoints;
791         brush->numplanes = numpoints + 2;
792         brush->planes = (void *)(brush + 1);
793         brush->points = (colpointf_t *)points;
794         Host_Error("Collision_AllocBrushFromPermanentPolygonFloat: FIXME: this code needs to be updated to generate a mesh...\n");
795         return brush;
796 }
797
798 // NOTE: start and end of each brush pair must have same numplanes/numpoints
799 void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end)
800 {
801         int nplane, nplane2, fstartsolid, fendsolid, brushsolid;
802         float enterfrac, leavefrac, d1, d2, f, move, imove, newimpactnormal[3], enterfrac2;
803         const colplanef_t *startplane, *endplane;
804
805         enterfrac = -1;
806         enterfrac2 = -1;
807         leavefrac = 1;
808         fstartsolid = true;
809         fendsolid = true;
810
811         for (nplane = 0;nplane < thatbrush_start->numplanes + thisbrush_start->numplanes;nplane++)
812         {
813                 nplane2 = nplane;
814                 if (nplane2 >= thatbrush_start->numplanes)
815                 {
816                         nplane2 -= thatbrush_start->numplanes;
817                         startplane = thisbrush_start->planes + nplane2;
818                         endplane = thisbrush_end->planes + nplane2;
819                         if (developer.integer)
820                         {
821                                 // any brush with degenerate planes is not worth handling
822                                 if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
823                                 {
824                                         Con_Print("Collision_TraceBrushBrushFloat: degenerate thisbrush plane!\n");
825                                         return;
826                                 }
827                                 f = furthestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints);
828                                 if (fabs(f - startplane->dist) > 0.125f)
829                                         Con_Printf("startplane->dist %f != calculated %f (thisbrush_start)\n", startplane->dist, f);
830                         }
831                         d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints) - collision_startnudge.value;
832                         d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - furthestplanedist_float(endplane->normal, thatbrush_end->points, thatbrush_end->numpoints) - collision_endnudge.value;
833                 }
834                 else
835                 {
836                         startplane = thatbrush_start->planes + nplane2;
837                         endplane = thatbrush_end->planes + nplane2;
838                         if (developer.integer)
839                         {
840                                 // any brush with degenerate planes is not worth handling
841                                 if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
842                                 {
843                                         Con_Print("Collision_TraceBrushBrushFloat: degenerate thatbrush plane!\n");
844                                         return;
845                                 }
846                                 f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
847                                 if (fabs(f - startplane->dist) > 0.125f)
848                                         Con_Printf("startplane->dist %f != calculated %f (thatbrush_start)\n", startplane->dist, f);
849                         }
850                         d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - startplane->dist - collision_startnudge.value;
851                         d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - endplane->dist - collision_endnudge.value;
852                 }
853                 //Con_Printf("%c%i: d1 = %f, d2 = %f, d1 / (d1 - d2) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, d1, d2, d1 / (d1 - d2));
854
855                 move = d1 - d2;
856                 if (move > 0)
857                 {
858                         // moving into brush
859                         if (d2 > collision_enternudge.value)
860                                 return;
861                         if (d1 < 0)
862                                 continue;
863                         // enter
864                         fstartsolid = false;
865                         imove = 1 / move;
866                         f = (d1 - collision_enternudge.value) * imove;
867                         f = bound(0, f, 1);
868                         if (enterfrac < f)
869                         {
870                                 enterfrac = f;
871                                 enterfrac2 = f - collision_impactnudge.value * imove;
872                                 enterfrac2 = bound(0, enterfrac2, 1);
873                                 VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
874                         }
875                 }
876                 else if (move < 0)
877                 {
878                         // moving out of brush
879                         if (d1 > collision_leavenudge.value)
880                                 return;
881                         if (d2 < 0)
882                                 continue;
883                         // leave
884                         fendsolid = false;
885                         f = (d1 + collision_leavenudge.value) / move;
886                         f = bound(0, f, 1);
887                         if (leavefrac > f)
888                                 leavefrac = f;
889                 }
890                 else
891                 {
892                         // sliding along plane
893                         if (d1 > 0)
894                                 return;
895                 }
896         }
897
898         brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
899         if (fstartsolid)
900         {
901                 trace->startsupercontents |= thatbrush_start->supercontents;
902                 if (brushsolid)
903                 {
904                         trace->startsolid = true;
905                         if (fendsolid)
906                                 trace->allsolid = true;
907                 }
908         }
909
910         // LordHavoc: we need an epsilon nudge here because for a point trace the
911         // penetrating line segment is normally zero length if this brush was
912         // generated from a polygon (infinitely thin), and could even be slightly
913         // positive or negative due to rounding errors in that case.
914         if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
915         {
916 #if 0
917                 // broken
918                 if (thatbrush_start->ispolygon)
919                 {
920                         d1 = nearestplanedist_float(thatbrush_start->planes[0].normal, thisbrush_start->points, thisbrush_start->numpoints) - thatbrush_start->planes[0].dist - collision_startnudge.value;
921                         d2 = nearestplanedist_float(thatbrush_end->planes[0].normal, thisbrush_end->points, thisbrush_end->numpoints) - thatbrush_end->planes[0].dist - collision_endnudge.value;
922                         move = d1 - d2;
923                         if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
924                                 return;
925                         // enter
926                         imove = 1 / move;
927                         enterfrac = (d1 - collision_enternudge.value) * imove;
928                         if (enterfrac < trace->realfraction)
929                         {
930                                 enterfrac2 = enterfrac - collision_impactnudge.value * imove;
931                                 trace->realfraction = bound(0, enterfrac, 1);
932                                 trace->fraction = bound(0, enterfrac2, 1);
933                                 VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
934                         }
935                 }
936                 else
937 #endif
938                 {
939                         trace->realfraction = bound(0, enterfrac, 1);
940                         trace->fraction = bound(0, enterfrac2, 1);
941                         VectorCopy(newimpactnormal, trace->plane.normal);
942                 }
943         }
944 }
945
946 // NOTE: start and end brush pair must have same numplanes/numpoints
947 void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end)
948 {
949         int nplane, fstartsolid, fendsolid, brushsolid;
950         float enterfrac, leavefrac, d1, d2, f, move, imove, newimpactnormal[3], enterfrac2;
951         const colplanef_t *startplane, *endplane;
952
953         enterfrac = -1;
954         enterfrac2 = -1;
955         leavefrac = 1;
956         fstartsolid = true;
957         fendsolid = true;
958
959         for (nplane = 0;nplane < thatbrush_start->numplanes;nplane++)
960         {
961                 startplane = thatbrush_start->planes + nplane;
962                 endplane = thatbrush_end->planes + nplane;
963                 d1 = DotProduct(startplane->normal, linestart) - startplane->dist - collision_startnudge.value;
964                 d2 = DotProduct(endplane->normal, lineend) - endplane->dist - collision_endnudge.value;
965                 if (developer.integer)
966                 {
967                         // any brush with degenerate planes is not worth handling
968                         if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
969                         {
970                                 Con_Print("Collision_TraceLineBrushFloat: degenerate plane!\n");
971                                 return;
972                         }
973                         if (thatbrush_start->numpoints)
974                         {
975                                 f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
976                                 if (fabs(f - startplane->dist) > 0.125f)
977                                         Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f);
978                         }
979                 }
980
981                 move = d1 - d2;
982                 if (move > 0)
983                 {
984                         // moving into brush
985                         if (d2 >= 0)
986                                 return;
987                         if (d1 <= 0)
988                                 continue;
989                         // enter
990                         fstartsolid = false;
991                         imove = 1 / move;
992                         f = (d1 - collision_enternudge.value) * imove;
993                         if (enterfrac < f)
994                         {
995                                 enterfrac = f;
996                                 enterfrac2 = f - collision_impactnudge.value * imove;
997                                 VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
998                         }
999                 }
1000                 else
1001                 {
1002                         // moving out of brush
1003                         if (d1 >= 0)
1004                                 return;
1005                         if (d2 <= 0)
1006                                 continue;
1007                         // leave
1008                         fendsolid = false;
1009                         f = (d1 - collision_leavenudge.value) / move;
1010                         if (leavefrac > f)
1011                                 leavefrac = f;
1012                 }
1013         }
1014
1015         brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
1016         if (fstartsolid)
1017         {
1018                 trace->startsupercontents |= thatbrush_start->supercontents;
1019                 if (brushsolid)
1020                 {
1021                         trace->startsolid = true;
1022                         if (fendsolid)
1023                                 trace->allsolid = true;
1024                 }
1025         }
1026
1027         // LordHavoc: we need an epsilon nudge here because for a point trace the
1028         // penetrating line segment is normally zero length if this brush was
1029         // generated from a polygon (infinitely thin), and could even be slightly
1030         // positive or negative due to rounding errors in that case.
1031         if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
1032         {
1033 #if 0
1034                 // broken
1035                 if (thatbrush_start->ispolygon)
1036                 {
1037                         d1 = DotProduct(thatbrush_start->planes[0].normal, linestart) - thatbrush_start->planes[0].dist - collision_startnudge.value;
1038                         d2 = DotProduct(thatbrush_end->planes[0].normal, lineend) - thatbrush_end->planes[0].dist - collision_endnudge.value;
1039                         move = d1 - d2;
1040                         if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
1041                                 return;
1042                         // enter
1043                         imove = 1 / move;
1044                         enterfrac = (d1 - collision_enternudge.value) * imove;
1045                         if (enterfrac < trace->realfraction)
1046                         {
1047                                 enterfrac2 = enterfrac - collision_impactnudge.value * imove;
1048                                 trace->realfraction = bound(0, enterfrac, 1);
1049                                 trace->fraction = bound(0, enterfrac2, 1);
1050                                 VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
1051                         }
1052                 }
1053                 else
1054 #endif
1055                 {
1056                         trace->realfraction = bound(0, enterfrac, 1);
1057                         trace->fraction = bound(0, enterfrac2, 1);
1058                         VectorCopy(newimpactnormal, trace->plane.normal);
1059                 }
1060         }
1061 }
1062
1063 void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
1064 {
1065         int nplane;
1066         const colplanef_t *plane;
1067
1068         for (nplane = 0, plane = thatbrush->planes;nplane < thatbrush->numplanes;nplane++, plane++)
1069                 if (DotProduct(plane->normal, point) > plane->dist)
1070                         return;
1071
1072         trace->startsupercontents |= thatbrush->supercontents;
1073         if (trace->hitsupercontentsmask & thatbrush->supercontents)
1074         {
1075                 trace->startsolid = true;
1076                 trace->allsolid = true;
1077         }
1078 }
1079
1080 static colpointf_t polyf_points[256];
1081 static colplanef_t polyf_planes[256 + 2];
1082 static colbrushf_t polyf_brush;
1083
1084 void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t *out, float fractionprecision, float invfractionprecision)
1085 {
1086         while (numpoints--)
1087         {
1088                 out->v[0] = floor(in->v[0] * fractionprecision + 0.5f) * invfractionprecision;
1089                 out->v[1] = floor(in->v[1] * fractionprecision + 0.5f) * invfractionprecision;
1090                 out->v[2] = floor(in->v[2] * fractionprecision + 0.5f) * invfractionprecision;
1091         }
1092 }
1093
1094 void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents)
1095 {
1096         if (numpoints > 256)
1097         {
1098                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1099                 return;
1100         }
1101         polyf_brush.numpoints = numpoints;
1102         polyf_brush.numplanes = numpoints + 2;
1103         //polyf_brush.points = (colpointf_t *)points;
1104         polyf_brush.planes = polyf_planes;
1105         polyf_brush.supercontents = supercontents;
1106         polyf_brush.points = polyf_points;
1107         Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1108         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1109         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1110         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1111 }
1112
1113 void Collision_TraceBrushTriangleMeshFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs)
1114 {
1115         int i;
1116         float facemins[3], facemaxs[3];
1117         polyf_brush.numpoints = 3;
1118         polyf_brush.numplanes = 5;
1119         polyf_brush.points = polyf_points;
1120         polyf_brush.planes = polyf_planes;
1121         polyf_brush.supercontents = supercontents;
1122         for (i = 0;i < numtriangles;i++, element3i += 3)
1123         {
1124                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1125                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1126                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1127                 Collision_SnapCopyPoints(3, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1128                 facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])) - 1;
1129                 facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])) - 1;
1130                 facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])) - 1;
1131                 facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])) + 1;
1132                 facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])) + 1;
1133                 facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])) + 1;
1134                 if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs))
1135                 {
1136                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1137                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1138                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1139                 }
1140         }
1141 }
1142
1143 void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents)
1144 {
1145         if (numpoints > 256)
1146         {
1147                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1148                 return;
1149         }
1150         polyf_brush.numpoints = numpoints;
1151         polyf_brush.numplanes = numpoints + 2;
1152         //polyf_brush.points = (colpointf_t *)points;
1153         polyf_brush.points = polyf_points;
1154         Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1155         polyf_brush.planes = polyf_planes;
1156         polyf_brush.supercontents = supercontents;
1157         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1158         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1159         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1160 }
1161
1162 void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs)
1163 {
1164         int i;
1165 #if 1
1166         // FIXME: snap vertices?
1167         for (i = 0;i < numtriangles;i++, element3i += 3)
1168                 Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[0] * 3, vertex3f + element3i[1] * 3, vertex3f + element3i[2] * 3);
1169 #else
1170         polyf_brush.numpoints = 3;
1171         polyf_brush.numplanes = 5;
1172         polyf_brush.points = polyf_points;
1173         polyf_brush.planes = polyf_planes;
1174         polyf_brush.supercontents = supercontents;
1175         for (i = 0;i < numtriangles;i++, element3i += 3)
1176         {
1177                 float facemins[3], facemaxs[3];
1178                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1179                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1180                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1181                 Collision_SnapCopyPoints(numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1182                 facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])) - 1;
1183                 facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])) - 1;
1184                 facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])) - 1;
1185                 facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])) + 1;
1186                 facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])) + 1;
1187                 facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])) + 1;
1188                 if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs))
1189                 {
1190                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1191                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1192                         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1193                 }
1194         }
1195 #endif
1196 }
1197
1198
1199 static colpointf_t polyf_pointsstart[256], polyf_pointsend[256];
1200 static colplanef_t polyf_planesstart[256 + 2], polyf_planesend[256 + 2];
1201 static colbrushf_t polyf_brushstart, polyf_brushend;
1202
1203 void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend, int supercontents)
1204 {
1205         int i;
1206         if (numpoints > 256)
1207         {
1208                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1209                 return;
1210         }
1211         polyf_brushstart.numpoints = numpoints;
1212         polyf_brushstart.numplanes = numpoints + 2;
1213         polyf_brushstart.points = polyf_pointsstart;//(colpointf_t *)points;
1214         polyf_brushstart.planes = polyf_planesstart;
1215         polyf_brushstart.supercontents = supercontents;
1216         for (i = 0;i < numpoints;i++)
1217                 Matrix4x4_Transform(polygonmatrixstart, points + i * 3, polyf_brushstart.points[i].v);
1218         polyf_brushend.numpoints = numpoints;
1219         polyf_brushend.numplanes = numpoints + 2;
1220         polyf_brushend.points = polyf_pointsend;//(colpointf_t *)points;
1221         polyf_brushend.planes = polyf_planesend;
1222         polyf_brushend.supercontents = supercontents;
1223         for (i = 0;i < numpoints;i++)
1224                 Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v);
1225         Collision_SnapCopyPoints(numpoints, polyf_pointsstart, polyf_pointsstart, COLLISION_SNAPSCALE, COLLISION_SNAP);
1226         Collision_SnapCopyPoints(numpoints, polyf_pointsend, polyf_pointsend, COLLISION_SNAPSCALE, COLLISION_SNAP);
1227         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart);
1228         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushend);
1229
1230         //Collision_PrintBrushAsQHull(&polyf_brushstart, "polyf_brushstart");
1231         //Collision_PrintBrushAsQHull(&polyf_brushend, "polyf_brushend");
1232
1233         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brushstart, &polyf_brushend);
1234 }
1235
1236
1237
1238 #define MAX_BRUSHFORBOX 16
1239 static int brushforbox_index = 0;
1240 static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8];
1241 static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6];
1242 static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX];
1243 static colbrushf_t brushforpoint_brush[MAX_BRUSHFORBOX];
1244
1245 void Collision_InitBrushForBox(void)
1246 {
1247         int i;
1248         for (i = 0;i < MAX_BRUSHFORBOX;i++)
1249         {
1250                 brushforbox_brush[i].supercontents = SUPERCONTENTS_SOLID;
1251                 brushforbox_brush[i].numpoints = 8;
1252                 brushforbox_brush[i].numplanes = 6;
1253                 brushforbox_brush[i].points = brushforbox_point + i * 8;
1254                 brushforbox_brush[i].planes = brushforbox_plane + i * 6;
1255                 brushforpoint_brush[i].supercontents = SUPERCONTENTS_SOLID;
1256                 brushforpoint_brush[i].numpoints = 1;
1257                 brushforpoint_brush[i].numplanes = 0;
1258                 brushforpoint_brush[i].points = brushforbox_point + i * 8;
1259                 brushforpoint_brush[i].planes = brushforbox_plane + i * 6;
1260         }
1261 }
1262
1263 colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, const vec3_t maxs)
1264 {
1265         int i, j;
1266         vec3_t v;
1267         colbrushf_t *brush;
1268         if (brushforbox_brush[0].numpoints == 0)
1269                 Collision_InitBrushForBox();
1270         if (VectorCompare(mins, maxs))
1271         {
1272                 // point brush
1273                 brush = brushforpoint_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1274                 VectorCopy(mins, brush->points->v);
1275         }
1276         else
1277         {
1278                 brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1279                 // FIXME: optimize
1280                 for (i = 0;i < 8;i++)
1281                 {
1282                         v[0] = i & 1 ? maxs[0] : mins[0];
1283                         v[1] = i & 2 ? maxs[1] : mins[1];
1284                         v[2] = i & 4 ? maxs[2] : mins[2];
1285                         Matrix4x4_Transform(matrix, v, brush->points[i].v);
1286                 }
1287                 // FIXME: optimize!
1288                 for (i = 0;i < 6;i++)
1289                 {
1290                         VectorClear(v);
1291                         v[i >> 1] = i & 1 ? 1 : -1;
1292                         Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal);
1293                         VectorNormalize(brush->planes[i].normal);
1294                 }
1295         }
1296         for (j = 0;j < brush->numplanes;j++)
1297                 brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
1298         VectorCopy(brush->points[0].v, brush->mins);
1299         VectorCopy(brush->points[0].v, brush->maxs);
1300         for (j = 1;j < brush->numpoints;j++)
1301         {
1302                 brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
1303                 brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
1304                 brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
1305                 brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
1306                 brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
1307                 brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
1308         }
1309         brush->mins[0] -= 1;
1310         brush->mins[1] -= 1;
1311         brush->mins[2] -= 1;
1312         brush->maxs[0] += 1;
1313         brush->maxs[1] += 1;
1314         brush->maxs[2] += 1;
1315         Collision_ValidateBrush(brush);
1316         return brush;
1317 }
1318
1319 void Collision_ClipTrace_BrushBox(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
1320 {
1321         colbrushf_t *boxbrush, *thisbrush_start, *thisbrush_end;
1322         matrix4x4_t identitymatrix;
1323         vec3_t startmins, startmaxs, endmins, endmaxs;
1324
1325         // create brushes for the collision
1326         VectorAdd(start, mins, startmins);
1327         VectorAdd(start, maxs, startmaxs);
1328         VectorAdd(end, mins, endmins);
1329         VectorAdd(end, maxs, endmaxs);
1330         Matrix4x4_CreateIdentity(&identitymatrix);
1331         boxbrush = Collision_BrushForBox(&identitymatrix, cmins, cmaxs);
1332         thisbrush_start = Collision_BrushForBox(&identitymatrix, startmins, startmaxs);
1333         thisbrush_end = Collision_BrushForBox(&identitymatrix, endmins, endmaxs);
1334
1335         memset(trace, 0, sizeof(trace_t));
1336         trace->hitsupercontentsmask = hitsupercontentsmask;
1337         trace->fraction = 1;
1338         trace->realfraction = 1;
1339         trace->allsolid = true;
1340         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, boxbrush, boxbrush);
1341 }
1342
1343 // LordHavoc: currently unused and not yet tested
1344 // note: this can be used for tracing a moving sphere vs a stationary sphere,
1345 // by simply adding the moving sphere's radius to the sphereradius parameter,
1346 // all the results are correct (impactpoint, impactnormal, and fraction)
1347 float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double *sphereorigin, double sphereradius, double *impactpoint, double *impactnormal)
1348 {
1349         double dir[3], scale, v[3], deviationdist, impactdist, linelength;
1350         // make sure the impactpoint and impactnormal are valid even if there is
1351         // no collision
1352         impactpoint[0] = lineend[0];
1353         impactpoint[1] = lineend[1];
1354         impactpoint[2] = lineend[2];
1355         impactnormal[0] = 0;
1356         impactnormal[1] = 0;
1357         impactnormal[2] = 0;
1358         // calculate line direction
1359         dir[0] = lineend[0] - linestart[0];
1360         dir[1] = lineend[1] - linestart[1];
1361         dir[2] = lineend[2] - linestart[2];
1362         // normalize direction
1363         linelength = sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1364         if (linelength)
1365         {
1366                 scale = 1.0 / linelength;
1367                 dir[0] *= scale;
1368                 dir[1] *= scale;
1369                 dir[2] *= scale;
1370         }
1371         // this dotproduct calculates the distance along the line at which the
1372         // sphere origin is (nearest point to the sphere origin on the line)
1373         impactdist = dir[0] * (sphereorigin[0] - linestart[0]) + dir[1] * (sphereorigin[1] - linestart[1]) + dir[2] * (sphereorigin[2] - linestart[2]);
1374         // calculate point on line at that distance, and subtract the
1375         // sphereorigin from it, so we have a vector to measure for the distance
1376         // of the line from the sphereorigin (deviation, how off-center it is)
1377         v[0] = linestart[0] + impactdist * dir[0] - sphereorigin[0];
1378         v[1] = linestart[1] + impactdist * dir[1] - sphereorigin[1];
1379         v[2] = linestart[2] + impactdist * dir[2] - sphereorigin[2];
1380         deviationdist = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
1381         // if outside the radius, it's a miss for sure
1382         // (we do this comparison using squared radius to avoid a sqrt)
1383         if (deviationdist > sphereradius*sphereradius)
1384                 return 1; // miss (off to the side)
1385         // nudge back to find the correct impact distance
1386         impactdist += (sqrt(deviationdist) - sphereradius);
1387         if (impactdist >= linelength)
1388                 return 1; // miss (not close enough)
1389         if (impactdist < 0)
1390                 return 1; // miss (linestart is past or inside sphere)
1391         // calculate new impactpoint
1392         impactpoint[0] = linestart[0] + impactdist * dir[0];
1393         impactpoint[1] = linestart[1] + impactdist * dir[1];
1394         impactpoint[2] = linestart[2] + impactdist * dir[2];
1395         // calculate impactnormal (surface normal at point of impact)
1396         impactnormal[0] = impactpoint[0] - sphereorigin[0];
1397         impactnormal[1] = impactpoint[1] - sphereorigin[1];
1398         impactnormal[2] = impactpoint[2] - sphereorigin[2];
1399         // normalize impactnormal
1400         scale = impactnormal[0] * impactnormal[0] + impactnormal[1] * impactnormal[1] + impactnormal[2] * impactnormal[2];
1401         if (scale)
1402         {
1403                 scale = 1.0 / sqrt(scale);
1404                 impactnormal[0] *= scale;
1405                 impactnormal[1] *= scale;
1406                 impactnormal[2] *= scale;
1407         }
1408         // return fraction of movement distance
1409         return impactdist / linelength;
1410 }
1411
1412 void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const float *point0, const float *point1, const float *point2)
1413 {
1414         float d1, d2, d, f, fnudged, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, edge[3];
1415
1416         // this code is designed for clockwise triangles, conversion to
1417         // counterclockwise would require swapping some things around...
1418         // it is easier to simply swap the point0 and point2 parameters to this
1419         // function when calling it than it is to rewire the internals.
1420
1421         // calculate the faceplanenormal of the triangle, this represents the front side
1422         TriangleNormal(point0, point1, point2, faceplanenormal);
1423         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
1424         if (DotProduct(faceplanenormal, faceplanenormal) < 0.0001f)
1425                 return;
1426         // normalize the normal
1427         VectorNormalize(faceplanenormal);
1428         // calculate the distance
1429         faceplanedist = DotProduct(point0, faceplanenormal);
1430
1431         // calculate the start distance
1432         d1 = DotProduct(faceplanenormal, linestart) - faceplanedist;
1433         // if start point is on the back side there is no collision
1434         // (we don't care about traces going through the triangle the wrong way)
1435         if (d1 < 0)
1436                 return;
1437
1438         // calculate the end distance
1439         d2 = DotProduct(faceplanenormal, lineend) - faceplanedist;
1440         // if both are in front, there is no collision
1441         if (d2 >= 0)
1442                 return;
1443
1444         // from here on we know d1 is >= 0 and d2 is < 0
1445         // this means the line starts infront and ends behind, passing through it
1446
1447         // calculate the recipricol of the distance delta,
1448         // so we can use it multiple times cheaply (instead of division)
1449         d = 1.0f / (d1 - d2);
1450         // calculate the impact fraction by taking the start distance (> 0)
1451         // and subtracting the face plane distance (this is the distance of the
1452         // triangle along that same normal)
1453         // then multiply by the recipricol distance delta
1454         f = d1 * d;
1455         // skip out if this impact is further away than previous ones
1456         if (f > trace->realfraction)
1457                 return;
1458         // calculate the perfect impact point for classification of insidedness
1459         impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1460         impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1461         impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1462
1463         // calculate the edge normal and reject if impact is outside triangle
1464         // (an edge normal faces away from the triangle, to get the desired normal
1465         //  a crossproduct with the faceplanenormal is used, and because of the way
1466         // the insidedness comparison is written it does not need to be normalized)
1467         
1468         VectorSubtract(point2, point0, edge);
1469         CrossProduct(edge, faceplanenormal, edgenormal);
1470         if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1471                 return;
1472
1473         VectorSubtract(point0, point1, edge);
1474         CrossProduct(edge, faceplanenormal, edgenormal);
1475         if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1476                 return;
1477
1478         VectorSubtract(point1, point2, edge);
1479         CrossProduct(edge, faceplanenormal, edgenormal);
1480         if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1481                 return;
1482
1483         // store the new trace fraction
1484         trace->realfraction = bound(0, f, 1);
1485
1486         // calculate a nudged fraction to keep it out of the surface
1487         // (the main fraction remains perfect)
1488         fnudged = (d1 - collision_impactnudge.value) * d;
1489         trace->fraction = bound(0, fnudged, 1);
1490
1491         // store the new trace endpos
1492         // not needed, it's calculated later when the trace is finished
1493         //trace->endpos[0] = linestart[0] + fnudged * (lineend[0] - linestart[0]);
1494         //trace->endpos[1] = linestart[1] + fnudged * (lineend[1] - linestart[1]);
1495         //trace->endpos[2] = linestart[2] + fnudged * (lineend[2] - linestart[2]);
1496
1497         // store the new trace plane (because collisions only happen from
1498         // the front this is always simply the triangle normal, never flipped)
1499         VectorCopy(faceplanenormal, trace->plane.normal);
1500         trace->plane.dist = faceplanedist;
1501 }
1502
1503 typedef struct colbspnode_s
1504 {
1505         mplane_t plane;
1506         struct colbspnode_s *children[2];
1507         // the node is reallocated or split if max is reached
1508         int numcolbrushf;
1509         int maxcolbrushf;
1510         colbrushf_t **colbrushflist;
1511         //int numcolbrushd;
1512         //int maxcolbrushd;
1513         //colbrushd_t **colbrushdlist;
1514 }
1515 colbspnode_t;
1516
1517 typedef struct colbsp_s
1518 {
1519         mempool_t *mempool;
1520         colbspnode_t *nodes;
1521 }
1522 colbsp_t;
1523
1524 colbsp_t *Collision_CreateCollisionBSP(mempool_t *mempool)
1525 {
1526         colbsp_t *bsp;
1527         bsp = Mem_Alloc(mempool, sizeof(colbsp_t));
1528         bsp->mempool = mempool;
1529         bsp->nodes = Mem_Alloc(bsp->mempool, sizeof(colbspnode_t));
1530         return bsp;
1531 }
1532
1533 void Collision_FreeCollisionBSPNode(colbspnode_t *node)
1534 {
1535         if (node->children[0])
1536                 Collision_FreeCollisionBSPNode(node->children[0]);
1537         if (node->children[1])
1538                 Collision_FreeCollisionBSPNode(node->children[1]);
1539         while (--node->numcolbrushf)
1540                 Mem_Free(node->colbrushflist[node->numcolbrushf]);
1541         //while (--node->numcolbrushd)
1542         //      Mem_Free(node->colbrushdlist[node->numcolbrushd]);
1543         Mem_Free(node);
1544 }
1545
1546 void Collision_FreeCollisionBSP(colbsp_t *bsp)
1547 {
1548         Collision_FreeCollisionBSPNode(bsp->nodes);
1549         Mem_Free(bsp);
1550 }
1551
1552 void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac)
1553 {
1554         int i;
1555         colpointf_t *ps, *pe;
1556         float tempstart[3], tempend[3];
1557         VectorLerp(start->points[0].v, startfrac, end->points[0].v, mins);
1558         VectorCopy(mins, maxs);
1559         for (i = 0, ps = start->points, pe = end->points;i < start->numpoints;i++, ps++, pe++)
1560         {
1561                 VectorLerp(ps->v, startfrac, pe->v, tempstart);
1562                 VectorLerp(ps->v, endfrac, pe->v, tempend);
1563                 mins[0] = min(mins[0], min(tempstart[0], tempend[0]));
1564                 mins[1] = min(mins[1], min(tempstart[1], tempend[1]));
1565                 mins[2] = min(mins[2], min(tempstart[2], tempend[2]));
1566                 maxs[0] = min(maxs[0], min(tempstart[0], tempend[0]));
1567                 maxs[1] = min(maxs[1], min(tempstart[1], tempend[1]));
1568                 maxs[2] = min(maxs[2], min(tempstart[2], tempend[2]));
1569         }
1570         mins[0] -= 1;
1571         mins[1] -= 1;
1572         mins[2] -= 1;
1573         maxs[0] += 1;
1574         maxs[1] += 1;
1575         maxs[2] += 1;
1576 }
1577