]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - collision.c
reworked brush collisions to support edgedir cross products, this should
[xonotic/darkplaces.git] / collision.c
1
2 #include "quakedef.h"
3 #include "polygon.h"
4
5 #define COLLISION_EDGEDIR_DIST_EPSILON (1.0f / 1048576.0f)
6 #define COLLISION_SNAPSCALE (32.0f)
7 #define COLLISION_SNAP (1.0f / COLLISION_SNAPSCALE)
8 #define COLLISION_SNAP2 (2.0f / COLLISION_SNAPSCALE)
9 #define COLLISION_PLANE_DIST_EPSILON (2.0f / COLLISION_SNAPSCALE)
10
11 cvar_t collision_impactnudge = {0, "collision_impactnudge", "0.03125", "how much to back off from the impact"};
12 cvar_t collision_startnudge = {0, "collision_startnudge", "0", "how much to bias collision trace start"};
13 cvar_t collision_endnudge = {0, "collision_endnudge", "0", "how much to bias collision trace end"};
14 cvar_t collision_enternudge = {0, "collision_enternudge", "0", "how much to bias collision entry fraction"};
15 cvar_t collision_leavenudge = {0, "collision_leavenudge", "0", "how much to bias collision exit fraction"};
16 cvar_t collision_prefernudgedfraction = {0, "collision_prefernudgedfraction", "1", "whether to sort collision events by nudged fraction (1) or real fraction (0)"};
17
18 void Collision_Init (void)
19 {
20         Cvar_RegisterVariable(&collision_impactnudge);
21         Cvar_RegisterVariable(&collision_startnudge);
22         Cvar_RegisterVariable(&collision_endnudge);
23         Cvar_RegisterVariable(&collision_enternudge);
24         Cvar_RegisterVariable(&collision_leavenudge);
25         Cvar_RegisterVariable(&collision_prefernudgedfraction);
26 }
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 void Collision_PrintBrushAsQHull(colbrushf_t *brush, const char *name)
42 {
43         int i;
44         Con_Printf("3 %s\n%i\n", name, brush->numpoints);
45         for (i = 0;i < brush->numpoints;i++)
46                 Con_Printf("%f %f %f\n", brush->points[i].v[0], brush->points[i].v[1], brush->points[i].v[2]);
47         // FIXME: optimize!
48         Con_Printf("4\n%i\n", brush->numplanes);
49         for (i = 0;i < brush->numplanes;i++)
50                 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);
51 }
52
53 void Collision_ValidateBrush(colbrushf_t *brush)
54 {
55         int j, k, pointsoffplanes, pointonplanes, pointswithinsufficientplanes, printbrush;
56         float d;
57         printbrush = false;
58         if (!brush->numpoints)
59         {
60                 Con_Print("Collision_ValidateBrush: brush with no points!\n");
61                 printbrush = true;
62         }
63 #if 0
64         // it's ok for a brush to have one point and no planes...
65         if (brush->numplanes == 0 && brush->numpoints != 1)
66         {
67                 Con_Print("Collision_ValidateBrush: brush with no planes and more than one point!\n");
68                 printbrush = true;
69         }
70 #endif
71         if (brush->numplanes)
72         {
73                 pointsoffplanes = 0;
74                 pointswithinsufficientplanes = 0;
75                 for (k = 0;k < brush->numplanes;k++)
76                         if (DotProduct(brush->planes[k].normal, brush->planes[k].normal) < 0.0001f)
77                                 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);
78                 for (j = 0;j < brush->numpoints;j++)
79                 {
80                         pointonplanes = 0;
81                         for (k = 0;k < brush->numplanes;k++)
82                         {
83                                 d = DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist;
84                                 if (d > COLLISION_PLANE_DIST_EPSILON)
85                                 {
86                                         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);
87                                         printbrush = true;
88                                 }
89                                 if (fabs(d) > COLLISION_PLANE_DIST_EPSILON)
90                                         pointsoffplanes++;
91                                 else
92                                         pointonplanes++;
93                         }
94                         if (pointonplanes < 3)
95                                 pointswithinsufficientplanes++;
96                 }
97                 if (pointswithinsufficientplanes)
98                 {
99                         Con_Print("Collision_ValidateBrush: some points have insufficient planes, every point must be on at least 3 planes to form a corner.\n");
100                         printbrush = true;
101                 }
102                 if (pointsoffplanes == 0) // all points are on all planes
103                 {
104                         Con_Print("Collision_ValidateBrush: all points lie on all planes (degenerate, no brush volume!)\n");
105                         printbrush = true;
106                 }
107         }
108         if (printbrush)
109                 Collision_PrintBrushAsQHull(brush, "unnamed");
110 }
111
112 float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
113 {
114         float dist, bestdist;
115         if (!numpoints)
116                 return 0;
117         bestdist = DotProduct(points->v, normal);
118         points++;
119         while(--numpoints)
120         {
121                 dist = DotProduct(points->v, normal);
122                 bestdist = min(bestdist, dist);
123                 points++;
124         }
125         return bestdist;
126 }
127
128 float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
129 {
130         float dist, bestdist;
131         if (!numpoints)
132                 return 0;
133         bestdist = DotProduct(points->v, normal);
134         points++;
135         while(--numpoints)
136         {
137                 dist = DotProduct(points->v, normal);
138                 bestdist = max(bestdist, dist);
139                 points++;
140         }
141         return bestdist;
142 }
143
144 void Collision_CalcEdgeDirsForPolygonBrushFloat(colbrushf_t *brush)
145 {
146         int i, j;
147         for (i = 0, j = brush->numpoints - 1;i < brush->numpoints;j = i, i++)
148                 VectorSubtract(brush->points[i].v, brush->points[j].v, brush->edgedirs[j].v);
149 }
150
151 colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const colplanef_t *originalplanes, int supercontents, int q3surfaceflags, texture_t *texture)
152 {
153         // TODO: planesbuf could be replaced by a remapping table
154         int j, k, l, m, w, xyzflags;
155         int numpointsbuf = 0, maxpointsbuf = 256, numedgedirsbuf = 0, maxedgedirsbuf = 256, numplanesbuf = 0, maxplanesbuf = 256, numelementsbuf = 0, maxelementsbuf = 256;
156         double maxdist;
157         colbrushf_t *brush;
158         colpointf_t pointsbuf[256];
159         colpointf_t edgedirsbuf[256];
160         colplanef_t planesbuf[256];
161         int elementsbuf[1024];
162         int polypointbuf[256];
163         int pmaxpoints = 64;
164         int pnumpoints;
165         double p[2][3*64];
166 #if 0
167         // enable these if debugging to avoid seeing garbage in unused data-
168         memset(pointsbuf, 0, sizeof(pointsbuf));
169         memset(edgedirsbuf, 0, sizeof(edgedirsbuf));
170         memset(planesbuf, 0, sizeof(planesbuf));
171         memset(elementsbuf, 0, sizeof(elementsbuf));
172         memset(polypointbuf, 0, sizeof(polypointbuf));
173         memset(p, 0, sizeof(p));
174 #endif
175         // figure out how large a bounding box we need to properly compute this brush
176         maxdist = 0;
177         for (j = 0;j < numoriginalplanes;j++)
178                 maxdist = max(maxdist, fabs(originalplanes[j].dist));
179         // now make it large enough to enclose the entire brush, and round it off to a reasonable multiple of 1024
180         maxdist = floor(maxdist * (4.0 / 1024.0) + 2) * 1024.0;
181         // construct a collision brush (points, planes, and renderable mesh) from
182         // a set of planes, this also optimizes out any unnecessary planes (ones
183         // whose polygon is clipped away by the other planes)
184         for (j = 0;j < numoriginalplanes;j++)
185         {
186                 // add the plane uniquely (no duplicates)
187                 for (k = 0;k < numplanesbuf;k++)
188                         if (VectorCompare(planesbuf[k].normal, originalplanes[j].normal) && planesbuf[k].dist == originalplanes[j].dist)
189                                 break;
190                 // if the plane is a duplicate, skip it
191                 if (k < numplanesbuf)
192                         continue;
193                 // check if there are too many and skip the brush
194                 if (numplanesbuf >= maxplanesbuf)
195                 {
196                         Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many planes for buffer\n");
197                         return NULL;
198                 }
199
200                 // add the new plane
201                 VectorCopy(originalplanes[j].normal, planesbuf[numplanesbuf].normal);
202                 planesbuf[numplanesbuf].dist = originalplanes[j].dist;
203                 planesbuf[numplanesbuf].q3surfaceflags = originalplanes[j].q3surfaceflags;
204                 planesbuf[numplanesbuf].texture = originalplanes[j].texture;
205                 numplanesbuf++;
206
207                 // create a large polygon from the plane
208                 w = 0;
209                 PolygonD_QuadForPlane(p[w], originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist, maxdist);
210                 pnumpoints = 4;
211                 // clip it by all other planes
212                 for (k = 0;k < numoriginalplanes && pnumpoints >= 3 && pnumpoints <= pmaxpoints;k++)
213                 {
214                         // skip the plane this polygon
215                         // (nothing happens if it is processed, this is just an optimization)
216                         if (k != j)
217                         {
218                                 // we want to keep the inside of the brush plane so we flip
219                                 // the cutting plane
220                                 PolygonD_Divide(pnumpoints, p[w], -originalplanes[k].normal[0], -originalplanes[k].normal[1], -originalplanes[k].normal[2], -originalplanes[k].dist, COLLISION_PLANE_DIST_EPSILON, pmaxpoints, p[!w], &pnumpoints, 0, NULL, NULL, NULL);
221                                 w = !w;
222                         }
223                 }
224
225                 // if nothing is left, skip it
226                 if (pnumpoints < 3)
227                 {
228                         //Con_DPrintf("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);
229                         continue;
230                 }
231
232                 for (k = 0;k < pnumpoints;k++)
233                 {
234                         int l, m;
235                         m = 0;
236                         for (l = 0;l < numoriginalplanes;l++)
237                                 if (fabs(DotProduct(&p[w][k*3], originalplanes[l].normal) - originalplanes[l].dist) < COLLISION_PLANE_DIST_EPSILON)
238                                         m++;
239                         if (m < 3)
240                                 break;
241                 }
242                 if (k < pnumpoints)
243                 {
244                         Con_DPrintf("Collision_NewBrushFromPlanes: warning: polygon point does not lie on at least 3 planes\n");
245                         //return NULL;
246                 }
247
248                 // check if there are too many polygon vertices for buffer
249                 if (pnumpoints > pmaxpoints)
250                 {
251                         Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
252                         return NULL;
253                 }
254
255                 // check if there are too many triangle elements for buffer
256                 if (numelementsbuf + (pnumpoints - 2) * 3 > maxelementsbuf)
257                 {
258                         Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many triangle elements for buffer\n");
259                         return NULL;
260                 }
261
262                 // add the unique points for this polygon
263                 for (k = 0;k < pnumpoints;k++)
264                 {
265                         float v[3];
266                         // downgrade to float precision before comparing
267                         VectorCopy(&p[w][k*3], v);
268
269                         // check if there is already a matching point (no duplicates)
270                         for (m = 0;m < numpointsbuf;m++)
271                                 if (VectorDistance2(v, pointsbuf[m].v) < COLLISION_SNAP2)
272                                         break;
273
274                         // if there is no match, add a new one
275                         if (m == numpointsbuf)
276                         {
277                                 // check if there are too many and skip the brush
278                                 if (numpointsbuf >= maxpointsbuf)
279                                 {
280                                         Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
281                                         return NULL;
282                                 }
283                                 // add the new one
284                                 VectorCopy(&p[w][k*3], pointsbuf[numpointsbuf].v);
285                                 numpointsbuf++;
286                         }
287
288                         // store the index into a buffer
289                         polypointbuf[k] = m;
290                 }
291
292                 // add the triangles for the polygon
293                 // (this particular code makes a triangle fan)
294                 for (k = 0;k < pnumpoints - 2;k++)
295                 {
296                         elementsbuf[numelementsbuf++] = polypointbuf[0];
297                         elementsbuf[numelementsbuf++] = polypointbuf[k + 1];
298                         elementsbuf[numelementsbuf++] = polypointbuf[k + 2];
299                 }
300
301                 // add the unique edgedirs for this polygon
302                 for (k = 0, l = pnumpoints-1;k < pnumpoints;l = k, k++)
303                 {
304                         float dir[3];
305                         // downgrade to float precision before comparing
306                         VectorSubtract(&p[w][k*3], &p[w][l*3], dir);
307                         VectorNormalize(dir);
308
309                         // check if there is already a matching edgedir (no duplicates)
310                         for (m = 0;m < numedgedirsbuf;m++)
311                                 if (VectorDistance2(dir, edgedirsbuf[m].v) < COLLISION_EDGEDIR_DIST_EPSILON)
312                                         break;
313
314                         // if there is no match, add a new one
315                         if (m == numedgedirsbuf)
316                         {
317                                 // check if there are too many and skip the brush
318                                 if (numedgedirsbuf >= maxedgedirsbuf)
319                                 {
320                                         Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many edgedirs for buffer\n");
321                                         return NULL;
322                                 }
323                                 // add the new one
324                                 VectorCopy(dir, edgedirsbuf[numedgedirsbuf].v);
325                                 numedgedirsbuf++;
326                         }
327                 }
328         }
329
330         // if nothing is left, there's nothing to allocate
331         if (numplanesbuf < 4)
332         {
333                 Con_DPrintf("Collision_NewBrushFromPlanes: failed to build collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
334                 return NULL;
335         }
336
337         // if no triangles or points could be constructed, then this routine failed but the brush is not discarded
338         if (numelementsbuf < 12 || numpointsbuf < 4)
339                 Con_DPrintf("Collision_NewBrushFromPlanes: unable to rebuild triangles/points for collision brush: %i triangles, %i planes (input was %i planes), %i vertices\n", numelementsbuf / 3, numplanesbuf, numoriginalplanes, numpointsbuf);
340
341         // validate plane distances
342         for (j = 0;j < numplanesbuf;j++)
343         {
344                 float d = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpointsbuf);
345                 if (fabs(planesbuf[j].dist - d) > COLLISION_PLANE_DIST_EPSILON)
346                         Con_DPrintf("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);
347         }
348
349         // allocate the brush and copy to it
350         brush = (colbrushf_t *)Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colpointf_t) * numpointsbuf + sizeof(colpointf_t) * numedgedirsbuf + sizeof(colplanef_t) * numplanesbuf + sizeof(int) * numelementsbuf);
351         brush->supercontents = supercontents;
352         brush->numplanes = numplanesbuf;
353         brush->numedgedirs = numedgedirsbuf;
354         brush->numpoints = numpointsbuf;
355         brush->numtriangles = numelementsbuf / 3;
356         brush->planes = (colplanef_t *)(brush + 1);
357         brush->points = (colpointf_t *)(brush->planes + brush->numplanes);
358         brush->edgedirs = (colpointf_t *)(brush->points + brush->numpoints);
359         brush->elements = (int *)(brush->points + brush->numpoints);
360         brush->q3surfaceflags = q3surfaceflags;
361         brush->texture = texture;
362         for (j = 0;j < brush->numpoints;j++)
363         {
364                 brush->points[j].v[0] = pointsbuf[j].v[0];
365                 brush->points[j].v[1] = pointsbuf[j].v[1];
366                 brush->points[j].v[2] = pointsbuf[j].v[2];
367         }
368         for (j = 0;j < brush->numedgedirs;j++)
369         {
370                 brush->edgedirs[j].v[0] = edgedirsbuf[j].v[0];
371                 brush->edgedirs[j].v[1] = edgedirsbuf[j].v[1];
372                 brush->edgedirs[j].v[2] = edgedirsbuf[j].v[2];
373         }
374         for (j = 0;j < brush->numplanes;j++)
375         {
376                 brush->planes[j].normal[0] = planesbuf[j].normal[0];
377                 brush->planes[j].normal[1] = planesbuf[j].normal[1];
378                 brush->planes[j].normal[2] = planesbuf[j].normal[2];
379                 brush->planes[j].dist = planesbuf[j].dist;
380                 brush->planes[j].q3surfaceflags = planesbuf[j].q3surfaceflags;
381                 brush->planes[j].texture = planesbuf[j].texture;
382         }
383         for (j = 0;j < brush->numtriangles * 3;j++)
384                 brush->elements[j] = elementsbuf[j];
385
386         xyzflags = 0;
387         VectorClear(brush->mins);
388         VectorClear(brush->maxs);
389         for (j = 0;j < min(6, numoriginalplanes);j++)
390         {
391                      if (originalplanes[j].normal[0] ==  1) {xyzflags |=  1;brush->maxs[0] =  originalplanes[j].dist;}
392                 else if (originalplanes[j].normal[0] == -1) {xyzflags |=  2;brush->mins[0] = -originalplanes[j].dist;}
393                 else if (originalplanes[j].normal[1] ==  1) {xyzflags |=  4;brush->maxs[1] =  originalplanes[j].dist;}
394                 else if (originalplanes[j].normal[1] == -1) {xyzflags |=  8;brush->mins[1] = -originalplanes[j].dist;}
395                 else if (originalplanes[j].normal[2] ==  1) {xyzflags |= 16;brush->maxs[2] =  originalplanes[j].dist;}
396                 else if (originalplanes[j].normal[2] == -1) {xyzflags |= 32;brush->mins[2] = -originalplanes[j].dist;}
397         }
398         // if not all xyzflags were set, then this is not a brush from q3map/q3map2, and needs reconstruction of the bounding box
399         // (this case works for any brush with valid points, but sometimes brushes are not reconstructed properly and hence the points are not valid, so this is reserved as a fallback case)
400         if (xyzflags != 63)
401         {
402                 VectorCopy(brush->points[0].v, brush->mins);
403                 VectorCopy(brush->points[0].v, brush->maxs);
404                 for (j = 1;j < brush->numpoints;j++)
405                 {
406                         brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
407                         brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
408                         brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
409                         brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
410                         brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
411                         brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
412                 }
413         }
414         brush->mins[0] -= 1;
415         brush->mins[1] -= 1;
416         brush->mins[2] -= 1;
417         brush->maxs[0] += 1;
418         brush->maxs[1] += 1;
419         brush->maxs[2] += 1;
420         Collision_ValidateBrush(brush);
421         return brush;
422 }
423
424
425
426 void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush)
427 {
428         int i;
429         float edge0[3], edge1[3], edge2[3], normal[3], dist, bestdist;
430         colpointf_t *p, *p2;
431
432         // FIXME: these probably don't actually need to be normalized if the collision code does not care
433         if (brush->numpoints == 3)
434         {
435                 // optimized triangle case
436                 TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal);
437                 if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f)
438                 {
439                         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
440                         brush->numplanes = 0;
441                         return;
442                 }
443                 else
444                 {
445                         brush->numplanes = 5;
446                         brush->numedgedirs = 3;
447                         VectorNormalize(brush->planes[0].normal);
448                         brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal);
449                         VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
450                         brush->planes[1].dist = -brush->planes[0].dist;
451                         VectorSubtract(brush->points[2].v, brush->points[0].v, edge0);
452                         VectorSubtract(brush->points[0].v, brush->points[1].v, edge1);
453                         VectorSubtract(brush->points[1].v, brush->points[2].v, edge2);
454                         VectorCopy(edge0, brush->edgedirs[0].v);
455                         VectorCopy(edge1, brush->edgedirs[1].v);
456                         VectorCopy(edge2, brush->edgedirs[2].v);
457 #if 1
458                         {
459                                 float projectionnormal[3], projectionedge0[3], projectionedge1[3], projectionedge2[3];
460                                 int i, best;
461                                 float dist, bestdist;
462                                 bestdist = fabs(brush->planes[0].normal[0]);
463                                 best = 0;
464                                 for (i = 1;i < 3;i++)
465                                 {
466                                         dist = fabs(brush->planes[0].normal[i]);
467                                         if (bestdist < dist)
468                                         {
469                                                 bestdist = dist;
470                                                 best = i;
471                                         }
472                                 }
473                                 VectorClear(projectionnormal);
474                                 if (brush->planes[0].normal[best] < 0)
475                                         projectionnormal[best] = -1;
476                                 else
477                                         projectionnormal[best] = 1;
478                                 VectorCopy(edge0, projectionedge0);
479                                 VectorCopy(edge1, projectionedge1);
480                                 VectorCopy(edge2, projectionedge2);
481                                 projectionedge0[best] = 0;
482                                 projectionedge1[best] = 0;
483                                 projectionedge2[best] = 0;
484                                 CrossProduct(projectionedge0, projectionnormal, brush->planes[2].normal);
485                                 CrossProduct(projectionedge1, projectionnormal, brush->planes[3].normal);
486                                 CrossProduct(projectionedge2, projectionnormal, brush->planes[4].normal);
487                         }
488 #else
489                         CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal);
490                         CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal);
491                         CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal);
492 #endif
493                         VectorNormalize(brush->planes[2].normal);
494                         VectorNormalize(brush->planes[3].normal);
495                         VectorNormalize(brush->planes[4].normal);
496                         brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal);
497                         brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal);
498                         brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal);
499
500                         if (developer.integer >= 100)
501                         {
502                                 // validation code
503 #if 0
504                                 float temp[3];
505
506                                 VectorSubtract(brush->points[0].v, brush->points[1].v, edge0);
507                                 VectorSubtract(brush->points[2].v, brush->points[1].v, edge1);
508                                 CrossProduct(edge0, edge1, normal);
509                                 VectorNormalize(normal);
510                                 VectorSubtract(normal, brush->planes[0].normal, temp);
511                                 if (VectorLength(temp) > 0.01f)
512                                         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]);
513                                 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)
514                                         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);
515 #if 0
516                                 if (fabs(DotProduct(brush->planes[2].normal, brush->planes[0].normal)) > 0.01f)
517                                         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);
518                                 if (fabs(DotProduct(brush->planes[3].normal, brush->planes[0].normal)) > 0.01f)
519                                         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);
520                                 if (fabs(DotProduct(brush->planes[4].normal, brush->planes[0].normal)) > 0.01f)
521                                         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);
522                                 if (fabs(DotProduct(brush->planes[2].normal, edge0)) > 0.01f)
523                                         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]);
524                                 if (fabs(DotProduct(brush->planes[3].normal, edge1)) > 0.01f)
525                                         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]);
526                                 if (fabs(DotProduct(brush->planes[4].normal, edge2)) > 0.01f)
527                                         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]);
528 #endif
529 #endif
530                                 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)
531                                         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);
532                                 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)
533                                         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);
534                                 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)
535                                         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);
536                                 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)
537                                         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);
538                                 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)
539                                         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);
540                         }
541                 }
542         }
543         else
544         {
545                 // choose best surface normal for polygon's plane
546                 bestdist = 0;
547                 for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++)
548                 {
549                         VectorSubtract(p[-1].v, p[0].v, edge0);
550                         VectorSubtract(p[1].v, p[0].v, edge1);
551                         CrossProduct(edge0, edge1, normal);
552                         //TriangleNormal(p[-1].v, p[0].v, p[1].v, normal);
553                         dist = DotProduct(normal, normal);
554                         if (i == 0 || bestdist < dist)
555                         {
556                                 bestdist = dist;
557                                 VectorCopy(normal, brush->planes->normal);
558                         }
559                 }
560                 if (bestdist < 0.0001f)
561                 {
562                         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
563                         brush->numplanes = 0;
564                         return;
565                 }
566                 else
567                 {
568                         brush->numplanes = brush->numpoints + 2;
569                         VectorNormalize(brush->planes->normal);
570                         brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal);
571
572                         // negate plane to create other side
573                         VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
574                         brush->planes[1].dist = -brush->planes[0].dist;
575                         for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++)
576                         {
577                                 VectorSubtract(p->v, p2->v, edge0);
578                                 CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal);
579                                 VectorNormalize(brush->planes[i + 2].normal);
580                                 brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal);
581                         }
582                 }
583         }
584
585         if (developer.integer >= 100)
586         {
587                 // validity check - will be disabled later
588                 Collision_ValidateBrush(brush);
589                 for (i = 0;i < brush->numplanes;i++)
590                 {
591                         int j;
592                         for (j = 0, p = brush->points;j < brush->numpoints;j++, p++)
593                                 if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + COLLISION_PLANE_DIST_EPSILON)
594                                         Con_Printf("Error in brush plane generation, plane %i\n", i);
595                 }
596         }
597 }
598
599 colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents, int q3surfaceflags, texture_t *texture)
600 {
601         colbrushf_t *brush;
602         brush = (colbrushf_t *)Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colplanef_t) * (numpoints + 2) + sizeof(colpointf_t) * numpoints);
603         brush->supercontents = supercontents;
604         brush->numpoints = numpoints;
605         brush->numedgedirs = numpoints;
606         brush->numplanes = numpoints + 2;
607         brush->planes = (colplanef_t *)(brush + 1);
608         brush->points = (colpointf_t *)points;
609         brush->edgedirs = (colpointf_t *)(brush->planes + brush->numplanes);
610         brush->q3surfaceflags = q3surfaceflags;
611         brush->texture = texture;
612         Sys_Error("Collision_AllocBrushFromPermanentPolygonFloat: FIXME: this code needs to be updated to generate a mesh...");
613         return brush;
614 }
615
616 // NOTE: start and end of each brush pair must have same numplanes/numpoints
617 void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *trace_start, const colbrushf_t *trace_end, const colbrushf_t *other_start, const colbrushf_t *other_end)
618 {
619         int nplane, nplane2, nedge1, nedge2, hitq3surfaceflags = 0;
620         int tracenumedgedirs = trace_start->numedgedirs;
621         //int othernumedgedirs = other_start->numedgedirs;
622         int tracenumpoints = trace_start->numpoints;
623         int othernumpoints = other_start->numpoints;
624         int numplanes1 = trace_start->numplanes;
625         int numplanes2 = numplanes1 + other_start->numplanes;
626         int numplanes3 = numplanes2 + trace_start->numedgedirs * other_start->numedgedirs * 2;
627         vec_t enterfrac = -1, leavefrac = 1, startdist, enddist, ie, f, imove, enterfrac2 = -1;
628         vec4_t startplane;
629         vec4_t endplane;
630         vec4_t newimpactplane;
631         texture_t *hittexture = NULL;
632         vec_t startdepth = trace->startdepth;
633         vec3_t startdepthnormal;
634
635         VectorCopy(trace->startdepthnormal, startdepthnormal);
636         Vector4Clear(newimpactplane);
637
638         // Separating Axis Theorem:
639         // if a supporting vector (plane normal) can be found that separates two
640         // objects, they are not colliding.
641         //
642         // Minkowski Sum:
643         // reduce the size of one object to a point while enlarging the other to
644         // represent the space that point can not occupy.
645         //
646         // try every plane we can construct between the two brushes and measure
647         // the distance between them.
648         for (nplane = 0;nplane < numplanes3;nplane++)
649         {
650                 if (nplane < numplanes1)
651                 {
652                         nplane2 = nplane;
653                         VectorCopy(trace_start->planes[nplane2].normal, startplane);
654                         VectorCopy(trace_end->planes[nplane2].normal, endplane);
655                 }
656                 else if (nplane < numplanes2)
657                 {
658                         nplane2 = nplane - numplanes1;
659                         VectorCopy(other_start->planes[nplane2].normal, startplane);
660                         VectorCopy(other_end->planes[nplane2].normal, endplane);
661                 }
662                 else
663                 {
664                         // pick an edgedir from each brush and cross them
665                         nplane2 = nplane - numplanes2;
666                         nedge1 = nplane2 >> 1;
667                         nedge2 = nedge1 / tracenumedgedirs;
668                         nedge1 -= nedge2 * tracenumedgedirs;
669                         if (nplane2 & 1)
670                         {
671                                 CrossProduct(trace_start->edgedirs[nedge1].v, other_start->edgedirs[nedge2].v, startplane);
672                                 CrossProduct(trace_end->edgedirs[nedge1].v, other_end->edgedirs[nedge2].v, endplane);
673                         }
674                         else
675                         {
676                                 CrossProduct(other_start->edgedirs[nedge2].v, trace_start->edgedirs[nedge1].v, startplane);
677                                 CrossProduct(other_end->edgedirs[nedge2].v, trace_end->edgedirs[nedge1].v, endplane);
678                         }
679                         VectorNormalize(startplane);
680                         VectorNormalize(endplane);
681                 }
682                 startplane[3] = furthestplanedist_float(startplane, other_start->points, othernumpoints);
683                 endplane[3] = furthestplanedist_float(startplane, other_end->points, othernumpoints);
684                 startdist = nearestplanedist_float(startplane, trace_start->points, tracenumpoints) - startplane[3] - collision_startnudge.value;
685                 enddist = nearestplanedist_float(endplane, trace_end->points, tracenumpoints) - endplane[3] - collision_endnudge.value;
686                 //Con_Printf("%c%i: startdist = %f, enddist = %f, startdist / (startdist - enddist) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, startdist, enddist, startdist / (startdist - enddist));
687
688                 // aside from collisions, this is also used for error correction
689                 if (startdist < 0 && trace->startdepth < startdist)
690                 {
691                         trace->startdepth = startdist;
692                         VectorCopy(startplane, trace->startdepthnormal);
693                 }
694
695                 if (startdist > enddist)
696                 {
697                         // moving into brush
698                         if (enddist >= collision_enternudge.value)
699                                 return;
700                         if (startdist > 0)
701                         {
702                                 // enter
703                                 imove = 1 / (startdist - enddist);
704                                 f = (startdist - collision_enternudge.value) * imove;
705                                 if (f < 0)
706                                         f = 0;
707                                 // check if this will reduce the collision time range
708                                 if (enterfrac < f)
709                                 {
710                                         // reduced collision time range
711                                         enterfrac = f;
712                                         // if the collision time range is now empty, no collision
713                                         if (enterfrac > leavefrac)
714                                                 return;
715                                         // if the collision would be further away than the trace's
716                                         // existing collision data, we don't care about this
717                                         // collision
718                                         if (enterfrac > trace->realfraction)
719                                                 return;
720                                         // calculate the nudged fraction and impact normal we'll
721                                         // need if we accept this collision later
722                                         enterfrac2 = (startdist - collision_impactnudge.value) * imove;
723                                         ie = 1.0f - enterfrac;
724                                         newimpactplane[0] = startplane[0] * ie + endplane[0] * enterfrac;
725                                         newimpactplane[1] = startplane[1] * ie + endplane[1] * enterfrac;
726                                         newimpactplane[2] = startplane[2] * ie + endplane[2] * enterfrac;
727                                         newimpactplane[3] = startplane[3] * ie + endplane[3] * enterfrac;
728                                         if (nplane < numplanes1)
729                                         {
730                                                 // use the plane from trace
731                                                 nplane2 = nplane;
732                                                 hitq3surfaceflags = trace_start->planes[nplane2].q3surfaceflags;
733                                                 hittexture = trace_start->planes[nplane2].texture;
734                                         }
735                                         else if (nplane < numplanes2)
736                                         {
737                                                 // use the plane from other
738                                                 nplane2 = nplane - numplanes1;
739                                                 hitq3surfaceflags = other_start->planes[nplane2].q3surfaceflags;
740                                                 hittexture = other_start->planes[nplane2].texture;
741                                         }
742                                         else
743                                         {
744                                                 hitq3surfaceflags = other_start->q3surfaceflags;
745                                                 hittexture = other_start->texture;
746                                         }
747                                 }
748                         }
749                 }
750                 else
751                 {
752                         // moving out of brush
753                         if (startdist > 0)
754                                 return;
755                         if (enddist > 0)
756                         {
757                                 // leave
758                                 f = (startdist + collision_leavenudge.value) / (startdist - enddist);
759                                 if (f > 1)
760                                         f = 1;
761                                 // check if this will reduce the collision time range
762                                 if (leavefrac > f)
763                                 {
764                                         // reduced collision time range
765                                         leavefrac = f;
766                                         // if the collision time range is now empty, no collision
767                                         if (enterfrac > leavefrac)
768                                                 return;
769                                 }
770                         }
771                 }
772         }
773
774         // at this point we know the trace overlaps the brush because it was not
775         // rejected at any point in the loop above
776
777         // see if the trace started outside the brush or not
778         if (enterfrac > -1)
779         {
780                 // started outside, and overlaps, therefore there is a collision here
781                 // store out the impact information
782                 if (trace->hitsupercontentsmask & other_start->supercontents)
783                 {
784                         trace->hitsupercontents = other_start->supercontents;
785                         trace->hitq3surfaceflags = hitq3surfaceflags;
786                         trace->hittexture = hittexture;
787                         trace->realfraction = bound(0, enterfrac, 1);
788                         trace->fraction = bound(0, enterfrac2, 1);
789                         if (collision_prefernudgedfraction.integer)
790                                 trace->realfraction = trace->fraction;
791                         VectorCopy(newimpactplane, trace->plane.normal);
792                         trace->plane.dist = newimpactplane[3];
793                 }
794         }
795         else
796         {
797                 // started inside, update startsolid and friends
798                 trace->startsupercontents |= other_start->supercontents;
799                 if (trace->hitsupercontentsmask & other_start->supercontents)
800                 {
801                         trace->startsolid = true;
802                         if (leavefrac < 1)
803                                 trace->allsolid = true;
804                         VectorCopy(newimpactplane, trace->plane.normal);
805                         trace->plane.dist = newimpactplane[3];
806                         if (trace->startdepth < startdepth)
807                         {
808                                 trace->startdepth = startdepth;
809                                 VectorCopy(startdepthnormal, trace->startdepthnormal);
810                         }
811                 }
812         }
813 }
814
815 // NOTE: start and end of each brush pair must have same numplanes/numpoints
816 void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *other_start, const colbrushf_t *other_end)
817 {
818         int nplane, hitq3surfaceflags = 0;
819         int numplanes = other_start->numplanes;
820         vec_t enterfrac = -1, leavefrac = 1, startdist, enddist, ie, f, imove, enterfrac2 = -1;
821         vec4_t startplane;
822         vec4_t endplane;
823         vec4_t newimpactplane;
824         texture_t *hittexture = NULL;
825         vec_t startdepth = trace->startdepth;
826         vec3_t startdepthnormal;
827
828         VectorCopy(trace->startdepthnormal, startdepthnormal);
829         Vector4Clear(newimpactplane);
830
831         // Separating Axis Theorem:
832         // if a supporting vector (plane normal) can be found that separates two
833         // objects, they are not colliding.
834         //
835         // Minkowski Sum:
836         // reduce the size of one object to a point while enlarging the other to
837         // represent the space that point can not occupy.
838         //
839         // try every plane we can construct between the two brushes and measure
840         // the distance between them.
841         for (nplane = 0;nplane < numplanes;nplane++)
842         {
843                 VectorCopy(other_start->planes[nplane].normal, startplane);
844                 startplane[3] = other_start->planes[nplane].dist;
845                 VectorCopy(other_end->planes[nplane].normal, endplane);
846                 endplane[3] = other_end->planes[nplane].dist;
847                 startdist = DotProduct(linestart, startplane) - startplane[3] - collision_startnudge.value;
848                 enddist = DotProduct(lineend, endplane) - endplane[3] - collision_endnudge.value;
849                 //Con_Printf("%c%i: startdist = %f, enddist = %f, startdist / (startdist - enddist) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, startdist, enddist, startdist / (startdist - enddist));
850
851                 // aside from collisions, this is also used for error correction
852                 if (startdist < 0 && trace->startdepth < startdist)
853                 {
854                         trace->startdepth = startdist;
855                         VectorCopy(startplane, trace->startdepthnormal);
856                 }
857
858                 if (startdist > enddist)
859                 {
860                         // moving into brush
861                         if (enddist >= collision_enternudge.value)
862                                 return;
863                         if (startdist > 0)
864                         {
865                                 // enter
866                                 imove = 1 / (startdist - enddist);
867                                 f = (startdist - collision_enternudge.value) * imove;
868                                 if (f < 0)
869                                         f = 0;
870                                 // check if this will reduce the collision time range
871                                 if (enterfrac < f)
872                                 {
873                                         // reduced collision time range
874                                         enterfrac = f;
875                                         // if the collision time range is now empty, no collision
876                                         if (enterfrac > leavefrac)
877                                                 return;
878                                         // if the collision would be further away than the trace's
879                                         // existing collision data, we don't care about this
880                                         // collision
881                                         if (enterfrac > trace->realfraction)
882                                                 return;
883                                         // calculate the nudged fraction and impact normal we'll
884                                         // need if we accept this collision later
885                                         enterfrac2 = (startdist - collision_impactnudge.value) * imove;
886                                         ie = 1.0f - enterfrac;
887                                         newimpactplane[0] = startplane[0] * ie + endplane[0] * enterfrac;
888                                         newimpactplane[1] = startplane[1] * ie + endplane[1] * enterfrac;
889                                         newimpactplane[2] = startplane[2] * ie + endplane[2] * enterfrac;
890                                         newimpactplane[3] = startplane[3] * ie + endplane[3] * enterfrac;
891                                         hitq3surfaceflags = other_start->planes[nplane].q3surfaceflags;
892                                         hittexture = other_start->planes[nplane].texture;
893                                 }
894                         }
895                 }
896                 else
897                 {
898                         // moving out of brush
899                         if (startdist > 0)
900                                 return;
901                         if (enddist > 0)
902                         {
903                                 // leave
904                                 f = (startdist + collision_leavenudge.value) / (startdist - enddist);
905                                 if (f > 1)
906                                         f = 1;
907                                 // check if this will reduce the collision time range
908                                 if (leavefrac > f)
909                                 {
910                                         // reduced collision time range
911                                         leavefrac = f;
912                                         // if the collision time range is now empty, no collision
913                                         if (enterfrac > leavefrac)
914                                                 return;
915                                 }
916                         }
917                 }
918         }
919
920         // at this point we know the trace overlaps the brush because it was not
921         // rejected at any point in the loop above
922
923         // see if the trace started outside the brush or not
924         if (enterfrac > -1)
925         {
926                 // started outside, and overlaps, therefore there is a collision here
927                 // store out the impact information
928                 if (trace->hitsupercontentsmask & other_start->supercontents)
929                 {
930                         trace->hitsupercontents = other_start->supercontents;
931                         trace->hitq3surfaceflags = hitq3surfaceflags;
932                         trace->hittexture = hittexture;
933                         trace->realfraction = bound(0, enterfrac, 1);
934                         trace->fraction = bound(0, enterfrac2, 1);
935                         if (collision_prefernudgedfraction.integer)
936                                 trace->realfraction = trace->fraction;
937                         VectorCopy(newimpactplane, trace->plane.normal);
938                         trace->plane.dist = newimpactplane[3];
939                 }
940         }
941         else
942         {
943                 // started inside, update startsolid and friends
944                 trace->startsupercontents |= other_start->supercontents;
945                 if (trace->hitsupercontentsmask & other_start->supercontents)
946                 {
947                         trace->startsolid = true;
948                         if (leavefrac < 1)
949                                 trace->allsolid = true;
950                         VectorCopy(newimpactplane, trace->plane.normal);
951                         trace->plane.dist = newimpactplane[3];
952                         if (trace->startdepth < startdepth)
953                         {
954                                 trace->startdepth = startdepth;
955                                 VectorCopy(startdepthnormal, trace->startdepthnormal);
956                         }
957                 }
958         }
959 }
960
961 qboolean Collision_PointInsideBrushFloat(const vec3_t point, const colbrushf_t *brush)
962 {
963         int nplane;
964         const colplanef_t *plane;
965
966         if (!BoxesOverlap(point, point, brush->mins, brush->maxs))
967                 return false;
968         for (nplane = 0, plane = brush->planes;nplane < brush->numplanes;nplane++, plane++)
969                 if (DotProduct(plane->normal, point) > plane->dist)
970                         return false;
971         return true;
972 }
973
974 void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
975 {
976         if (!Collision_PointInsideBrushFloat(point, thatbrush))
977                 return;
978
979         trace->startsupercontents |= thatbrush->supercontents;
980         if (trace->hitsupercontentsmask & thatbrush->supercontents)
981         {
982                 trace->startsolid = true;
983                 trace->allsolid = true;
984         }
985 }
986
987 static colpointf_t polyf_points[256];
988 static colpointf_t polyf_edgedirs[256];
989 static colplanef_t polyf_planes[256 + 2];
990 static colbrushf_t polyf_brush;
991
992 void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t *out, float fractionprecision, float invfractionprecision)
993 {
994         int i;
995         for (i = 0;i < numpoints;i++)
996         {
997                 out[i].v[0] = floor(in[i].v[0] * fractionprecision + 0.5f) * invfractionprecision;
998                 out[i].v[1] = floor(in[i].v[1] * fractionprecision + 0.5f) * invfractionprecision;
999                 out[i].v[2] = floor(in[i].v[2] * fractionprecision + 0.5f) * invfractionprecision;
1000         }
1001 }
1002
1003 void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents, int q3surfaceflags, texture_t *texture)
1004 {
1005         if (numpoints > 256)
1006         {
1007                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1008                 return;
1009         }
1010         polyf_brush.numpoints = numpoints;
1011         polyf_brush.numedgedirs = numpoints;
1012         polyf_brush.numplanes = numpoints + 2;
1013         //polyf_brush.points = (colpointf_t *)points;
1014         polyf_brush.planes = polyf_planes;
1015         polyf_brush.edgedirs = polyf_edgedirs;
1016         polyf_brush.supercontents = supercontents;
1017         polyf_brush.points = polyf_points;
1018         polyf_brush.q3surfaceflags = q3surfaceflags;
1019         polyf_brush.texture = texture;
1020         Collision_SnapCopyPoints(polyf_brush.numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1021         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1022         Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brush);
1023         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1024         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1025 }
1026
1027 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 stride, float *bbox6f, int supercontents, int q3surfaceflags, texture_t *texture, const vec3_t segmentmins, const vec3_t segmentmaxs)
1028 {
1029         int i;
1030         polyf_brush.numpoints = 3;
1031         polyf_brush.numedgedirs = 3;
1032         polyf_brush.numplanes = 5;
1033         polyf_brush.points = polyf_points;
1034         polyf_brush.edgedirs = polyf_edgedirs;
1035         polyf_brush.planes = polyf_planes;
1036         polyf_brush.supercontents = supercontents;
1037         polyf_brush.q3surfaceflags = q3surfaceflags;
1038         polyf_brush.texture = texture;
1039         for (i = 0;i < polyf_brush.numplanes;i++)
1040         {
1041                 polyf_brush.planes[i].q3surfaceflags = q3surfaceflags;
1042                 polyf_brush.planes[i].texture = texture;
1043         }
1044         if(stride)
1045         {
1046                 int k, cnt, tri;
1047                 cnt = (numtriangles + stride - 1) / stride;
1048                 for(i = 0; i < cnt; ++i)
1049                 {
1050                         if(BoxesOverlap(bbox6f + i * 6, bbox6f + i * 6 + 3, segmentmins, segmentmaxs))
1051                         {
1052                                 for(k = 0; k < stride; ++k)
1053                                 {
1054                                         tri = i * stride + k;
1055                                         if(tri >= numtriangles)
1056                                                 break;
1057                                         VectorCopy(vertex3f + element3i[tri * 3 + 0] * 3, polyf_points[0].v);
1058                                         VectorCopy(vertex3f + element3i[tri * 3 + 1] * 3, polyf_points[1].v);
1059                                         VectorCopy(vertex3f + element3i[tri * 3 + 2] * 3, polyf_points[2].v);
1060                                         Collision_SnapCopyPoints(polyf_brush.numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1061                                         Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brush);
1062                                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1063                                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1064                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1065                                 }
1066                         }
1067                 }
1068         }
1069         else
1070         {
1071                 for (i = 0;i < numtriangles;i++, element3i += 3)
1072                 {
1073                         if (TriangleOverlapsBox(vertex3f + element3i[0]*3, vertex3f + element3i[1]*3, vertex3f + element3i[2]*3, segmentmins, segmentmaxs))
1074                         {
1075                                 VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1076                                 VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1077                                 VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1078                                 Collision_SnapCopyPoints(polyf_brush.numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1079                                 Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brush);
1080                                 Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1081                                 //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1082                                 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush);
1083                         }
1084                 }
1085         }
1086 }
1087
1088 void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents, int q3surfaceflags, texture_t *texture)
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.numedgedirs = numpoints;
1097         polyf_brush.numplanes = numpoints + 2;
1098         //polyf_brush.points = (colpointf_t *)points;
1099         polyf_brush.points = polyf_points;
1100         Collision_SnapCopyPoints(polyf_brush.numpoints, (colpointf_t *)points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1101         polyf_brush.edgedirs = polyf_edgedirs;
1102         polyf_brush.planes = polyf_planes;
1103         polyf_brush.supercontents = supercontents;
1104         polyf_brush.q3surfaceflags = q3surfaceflags;
1105         polyf_brush.texture = texture;
1106         //Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brush);
1107         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1108         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1109         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1110 }
1111
1112 void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numtriangles, const int *element3i, const float *vertex3f, int stride, float *bbox6f, int supercontents, int q3surfaceflags, texture_t *texture, const vec3_t segmentmins, const vec3_t segmentmaxs)
1113 {
1114         int i;
1115 #if 1
1116         // FIXME: snap vertices?
1117         if(stride)
1118         {
1119                 int k, cnt, tri;
1120                 cnt = (numtriangles + stride - 1) / stride;
1121                 for(i = 0; i < cnt; ++i)
1122                 {
1123                         if(BoxesOverlap(bbox6f + i * 6, bbox6f + i * 6 + 3, segmentmins, segmentmaxs))
1124                         {
1125                                 for(k = 0; k < stride; ++k)
1126                                 {
1127                                         tri = i * stride + k;
1128                                         if(tri >= numtriangles)
1129                                                 break;
1130                                         Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[tri * 3 + 0] * 3, vertex3f + element3i[tri * 3 + 1] * 3, vertex3f + element3i[tri * 3 + 2] * 3, supercontents, q3surfaceflags, texture);
1131                                 }
1132                         }
1133                 }
1134         }
1135         else
1136         {
1137                 for (i = 0;i < numtriangles;i++, element3i += 3)
1138                         Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[0] * 3, vertex3f + element3i[1] * 3, vertex3f + element3i[2] * 3, supercontents, q3surfaceflags, texture);
1139         }
1140 #else
1141         polyf_brush.numpoints = 3;
1142         polyf_brush.numedgedirs = 3;
1143         polyf_brush.numplanes = 5;
1144         polyf_brush.points = polyf_points;
1145         polyf_brush.edgedirs = polyf_edgedirs;
1146         polyf_brush.planes = polyf_planes;
1147         polyf_brush.supercontents = supercontents;
1148         polyf_brush.q3surfaceflags = q3surfaceflags;
1149         polyf_brush.texture = texture;
1150         for (i = 0;i < polyf_brush.numplanes;i++)
1151         {
1152                 polyf_brush.planes[i].supercontents = supercontents;
1153                 polyf_brush.planes[i].q3surfaceflags = q3surfaceflags;
1154                 polyf_brush.planes[i].texture = texture;
1155         }
1156         for (i = 0;i < numtriangles;i++, element3i += 3)
1157         {
1158                 if (TriangleOverlapsBox(vertex3f + element3i[0]*3, vertex3 + [element3i[1]*3, vertex3f + element3i[2]*3, segmentmins, segmentmaxs))
1159                 {
1160                         VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v);
1161                         VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v);
1162                         VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v);
1163                         Collision_SnapCopyPoints(polyf_brush.numpoints, polyf_points, polyf_points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1164                         Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brush);
1165                         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush);
1166                         //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush");
1167                         Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush);
1168                 }
1169         }
1170 #endif
1171 }
1172
1173
1174 static colpointf_t polyf_pointsstart[256], polyf_pointsend[256];
1175 static colplanef_t polyf_planesstart[256 + 2], polyf_planesend[256 + 2];
1176 static colbrushf_t polyf_brushstart, polyf_brushend;
1177
1178 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, int q3surfaceflags, texture_t *texture)
1179 {
1180         int i;
1181         if (numpoints > 256)
1182         {
1183                 Con_Print("Polygon with more than 256 points not supported yet (fixme!)\n");
1184                 return;
1185         }
1186         polyf_brushstart.numpoints = numpoints;
1187         polyf_brushstart.numedgedirs = numpoints;
1188         polyf_brushstart.numplanes = numpoints + 2;
1189         polyf_brushstart.points = polyf_pointsstart;//(colpointf_t *)points;
1190         polyf_brushstart.planes = polyf_planesstart;
1191         polyf_brushstart.supercontents = supercontents;
1192         polyf_brushstart.q3surfaceflags = q3surfaceflags;
1193         polyf_brushstart.texture = texture;
1194         for (i = 0;i < numpoints;i++)
1195                 Matrix4x4_Transform(polygonmatrixstart, points + i * 3, polyf_brushstart.points[i].v);
1196         polyf_brushend.numpoints = numpoints;
1197         polyf_brushend.numedgedirs = numpoints;
1198         polyf_brushend.numplanes = numpoints + 2;
1199         polyf_brushend.points = polyf_pointsend;//(colpointf_t *)points;
1200         polyf_brushend.planes = polyf_planesend;
1201         polyf_brushend.supercontents = supercontents;
1202         polyf_brushend.q3surfaceflags = q3surfaceflags;
1203         polyf_brushend.texture = texture;
1204         for (i = 0;i < numpoints;i++)
1205                 Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v);
1206         for (i = 0;i < polyf_brushstart.numplanes;i++)
1207         {
1208                 polyf_brushstart.planes[i].q3surfaceflags = q3surfaceflags;
1209                 polyf_brushstart.planes[i].texture = texture;
1210         }
1211         Collision_SnapCopyPoints(polyf_brushstart.numpoints, polyf_pointsstart, polyf_pointsstart, COLLISION_SNAPSCALE, COLLISION_SNAP);
1212         Collision_SnapCopyPoints(polyf_brushend.numpoints, polyf_pointsend, polyf_pointsend, COLLISION_SNAPSCALE, COLLISION_SNAP);
1213         Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brushstart);
1214         Collision_CalcEdgeDirsForPolygonBrushFloat(&polyf_brushend);
1215         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart);
1216         Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushend);
1217
1218         //Collision_PrintBrushAsQHull(&polyf_brushstart, "polyf_brushstart");
1219         //Collision_PrintBrushAsQHull(&polyf_brushend, "polyf_brushend");
1220
1221         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brushstart, &polyf_brushend);
1222 }
1223
1224
1225
1226 #define MAX_BRUSHFORBOX 16
1227 static unsigned int brushforbox_index = 0;
1228 // note: this relies on integer overflow to be consistent with modulo
1229 // MAX_BRUSHFORBOX, or in other words, MAX_BRUSHFORBOX must be a power of two!
1230 static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8];
1231 static colpointf_t brushforbox_edgedir[MAX_BRUSHFORBOX*6];
1232 static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6];
1233 static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX];
1234 static colbrushf_t brushforpoint_brush[MAX_BRUSHFORBOX];
1235
1236 void Collision_InitBrushForBox(void)
1237 {
1238         int i;
1239         for (i = 0;i < MAX_BRUSHFORBOX;i++)
1240         {
1241                 brushforbox_brush[i].numpoints = 8;
1242                 brushforbox_brush[i].numedgedirs = 6;
1243                 brushforbox_brush[i].numplanes = 6;
1244                 brushforbox_brush[i].points = brushforbox_point + i * 8;
1245                 brushforbox_brush[i].edgedirs = brushforbox_edgedir + i * 6;
1246                 brushforbox_brush[i].planes = brushforbox_plane + i * 6;
1247                 brushforpoint_brush[i].numpoints = 1;
1248                 brushforpoint_brush[i].numedgedirs = 0;
1249                 brushforpoint_brush[i].numplanes = 0;
1250                 brushforpoint_brush[i].points = brushforbox_point + i * 8;
1251                 brushforpoint_brush[i].edgedirs = brushforbox_edgedir + i * 6;
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, int supercontents, int q3surfaceflags, texture_t *texture)
1257 {
1258         int i, j;
1259         vec3_t v;
1260         colbrushf_t *brush;
1261         if (brushforbox_brush[0].numpoints == 0)
1262                 Collision_InitBrushForBox();
1263         // FIXME: these probably don't actually need to be normalized if the collision code does not care
1264         if (VectorCompare(mins, maxs))
1265         {
1266                 // point brush
1267                 brush = brushforpoint_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1268                 VectorCopy(mins, brush->points->v);
1269         }
1270         else
1271         {
1272                 brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX);
1273                 // FIXME: optimize
1274                 for (i = 0;i < 8;i++)
1275                 {
1276                         v[0] = i & 1 ? maxs[0] : mins[0];
1277                         v[1] = i & 2 ? maxs[1] : mins[1];
1278                         v[2] = i & 4 ? maxs[2] : mins[2];
1279                         Matrix4x4_Transform(matrix, v, brush->points[i].v);
1280                 }
1281                 // FIXME: optimize!
1282                 for (i = 0;i < 6;i++)
1283                 {
1284                         VectorClear(v);
1285                         v[i >> 1] = i & 1 ? 1 : -1;
1286                         Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal);
1287                         VectorNormalize(brush->planes[i].normal);
1288                         brush->planes[i].q3surfaceflags = q3surfaceflags;
1289                         brush->planes[i].texture = texture;
1290                         VectorCopy(brush->planes[i].normal, brush->edgedirs[i].v);
1291                 }
1292         }
1293         brush->supercontents = supercontents;
1294         brush->q3surfaceflags = q3surfaceflags;
1295         brush->texture = texture;
1296         for (j = 0;j < brush->numplanes;j++)
1297         {
1298                 brush->planes[j].q3surfaceflags = q3surfaceflags;
1299                 brush->planes[j].texture = texture;
1300                 brush->planes[j].dist = furthestplanedist_float(brush->planes[j].normal, brush->points, brush->numpoints);
1301         }
1302         VectorCopy(brush->points[0].v, brush->mins);
1303         VectorCopy(brush->points[0].v, brush->maxs);
1304         for (j = 1;j < brush->numpoints;j++)
1305         {
1306                 brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
1307                 brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
1308                 brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
1309                 brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
1310                 brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
1311                 brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
1312         }
1313         brush->mins[0] -= 1;
1314         brush->mins[1] -= 1;
1315         brush->mins[2] -= 1;
1316         brush->maxs[0] += 1;
1317         brush->maxs[1] += 1;
1318         brush->maxs[2] += 1;
1319         Collision_ValidateBrush(brush);
1320         return brush;
1321 }
1322
1323 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, int supercontents, int q3surfaceflags, texture_t *texture)
1324 {
1325         colbrushf_t *boxbrush, *thisbrush_start, *thisbrush_end;
1326         vec3_t startmins, startmaxs, endmins, endmaxs;
1327
1328         // create brushes for the collision
1329         VectorAdd(start, mins, startmins);
1330         VectorAdd(start, maxs, startmaxs);
1331         VectorAdd(end, mins, endmins);
1332         VectorAdd(end, maxs, endmaxs);
1333         boxbrush = Collision_BrushForBox(&identitymatrix, cmins, cmaxs, supercontents, q3surfaceflags, texture);
1334         thisbrush_start = Collision_BrushForBox(&identitymatrix, startmins, startmaxs, 0, 0, NULL);
1335         thisbrush_end = Collision_BrushForBox(&identitymatrix, endmins, endmaxs, 0, 0, NULL);
1336
1337         memset(trace, 0, sizeof(trace_t));
1338         trace->hitsupercontentsmask = hitsupercontentsmask;
1339         trace->fraction = 1;
1340         trace->realfraction = 1;
1341         trace->allsolid = true;
1342         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, boxbrush, boxbrush);
1343 }
1344
1345 //pseudocode for detecting line/sphere overlap without calculating an impact point
1346 //linesphereorigin = sphereorigin - linestart;linediff = lineend - linestart;linespherefrac = DotProduct(linesphereorigin, linediff) / DotProduct(linediff, linediff);return VectorLength2(linesphereorigin - bound(0, linespherefrac, 1) * linediff) >= sphereradius*sphereradius;
1347
1348 // LordHavoc: currently unused, but tested
1349 // note: this can be used for tracing a moving sphere vs a stationary sphere,
1350 // by simply adding the moving sphere's radius to the sphereradius parameter,
1351 // all the results are correct (impactpoint, impactnormal, and fraction)
1352 float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double *sphereorigin, double sphereradius, double *impactpoint, double *impactnormal)
1353 {
1354         double dir[3], scale, v[3], deviationdist, impactdist, linelength;
1355         // make sure the impactpoint and impactnormal are valid even if there is
1356         // no collision
1357         VectorCopy(lineend, impactpoint);
1358         VectorClear(impactnormal);
1359         // calculate line direction
1360         VectorSubtract(lineend, linestart, dir);
1361         // normalize direction
1362         linelength = VectorLength(dir);
1363         if (linelength)
1364         {
1365                 scale = 1.0 / linelength;
1366                 VectorScale(dir, scale, dir);
1367         }
1368         // this dotproduct calculates the distance along the line at which the
1369         // sphere origin is (nearest point to the sphere origin on the line)
1370         impactdist = DotProduct(sphereorigin, dir) - DotProduct(linestart, dir);
1371         // calculate point on line at that distance, and subtract the
1372         // sphereorigin from it, so we have a vector to measure for the distance
1373         // of the line from the sphereorigin (deviation, how off-center it is)
1374         VectorMA(linestart, impactdist, dir, v);
1375         VectorSubtract(v, sphereorigin, v);
1376         deviationdist = VectorLength2(v);
1377         // if outside the radius, it's a miss for sure
1378         // (we do this comparison using squared radius to avoid a sqrt)
1379         if (deviationdist > sphereradius*sphereradius)
1380                 return 1; // miss (off to the side)
1381         // nudge back to find the correct impact distance
1382         impactdist -= sphereradius - deviationdist/sphereradius;
1383         if (impactdist >= linelength)
1384                 return 1; // miss (not close enough)
1385         if (impactdist < 0)
1386                 return 1; // miss (linestart is past or inside sphere)
1387         // calculate new impactpoint
1388         VectorMA(linestart, impactdist, dir, impactpoint);
1389         // calculate impactnormal (surface normal at point of impact)
1390         VectorSubtract(impactpoint, sphereorigin, impactnormal);
1391         // normalize impactnormal
1392         VectorNormalize(impactnormal);
1393         // return fraction of movement distance
1394         return impactdist / linelength;
1395 }
1396
1397 void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const float *point0, const float *point1, const float *point2, int supercontents, int q3surfaceflags, texture_t *texture)
1398 {
1399 #if 1
1400         // more optimized
1401         float d1, d2, d, f, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, faceplanenormallength2, edge01[3], edge21[3], edge02[3];
1402
1403         // this function executes:
1404         // 32 ops when line starts behind triangle
1405         // 38 ops when line ends infront of triangle
1406         // 43 ops when line fraction is already closer than this triangle
1407         // 72 ops when line is outside edge 01
1408         // 92 ops when line is outside edge 21
1409         // 115 ops when line is outside edge 02
1410         // 123 ops when line impacts triangle and updates trace results
1411
1412         // this code is designed for clockwise triangles, conversion to
1413         // counterclockwise would require swapping some things around...
1414         // it is easier to simply swap the point0 and point2 parameters to this
1415         // function when calling it than it is to rewire the internals.
1416
1417         // calculate the faceplanenormal of the triangle, this represents the front side
1418         // 15 ops
1419         VectorSubtract(point0, point1, edge01);
1420         VectorSubtract(point2, point1, edge21);
1421         CrossProduct(edge01, edge21, faceplanenormal);
1422         // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
1423         // 6 ops
1424         faceplanenormallength2 = DotProduct(faceplanenormal, faceplanenormal);
1425         if (faceplanenormallength2 < 0.0001f)
1426                 return;
1427         // calculate the distance
1428         // 5 ops
1429         faceplanedist = DotProduct(point0, faceplanenormal);
1430
1431         // if start point is on the back side there is no collision
1432         // (we don't care about traces going through the triangle the wrong way)
1433
1434         // calculate the start distance
1435         // 6 ops
1436         d1 = DotProduct(faceplanenormal, linestart);
1437         if (d1 <= faceplanedist)
1438                 return;
1439
1440         // calculate the end distance
1441         // 6 ops
1442         d2 = DotProduct(faceplanenormal, lineend);
1443         // if both are in front, there is no collision
1444         if (d2 >= faceplanedist)
1445                 return;
1446
1447         // from here on we know d1 is >= 0 and d2 is < 0
1448         // this means the line starts infront and ends behind, passing through it
1449
1450         // calculate the recipricol of the distance delta,
1451         // so we can use it multiple times cheaply (instead of division)
1452         // 2 ops
1453         d = 1.0f / (d1 - d2);
1454         // calculate the impact fraction by taking the start distance (> 0)
1455         // and subtracting the face plane distance (this is the distance of the
1456         // triangle along that same normal)
1457         // then multiply by the recipricol distance delta
1458         // 2 ops
1459         f = (d1 - faceplanedist) * d;
1460         // skip out if this impact is further away than previous ones
1461         // 1 ops
1462         if (f > trace->realfraction)
1463                 return;
1464         // calculate the perfect impact point for classification of insidedness
1465         // 9 ops
1466         impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1467         impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1468         impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1469
1470         // calculate the edge normal and reject if impact is outside triangle
1471         // (an edge normal faces away from the triangle, to get the desired normal
1472         //  a crossproduct with the faceplanenormal is used, and because of the way
1473         // the insidedness comparison is written it does not need to be normalized)
1474
1475         // first use the two edges from the triangle plane math
1476         // the other edge only gets calculated if the point survives that long
1477
1478         // 20 ops
1479         CrossProduct(edge01, faceplanenormal, edgenormal);
1480         if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1481                 return;
1482
1483         // 20 ops
1484         CrossProduct(faceplanenormal, edge21, edgenormal);
1485         if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1486                 return;
1487
1488         // 23 ops
1489         VectorSubtract(point0, point2, edge02);
1490         CrossProduct(faceplanenormal, edge02, edgenormal);
1491         if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1492                 return;
1493
1494         // 8 ops (rare)
1495
1496         // store the new trace fraction
1497         trace->realfraction = f;
1498
1499         // calculate a nudged fraction to keep it out of the surface
1500         // (the main fraction remains perfect)
1501         trace->fraction = f - collision_impactnudge.value * d;
1502
1503         if (collision_prefernudgedfraction.integer)
1504                 trace->realfraction = trace->fraction;
1505
1506         // store the new trace plane (because collisions only happen from
1507         // the front this is always simply the triangle normal, never flipped)
1508         d = 1.0 / sqrt(faceplanenormallength2);
1509         VectorScale(faceplanenormal, d, trace->plane.normal);
1510         trace->plane.dist = faceplanedist * d;
1511
1512         trace->hitsupercontents = supercontents;
1513         trace->hitq3surfaceflags = q3surfaceflags;
1514         trace->hittexture = texture;
1515 #else
1516         float d1, d2, d, f, fnudged, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, edge[3];
1517
1518         // this code is designed for clockwise triangles, conversion to
1519         // counterclockwise would require swapping some things around...
1520         // it is easier to simply swap the point0 and point2 parameters to this
1521         // function when calling it than it is to rewire the internals.
1522
1523         // calculate the unnormalized faceplanenormal of the triangle,
1524         // this represents the front side
1525         TriangleNormal(point0, point1, point2, faceplanenormal);
1526         // there's no point in processing a degenerate triangle
1527         // (GIGO - Garbage In, Garbage Out)
1528         if (DotProduct(faceplanenormal, faceplanenormal) < 0.0001f)
1529                 return;
1530         // calculate the unnormalized distance
1531         faceplanedist = DotProduct(point0, faceplanenormal);
1532
1533         // calculate the unnormalized start distance
1534         d1 = DotProduct(faceplanenormal, linestart) - faceplanedist;
1535         // if start point is on the back side there is no collision
1536         // (we don't care about traces going through the triangle the wrong way)
1537         if (d1 <= 0)
1538                 return;
1539
1540         // calculate the unnormalized end distance
1541         d2 = DotProduct(faceplanenormal, lineend) - faceplanedist;
1542         // if both are in front, there is no collision
1543         if (d2 >= 0)
1544                 return;
1545
1546         // from here on we know d1 is >= 0 and d2 is < 0
1547         // this means the line starts infront and ends behind, passing through it
1548
1549         // calculate the recipricol of the distance delta,
1550         // so we can use it multiple times cheaply (instead of division)
1551         d = 1.0f / (d1 - d2);
1552         // calculate the impact fraction by taking the start distance (> 0)
1553         // and subtracting the face plane distance (this is the distance of the
1554         // triangle along that same normal)
1555         // then multiply by the recipricol distance delta
1556         f = d1 * d;
1557         // skip out if this impact is further away than previous ones
1558         if (f > trace->realfraction)
1559                 return;
1560         // calculate the perfect impact point for classification of insidedness
1561         impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1562         impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1563         impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1564
1565         // calculate the edge normal and reject if impact is outside triangle
1566         // (an edge normal faces away from the triangle, to get the desired normal
1567         //  a crossproduct with the faceplanenormal is used, and because of the way
1568         // the insidedness comparison is written it does not need to be normalized)
1569
1570         VectorSubtract(point2, point0, edge);
1571         CrossProduct(edge, faceplanenormal, edgenormal);
1572         if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1573                 return;
1574
1575         VectorSubtract(point0, point1, edge);
1576         CrossProduct(edge, faceplanenormal, edgenormal);
1577         if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1578                 return;
1579
1580         VectorSubtract(point1, point2, edge);
1581         CrossProduct(edge, faceplanenormal, edgenormal);
1582         if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1583                 return;
1584
1585         // store the new trace fraction
1586         trace->realfraction = bound(0, f, 1);
1587
1588         // store the new trace plane (because collisions only happen from
1589         // the front this is always simply the triangle normal, never flipped)
1590         VectorNormalize(faceplanenormal);
1591         VectorCopy(faceplanenormal, trace->plane.normal);
1592         trace->plane.dist = DotProduct(point0, faceplanenormal);
1593
1594         // calculate the normalized start and end distances
1595         d1 = DotProduct(trace->plane.normal, linestart) - trace->plane.dist;
1596         d2 = DotProduct(trace->plane.normal, lineend) - trace->plane.dist;
1597
1598         // calculate a nudged fraction to keep it out of the surface
1599         // (the main fraction remains perfect)
1600         fnudged = (d1 - collision_impactnudge.value) / (d1 - d2);
1601         trace->fraction = bound(0, fnudged, 1);
1602
1603         // store the new trace endpos
1604         // not needed, it's calculated later when the trace is finished
1605         //trace->endpos[0] = linestart[0] + fnudged * (lineend[0] - linestart[0]);
1606         //trace->endpos[1] = linestart[1] + fnudged * (lineend[1] - linestart[1]);
1607         //trace->endpos[2] = linestart[2] + fnudged * (lineend[2] - linestart[2]);
1608         trace->hitsupercontents = supercontents;
1609         trace->hitq3surfaceflags = q3surfaceflags;
1610         trace->hittexture = texture;
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 = (colbsp_t *)Mem_Alloc(mempool, sizeof(colbsp_t));
1639         bsp->mempool = mempool;
1640         bsp->nodes = (colbspnode_t *)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
1689 //===========================================
1690
1691 void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
1692 {
1693         float starttransformed[3], endtransformed[3];
1694
1695         memset(trace, 0, sizeof(*trace));
1696         trace->fraction = trace->realfraction = 1;
1697
1698         Matrix4x4_Transform(inversematrix, start, starttransformed);
1699         Matrix4x4_Transform(inversematrix, end, endtransformed);
1700 #if COLLISIONPARANOID >= 3
1701         Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]);
1702 #endif
1703
1704         if (model && model->TraceBox)
1705                 model->TraceBox(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask);
1706         else
1707                 Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
1708         trace->fraction = bound(0, trace->fraction, 1);
1709         trace->realfraction = bound(0, trace->realfraction, 1);
1710
1711         VectorLerp(start, trace->fraction, end, trace->endpos);
1712         // transform plane
1713         // NOTE: this relies on plane.dist being directly after plane.normal
1714         Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
1715 }
1716
1717 void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontents)
1718 {
1719         memset(trace, 0, sizeof(*trace));
1720         trace->fraction = trace->realfraction = 1;
1721         if (model && model->TraceBox)
1722                 model->TraceBox(model, 0, trace, start, mins, maxs, end, hitsupercontents);
1723         trace->fraction = bound(0, trace->fraction, 1);
1724         trace->realfraction = bound(0, trace->realfraction, 1);
1725         VectorLerp(start, trace->fraction, end, trace->endpos);
1726 }
1727
1728 void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
1729 {
1730         float starttransformed[3], endtransformed[3];
1731
1732         memset(trace, 0, sizeof(*trace));
1733         trace->fraction = trace->realfraction = 1;
1734
1735         Matrix4x4_Transform(inversematrix, start, starttransformed);
1736         Matrix4x4_Transform(inversematrix, end, endtransformed);
1737 #if COLLISIONPARANOID >= 3
1738         Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]);
1739 #endif
1740
1741         if (model && model->TraceLine)
1742                 model->TraceLine(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, endtransformed, hitsupercontentsmask);
1743         else
1744                 Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
1745         trace->fraction = bound(0, trace->fraction, 1);
1746         trace->realfraction = bound(0, trace->realfraction, 1);
1747
1748         VectorLerp(start, trace->fraction, end, trace->endpos);
1749         // transform plane
1750         // NOTE: this relies on plane.dist being directly after plane.normal
1751         Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
1752 }
1753
1754 void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents)
1755 {
1756         memset(trace, 0, sizeof(*trace));
1757         trace->fraction = trace->realfraction = 1;
1758         if (model && model->TraceLine)
1759                 model->TraceLine(model, 0, trace, start, end, hitsupercontents);
1760         trace->fraction = bound(0, trace->fraction, 1);
1761         trace->realfraction = bound(0, trace->realfraction, 1);
1762         VectorLerp(start, trace->fraction, end, trace->endpos);
1763 }
1764
1765 void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, int frame, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask)
1766 {
1767         float starttransformed[3];
1768
1769         memset(trace, 0, sizeof(*trace));
1770         trace->fraction = trace->realfraction = 1;
1771
1772         Matrix4x4_Transform(inversematrix, start, starttransformed);
1773 #if COLLISIONPARANOID >= 3
1774         Con_Printf("trans(%f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2]);
1775 #endif
1776
1777         if (model && model->TracePoint)
1778                 model->TracePoint(model, bound(0, frame, (model->numframes - 1)), trace, starttransformed, hitsupercontentsmask);
1779         else
1780                 Collision_ClipTrace_Point(trace, bodymins, bodymaxs, starttransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
1781
1782         VectorCopy(start, trace->endpos);
1783         // transform plane
1784         // NOTE: this relies on plane.dist being directly after plane.normal
1785         Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
1786 }
1787
1788 void Collision_ClipPointToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, int hitsupercontents)
1789 {
1790         memset(trace, 0, sizeof(*trace));
1791         trace->fraction = trace->realfraction = 1;
1792         if (model && model->TracePoint)
1793                 model->TracePoint(model, 0, trace, start, hitsupercontents);
1794         VectorCopy(start, trace->endpos);
1795 }
1796
1797 void Collision_CombineTraces(trace_t *cliptrace, const trace_t *trace, void *touch, qboolean isbmodel)
1798 {
1799         // take the 'best' answers from the new trace and combine with existing data
1800         if (trace->allsolid)
1801                 cliptrace->allsolid = true;
1802         if (trace->startsolid)
1803         {
1804                 if (isbmodel)
1805                         cliptrace->bmodelstartsolid = true;
1806                 cliptrace->startsolid = true;
1807                 if (cliptrace->realfraction == 1)
1808                         cliptrace->ent = touch;
1809         }
1810         // don't set this except on the world, because it can easily confuse
1811         // monsters underwater if there's a bmodel involved in the trace
1812         // (inopen && inwater is how they check water visibility)
1813         //if (trace->inopen)
1814         //      cliptrace->inopen = true;
1815         if (trace->inwater)
1816                 cliptrace->inwater = true;
1817         if ((trace->realfraction <= cliptrace->realfraction) && (VectorLength2(trace->plane.normal) > 0))
1818         {
1819                 cliptrace->fraction = trace->fraction;
1820                 cliptrace->realfraction = trace->realfraction;
1821                 VectorCopy(trace->endpos, cliptrace->endpos);
1822                 cliptrace->plane = trace->plane;
1823                 cliptrace->ent = touch;
1824                 cliptrace->hitsupercontents = trace->hitsupercontents;
1825                 cliptrace->hitq3surfaceflags = trace->hitq3surfaceflags;
1826                 cliptrace->hittexture = trace->hittexture;
1827         }
1828         cliptrace->startsupercontents |= trace->startsupercontents;
1829 }
1830
1831 void Collision_ShortenTrace(trace_t *trace, float shorten_factor, const vec3_t end)
1832 {
1833         // now undo our moving end 1 qu farther...
1834         trace->fraction = bound(trace->fraction, trace->fraction / shorten_factor - 1e-6, 1); // we subtract 1e-6 to guard for roundoff errors
1835         trace->realfraction = bound(trace->realfraction, trace->realfraction / shorten_factor - 1e-6, 1); // we subtract 1e-6 to guard for roundoff errors
1836         if(trace->fraction >= 1) // trace would NOT hit if not expanded!
1837         {
1838                 trace->fraction = 1;
1839                 trace->realfraction = 1;
1840                 VectorCopy(end, trace->endpos);
1841                 memset(&trace->plane, 0, sizeof(trace->plane));
1842                 trace->ent = NULL;
1843                 trace->hitsupercontentsmask = 0;
1844                 trace->hitsupercontents = 0;
1845                 trace->hitq3surfaceflags = 0;
1846                 trace->hittexture = NULL;
1847         }
1848 }