]> icculus.org git repositories - divverent/darkplaces.git/blob - collision.c
fixed a crash with lights that have no clusters (a light outside the level) by alloca...
[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         // FIXME: these probably don't actually need to be normalized if the collision code does not care
624         if (brush->numpoints == 3)
625         {
626                 // optimized triangle case
627                 TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal);
628                 if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f)
629                 {
630                         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
631                         brush->numplanes = 0;
632                         return;
633                 }
634                 else
635                 {
636                         brush->numplanes = 5;
637                         VectorNormalize(brush->planes[0].normal);
638                         brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal);
639                         VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
640                         brush->planes[1].dist = -brush->planes[0].dist;
641                         VectorSubtract(brush->points[2].v, brush->points[0].v, edge0);
642                         VectorSubtract(brush->points[0].v, brush->points[1].v, edge1);
643                         VectorSubtract(brush->points[1].v, brush->points[2].v, edge2);
644 #if 1
645                         {
646                                 float projectionnormal[3], projectionedge0[3], projectionedge1[3], projectionedge2[3];
647                                 int i, best;
648                                 float dist, bestdist;
649                                 bestdist = fabs(brush->planes[0].normal[0]);
650                                 best = 0;
651                                 for (i = 1;i < 3;i++)
652                                 {
653                                         dist = fabs(brush->planes[0].normal[i]);
654                                         if (bestdist < dist)
655                                         {
656                                                 bestdist = dist;
657                                                 best = i;
658                                         }
659                                 }
660                                 VectorClear(projectionnormal);
661                                 if (brush->planes[0].normal[best] < 0)
662                                         projectionnormal[best] = -1;
663                                 else
664                                         projectionnormal[best] = 1;
665                                 VectorCopy(edge0, projectionedge0);
666                                 VectorCopy(edge1, projectionedge1);
667                                 VectorCopy(edge2, projectionedge2);
668                                 projectionedge0[best] = 0;
669                                 projectionedge1[best] = 0;
670                                 projectionedge2[best] = 0;
671                                 CrossProduct(projectionedge0, projectionnormal, brush->planes[2].normal);
672                                 CrossProduct(projectionedge1, projectionnormal, brush->planes[3].normal);
673                                 CrossProduct(projectionedge2, projectionnormal, brush->planes[4].normal);
674                         }
675 #else
676                         CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal);
677                         CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal);
678                         CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal);
679 #endif
680                         VectorNormalize(brush->planes[2].normal);
681                         VectorNormalize(brush->planes[3].normal);
682                         VectorNormalize(brush->planes[4].normal);
683                         brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal);
684                         brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal);
685                         brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal);
686
687                         if (developer.integer)
688                         {
689                                 // validation code
690 #if 0
691                                 float temp[3];
692
693                                 VectorSubtract(brush->points[0].v, brush->points[1].v, edge0);
694                                 VectorSubtract(brush->points[2].v, brush->points[1].v, edge1);
695                                 CrossProduct(edge0, edge1, normal);
696                                 VectorNormalize(normal);
697                                 VectorSubtract(normal, brush->planes[0].normal, temp);
698                                 if (VectorLength(temp) > 0.01f)
699                                         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]);
700                                 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)
701                                         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);
702 #if 0
703                                 if (fabs(DotProduct(brush->planes[2].normal, brush->planes[0].normal)) > 0.01f)
704                                         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);
705                                 if (fabs(DotProduct(brush->planes[3].normal, brush->planes[0].normal)) > 0.01f)
706                                         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);
707                                 if (fabs(DotProduct(brush->planes[4].normal, brush->planes[0].normal)) > 0.01f)
708                                         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);
709                                 if (fabs(DotProduct(brush->planes[2].normal, edge0)) > 0.01f)
710                                         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]);
711                                 if (fabs(DotProduct(brush->planes[3].normal, edge1)) > 0.01f)
712                                         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]);
713                                 if (fabs(DotProduct(brush->planes[4].normal, edge2)) > 0.01f)
714                                         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]);
715 #endif
716 #endif
717                                 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)
718                                         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);
719                                 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)
720                                         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);
721                                 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)
722                                         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);
723                                 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)
724                                         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);
725                                 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)
726                                         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);
727                         }
728                 }
729         }
730         else
731         {
732                 // choose best surface normal for polygon's plane
733                 bestdist = 0;
734                 for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++)
735                 {
736                         VectorSubtract(p[-1].v, p[0].v, edge0);
737                         VectorSubtract(p[1].v, p[0].v, edge1);
738                         CrossProduct(edge0, edge1, normal);
739                         //TriangleNormal(p[-1].v, p[0].v, p[1].v, normal);
740                         dist = DotProduct(normal, normal);
741                         if (i == 0 || bestdist < dist)
742                         {
743                                 bestdist = dist;
744                                 VectorCopy(normal, brush->planes->normal);
745                         }
746                 }
747                 if (bestdist < 0.0001f)
748                 {
749                         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
750                         brush->numplanes = 0;
751                         return;
752                 }
753                 else
754                 {
755                         brush->numplanes = brush->numpoints + 2;
756                         VectorNormalize(brush->planes->normal);
757                         brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal);
758
759                         // negate plane to create other side
760                         VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
761                         brush->planes[1].dist = -brush->planes[0].dist;
762                         for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++)
763                         {
764                                 VectorSubtract(p->v, p2->v, edge0);
765                                 CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal);
766                                 VectorNormalize(brush->planes[i + 2].normal);
767                                 brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal);
768                         }
769                 }
770         }
771
772         if (developer.integer)
773         {
774                 // validity check - will be disabled later
775                 Collision_ValidateBrush(brush);
776                 for (i = 0;i < brush->numplanes;i++)
777                 {
778                         int j;
779                         for (j = 0, p = brush->points;j < brush->numpoints;j++, p++)
780                                 if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + (1.0 / 32.0))
781                                         Con_Printf("Error in brush plane generation, plane %i\n", i);
782                 }
783         }
784 }
785
786 colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents)
787 {
788         colbrushf_t *brush;
789         brush = Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colplanef_t) * (numpoints + 2));
790         brush->supercontents = supercontents;
791         brush->numpoints = numpoints;
792         brush->numplanes = numpoints + 2;
793         brush->planes = (void *)(brush + 1);
794         brush->points = (colpointf_t *)points;
795         Host_Error("Collision_AllocBrushFromPermanentPolygonFloat: FIXME: this code needs to be updated to generate a mesh...\n");
796         return brush;
797 }
798
799 // NOTE: start and end of each brush pair must have same numplanes/numpoints
800 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)
801 {
802         int nplane, nplane2, fstartsolid, fendsolid, brushsolid;
803         float enterfrac, leavefrac, d1, d2, f, imove, newimpactnormal[3], enterfrac2;
804         const colplanef_t *startplane, *endplane;
805
806         enterfrac = -1;
807         enterfrac2 = -1;
808         leavefrac = 1;
809         fstartsolid = true;
810         fendsolid = true;
811
812         for (nplane = 0;nplane < thatbrush_start->numplanes + thisbrush_start->numplanes;nplane++)
813         {
814                 nplane2 = nplane;
815                 if (nplane2 >= thatbrush_start->numplanes)
816                 {
817                         nplane2 -= thatbrush_start->numplanes;
818                         startplane = thisbrush_start->planes + nplane2;
819                         endplane = thisbrush_end->planes + nplane2;
820                         if (developer.integer)
821                         {
822                                 // any brush with degenerate planes is not worth handling
823                                 if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
824                                 {
825                                         Con_Print("Collision_TraceBrushBrushFloat: degenerate thisbrush plane!\n");
826                                         return;
827                                 }
828                                 f = furthestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints);
829                                 if (fabs(f - startplane->dist) > 0.125f)
830                                         Con_Printf("startplane->dist %f != calculated %f (thisbrush_start)\n", startplane->dist, f);
831                         }
832                         d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints) - collision_startnudge.value;
833                         d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - furthestplanedist_float(endplane->normal, thatbrush_end->points, thatbrush_end->numpoints) - collision_endnudge.value;
834                 }
835                 else
836                 {
837                         startplane = thatbrush_start->planes + nplane2;
838                         endplane = thatbrush_end->planes + nplane2;
839                         if (developer.integer)
840                         {
841                                 // any brush with degenerate planes is not worth handling
842                                 if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
843                                 {
844                                         Con_Print("Collision_TraceBrushBrushFloat: degenerate thatbrush plane!\n");
845                                         return;
846                                 }
847                                 f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
848                                 if (fabs(f - startplane->dist) > 0.125f)
849                                         Con_Printf("startplane->dist %f != calculated %f (thatbrush_start)\n", startplane->dist, f);
850                         }
851                         d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - startplane->dist - collision_startnudge.value;
852                         d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - endplane->dist - collision_endnudge.value;
853                 }
854                 //Con_Printf("%c%i: d1 = %f, d2 = %f, d1 / (d1 - d2) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, d1, d2, d1 / (d1 - d2));
855
856                 if(d1 > d2)
857                 {
858                         // moving into brush
859                         if(d2 > 0)
860                                 return;
861                         if(d1 > 0)
862                         {
863                                 // enter
864                                 fstartsolid = false;
865                                 imove = 1 / (d1 - d2);
866                                 f = (d1 - collision_enternudge.value) * imove;
867                                 if (enterfrac < f)
868                                 {
869                                         enterfrac = f;
870                                         enterfrac2 = f - collision_impactnudge.value * imove;
871                                         VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
872                                 }
873                         }
874                 }
875                 else
876                 {
877                         // moving out of brush
878                         if(d1 > 0)
879                                 return;
880                         if(d2 > 0)
881                         {
882                                 // leave
883                                 fendsolid = false;
884                                 f = (d1 + collision_leavenudge.value) / (d1 - d2);
885                                 if (leavefrac > f)
886                                         leavefrac = f;
887                         }
888                 }
889         }
890
891         brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
892         if (fstartsolid)
893         {
894                 trace->startsupercontents |= thatbrush_start->supercontents;
895                 if (brushsolid)
896                 {
897                         trace->startsolid = true;
898                         if (fendsolid)
899                                 trace->allsolid = true;
900                 }
901         }
902
903         // LordHavoc: we need an epsilon nudge here because for a point trace the
904         // penetrating line segment is normally zero length if this brush was
905         // generated from a polygon (infinitely thin), and could even be slightly
906         // positive or negative due to rounding errors in that case.
907         if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
908         {
909 #if 0
910                 // broken
911                 if (thatbrush_start->ispolygon)
912                 {
913                         d1 = nearestplanedist_float(thatbrush_start->planes[0].normal, thisbrush_start->points, thisbrush_start->numpoints) - thatbrush_start->planes[0].dist - collision_startnudge.value;
914                         d2 = nearestplanedist_float(thatbrush_end->planes[0].normal, thisbrush_end->points, thisbrush_end->numpoints) - thatbrush_end->planes[0].dist - collision_endnudge.value;
915                         move = d1 - d2;
916                         if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
917                                 return;
918                         // enter
919                         imove = 1 / move;
920                         enterfrac = (d1 - collision_enternudge.value) * imove;
921                         if (enterfrac < trace->realfraction)
922                         {
923                                 enterfrac2 = enterfrac - collision_impactnudge.value * imove;
924                                 trace->realfraction = bound(0, enterfrac, 1);
925                                 trace->fraction = bound(0, enterfrac2, 1);
926                                 VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
927                         }
928                 }
929                 else
930 #endif
931                 {
932                         trace->realfraction = bound(0, enterfrac, 1);
933                         trace->fraction = bound(0, enterfrac2, 1);
934                         VectorCopy(newimpactnormal, trace->plane.normal);
935                 }
936         }
937 }
938
939 // NOTE: start and end brush pair must have same numplanes/numpoints
940 void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end)
941 {
942         int nplane, fstartsolid, fendsolid, brushsolid;
943         float enterfrac, leavefrac, d1, d2, f, imove, newimpactnormal[3], enterfrac2;
944         const colplanef_t *startplane, *endplane;
945
946         enterfrac = -1;
947         enterfrac2 = -1;
948         leavefrac = 1;
949         fstartsolid = true;
950         fendsolid = true;
951
952         for (nplane = 0;nplane < thatbrush_start->numplanes;nplane++)
953         {
954                 startplane = thatbrush_start->planes + nplane;
955                 endplane = thatbrush_end->planes + nplane;
956                 d1 = DotProduct(startplane->normal, linestart) - startplane->dist - collision_startnudge.value;
957                 d2 = DotProduct(endplane->normal, lineend) - endplane->dist - collision_endnudge.value;
958                 if (developer.integer)
959                 {
960                         // any brush with degenerate planes is not worth handling
961                         if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f)
962                         {
963                                 Con_Print("Collision_TraceLineBrushFloat: degenerate plane!\n");
964                                 return;
965                         }
966                         if (thatbrush_start->numpoints)
967                         {
968                                 f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints);
969                                 if (fabs(f - startplane->dist) > 0.125f)
970                                         Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f);
971                         }
972                 }
973
974                 if (d1 > d2)
975                 {
976                         // moving into brush
977                         if (d2 > 0)
978                                 return;
979                         if (d1 > 0)
980                         {
981                                 // enter
982                                 fstartsolid = false;
983                                 imove = 1 / (d1 - d2);
984                                 f = (d1 - collision_enternudge.value) * imove;
985                                 if (enterfrac < f)
986                                 {
987                                         enterfrac = f;
988                                         enterfrac2 = f - collision_impactnudge.value * imove;
989                                         VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal);
990                                 }
991                         }
992                 }
993                 else
994                 {
995                         // moving out of brush
996                         if (d1 > 0)
997                                 return;
998                         if (d2 > 0)
999                         {
1000                                 // leave
1001                                 fendsolid = false;
1002                                 f = (d1 + collision_leavenudge.value) / (d1 - d2);
1003                                 if (leavefrac > f)
1004                                         leavefrac = f;
1005                         }
1006                 }
1007         }
1008
1009         brushsolid = trace->hitsupercontentsmask & thatbrush_start->supercontents;
1010         if (fstartsolid)
1011         {
1012                 trace->startsupercontents |= thatbrush_start->supercontents;
1013                 if (brushsolid)
1014                 {
1015                         trace->startsolid = true;
1016                         if (fendsolid)
1017                                 trace->allsolid = true;
1018                 }
1019         }
1020
1021         // LordHavoc: we need an epsilon nudge here because for a point trace the
1022         // penetrating line segment is normally zero length if this brush was
1023         // generated from a polygon (infinitely thin), and could even be slightly
1024         // positive or negative due to rounding errors in that case.
1025         if (brushsolid && enterfrac > -1 && enterfrac < trace->realfraction && enterfrac - (1.0f / 1024.0f) <= leavefrac)
1026         {
1027 #if 0
1028                 // broken
1029                 if (thatbrush_start->ispolygon)
1030                 {
1031                         d1 = DotProduct(thatbrush_start->planes[0].normal, linestart) - thatbrush_start->planes[0].dist - collision_startnudge.value;
1032                         d2 = DotProduct(thatbrush_end->planes[0].normal, lineend) - thatbrush_end->planes[0].dist - collision_endnudge.value;
1033                         move = d1 - d2;
1034                         if (move <= 0 || d2 > collision_enternudge.value || d1 < 0)
1035                                 return;
1036                         // enter
1037                         imove = 1 / move;
1038                         enterfrac = (d1 - collision_enternudge.value) * imove;
1039                         if (enterfrac < trace->realfraction)
1040                         {
1041                                 enterfrac2 = enterfrac - collision_impactnudge.value * imove;
1042                                 trace->realfraction = bound(0, enterfrac, 1);
1043                                 trace->fraction = bound(0, enterfrac2, 1);
1044                                 VectorLerp(thatbrush_start->planes[0].normal, enterfrac, thatbrush_end->planes[0].normal, trace->plane.normal);
1045                         }
1046                 }
1047                 else
1048 #endif
1049                 {
1050                         trace->realfraction = bound(0, enterfrac, 1);
1051                         trace->fraction = bound(0, enterfrac2, 1);
1052                         VectorCopy(newimpactnormal, trace->plane.normal);
1053                 }
1054         }
1055 }
1056
1057 void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
1058 {
1059         int nplane;
1060         const colplanef_t *plane;
1061
1062         for (nplane = 0, plane = thatbrush->planes;nplane < thatbrush->numplanes;nplane++, plane++)
1063                 if (DotProduct(plane->normal, point) > plane->dist)
1064                         return;
1065
1066         trace->startsupercontents |= thatbrush->supercontents;
1067         if (trace->hitsupercontentsmask & thatbrush->supercontents)
1068         {
1069                 trace->startsolid = true;
1070                 trace->allsolid = true;
1071         }
1072 }
1073
1074 static colpointf_t polyf_points[256];
1075 static colplanef_t polyf_planes[256 + 2];
1076 static colbrushf_t polyf_brush;
1077
1078 void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t *out, float fractionprecision, float invfractionprecision)
1079 {
1080         while (numpoints--)
1081         {
1082                 out->v[0] = floor(in->v[0] * fractionprecision + 0.5f) * invfractionprecision;
1083                 out->v[1] = floor(in->v[1] * fractionprecision + 0.5f) * invfractionprecision;
1084                 out->v[2] = floor(in->v[2] * fractionprecision + 0.5f) * invfractionprecision;
1085         }
1086 }
1087
1088 void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents)
1089 {
1090         if (numpoints > 256)
1091         {
1092                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1093                 return;
1094         }
1095         polyf_brush.numpoints = numpoints;
1096         polyf_brush.numplanes = numpoints + 2;
1097         //polyf_brush.points = (colpointf_t *)points;
1098         polyf_brush.planes = polyf_planes;
1099         polyf_brush.supercontents = supercontents;
1100         polyf_brush.points = polyf_points;
1101         Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1102         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1103         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1104         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1105 }
1106
1107 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)
1108 {
1109         int i;
1110         float facemins[3], facemaxs[3];
1111         polyf_brush.numpoints = 3;
1112         polyf_brush.numplanes = 5;
1113         polyf_brush.points = polyf_points;
1114         polyf_brush.planes = polyf_planes;
1115         polyf_brush.supercontents = supercontents;
1116         for (i = 0;i < numtriangles;i++, element3i += 3)
1117         {
1118                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1119                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1120                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1121                 Collision_SnapCopyPoints(3, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1122                 facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])) - 1;
1123                 facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])) - 1;
1124                 facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])) - 1;
1125                 facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])) + 1;
1126                 facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])) + 1;
1127                 facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])) + 1;
1128                 if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs))
1129                 {
1130                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1131                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1132                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1133                 }
1134         }
1135 }
1136
1137 void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents)
1138 {
1139         if (numpoints > 256)
1140         {
1141                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1142                 return;
1143         }
1144         polyf_brush.numpoints = numpoints;
1145         polyf_brush.numplanes = numpoints + 2;
1146         //polyf_brush.points = (colpointf_t *)points;
1147         polyf_brush.points = polyf_points;
1148         Collision_SnapCopyPoints(numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1149         polyf_brush.planes = polyf_planes;
1150         polyf_brush.supercontents = supercontents;
1151         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1152         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1153         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1154 }
1155
1156 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)
1157 {
1158         int i;
1159 #if 1
1160         // FIXME: snap vertices?
1161         for (i = 0;i < numtriangles;i++, element3i += 3)
1162                 Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[0] * 3, vertex3f + element3i[1] * 3, vertex3f + element3i[2] * 3);
1163 #else
1164         polyf_brush.numpoints = 3;
1165         polyf_brush.numplanes = 5;
1166         polyf_brush.points = polyf_points;
1167         polyf_brush.planes = polyf_planes;
1168         polyf_brush.supercontents = supercontents;
1169         for (i = 0;i < numtriangles;i++, element3i += 3)
1170         {
1171                 float facemins[3], facemaxs[3];
1172                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1173                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1174                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1175                 Collision_SnapCopyPoints(numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1176                 facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])) - 1;
1177                 facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])) - 1;
1178                 facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])) - 1;
1179                 facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])) + 1;
1180                 facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])) + 1;
1181                 facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])) + 1;
1182                 if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs))
1183                 {
1184                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1185                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1186                         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1187                 }
1188         }
1189 #endif
1190 }
1191
1192
1193 static colpointf_t polyf_pointsstart[256], polyf_pointsend[256];
1194 static colplanef_t polyf_planesstart[256 + 2], polyf_planesend[256 + 2];
1195 static colbrushf_t polyf_brushstart, polyf_brushend;
1196
1197 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)
1198 {
1199         int i;
1200         if (numpoints > 256)
1201         {
1202                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1203                 return;
1204         }
1205         polyf_brushstart.numpoints = numpoints;
1206         polyf_brushstart.numplanes = numpoints + 2;
1207         polyf_brushstart.points = polyf_pointsstart;//(colpointf_t *)points;
1208         polyf_brushstart.planes = polyf_planesstart;
1209         polyf_brushstart.supercontents = supercontents;
1210         for (i = 0;i < numpoints;i++)
1211                 Matrix4x4_Transform(polygonmatrixstart, points + i * 3, polyf_brushstart.points[i].v);
1212         polyf_brushend.numpoints = numpoints;
1213         polyf_brushend.numplanes = numpoints + 2;
1214         polyf_brushend.points = polyf_pointsend;//(colpointf_t *)points;
1215         polyf_brushend.planes = polyf_planesend;
1216         polyf_brushend.supercontents = supercontents;
1217         for (i = 0;i < numpoints;i++)
1218                 Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v);
1219         Collision_SnapCopyPoints(numpoints, polyf_pointsstart, polyf_pointsstart, COLLISION_SNAPSCALE, COLLISION_SNAP);
1220         Collision_SnapCopyPoints(numpoints, polyf_pointsend, polyf_pointsend, COLLISION_SNAPSCALE, COLLISION_SNAP);
1221         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart);
1222         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushend);
1223
1224         //Collision_PrintBrushAsQHull(&polyf_brushstart, "polyf_brushstart");
1225         //Collision_PrintBrushAsQHull(&polyf_brushend, "polyf_brushend");
1226
1227         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brushstart, &polyf_brushend);
1228 }
1229
1230
1231
1232 #define MAX_BRUSHFORBOX 16
1233 static int brushforbox_index = 0;
1234 static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8];
1235 static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6];
1236 static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX];
1237 static colbrushf_t brushforpoint_brush[MAX_BRUSHFORBOX];
1238
1239 void Collision_InitBrushForBox(void)
1240 {
1241         int i;
1242         for (i = 0;i < MAX_BRUSHFORBOX;i++)
1243         {
1244                 brushforbox_brush[i].supercontents = SUPERCONTENTS_SOLID;
1245                 brushforbox_brush[i].numpoints = 8;
1246                 brushforbox_brush[i].numplanes = 6;
1247                 brushforbox_brush[i].points = brushforbox_point + i * 8;
1248                 brushforbox_brush[i].planes = brushforbox_plane + i * 6;
1249                 brushforpoint_brush[i].supercontents = SUPERCONTENTS_SOLID;
1250                 brushforpoint_brush[i].numpoints = 1;
1251                 brushforpoint_brush[i].numplanes = 0;
1252                 brushforpoint_brush[i].points = brushforbox_point + i * 8;
1253                 brushforpoint_brush[i].planes = brushforbox_plane + i * 6;
1254         }
1255 }
1256
1257 colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, const vec3_t maxs)
1258 {
1259         int i, j;
1260         vec3_t v;
1261         colbrushf_t *brush;
1262         if (brushforbox_brush[0].numpoints == 0)
1263                 Collision_InitBrushForBox();
1264         // FIXME: these probably don't actually need to be normalized if the collision code does not care
1265         if (VectorCompare(mins, maxs))
1266         {
1267                 // point brush
1268                 brush = brushforpoint_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1269                 VectorCopy(mins, brush->points->v);
1270         }
1271         else
1272         {
1273                 brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1274                 // FIXME: optimize
1275                 for (i = 0;i < 8;i++)
1276                 {
1277                         v[0] = i & 1 ? maxs[0] : mins[0];
1278                         v[1] = i & 2 ? maxs[1] : mins[1];
1279                         v[2] = i & 4 ? maxs[2] : mins[2];
1280                         Matrix4x4_Transform(matrix, v, brush->points[i].v);
1281                 }
1282                 // FIXME: optimize!
1283                 for (i = 0;i < 6;i++)
1284                 {
1285                         VectorClear(v);
1286                         v[i >> 1] = i & 1 ? 1 : -1;
1287                         Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal);
1288                         VectorNormalize(brush->planes[i].normal);
1289                 }
1290         }
1291         for (j = 0;j < brush->numplanes;j++)
1292                 brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
1293         VectorCopy(brush->points[0].v, brush->mins);
1294         VectorCopy(brush->points[0].v, brush->maxs);
1295         for (j = 1;j < brush->numpoints;j++)
1296         {
1297                 brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
1298                 brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
1299                 brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
1300                 brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
1301                 brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
1302                 brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
1303         }
1304         brush->mins[0] -= 1;
1305         brush->mins[1] -= 1;
1306         brush->mins[2] -= 1;
1307         brush->maxs[0] += 1;
1308         brush->maxs[1] += 1;
1309         brush->maxs[2] += 1;
1310         Collision_ValidateBrush(brush);
1311         return brush;
1312 }
1313
1314 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)
1315 {
1316         colbrushf_t *boxbrush, *thisbrush_start, *thisbrush_end;
1317         matrix4x4_t identitymatrix;
1318         vec3_t startmins, startmaxs, endmins, endmaxs;
1319
1320         // create brushes for the collision
1321         VectorAdd(start, mins, startmins);
1322         VectorAdd(start, maxs, startmaxs);
1323         VectorAdd(end, mins, endmins);
1324         VectorAdd(end, maxs, endmaxs);
1325         Matrix4x4_CreateIdentity(&identitymatrix);
1326         boxbrush = Collision_BrushForBox(&identitymatrix, cmins, cmaxs);
1327         thisbrush_start = Collision_BrushForBox(&identitymatrix, startmins, startmaxs);
1328         thisbrush_end = Collision_BrushForBox(&identitymatrix, endmins, endmaxs);
1329
1330         memset(trace, 0, sizeof(trace_t));
1331         trace->hitsupercontentsmask = hitsupercontentsmask;
1332         trace->fraction = 1;
1333         trace->realfraction = 1;
1334         trace->allsolid = true;
1335         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, boxbrush, boxbrush);
1336 }
1337
1338 // LordHavoc: currently unused and not yet tested
1339 // note: this can be used for tracing a moving sphere vs a stationary sphere,
1340 // by simply adding the moving sphere's radius to the sphereradius parameter,
1341 // all the results are correct (impactpoint, impactnormal, and fraction)
1342 float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double *sphereorigin, double sphereradius, double *impactpoint, double *impactnormal)
1343 {
1344         double dir[3], scale, v[3], deviationdist, impactdist, linelength;
1345         // make sure the impactpoint and impactnormal are valid even if there is
1346         // no collision
1347         impactpoint[0] = lineend[0];
1348         impactpoint[1] = lineend[1];
1349         impactpoint[2] = lineend[2];
1350         impactnormal[0] = 0;
1351         impactnormal[1] = 0;
1352         impactnormal[2] = 0;
1353         // calculate line direction
1354         dir[0] = lineend[0] - linestart[0];
1355         dir[1] = lineend[1] - linestart[1];
1356         dir[2] = lineend[2] - linestart[2];
1357         // normalize direction
1358         linelength = sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
1359         if (linelength)
1360         {
1361                 scale = 1.0 / linelength;
1362                 dir[0] *= scale;
1363                 dir[1] *= scale;
1364                 dir[2] *= scale;
1365         }
1366         // this dotproduct calculates the distance along the line at which the
1367         // sphere origin is (nearest point to the sphere origin on the line)
1368         impactdist = dir[0] * (sphereorigin[0] - linestart[0]) + dir[1] * (sphereorigin[1] - linestart[1]) + dir[2] * (sphereorigin[2] - linestart[2]);
1369         // calculate point on line at that distance, and subtract the
1370         // sphereorigin from it, so we have a vector to measure for the distance
1371         // of the line from the sphereorigin (deviation, how off-center it is)
1372         v[0] = linestart[0] + impactdist * dir[0] - sphereorigin[0];
1373         v[1] = linestart[1] + impactdist * dir[1] - sphereorigin[1];
1374         v[2] = linestart[2] + impactdist * dir[2] - sphereorigin[2];
1375         deviationdist = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
1376         // if outside the radius, it's a miss for sure
1377         // (we do this comparison using squared radius to avoid a sqrt)
1378         if (deviationdist > sphereradius*sphereradius)
1379                 return 1; // miss (off to the side)
1380         // nudge back to find the correct impact distance
1381         impactdist += (sqrt(deviationdist) - sphereradius);
1382         if (impactdist >= linelength)
1383                 return 1; // miss (not close enough)
1384         if (impactdist < 0)
1385                 return 1; // miss (linestart is past or inside sphere)
1386         // calculate new impactpoint
1387         impactpoint[0] = linestart[0] + impactdist * dir[0];
1388         impactpoint[1] = linestart[1] + impactdist * dir[1];
1389         impactpoint[2] = linestart[2] + impactdist * dir[2];
1390         // calculate impactnormal (surface normal at point of impact)
1391         impactnormal[0] = impactpoint[0] - sphereorigin[0];
1392         impactnormal[1] = impactpoint[1] - sphereorigin[1];
1393         impactnormal[2] = impactpoint[2] - sphereorigin[2];
1394         // normalize impactnormal
1395         scale = impactnormal[0] * impactnormal[0] + impactnormal[1] * impactnormal[1] + impactnormal[2] * impactnormal[2];
1396         if (scale)
1397         {
1398                 scale = 1.0 / sqrt(scale);
1399                 impactnormal[0] *= scale;
1400                 impactnormal[1] *= scale;
1401                 impactnormal[2] *= scale;
1402         }
1403         // return fraction of movement distance
1404         return impactdist / linelength;
1405 }
1406
1407 void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const float *point0, const float *point1, const float *point2)
1408 {
1409 #if 1
1410         // more optimized
1411         float d1, d2, d, f, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, faceplanenormallength2, edge01[3], edge21[3], edge02[3];
1412
1413         // this function executes:
1414         // 32 ops when line starts behind triangle
1415         // 38 ops when line ends infront of triangle
1416         // 43 ops when line fraction is already closer than this triangle
1417         // 72 ops when line is outside edge 01
1418         // 92 ops when line is outside edge 21
1419         // 115 ops when line is outside edge 02
1420         // 123 ops when line impacts triangle and updates trace results
1421
1422         // this code is designed for clockwise triangles, conversion to
1423         // counterclockwise would require swapping some things around...
1424         // it is easier to simply swap the point0 and point2 parameters to this
1425         // function when calling it than it is to rewire the internals.
1426
1427         // calculate the faceplanenormal of the triangle, this represents the front side
1428         // 15 ops
1429         VectorSubtract(point0, point1, edge01);
1430         VectorSubtract(point2, point1, edge21);
1431         CrossProduct(edge01, edge21, faceplanenormal);
1432         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
1433         // 6 ops
1434         faceplanenormallength2 = DotProduct(faceplanenormal, faceplanenormal);
1435         if (faceplanenormallength2 < 0.0001f)
1436                 return;
1437         // calculate the distance
1438         // 5 ops
1439         faceplanedist = DotProduct(point0, faceplanenormal);
1440
1441         // if start point is on the back side there is no collision
1442         // (we don't care about traces going through the triangle the wrong way)
1443
1444         // calculate the start distance
1445         // 6 ops
1446         d1 = DotProduct(faceplanenormal, linestart);
1447         if (d1 <= faceplanedist)
1448                 return;
1449
1450         // calculate the end distance
1451         // 6 ops
1452         d2 = DotProduct(faceplanenormal, lineend);
1453         // if both are in front, there is no collision
1454         if (d2 >= faceplanedist)
1455                 return;
1456
1457         // from here on we know d1 is >= 0 and d2 is < 0
1458         // this means the line starts infront and ends behind, passing through it
1459
1460         // calculate the recipricol of the distance delta,
1461         // so we can use it multiple times cheaply (instead of division)
1462         // 2 ops
1463         d = 1.0f / (d1 - d2);
1464         // calculate the impact fraction by taking the start distance (> 0)
1465         // and subtracting the face plane distance (this is the distance of the
1466         // triangle along that same normal)
1467         // then multiply by the recipricol distance delta
1468         // 2 ops
1469         f = (d1 - faceplanedist) * d;
1470         // skip out if this impact is further away than previous ones
1471         // 1 ops
1472         if (f > trace->realfraction)
1473                 return;
1474         // calculate the perfect impact point for classification of insidedness
1475         // 9 ops
1476         impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1477         impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1478         impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1479
1480         // calculate the edge normal and reject if impact is outside triangle
1481         // (an edge normal faces away from the triangle, to get the desired normal
1482         //  a crossproduct with the faceplanenormal is used, and because of the way
1483         // the insidedness comparison is written it does not need to be normalized)
1484
1485         // first use the two edges from the triangle plane math
1486         // the other edge only gets calculated if the point survives that long
1487
1488         // 20 ops
1489         CrossProduct(edge01, faceplanenormal, edgenormal);
1490         if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1491                 return;
1492
1493         // 20 ops
1494         CrossProduct(faceplanenormal, edge21, edgenormal);
1495         if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1496                 return;
1497
1498         // 23 ops
1499         VectorSubtract(point0, point2, edge02);
1500         CrossProduct(faceplanenormal, edge02, edgenormal);
1501         if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1502                 return;
1503
1504         // 8 ops (rare)
1505
1506         // store the new trace fraction
1507         trace->realfraction = f;
1508
1509         // calculate a nudged fraction to keep it out of the surface
1510         // (the main fraction remains perfect)
1511         trace->fraction = f - collision_impactnudge.value * d;
1512
1513         // store the new trace plane (because collisions only happen from
1514         // the front this is always simply the triangle normal, never flipped)
1515         d = 1.0 / sqrt(faceplanenormallength2);
1516         VectorScale(faceplanenormal, d, trace->plane.normal);
1517         trace->plane.dist = faceplanedist * d;
1518 #else
1519         float d1, d2, d, f, fnudged, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, edge[3];
1520
1521         // this code is designed for clockwise triangles, conversion to
1522         // counterclockwise would require swapping some things around...
1523         // it is easier to simply swap the point0 and point2 parameters to this
1524         // function when calling it than it is to rewire the internals.
1525
1526         // calculate the unnormalized faceplanenormal of the triangle,
1527         // this represents the front side
1528         TriangleNormal(point0, point1, point2, faceplanenormal);
1529         // there's no point in processing a degenerate triangle
1530         // (GIGO - Garbage In, Garbage Out)
1531         if (DotProduct(faceplanenormal, faceplanenormal) < 0.0001f)
1532                 return;
1533         // calculate the unnormalized distance
1534         faceplanedist = DotProduct(point0, faceplanenormal);
1535
1536         // calculate the unnormalized start distance
1537         d1 = DotProduct(faceplanenormal, linestart) - faceplanedist;
1538         // if start point is on the back side there is no collision
1539         // (we don't care about traces going through the triangle the wrong way)
1540         if (d1 <= 0)
1541                 return;
1542
1543         // calculate the unnormalized end distance
1544         d2 = DotProduct(faceplanenormal, lineend) - faceplanedist;
1545         // if both are in front, there is no collision
1546         if (d2 >= 0)
1547                 return;
1548
1549         // from here on we know d1 is >= 0 and d2 is < 0
1550         // this means the line starts infront and ends behind, passing through it
1551
1552         // calculate the recipricol of the distance delta,
1553         // so we can use it multiple times cheaply (instead of division)
1554         d = 1.0f / (d1 - d2);
1555         // calculate the impact fraction by taking the start distance (> 0)
1556         // and subtracting the face plane distance (this is the distance of the
1557         // triangle along that same normal)
1558         // then multiply by the recipricol distance delta
1559         f = d1 * d;
1560         // skip out if this impact is further away than previous ones
1561         if (f > trace->realfraction)
1562                 return;
1563         // calculate the perfect impact point for classification of insidedness
1564         impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1565         impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1566         impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1567
1568         // calculate the edge normal and reject if impact is outside triangle
1569         // (an edge normal faces away from the triangle, to get the desired normal
1570         //  a crossproduct with the faceplanenormal is used, and because of the way
1571         // the insidedness comparison is written it does not need to be normalized)
1572
1573         VectorSubtract(point2, point0, edge);
1574         CrossProduct(edge, faceplanenormal, edgenormal);
1575         if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1576                 return;
1577
1578         VectorSubtract(point0, point1, edge);
1579         CrossProduct(edge, faceplanenormal, edgenormal);
1580         if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1581                 return;
1582
1583         VectorSubtract(point1, point2, edge);
1584         CrossProduct(edge, faceplanenormal, edgenormal);
1585         if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1586                 return;
1587
1588         // store the new trace fraction
1589         trace->realfraction = bound(0, f, 1);
1590
1591         // store the new trace plane (because collisions only happen from
1592         // the front this is always simply the triangle normal, never flipped)
1593         VectorNormalize(faceplanenormal);
1594         VectorCopy(faceplanenormal, trace->plane.normal);
1595         trace->plane.dist = DotProduct(point0, faceplanenormal);
1596
1597         // calculate the normalized start and end distances
1598         d1 = DotProduct(trace->plane.normal, linestart) - trace->plane.dist;
1599         d2 = DotProduct(trace->plane.normal, lineend) - trace->plane.dist;
1600
1601         // calculate a nudged fraction to keep it out of the surface
1602         // (the main fraction remains perfect)
1603         fnudged = (d1 - collision_impactnudge.value) / (d1 - d2);
1604         trace->fraction = bound(0, fnudged, 1);
1605
1606         // store the new trace endpos
1607         // not needed, it's calculated later when the trace is finished
1608         //trace->endpos[0] = linestart[0] + fnudged * (lineend[0] - linestart[0]);
1609         //trace->endpos[1] = linestart[1] + fnudged * (lineend[1] - linestart[1]);
1610         //trace->endpos[2] = linestart[2] + fnudged * (lineend[2] - linestart[2]);
1611 #endif
1612 }
1613
1614 typedef struct colbspnode_s
1615 {
1616         mplane_t plane;
1617         struct colbspnode_s *children[2];
1618         // the node is reallocated or split if max is reached
1619         int numcolbrushf;
1620         int maxcolbrushf;
1621         colbrushf_t **colbrushflist;
1622         //int numcolbrushd;
1623         //int maxcolbrushd;
1624         //colbrushd_t **colbrushdlist;
1625 }
1626 colbspnode_t;
1627
1628 typedef struct colbsp_s
1629 {
1630         mempool_t *mempool;
1631         colbspnode_t *nodes;
1632 }
1633 colbsp_t;
1634
1635 colbsp_t *Collision_CreateCollisionBSP(mempool_t *mempool)
1636 {
1637         colbsp_t *bsp;
1638         bsp = Mem_Alloc(mempool, sizeof(colbsp_t));
1639         bsp->mempool = mempool;
1640         bsp->nodes = Mem_Alloc(bsp->mempool, sizeof(colbspnode_t));
1641         return bsp;
1642 }
1643
1644 void Collision_FreeCollisionBSPNode(colbspnode_t *node)
1645 {
1646         if (node->children[0])
1647                 Collision_FreeCollisionBSPNode(node->children[0]);
1648         if (node->children[1])
1649                 Collision_FreeCollisionBSPNode(node->children[1]);
1650         while (--node->numcolbrushf)
1651                 Mem_Free(node->colbrushflist[node->numcolbrushf]);
1652         //while (--node->numcolbrushd)
1653         //      Mem_Free(node->colbrushdlist[node->numcolbrushd]);
1654         Mem_Free(node);
1655 }
1656
1657 void Collision_FreeCollisionBSP(colbsp_t *bsp)
1658 {
1659         Collision_FreeCollisionBSPNode(bsp->nodes);
1660         Mem_Free(bsp);
1661 }
1662
1663 void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac)
1664 {
1665         int i;
1666         colpointf_t *ps, *pe;
1667         float tempstart[3], tempend[3];
1668         VectorLerp(start->points[0].v, startfrac, end->points[0].v, mins);
1669         VectorCopy(mins, maxs);
1670         for (i = 0, ps = start->points, pe = end->points;i < start->numpoints;i++, ps++, pe++)
1671         {
1672                 VectorLerp(ps->v, startfrac, pe->v, tempstart);
1673                 VectorLerp(ps->v, endfrac, pe->v, tempend);
1674                 mins[0] = min(mins[0], min(tempstart[0], tempend[0]));
1675                 mins[1] = min(mins[1], min(tempstart[1], tempend[1]));
1676                 mins[2] = min(mins[2], min(tempstart[2], tempend[2]));
1677                 maxs[0] = min(maxs[0], min(tempstart[0], tempend[0]));
1678                 maxs[1] = min(maxs[1], min(tempstart[1], tempend[1]));
1679                 maxs[2] = min(maxs[2], min(tempstart[2], tempend[2]));
1680         }
1681         mins[0] -= 1;
1682         mins[1] -= 1;
1683         mins[2] -= 1;
1684         maxs[0] += 1;
1685         maxs[1] += 1;
1686         maxs[2] += 1;
1687 }
1688