]> icculus.org git repositories - divverent/darkplaces.git/blob - collision.c
-Added support for 10 hostcache masks, which will be at the same time.
[divverent/darkplaces.git] / collision.c
1
2 #include "quakedef.h"
3 #include "polygon.h"
4
5 #define COLLISION_SNAPSCALE (8.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, 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                 if(d1 > d2)
856                 {
857                         // moving into brush
858                         if(d2 > 0)
859                                 return;
860                         if(d1 > 0)
861                         {
862                                 // enter
863                                 fstartsolid = false;
864                                 imove = 1 / (d1 - d2);
865                                 f = (d1 - collision_enternudge.value) * imove;
866                                 if (enterfrac < f)
867                                 {
868                                         enterfrac = f;
869                                         enterfrac2 = f - collision_impactnudge.value * imove;
870                                         VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
871                                 }
872                         }
873                 }
874                 else
875                 {
876                         // moving out of brush
877                         if(d1 > 0)
878                                 return;
879                         if(d2 > 0)
880                         {
881                                 // leave
882                                 fendsolid = false;
883                                 f = (d1 + collision_leavenudge.value) / (d1 - d2);
884                                 if (leavefrac > f)
885                                         leavefrac = f;
886                         }
887                 }
888         }
889
890         brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
891         if (fstartsolid)
892         {
893                 trace->startsupercontents |= thatbrush_start->supercontents;
894                 if (brushsolid)
895                 {
896                         trace->startsolid = true;
897                         if (fendsolid)
898                                 trace->allsolid = true;
899                 }
900         }
901
902         // LordHavoc: we need an epsilon nudge here because for a point trace the
903         // penetrating line segment is normally zero length if this brush was
904         // generated from a polygon (infinitely thin), and could even be slightly
905         // positive or negative due to rounding errors in that case.
906         if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
907         {
908 #if 0
909                 // broken
910                 if (thatbrush_start->ispolygon)
911                 {
912                         d1 = nearestplanedist_float(thatbrush_start->planes[0].normal, thisbrush_start->points, thisbrush_start->numpoints) - thatbrush_start->planes[0].dist - collision_startnudge.value;
913                         d2 = nearestplanedist_float(thatbrush_end->planes[0].normal, thisbrush_end->points, thisbrush_end->numpoints) - thatbrush_end->planes[0].dist - collision_endnudge.value;
914                         move = d1 - d2;
915                         if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
916                                 return;
917                         // enter
918                         imove = 1 / move;
919                         enterfrac = (d1 - collision_enternudge.value) * imove;
920                         if (enterfrac < trace->realfraction)
921                         {
922                                 enterfrac2 = enterfrac - collision_impactnudge.value * imove;
923                                 trace->realfraction = bound(0, enterfrac, 1);
924                                 trace->fraction = bound(0, enterfrac2, 1);
925                                 VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
926                         }
927                 }
928                 else
929 #endif
930                 {
931                         trace->realfraction = bound(0, enterfrac, 1);
932                         trace->fraction = bound(0, enterfrac2, 1);
933                         VectorCopy(newimpactnormal, trace->plane.normal);
934                 }
935         }
936 }
937
938 // NOTE: start and end brush pair must have same numplanes/numpoints
939 void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end)
940 {
941         int nplane, fstartsolid, fendsolid, brushsolid;
942         float enterfrac, leavefrac, d1, d2, f, imove, newimpactnormal[3], enterfrac2;
943         const colplanef_t *startplane, *endplane;
944
945         enterfrac = -1;
946         enterfrac2 = -1;
947         leavefrac = 1;
948         fstartsolid = true;
949         fendsolid = true;
950
951         for (nplane = 0;nplane < thatbrush_start->numplanes;nplane++)
952         {
953                 startplane = thatbrush_start->planes + nplane;
954                 endplane = thatbrush_end->planes + nplane;
955                 d1 = DotProduct(startplane->normal, linestart) - startplane->dist - collision_startnudge.value;
956                 d2 = DotProduct(endplane->normal, lineend) - endplane->dist - collision_endnudge.value;
957                 if (developer.integer)
958                 {
959                         // any brush with degenerate planes is not worth handling
960                         if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
961                         {
962                                 Con_Print("Collision_TraceLineBrushFloat: degenerate plane!\n");
963                                 return;
964                         }
965                         if (thatbrush_start->numpoints)
966                         {
967                                 f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
968                                 if (fabs(f - startplane->dist) > 0.125f)
969                                         Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f);
970                         }
971                 }
972
973                 if (d1 > d2)
974                 {
975                         // moving into brush
976                         if (d2 > 0)
977                                 return;
978                         if (d1 > 0)
979                         {
980                                 // enter
981                                 fstartsolid = false;
982                                 imove = 1 / (d1 - d2);
983                                 f = (d1 - collision_enternudge.value) * imove;
984                                 if (enterfrac < f)
985                                 {
986                                         enterfrac = f;
987                                         enterfrac2 = f - collision_impactnudge.value * imove;
988                                         VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
989                                 }
990                         }
991                 }
992                 else
993                 {
994                         // moving out of brush
995                         if (d1 > 0)
996                                 return;
997                         if (d2 > 0)
998                         {
999                                 // leave
1000                                 fendsolid = false;
1001                                 f = (d1 + collision_leavenudge.value) / (d1 - d2);
1002                                 if (leavefrac > f)
1003                                         leavefrac = f;
1004                         }
1005                 }
1006         }
1007
1008         brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
1009         if (fstartsolid)
1010         {
1011                 trace->startsupercontents |= thatbrush_start->supercontents;
1012                 if (brushsolid)
1013                 {
1014                         trace->startsolid = true;
1015                         if (fendsolid)
1016                                 trace->allsolid = true;
1017                 }
1018         }
1019
1020         // LordHavoc: we need an epsilon nudge here because for a point trace the
1021         // penetrating line segment is normally zero length if this brush was
1022         // generated from a polygon (infinitely thin), and could even be slightly
1023         // positive or negative due to rounding errors in that case.
1024         if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
1025         {
1026 #if 0
1027                 // broken
1028                 if (thatbrush_start->ispolygon)
1029                 {
1030                         d1 = DotProduct(thatbrush_start->planes[0].normal, linestart) - thatbrush_start->planes[0].dist - collision_startnudge.value;
1031                         d2 = DotProduct(thatbrush_end->planes[0].normal, lineend) - thatbrush_end->planes[0].dist - collision_endnudge.value;
1032                         move = d1 - d2;
1033                         if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
1034                                 return;
1035                         // enter
1036                         imove = 1 / move;
1037                         enterfrac = (d1 - collision_enternudge.value) * imove;
1038                         if (enterfrac < trace->realfraction)
1039                         {
1040                                 enterfrac2 = enterfrac - collision_impactnudge.value * imove;
1041                                 trace->realfraction = bound(0, enterfrac, 1);
1042                                 trace->fraction = bound(0, enterfrac2, 1);
1043                                 VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
1044                         }
1045                 }
1046                 else
1047 #endif
1048                 {
1049                         trace->realfraction = bound(0, enterfrac, 1);
1050                         trace->fraction = bound(0, enterfrac2, 1);
1051                         VectorCopy(newimpactnormal, trace->plane.normal);
1052                 }
1053         }
1054 }
1055
1056 void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
1057 {
1058         int nplane;
1059         const colplanef_t *plane;
1060
1061         for (nplane = 0, plane = thatbrush->planes;nplane < thatbrush->numplanes;nplane++, plane++)
1062                 if (DotProduct(plane->normal, point) > plane->dist)
1063                         return;
1064
1065         trace->startsupercontents |= thatbrush->supercontents;
1066         if (trace->hitsupercontentsmask & thatbrush->supercontents)
1067         {
1068                 trace->startsolid = true;
1069                 trace->allsolid = true;
1070         }
1071 }
1072
1073 static colpointf_t polyf_points[256];
1074 static colplanef_t polyf_planes[256 + 2];
1075 static colbrushf_t polyf_brush;
1076
1077 void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t *out, float fractionprecision, float invfractionprecision)
1078 {
1079         while (numpoints--)
1080         {
1081                 out->v[0] = floor(in->v[0] * fractionprecision + 0.5f) * invfractionprecision;
1082                 out->v[1] = floor(in->v[1] * fractionprecision + 0.5f) * invfractionprecision;
1083                 out->v[2] = floor(in->v[2] * fractionprecision + 0.5f) * invfractionprecision;
1084         }
1085 }
1086
1087 void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents)
1088 {
1089         if (numpoints > 256)
1090         {
1091                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1092                 return;
1093         }
1094         polyf_brush.numpoints = numpoints;
1095         polyf_brush.numplanes = numpoints + 2;
1096         //polyf_brush.points = (colpointf_t *)points;
1097         polyf_brush.planes = polyf_planes;
1098         polyf_brush.supercontents = supercontents;
1099         polyf_brush.points = polyf_points;
1100         Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1101         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1102         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1103         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1104 }
1105
1106 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)
1107 {
1108         int i;
1109         float facemins[3], facemaxs[3];
1110         polyf_brush.numpoints = 3;
1111         polyf_brush.numplanes = 5;
1112         polyf_brush.points = polyf_points;
1113         polyf_brush.planes = polyf_planes;
1114         polyf_brush.supercontents = supercontents;
1115         for (i = 0;i < numtriangles;i++, element3i += 3)
1116         {
1117                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1118                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1119                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1120                 Collision_SnapCopyPoints(3, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1121                 facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])) - 1;
1122                 facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])) - 1;
1123                 facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])) - 1;
1124                 facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])) + 1;
1125                 facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])) + 1;
1126                 facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])) + 1;
1127                 if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs))
1128                 {
1129                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1130                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1131                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1132                 }
1133         }
1134 }
1135
1136 void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents)
1137 {
1138         if (numpoints > 256)
1139         {
1140                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1141                 return;
1142         }
1143         polyf_brush.numpoints = numpoints;
1144         polyf_brush.numplanes = numpoints + 2;
1145         //polyf_brush.points = (colpointf_t *)points;
1146         polyf_brush.points = polyf_points;
1147         Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1148         polyf_brush.planes = polyf_planes;
1149         polyf_brush.supercontents = supercontents;
1150         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1151         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1152         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1153 }
1154
1155 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)
1156 {
1157         int i;
1158 #if 1
1159         // FIXME: snap vertices?
1160         for (i = 0;i < numtriangles;i++, element3i += 3)
1161                 Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[0] * 3, vertex3f + element3i[1] * 3, vertex3f + element3i[2] * 3);
1162 #else
1163         polyf_brush.numpoints = 3;
1164         polyf_brush.numplanes = 5;
1165         polyf_brush.points = polyf_points;
1166         polyf_brush.planes = polyf_planes;
1167         polyf_brush.supercontents = supercontents;
1168         for (i = 0;i < numtriangles;i++, element3i += 3)
1169         {
1170                 float facemins[3], facemaxs[3];
1171                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1172                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1173                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1174                 Collision_SnapCopyPoints(numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1175                 facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])) - 1;
1176                 facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])) - 1;
1177                 facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])) - 1;
1178                 facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])) + 1;
1179                 facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])) + 1;
1180                 facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])) + 1;
1181                 if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs))
1182                 {
1183                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1184                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1185                         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1186                 }
1187         }
1188 #endif
1189 }
1190
1191
1192 static colpointf_t polyf_pointsstart[256], polyf_pointsend[256];
1193 static colplanef_t polyf_planesstart[256 + 2], polyf_planesend[256 + 2];
1194 static colbrushf_t polyf_brushstart, polyf_brushend;
1195
1196 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)
1197 {
1198         int i;
1199         if (numpoints > 256)
1200         {
1201                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1202                 return;
1203         }
1204         polyf_brushstart.numpoints = numpoints;
1205         polyf_brushstart.numplanes = numpoints + 2;
1206         polyf_brushstart.points = polyf_pointsstart;//(colpointf_t *)points;
1207         polyf_brushstart.planes = polyf_planesstart;
1208         polyf_brushstart.supercontents = supercontents;
1209         for (i = 0;i < numpoints;i++)
1210                 Matrix4x4_Transform(polygonmatrixstart, points + i * 3, polyf_brushstart.points[i].v);
1211         polyf_brushend.numpoints = numpoints;
1212         polyf_brushend.numplanes = numpoints + 2;
1213         polyf_brushend.points = polyf_pointsend;//(colpointf_t *)points;
1214         polyf_brushend.planes = polyf_planesend;
1215         polyf_brushend.supercontents = supercontents;
1216         for (i = 0;i < numpoints;i++)
1217                 Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v);
1218         Collision_SnapCopyPoints(numpoints, polyf_pointsstart, polyf_pointsstart, COLLISION_SNAPSCALE, COLLISION_SNAP);
1219         Collision_SnapCopyPoints(numpoints, polyf_pointsend, polyf_pointsend, COLLISION_SNAPSCALE, COLLISION_SNAP);
1220         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart);
1221         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushend);
1222
1223         //Collision_PrintBrushAsQHull(&polyf_brushstart, "polyf_brushstart");
1224         //Collision_PrintBrushAsQHull(&polyf_brushend, "polyf_brushend");
1225
1226         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brushstart, &polyf_brushend);
1227 }
1228
1229
1230
1231 #define MAX_BRUSHFORBOX 16
1232 static int brushforbox_index = 0;
1233 static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8];
1234 static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6];
1235 static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX];
1236 static colbrushf_t brushforpoint_brush[MAX_BRUSHFORBOX];
1237
1238 void Collision_InitBrushForBox(void)
1239 {
1240         int i;
1241         for (i = 0;i < MAX_BRUSHFORBOX;i++)
1242         {
1243                 brushforbox_brush[i].supercontents = SUPERCONTENTS_SOLID;
1244                 brushforbox_brush[i].numpoints = 8;
1245                 brushforbox_brush[i].numplanes = 6;
1246                 brushforbox_brush[i].points = brushforbox_point + i * 8;
1247                 brushforbox_brush[i].planes = brushforbox_plane + i * 6;
1248                 brushforpoint_brush[i].supercontents = SUPERCONTENTS_SOLID;
1249                 brushforpoint_brush[i].numpoints = 1;
1250                 brushforpoint_brush[i].numplanes = 0;
1251                 brushforpoint_brush[i].points = brushforbox_point + i * 8;
1252                 brushforpoint_brush[i].planes = brushforbox_plane + i * 6;
1253         }
1254 }
1255
1256 colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, const vec3_t maxs)
1257 {
1258         int i, j;
1259         vec3_t v;
1260         colbrushf_t *brush;
1261         if (brushforbox_brush[0].numpoints == 0)
1262                 Collision_InitBrushForBox();
1263         if (VectorCompare(mins, maxs))
1264         {
1265                 // point brush
1266                 brush = brushforpoint_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1267                 VectorCopy(mins, brush->points->v);
1268         }
1269         else
1270         {
1271                 brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1272                 // FIXME: optimize
1273                 for (i = 0;i < 8;i++)
1274                 {
1275                         v[0] = i & 1 ? maxs[0] : mins[0];
1276                         v[1] = i & 2 ? maxs[1] : mins[1];
1277                         v[2] = i & 4 ? maxs[2] : mins[2];
1278                         Matrix4x4_Transform(matrix, v, brush->points[i].v);
1279                 }
1280                 // FIXME: optimize!
1281                 for (i = 0;i < 6;i++)
1282                 {
1283                         VectorClear(v);
1284                         v[i >> 1] = i & 1 ? 1 : -1;
1285                         Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal);
1286                         VectorNormalize(brush->planes[i].normal);
1287                 }
1288         }
1289         for (j = 0;j < brush->numplanes;j++)
1290                 brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
1291         VectorCopy(brush->points[0].v, brush->mins);
1292         VectorCopy(brush->points[0].v, brush->maxs);
1293         for (j = 1;j < brush->numpoints;j++)
1294         {
1295                 brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
1296                 brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
1297                 brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
1298                 brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
1299                 brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
1300                 brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
1301         }
1302         brush->mins[0] -= 1;
1303         brush->mins[1] -= 1;
1304         brush->mins[2] -= 1;
1305         brush->maxs[0] += 1;
1306         brush->maxs[1] += 1;
1307         brush->maxs[2] += 1;
1308         Collision_ValidateBrush(brush);
1309         return brush;
1310 }
1311
1312 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)
1313 {
1314         colbrushf_t *boxbrush, *thisbrush_start, *thisbrush_end;
1315         matrix4x4_t identitymatrix;
1316         vec3_t startmins, startmaxs, endmins, endmaxs;
1317
1318         // create brushes for the collision
1319         VectorAdd(start, mins, startmins);
1320         VectorAdd(start, maxs, startmaxs);
1321         VectorAdd(end, mins, endmins);
1322         VectorAdd(end, maxs, endmaxs);
1323         Matrix4x4_CreateIdentity(&identitymatrix);
1324         boxbrush = Collision_BrushForBox(&identitymatrix, cmins, cmaxs);
1325         thisbrush_start = Collision_BrushForBox(&identitymatrix, startmins, startmaxs);
1326         thisbrush_end = Collision_BrushForBox(&identitymatrix, endmins, endmaxs);
1327
1328         memset(trace, 0, sizeof(trace_t));
1329         trace->hitsupercontentsmask = hitsupercontentsmask;
1330         trace->fraction = 1;
1331         trace->realfraction = 1;
1332         trace->allsolid = true;
1333         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, boxbrush, boxbrush);
1334 }
1335
1336 // LordHavoc: currently unused and not yet tested
1337 // note: this can be used for tracing a moving sphere vs a stationary sphere,
1338 // by simply adding the moving sphere's radius to the sphereradius parameter,
1339 // all the results are correct (impactpoint, impactnormal, and fraction)
1340 float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double *sphereorigin, double sphereradius, double *impactpoint, double *impactnormal)
1341 {
1342         double dir[3], scale, v[3], deviationdist, impactdist, linelength;
1343         // make sure the impactpoint and impactnormal are valid even if there is
1344         // no collision
1345         impactpoint[0] = lineend[0];
1346         impactpoint[1] = lineend[1];
1347         impactpoint[2] = lineend[2];
1348         impactnormal[0] = 0;
1349         impactnormal[1] = 0;
1350         impactnormal[2] = 0;
1351         // calculate line direction
1352         dir[0] = lineend[0] - linestart[0];
1353         dir[1] = lineend[1] - linestart[1];
1354         dir[2] = lineend[2] - linestart[2];
1355         // normalize direction
1356         linelength = sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1357         if (linelength)
1358         {
1359                 scale = 1.0 / linelength;
1360                 dir[0] *= scale;
1361                 dir[1] *= scale;
1362                 dir[2] *= scale;
1363         }
1364         // this dotproduct calculates the distance along the line at which the
1365         // sphere origin is (nearest point to the sphere origin on the line)
1366         impactdist = dir[0] * (sphereorigin[0] - linestart[0]) + dir[1] * (sphereorigin[1] - linestart[1]) + dir[2] * (sphereorigin[2] - linestart[2]);
1367         // calculate point on line at that distance, and subtract the
1368         // sphereorigin from it, so we have a vector to measure for the distance
1369         // of the line from the sphereorigin (deviation, how off-center it is)
1370         v[0] = linestart[0] + impactdist * dir[0] - sphereorigin[0];
1371         v[1] = linestart[1] + impactdist * dir[1] - sphereorigin[1];
1372         v[2] = linestart[2] + impactdist * dir[2] - sphereorigin[2];
1373         deviationdist = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
1374         // if outside the radius, it's a miss for sure
1375         // (we do this comparison using squared radius to avoid a sqrt)
1376         if (deviationdist > sphereradius*sphereradius)
1377                 return 1; // miss (off to the side)
1378         // nudge back to find the correct impact distance
1379         impactdist += (sqrt(deviationdist) - sphereradius);
1380         if (impactdist >= linelength)
1381                 return 1; // miss (not close enough)
1382         if (impactdist < 0)
1383                 return 1; // miss (linestart is past or inside sphere)
1384         // calculate new impactpoint
1385         impactpoint[0] = linestart[0] + impactdist * dir[0];
1386         impactpoint[1] = linestart[1] + impactdist * dir[1];
1387         impactpoint[2] = linestart[2] + impactdist * dir[2];
1388         // calculate impactnormal (surface normal at point of impact)
1389         impactnormal[0] = impactpoint[0] - sphereorigin[0];
1390         impactnormal[1] = impactpoint[1] - sphereorigin[1];
1391         impactnormal[2] = impactpoint[2] - sphereorigin[2];
1392         // normalize impactnormal
1393         scale = impactnormal[0] * impactnormal[0] + impactnormal[1] * impactnormal[1] + impactnormal[2] * impactnormal[2];
1394         if (scale)
1395         {
1396                 scale = 1.0 / sqrt(scale);
1397                 impactnormal[0] *= scale;
1398                 impactnormal[1] *= scale;
1399                 impactnormal[2] *= scale;
1400         }
1401         // return fraction of movement distance
1402         return impactdist / linelength;
1403 }
1404
1405 void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const float *point0, const float *point1, const float *point2)
1406 {
1407         float d1, d2, d, f, fnudged, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, edge[3];
1408
1409         // this code is designed for clockwise triangles, conversion to
1410         // counterclockwise would require swapping some things around...
1411         // it is easier to simply swap the point0 and point2 parameters to this
1412         // function when calling it than it is to rewire the internals.
1413
1414         // calculate the unnormalized faceplanenormal of the triangle,
1415         // this represents the front side
1416         TriangleNormal(point0, point1, point2, faceplanenormal);
1417         // there's no point in processing a degenerate triangle
1418         // (GIGO - Garbage In, Garbage Out)
1419         if (DotProduct(faceplanenormal, faceplanenormal) < 0.0001f)
1420                 return;
1421         // calculate the unnormalized distance
1422         faceplanedist = DotProduct(point0, faceplanenormal);
1423
1424         // calculate the unnormalized start distance
1425         d1 = DotProduct(faceplanenormal, linestart) - faceplanedist;
1426         // if start point is on the back side there is no collision
1427         // (we don't care about traces going through the triangle the wrong way)
1428         if (d1 < 0)
1429                 return;
1430
1431         // calculate the unnormalized end distance
1432         d2 = DotProduct(faceplanenormal, lineend) - faceplanedist;
1433         // if both are in front, there is no collision
1434         if (d2 >= 0)
1435                 return;
1436
1437         // from here on we know d1 is >= 0 and d2 is < 0
1438         // this means the line starts infront and ends behind, passing through it
1439
1440         // calculate the recipricol of the distance delta,
1441         // so we can use it multiple times cheaply (instead of division)
1442         d = 1.0f / (d1 - d2);
1443         // calculate the impact fraction by taking the start distance (> 0)
1444         // and subtracting the face plane distance (this is the distance of the
1445         // triangle along that same normal)
1446         // then multiply by the recipricol distance delta
1447         f = d1 * d;
1448         // skip out if this impact is further away than previous ones
1449         if (f > trace->realfraction)
1450                 return;
1451         // calculate the perfect impact point for classification of insidedness
1452         impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1453         impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1454         impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1455
1456         // calculate the edge normal and reject if impact is outside triangle
1457         // (an edge normal faces away from the triangle, to get the desired normal
1458         //  a crossproduct with the faceplanenormal is used, and because of the way
1459         // the insidedness comparison is written it does not need to be normalized)
1460         
1461         VectorSubtract(point2, point0, edge);
1462         CrossProduct(edge, faceplanenormal, edgenormal);
1463         if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1464                 return;
1465
1466         VectorSubtract(point0, point1, edge);
1467         CrossProduct(edge, faceplanenormal, edgenormal);
1468         if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1469                 return;
1470
1471         VectorSubtract(point1, point2, edge);
1472         CrossProduct(edge, faceplanenormal, edgenormal);
1473         if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1474                 return;
1475
1476         // store the new trace fraction
1477         trace->realfraction = bound(0, f, 1);
1478
1479         // store the new trace plane (because collisions only happen from
1480         // the front this is always simply the triangle normal, never flipped)
1481         VectorNormalize(faceplanenormal);
1482         VectorCopy(faceplanenormal, trace->plane.normal);
1483         trace->plane.dist = DotProduct(point0, faceplanenormal);
1484
1485         // calculate the normalized start and end distances
1486         d1 = DotProduct(trace->plane.normal, linestart) - trace->plane.dist;
1487         d2 = DotProduct(trace->plane.normal, lineend) - trace->plane.dist;
1488
1489         // calculate a nudged fraction to keep it out of the surface
1490         // (the main fraction remains perfect)
1491         fnudged = (d1 - collision_impactnudge.value) / (d1 - d2);
1492         trace->fraction = bound(0, fnudged, 1);
1493
1494         // store the new trace endpos
1495         // not needed, it's calculated later when the trace is finished
1496         //trace->endpos[0] = linestart[0] + fnudged * (lineend[0] - linestart[0]);
1497         //trace->endpos[1] = linestart[1] + fnudged * (lineend[1] - linestart[1]);
1498         //trace->endpos[2] = linestart[2] + fnudged * (lineend[2] - linestart[2]);
1499 }
1500
1501 typedef struct colbspnode_s
1502 {
1503         mplane_t plane;
1504         struct colbspnode_s *children[2];
1505         // the node is reallocated or split if max is reached
1506         int numcolbrushf;
1507         int maxcolbrushf;
1508         colbrushf_t **colbrushflist;
1509         //int numcolbrushd;
1510         //int maxcolbrushd;
1511         //colbrushd_t **colbrushdlist;
1512 }
1513 colbspnode_t;
1514
1515 typedef struct colbsp_s
1516 {
1517         mempool_t *mempool;
1518         colbspnode_t *nodes;
1519 }
1520 colbsp_t;
1521
1522 colbsp_t *Collision_CreateCollisionBSP(mempool_t *mempool)
1523 {
1524         colbsp_t *bsp;
1525         bsp = Mem_Alloc(mempool, sizeof(colbsp_t));
1526         bsp->mempool = mempool;
1527         bsp->nodes = Mem_Alloc(bsp->mempool, sizeof(colbspnode_t));
1528         return bsp;
1529 }
1530
1531 void Collision_FreeCollisionBSPNode(colbspnode_t *node)
1532 {
1533         if (node->children[0])
1534                 Collision_FreeCollisionBSPNode(node->children[0]);
1535         if (node->children[1])
1536                 Collision_FreeCollisionBSPNode(node->children[1]);
1537         while (--node->numcolbrushf)
1538                 Mem_Free(node->colbrushflist[node->numcolbrushf]);
1539         //while (--node->numcolbrushd)
1540         //      Mem_Free(node->colbrushdlist[node->numcolbrushd]);
1541         Mem_Free(node);
1542 }
1543
1544 void Collision_FreeCollisionBSP(colbsp_t *bsp)
1545 {
1546         Collision_FreeCollisionBSPNode(bsp->nodes);
1547         Mem_Free(bsp);
1548 }
1549
1550 void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac)
1551 {
1552         int i;
1553         colpointf_t *ps, *pe;
1554         float tempstart[3], tempend[3];
1555         VectorLerp(start->points[0].v, startfrac, end->points[0].v, mins);
1556         VectorCopy(mins, maxs);
1557         for (i = 0, ps = start->points, pe = end->points;i < start->numpoints;i++, ps++, pe++)
1558         {
1559                 VectorLerp(ps->v, startfrac, pe->v, tempstart);
1560                 VectorLerp(ps->v, endfrac, pe->v, tempend);
1561                 mins[0] = min(mins[0], min(tempstart[0], tempend[0]));
1562                 mins[1] = min(mins[1], min(tempstart[1], tempend[1]));
1563                 mins[2] = min(mins[2], min(tempstart[2], tempend[2]));
1564                 maxs[0] = min(maxs[0], min(tempstart[0], tempend[0]));
1565                 maxs[1] = min(maxs[1], min(tempstart[1], tempend[1]));
1566                 maxs[2] = min(maxs[2], min(tempstart[2], tempend[2]));
1567         }
1568         mins[0] -= 1;
1569         mins[1] -= 1;
1570         mins[2] -= 1;
1571         maxs[0] += 1;
1572         maxs[1] += 1;
1573         maxs[2] += 1;
1574 }
1575