5 #define COLLISION_EDGEDIR_DOT_EPSILON (0.999f)
6 #define COLLISION_EDGECROSS_MINLENGTH2 (1.0f / 4194304.0f)
7 #define COLLISION_SNAPSCALE (32.0f)
8 #define COLLISION_SNAP (1.0f / COLLISION_SNAPSCALE)
9 #define COLLISION_SNAP2 (2.0f / COLLISION_SNAPSCALE)
10 #define COLLISION_PLANE_DIST_EPSILON (2.0f / COLLISION_SNAPSCALE)
12 cvar_t collision_impactnudge = {0, "collision_impactnudge", "0.03125", "how much to back off from the impact"};
13 cvar_t collision_extendmovelength = {0, "collision_extendmovelength", "16", "internal bias on trace length to ensure detection of collisions within the collision_impactnudge distance so that short moves do not degrade across frames (this does not alter the final trace length)"};
14 cvar_t collision_extendtraceboxlength = {0, "collision_extendtraceboxlength", "1", "internal bias for tracebox() qc builtin to account for collision_impactnudge (this does not alter the final trace length)"};
15 cvar_t collision_extendtracelinelength = {0, "collision_extendtracelinelength", "1", "internal bias for traceline() qc builtin to account for collision_impactnudge (this does not alter the final trace length)"};
16 cvar_t collision_debug_tracelineasbox = {0, "collision_debug_tracelineasbox", "0", "workaround for any bugs in Collision_TraceLineBrushFloat by using Collision_TraceBrushBrushFloat"};
17 cvar_t collision_cache = {0, "collision_cache", "1", "store results of collision traces for next frame to reuse if possible (optimization)"};
18 //cvar_t collision_triangle_neighborsides = {0, "collision_triangle_neighborsides", "1", "override automatic side generation if triangle has neighbors with face planes that form a convex edge (perfect solution, but can not work for all edges)"};
19 cvar_t collision_triangle_bevelsides = {0, "collision_triangle_bevelsides", "0", "generate sloped edge planes on triangles - if 0, see axialedgeplanes"};
20 cvar_t collision_triangle_axialsides = {0, "collision_triangle_axialsides", "1", "generate axially-aligned edge planes on triangles - otherwise use perpendicular edge planes"};
22 mempool_t *collision_mempool;
24 void Collision_Init (void)
26 Cvar_RegisterVariable(&collision_impactnudge);
27 Cvar_RegisterVariable(&collision_extendmovelength);
28 Cvar_RegisterVariable(&collision_extendtracelinelength);
29 Cvar_RegisterVariable(&collision_extendtraceboxlength);
30 Cvar_RegisterVariable(&collision_debug_tracelineasbox);
31 Cvar_RegisterVariable(&collision_cache);
32 // Cvar_RegisterVariable(&collision_triangle_neighborsides);
33 Cvar_RegisterVariable(&collision_triangle_bevelsides);
34 Cvar_RegisterVariable(&collision_triangle_axialsides);
35 collision_mempool = Mem_AllocPool("collision cache", 0, NULL);
36 Collision_Cache_Init(collision_mempool);
52 static void Collision_PrintBrushAsQHull(colbrushf_t *brush, const char *name)
55 Con_Printf("3 %s\n%i\n", name, brush->numpoints);
56 for (i = 0;i < brush->numpoints;i++)
57 Con_Printf("%f %f %f\n", brush->points[i].v[0], brush->points[i].v[1], brush->points[i].v[2]);
59 Con_Printf("4\n%i\n", brush->numplanes);
60 for (i = 0;i < brush->numplanes;i++)
61 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);
64 static void Collision_ValidateBrush(colbrushf_t *brush)
66 int j, k, pointsoffplanes, pointonplanes, pointswithinsufficientplanes, printbrush;
69 if (!brush->numpoints)
71 Con_Print("Collision_ValidateBrush: brush with no points!\n");
75 // it's ok for a brush to have one point and no planes...
76 if (brush->numplanes == 0 && brush->numpoints != 1)
78 Con_Print("Collision_ValidateBrush: brush with no planes and more than one point!\n");
85 pointswithinsufficientplanes = 0;
86 for (k = 0;k < brush->numplanes;k++)
87 if (DotProduct(brush->planes[k].normal, brush->planes[k].normal) < 0.0001f)
88 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);
89 for (j = 0;j < brush->numpoints;j++)
92 for (k = 0;k < brush->numplanes;k++)
94 d = DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist;
95 if (d > COLLISION_PLANE_DIST_EPSILON)
97 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);
100 if (fabs(d) > COLLISION_PLANE_DIST_EPSILON)
105 if (pointonplanes < 3)
106 pointswithinsufficientplanes++;
108 if (pointswithinsufficientplanes)
110 Con_Print("Collision_ValidateBrush: some points have insufficient planes, every point must be on at least 3 planes to form a corner.\n");
113 if (pointsoffplanes == 0) // all points are on all planes
115 Con_Print("Collision_ValidateBrush: all points lie on all planes (degenerate, no brush volume!)\n");
120 Collision_PrintBrushAsQHull(brush, "unnamed");
123 static float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
125 float dist, bestdist;
128 bestdist = DotProduct(points->v, normal);
132 dist = DotProduct(points->v, normal);
133 bestdist = min(bestdist, dist);
139 static float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints)
141 float dist, bestdist;
144 bestdist = DotProduct(points->v, normal);
148 dist = DotProduct(points->v, normal);
149 bestdist = max(bestdist, dist);
155 static void Collision_CalcEdgeDirsForPolygonBrushFloat(colbrushf_t *brush)
158 for (i = 0, j = brush->numpoints - 1;i < brush->numpoints;j = i, i++)
159 VectorSubtract(brush->points[i].v, brush->points[j].v, brush->edgedirs[j].v);
162 colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const colplanef_t *originalplanes, int supercontents, int q3surfaceflags, const texture_t *texture, int hasaabbplanes)
164 // TODO: planesbuf could be replaced by a remapping table
165 int j, k, l, m, w, xyzflags;
166 int numpointsbuf = 0, maxpointsbuf = 256, numedgedirsbuf = 0, maxedgedirsbuf = 256, numplanesbuf = 0, maxplanesbuf = 256, numelementsbuf = 0, maxelementsbuf = 256;
170 colpointf_t pointsbuf[256];
171 colpointf_t edgedirsbuf[256];
172 colplanef_t planesbuf[256];
173 int elementsbuf[1024];
174 int polypointbuf[256];
179 // enable these if debugging to avoid seeing garbage in unused data-
180 memset(pointsbuf, 0, sizeof(pointsbuf));
181 memset(edgedirsbuf, 0, sizeof(edgedirsbuf));
182 memset(planesbuf, 0, sizeof(planesbuf));
183 memset(elementsbuf, 0, sizeof(elementsbuf));
184 memset(polypointbuf, 0, sizeof(polypointbuf));
185 memset(p, 0, sizeof(p));
188 // check if there are too many planes and skip the brush
189 if (numoriginalplanes >= maxplanesbuf)
191 Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many planes for buffer\n");
195 // figure out how large a bounding box we need to properly compute this brush
197 for (j = 0;j < numoriginalplanes;j++)
198 maxdist = max(maxdist, fabs(originalplanes[j].dist));
199 // now make it large enough to enclose the entire brush, and round it off to a reasonable multiple of 1024
200 maxdist = floor(maxdist * (4.0 / 1024.0) + 2) * 1024.0;
201 // construct a collision brush (points, planes, and renderable mesh) from
202 // a set of planes, this also optimizes out any unnecessary planes (ones
203 // whose polygon is clipped away by the other planes)
204 for (j = 0;j < numoriginalplanes;j++)
207 VectorCopy(originalplanes[j].normal, planesbuf[numplanesbuf].normal);
208 planesbuf[numplanesbuf].dist = originalplanes[j].dist;
209 planesbuf[numplanesbuf].q3surfaceflags = originalplanes[j].q3surfaceflags;
210 planesbuf[numplanesbuf].texture = originalplanes[j].texture;
213 // create a large polygon from the plane
215 PolygonD_QuadForPlane(p[w], originalplanes[j].normal[0], originalplanes[j].normal[1], originalplanes[j].normal[2], originalplanes[j].dist, maxdist);
217 // clip it by all other planes
218 for (k = 0;k < numoriginalplanes && pnumpoints >= 3 && pnumpoints <= pmaxpoints;k++)
220 // skip the plane this polygon
221 // (nothing happens if it is processed, this is just an optimization)
224 // we want to keep the inside of the brush plane so we flip
226 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);
231 // if nothing is left, skip it
234 //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);
238 for (k = 0;k < pnumpoints;k++)
242 for (l = 0;l < numoriginalplanes;l++)
243 if (fabs(DotProduct(&p[w][k*3], originalplanes[l].normal) - originalplanes[l].dist) < COLLISION_PLANE_DIST_EPSILON)
250 Con_DPrintf("Collision_NewBrushFromPlanes: warning: polygon point does not lie on at least 3 planes\n");
254 // check if there are too many polygon vertices for buffer
255 if (pnumpoints > pmaxpoints)
257 Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
261 // check if there are too many triangle elements for buffer
262 if (numelementsbuf + (pnumpoints - 2) * 3 > maxelementsbuf)
264 Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many triangle elements for buffer\n");
268 // add the unique points for this polygon
269 for (k = 0;k < pnumpoints;k++)
272 // downgrade to float precision before comparing
273 VectorCopy(&p[w][k*3], v);
275 // check if there is already a matching point (no duplicates)
276 for (m = 0;m < numpointsbuf;m++)
277 if (VectorDistance2(v, pointsbuf[m].v) < COLLISION_SNAP2)
280 // if there is no match, add a new one
281 if (m == numpointsbuf)
283 // check if there are too many and skip the brush
284 if (numpointsbuf >= maxpointsbuf)
286 Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many points for buffer\n");
290 VectorCopy(&p[w][k*3], pointsbuf[numpointsbuf].v);
294 // store the index into a buffer
298 // add the triangles for the polygon
299 // (this particular code makes a triangle fan)
300 for (k = 0;k < pnumpoints - 2;k++)
302 elementsbuf[numelementsbuf++] = polypointbuf[0];
303 elementsbuf[numelementsbuf++] = polypointbuf[k + 1];
304 elementsbuf[numelementsbuf++] = polypointbuf[k + 2];
307 // add the unique edgedirs for this polygon
308 for (k = 0, l = pnumpoints-1;k < pnumpoints;l = k, k++)
311 // downgrade to float precision before comparing
312 VectorSubtract(&p[w][k*3], &p[w][l*3], dir);
313 VectorNormalize(dir);
315 // check if there is already a matching edgedir (no duplicates)
316 for (m = 0;m < numedgedirsbuf;m++)
317 if (DotProduct(dir, edgedirsbuf[m].v) >= COLLISION_EDGEDIR_DOT_EPSILON)
319 // skip this if there is
320 if (m < numedgedirsbuf)
323 // try again with negated edgedir
324 VectorNegate(dir, dir);
325 // check if there is already a matching edgedir (no duplicates)
326 for (m = 0;m < numedgedirsbuf;m++)
327 if (DotProduct(dir, edgedirsbuf[m].v) >= COLLISION_EDGEDIR_DOT_EPSILON)
329 // if there is no match, add a new one
330 if (m == numedgedirsbuf)
332 // check if there are too many and skip the brush
333 if (numedgedirsbuf >= maxedgedirsbuf)
335 Con_DPrint("Collision_NewBrushFromPlanes: failed to build collision brush: too many edgedirs for buffer\n");
339 VectorCopy(dir, edgedirsbuf[numedgedirsbuf].v);
344 // if any normal is not purely axial, it's not an axis-aligned box
345 if (isaabb && (originalplanes[j].normal[0] == 0) + (originalplanes[j].normal[1] == 0) + (originalplanes[j].normal[2] == 0) < 2)
349 // if nothing is left, there's nothing to allocate
350 if (numplanesbuf < 4)
352 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);
356 // if no triangles or points could be constructed, then this routine failed but the brush is not discarded
357 if (numelementsbuf < 12 || numpointsbuf < 4)
358 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);
360 // validate plane distances
361 for (j = 0;j < numplanesbuf;j++)
363 float d = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpointsbuf);
364 if (fabs(planesbuf[j].dist - d) > COLLISION_PLANE_DIST_EPSILON)
365 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);
368 // allocate the brush and copy to it
369 brush = (colbrushf_t *)Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colpointf_t) * numpointsbuf + sizeof(colpointf_t) * numedgedirsbuf + sizeof(colplanef_t) * numplanesbuf + sizeof(int) * numelementsbuf);
370 brush->isaabb = isaabb;
371 brush->hasaabbplanes = hasaabbplanes;
372 brush->supercontents = supercontents;
373 brush->numplanes = numplanesbuf;
374 brush->numedgedirs = numedgedirsbuf;
375 brush->numpoints = numpointsbuf;
376 brush->numtriangles = numelementsbuf / 3;
377 brush->planes = (colplanef_t *)(brush + 1);
378 brush->points = (colpointf_t *)(brush->planes + brush->numplanes);
379 brush->edgedirs = (colpointf_t *)(brush->points + brush->numpoints);
380 brush->elements = (int *)(brush->points + brush->numpoints);
381 brush->q3surfaceflags = q3surfaceflags;
382 brush->texture = texture;
383 for (j = 0;j < brush->numpoints;j++)
385 brush->points[j].v[0] = pointsbuf[j].v[0];
386 brush->points[j].v[1] = pointsbuf[j].v[1];
387 brush->points[j].v[2] = pointsbuf[j].v[2];
389 for (j = 0;j < brush->numedgedirs;j++)
391 brush->edgedirs[j].v[0] = edgedirsbuf[j].v[0];
392 brush->edgedirs[j].v[1] = edgedirsbuf[j].v[1];
393 brush->edgedirs[j].v[2] = edgedirsbuf[j].v[2];
395 for (j = 0;j < brush->numplanes;j++)
397 brush->planes[j].normal[0] = planesbuf[j].normal[0];
398 brush->planes[j].normal[1] = planesbuf[j].normal[1];
399 brush->planes[j].normal[2] = planesbuf[j].normal[2];
400 brush->planes[j].dist = planesbuf[j].dist;
401 brush->planes[j].q3surfaceflags = planesbuf[j].q3surfaceflags;
402 brush->planes[j].texture = planesbuf[j].texture;
404 for (j = 0;j < brush->numtriangles * 3;j++)
405 brush->elements[j] = elementsbuf[j];
408 VectorClear(brush->mins);
409 VectorClear(brush->maxs);
410 for (j = 0;j < min(6, numoriginalplanes);j++)
412 if (originalplanes[j].normal[0] == 1) {xyzflags |= 1;brush->maxs[0] = originalplanes[j].dist;}
413 else if (originalplanes[j].normal[0] == -1) {xyzflags |= 2;brush->mins[0] = -originalplanes[j].dist;}
414 else if (originalplanes[j].normal[1] == 1) {xyzflags |= 4;brush->maxs[1] = originalplanes[j].dist;}
415 else if (originalplanes[j].normal[1] == -1) {xyzflags |= 8;brush->mins[1] = -originalplanes[j].dist;}
416 else if (originalplanes[j].normal[2] == 1) {xyzflags |= 16;brush->maxs[2] = originalplanes[j].dist;}
417 else if (originalplanes[j].normal[2] == -1) {xyzflags |= 32;brush->mins[2] = -originalplanes[j].dist;}
419 // if not all xyzflags were set, then this is not a brush from q3map/q3map2, and needs reconstruction of the bounding box
420 // (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)
423 VectorCopy(brush->points[0].v, brush->mins);
424 VectorCopy(brush->points[0].v, brush->maxs);
425 for (j = 1;j < brush->numpoints;j++)
427 brush->mins[0] = min(brush->mins[0], brush->points[j].v[0]);
428 brush->mins[1] = min(brush->mins[1], brush->points[j].v[1]);
429 brush->mins[2] = min(brush->mins[2], brush->points[j].v[2]);
430 brush->maxs[0] = max(brush->maxs[0], brush->points[j].v[0]);
431 brush->maxs[1] = max(brush->maxs[1], brush->points[j].v[1]);
432 brush->maxs[2] = max(brush->maxs[2], brush->points[j].v[2]);
441 Collision_ValidateBrush(brush);
447 void Collision_CalcPlanesForTriangleBrushFloat(colbrushf_t *brush)
450 float edge0[3], edge1[3], edge2[3];
453 TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal);
454 if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f)
456 // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
457 // note that some of these exist in q3bsp bspline patches
458 brush->numplanes = 0;
462 // there are 5 planes (front, back, sides) and 3 edges
463 brush->numplanes = 5;
464 brush->numedgedirs = 3;
465 VectorNormalize(brush->planes[0].normal);
466 brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal);
467 VectorNegate(brush->planes[0].normal, brush->planes[1].normal);
468 brush->planes[1].dist = -brush->planes[0].dist;
469 // edge directions are easy to calculate
470 VectorSubtract(brush->points[2].v, brush->points[0].v, edge0);
471 VectorSubtract(brush->points[0].v, brush->points[1].v, edge1);
472 VectorSubtract(brush->points[1].v, brush->points[2].v, edge2);
473 VectorCopy(edge0, brush->edgedirs[0].v);
474 VectorCopy(edge1, brush->edgedirs[1].v);
475 VectorCopy(edge2, brush->edgedirs[2].v);
476 // now select an algorithm to generate the side planes
477 if (collision_triangle_bevelsides.integer)
479 // use 45 degree slopes at the edges of the triangle to make a sinking trace error turn into "riding up" the slope rather than getting stuck
480 CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal);
481 CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal);
482 CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal);
483 VectorNormalize(brush->planes[2].normal);
484 VectorNormalize(brush->planes[3].normal);
485 VectorNormalize(brush->planes[4].normal);
486 VectorAdd(brush->planes[2].normal, brush->planes[0].normal, brush->planes[2].normal);
487 VectorAdd(brush->planes[3].normal, brush->planes[0].normal, brush->planes[3].normal);
488 VectorAdd(brush->planes[4].normal, brush->planes[0].normal, brush->planes[4].normal);
489 VectorNormalize(brush->planes[2].normal);
490 VectorNormalize(brush->planes[3].normal);
491 VectorNormalize(brush->planes[4].normal);
493 else if (collision_triangle_axialsides.integer)
495 float projectionnormal[3], projectionedge0[3], projectionedge1[3], projectionedge2[3];
497 float dist, bestdist;
498 bestdist = fabs(brush->planes[0].normal[0]);
500 for (i = 1;i < 3;i++)
502 dist = fabs(brush->planes[0].normal[i]);
509 VectorClear(projectionnormal);
510 if (brush->planes[0].normal[best] < 0)
511 projectionnormal[best] = -1;
513 projectionnormal[best] = 1;
514 VectorCopy(edge0, projectionedge0);
515 VectorCopy(edge1, projectionedge1);
516 VectorCopy(edge2, projectionedge2);
517 projectionedge0[best] = 0;
518 projectionedge1[best] = 0;
519 projectionedge2[best] = 0;
520 CrossProduct(projectionedge0, projectionnormal, brush->planes[2].normal);
521 CrossProduct(projectionedge1, projectionnormal, brush->planes[3].normal);
522 CrossProduct(projectionedge2, projectionnormal, brush->planes[4].normal);
523 VectorNormalize(brush->planes[2].normal);
524 VectorNormalize(brush->planes[3].normal);
525 VectorNormalize(brush->planes[4].normal);
529 CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal);
530 CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal);
531 CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal);
532 VectorNormalize(brush->planes[2].normal);
533 VectorNormalize(brush->planes[3].normal);
534 VectorNormalize(brush->planes[4].normal);
536 brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal);
537 brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal);
538 brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal);
540 if (developer_extra.integer)
542 // validity check - will be disabled later
543 Collision_ValidateBrush(brush);
544 for (i = 0;i < brush->numplanes;i++)
547 for (j = 0, p = brush->points;j < brush->numpoints;j++, p++)
548 if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + COLLISION_PLANE_DIST_EPSILON)
549 Con_DPrintf("Error in brush plane generation, plane %i\n", i);
554 colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents, int q3surfaceflags, const texture_t *texture)
557 brush = (colbrushf_t *)Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colplanef_t) * (numpoints + 2) + sizeof(colpointf_t) * numpoints);
558 brush->isaabb = false;
559 brush->hasaabbplanes = false;
560 brush->supercontents = supercontents;
561 brush->numpoints = numpoints;
562 brush->numedgedirs = numpoints;
563 brush->numplanes = numpoints + 2;
564 brush->planes = (colplanef_t *)(brush + 1);
565 brush->points = (colpointf_t *)points;
566 brush->edgedirs = (colpointf_t *)(brush->planes + brush->numplanes);
567 brush->q3surfaceflags = q3surfaceflags;
568 brush->texture = texture;
569 Sys_Error("Collision_AllocBrushFromPermanentPolygonFloat: FIXME: this code needs to be updated to generate a mesh...");
573 // NOTE: start and end of each brush pair must have same numplanes/numpoints
574 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)
576 int nplane, nplane2, nedge1, nedge2, hitq3surfaceflags = 0;
577 int tracenumedgedirs = trace_start->numedgedirs;
578 //int othernumedgedirs = other_start->numedgedirs;
579 int tracenumpoints = trace_start->numpoints;
580 int othernumpoints = other_start->numpoints;
581 int numplanes1 = other_start->numplanes;
582 int numplanes2 = numplanes1 + trace_start->numplanes;
583 int numplanes3 = numplanes2 + trace_start->numedgedirs * other_start->numedgedirs * 2;
584 vec_t enterfrac = -1, leavefrac = 1, startdist, enddist, ie, f, imove, enterfrac2 = -1;
587 vec4_t newimpactplane;
588 const texture_t *hittexture = NULL;
589 vec_t startdepth = 1;
590 vec3_t startdepthnormal;
592 VectorClear(startdepthnormal);
593 Vector4Clear(newimpactplane);
595 // fast case for AABB vs compiled brushes (which begin with AABB planes and also have precomputed bevels for AABB collisions)
596 if (trace_start->isaabb && other_start->hasaabbplanes)
597 numplanes3 = numplanes2 = numplanes1;
599 // Separating Axis Theorem:
600 // if a supporting vector (plane normal) can be found that separates two
601 // objects, they are not colliding.
604 // reduce the size of one object to a point while enlarging the other to
605 // represent the space that point can not occupy.
607 // try every plane we can construct between the two brushes and measure
608 // the distance between them.
609 for (nplane = 0;nplane < numplanes3;nplane++)
611 if (nplane < numplanes1)
614 VectorCopy(other_start->planes[nplane2].normal, startplane);
615 VectorCopy(other_end->planes[nplane2].normal, endplane);
617 else if (nplane < numplanes2)
619 nplane2 = nplane - numplanes1;
620 VectorCopy(trace_start->planes[nplane2].normal, startplane);
621 VectorCopy(trace_end->planes[nplane2].normal, endplane);
625 // pick an edgedir from each brush and cross them
626 nplane2 = nplane - numplanes2;
627 nedge1 = nplane2 >> 1;
628 nedge2 = nedge1 / tracenumedgedirs;
629 nedge1 -= nedge2 * tracenumedgedirs;
632 CrossProduct(trace_start->edgedirs[nedge1].v, other_start->edgedirs[nedge2].v, startplane);
633 CrossProduct(trace_end->edgedirs[nedge1].v, other_end->edgedirs[nedge2].v, endplane);
637 CrossProduct(other_start->edgedirs[nedge2].v, trace_start->edgedirs[nedge1].v, startplane);
638 CrossProduct(other_end->edgedirs[nedge2].v, trace_end->edgedirs[nedge1].v, endplane);
640 if (VectorLength2(startplane) < COLLISION_EDGECROSS_MINLENGTH2 || VectorLength2(endplane) < COLLISION_EDGECROSS_MINLENGTH2)
641 continue; // degenerate crossproducts
642 VectorNormalize(startplane);
643 VectorNormalize(endplane);
645 startplane[3] = furthestplanedist_float(startplane, other_start->points, othernumpoints);
646 endplane[3] = furthestplanedist_float(startplane, other_end->points, othernumpoints);
647 startdist = nearestplanedist_float(startplane, trace_start->points, tracenumpoints) - startplane[3];
648 enddist = nearestplanedist_float(endplane, trace_end->points, tracenumpoints) - endplane[3];
649 //Con_Printf("%c%i: startdist = %f, enddist = %f, startdist / (startdist - enddist) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, startdist, enddist, startdist / (startdist - enddist));
651 // aside from collisions, this is also used for error correction
652 if (startdist <= collision_impactnudge.value && nplane < numplanes1 && (startdepth < startdist || startdepth == 1))
654 startdepth = startdist;
655 VectorCopy(startplane, startdepthnormal);
658 if (startdist > enddist)
666 imove = 1 / (startdist - enddist);
667 f = startdist * imove;
668 // check if this will reduce the collision time range
671 // reduced collision time range
673 // if the collision time range is now empty, no collision
674 if (enterfrac > leavefrac)
676 // calculate the nudged fraction and impact normal we'll
677 // need if we accept this collision later
678 enterfrac2 = (startdist - collision_impactnudge.value) * imove;
679 // if the collision would be further away than the trace's
680 // existing collision data, we don't care about this
682 if (enterfrac2 >= trace->fraction)
684 ie = 1.0f - enterfrac;
685 newimpactplane[0] = startplane[0] * ie + endplane[0] * enterfrac;
686 newimpactplane[1] = startplane[1] * ie + endplane[1] * enterfrac;
687 newimpactplane[2] = startplane[2] * ie + endplane[2] * enterfrac;
688 newimpactplane[3] = startplane[3] * ie + endplane[3] * enterfrac;
689 if (nplane < numplanes1)
691 // use the plane from other
693 hitq3surfaceflags = other_start->planes[nplane2].q3surfaceflags;
694 hittexture = other_start->planes[nplane2].texture;
696 else if (nplane < numplanes2)
698 // use the plane from trace
699 nplane2 = nplane - numplanes1;
700 hitq3surfaceflags = trace_start->planes[nplane2].q3surfaceflags;
701 hittexture = trace_start->planes[nplane2].texture;
705 hitq3surfaceflags = other_start->q3surfaceflags;
706 hittexture = other_start->texture;
713 // moving out of brush
719 f = startdist / (startdist - enddist);
720 // check if this will reduce the collision time range
723 // reduced collision time range
725 // if the collision time range is now empty, no collision
726 if (enterfrac > leavefrac)
733 // at this point we know the trace overlaps the brush because it was not
734 // rejected at any point in the loop above
736 // see if the trace started outside the brush or not
739 // started outside, and overlaps, therefore there is a collision here
740 // store out the impact information
741 if (trace->hitsupercontentsmask & other_start->supercontents)
743 trace->hitsupercontents = other_start->supercontents;
744 trace->hitq3surfaceflags = hitq3surfaceflags;
745 trace->hittexture = hittexture;
746 trace->fraction = bound(0, enterfrac2, 1);
747 VectorCopy(newimpactplane, trace->plane.normal);
748 trace->plane.dist = newimpactplane[3];
753 // started inside, update startsolid and friends
754 trace->startsupercontents |= other_start->supercontents;
755 if (trace->hitsupercontentsmask & other_start->supercontents)
757 trace->startsolid = true;
759 trace->allsolid = true;
760 VectorCopy(newimpactplane, trace->plane.normal);
761 trace->plane.dist = newimpactplane[3];
762 if (trace->startdepth > startdepth)
764 trace->startdepth = startdepth;
765 VectorCopy(startdepthnormal, trace->startdepthnormal);
771 // NOTE: start and end of each brush pair must have same numplanes/numpoints
772 void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *other_start, const colbrushf_t *other_end)
774 int nplane, hitq3surfaceflags = 0;
775 int numplanes = other_start->numplanes;
776 vec_t enterfrac = -1, leavefrac = 1, startdist, enddist, ie, f, imove, enterfrac2 = -1;
779 vec4_t newimpactplane;
780 const texture_t *hittexture = NULL;
781 vec_t startdepth = 1;
782 vec3_t startdepthnormal;
784 if (collision_debug_tracelineasbox.integer)
786 colboxbrushf_t thisbrush_start, thisbrush_end;
787 Collision_BrushForBox(&thisbrush_start, linestart, linestart, 0, 0, NULL);
788 Collision_BrushForBox(&thisbrush_end, lineend, lineend, 0, 0, NULL);
789 Collision_TraceBrushBrushFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, other_start, other_end);
793 VectorClear(startdepthnormal);
794 Vector4Clear(newimpactplane);
796 // Separating Axis Theorem:
797 // if a supporting vector (plane normal) can be found that separates two
798 // objects, they are not colliding.
801 // reduce the size of one object to a point while enlarging the other to
802 // represent the space that point can not occupy.
804 // try every plane we can construct between the two brushes and measure
805 // the distance between them.
806 for (nplane = 0;nplane < numplanes;nplane++)
808 VectorCopy(other_start->planes[nplane].normal, startplane);
809 startplane[3] = other_start->planes[nplane].dist;
810 VectorCopy(other_end->planes[nplane].normal, endplane);
811 endplane[3] = other_end->planes[nplane].dist;
812 startdist = DotProduct(linestart, startplane) - startplane[3];
813 enddist = DotProduct(lineend, endplane) - endplane[3];
814 //Con_Printf("%c%i: startdist = %f, enddist = %f, startdist / (startdist - enddist) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, startdist, enddist, startdist / (startdist - enddist));
816 // aside from collisions, this is also used for error correction
817 if (startdist < collision_impactnudge.value && (startdepth < startdist || startdepth == 1))
819 startdepth = startdist;
820 VectorCopy(startplane, startdepthnormal);
823 if (startdist > enddist)
831 imove = 1 / (startdist - enddist);
832 f = startdist * imove;
833 // check if this will reduce the collision time range
836 // reduced collision time range
838 // if the collision time range is now empty, no collision
839 if (enterfrac > leavefrac)
841 // calculate the nudged fraction and impact normal we'll
842 // need if we accept this collision later
843 enterfrac2 = (startdist - collision_impactnudge.value) * imove;
844 // if the collision would be further away than the trace's
845 // existing collision data, we don't care about this
847 if (enterfrac2 >= trace->fraction)
849 ie = 1.0f - enterfrac;
850 newimpactplane[0] = startplane[0] * ie + endplane[0] * enterfrac;
851 newimpactplane[1] = startplane[1] * ie + endplane[1] * enterfrac;
852 newimpactplane[2] = startplane[2] * ie + endplane[2] * enterfrac;
853 newimpactplane[3] = startplane[3] * ie + endplane[3] * enterfrac;
854 hitq3surfaceflags = other_start->planes[nplane].q3surfaceflags;
855 hittexture = other_start->planes[nplane].texture;
861 // moving out of brush
867 f = startdist / (startdist - enddist);
868 // check if this will reduce the collision time range
871 // reduced collision time range
873 // if the collision time range is now empty, no collision
874 if (enterfrac > leavefrac)
881 // at this point we know the trace overlaps the brush because it was not
882 // rejected at any point in the loop above
884 // see if the trace started outside the brush or not
887 // started outside, and overlaps, therefore there is a collision here
888 // store out the impact information
889 if (trace->hitsupercontentsmask & other_start->supercontents)
891 trace->hitsupercontents = other_start->supercontents;
892 trace->hitq3surfaceflags = hitq3surfaceflags;
893 trace->hittexture = hittexture;
894 trace->fraction = bound(0, enterfrac2, 1);
895 VectorCopy(newimpactplane, trace->plane.normal);
896 trace->plane.dist = newimpactplane[3];
901 // started inside, update startsolid and friends
902 trace->startsupercontents |= other_start->supercontents;
903 if (trace->hitsupercontentsmask & other_start->supercontents)
905 trace->startsolid = true;
907 trace->allsolid = true;
908 VectorCopy(newimpactplane, trace->plane.normal);
909 trace->plane.dist = newimpactplane[3];
910 if (trace->startdepth > startdepth)
912 trace->startdepth = startdepth;
913 VectorCopy(startdepthnormal, trace->startdepthnormal);
919 qboolean Collision_PointInsideBrushFloat(const vec3_t point, const colbrushf_t *brush)
922 const colplanef_t *plane;
924 if (!BoxesOverlap(point, point, brush->mins, brush->maxs))
926 for (nplane = 0, plane = brush->planes;nplane < brush->numplanes;nplane++, plane++)
927 if (DotProduct(plane->normal, point) > plane->dist)
932 void Collision_TracePointBrushFloat(trace_t *trace, const vec3_t point, const colbrushf_t *thatbrush)
934 if (!Collision_PointInsideBrushFloat(point, thatbrush))
937 trace->startsupercontents |= thatbrush->supercontents;
938 if (trace->hitsupercontentsmask & thatbrush->supercontents)
940 trace->startsolid = true;
941 trace->allsolid = true;
945 static void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t *out, float fractionprecision, float invfractionprecision)
948 for (i = 0;i < numpoints;i++)
950 out[i].v[0] = floor(in[i].v[0] * fractionprecision + 0.5f) * invfractionprecision;
951 out[i].v[1] = floor(in[i].v[1] * fractionprecision + 0.5f) * invfractionprecision;
952 out[i].v[2] = floor(in[i].v[2] * fractionprecision + 0.5f) * invfractionprecision;
956 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, const texture_t *texture, const vec3_t segmentmins, const vec3_t segmentmaxs)
959 colpointf_t points[3];
960 colpointf_t edgedirs[3];
961 colplanef_t planes[5];
963 memset(&brush, 0, sizeof(brush));
964 brush.isaabb = false;
965 brush.hasaabbplanes = false;
967 brush.numedgedirs = 3;
969 brush.points = points;
970 brush.edgedirs = edgedirs;
971 brush.planes = planes;
972 brush.supercontents = supercontents;
973 brush.q3surfaceflags = q3surfaceflags;
974 brush.texture = texture;
975 for (i = 0;i < brush.numplanes;i++)
977 brush.planes[i].q3surfaceflags = q3surfaceflags;
978 brush.planes[i].texture = texture;
983 cnt = (numtriangles + stride - 1) / stride;
984 for(i = 0; i < cnt; ++i)
986 if(BoxesOverlap(bbox6f + i * 6, bbox6f + i * 6 + 3, segmentmins, segmentmaxs))
988 for(k = 0; k < stride; ++k)
990 tri = i * stride + k;
991 if(tri >= numtriangles)
993 VectorCopy(vertex3f + element3i[tri * 3 + 0] * 3, points[0].v);
994 VectorCopy(vertex3f + element3i[tri * 3 + 1] * 3, points[1].v);
995 VectorCopy(vertex3f + element3i[tri * 3 + 2] * 3, points[2].v);
996 Collision_SnapCopyPoints(brush.numpoints, points, points, COLLISION_SNAPSCALE, COLLISION_SNAP);
997 Collision_CalcEdgeDirsForPolygonBrushFloat(&brush);
998 Collision_CalcPlanesForTriangleBrushFloat(&brush);
999 //Collision_PrintBrushAsQHull(&brush, "brush");
1000 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &brush, &brush);
1005 else if(stride == 0)
1007 for (i = 0;i < numtriangles;i++, element3i += 3)
1009 if (TriangleBBoxOverlapsBox(vertex3f + element3i[0]*3, vertex3f + element3i[1]*3, vertex3f + element3i[2]*3, segmentmins, segmentmaxs))
1011 VectorCopy(vertex3f + element3i[0] * 3, points[0].v);
1012 VectorCopy(vertex3f + element3i[1] * 3, points[1].v);
1013 VectorCopy(vertex3f + element3i[2] * 3, points[2].v);
1014 Collision_SnapCopyPoints(brush.numpoints, points, points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1015 Collision_CalcEdgeDirsForPolygonBrushFloat(&brush);
1016 Collision_CalcPlanesForTriangleBrushFloat(&brush);
1017 //Collision_PrintBrushAsQHull(&brush, "brush");
1018 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &brush, &brush);
1024 for (i = 0;i < numtriangles;i++, element3i += 3)
1026 VectorCopy(vertex3f + element3i[0] * 3, points[0].v);
1027 VectorCopy(vertex3f + element3i[1] * 3, points[1].v);
1028 VectorCopy(vertex3f + element3i[2] * 3, points[2].v);
1029 Collision_SnapCopyPoints(brush.numpoints, points, points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1030 Collision_CalcEdgeDirsForPolygonBrushFloat(&brush);
1031 Collision_CalcPlanesForTriangleBrushFloat(&brush);
1032 //Collision_PrintBrushAsQHull(&brush, "brush");
1033 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &brush, &brush);
1038 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, const texture_t *texture, const vec3_t segmentmins, const vec3_t segmentmaxs)
1041 // FIXME: snap vertices?
1045 cnt = (numtriangles + stride - 1) / stride;
1046 for(i = 0; i < cnt; ++i)
1048 if(BoxesOverlap(bbox6f + i * 6, bbox6f + i * 6 + 3, segmentmins, segmentmaxs))
1050 for(k = 0; k < stride; ++k)
1052 tri = i * stride + k;
1053 if(tri >= numtriangles)
1055 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);
1062 for (i = 0;i < numtriangles;i++, element3i += 3)
1063 Collision_TraceLineTriangleFloat(trace, linestart, lineend, vertex3f + element3i[0] * 3, vertex3f + element3i[1] * 3, vertex3f + element3i[2] * 3, supercontents, q3surfaceflags, texture);
1067 void Collision_TraceBrushTriangleFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, const float *v0, const float *v1, const float *v2, int supercontents, int q3surfaceflags, const texture_t *texture)
1070 colpointf_t points[3];
1071 colpointf_t edgedirs[3];
1072 colplanef_t planes[5];
1074 memset(&brush, 0, sizeof(brush));
1075 brush.isaabb = false;
1076 brush.hasaabbplanes = false;
1077 brush.numpoints = 3;
1078 brush.numedgedirs = 3;
1079 brush.numplanes = 5;
1080 brush.points = points;
1081 brush.edgedirs = edgedirs;
1082 brush.planes = planes;
1083 brush.supercontents = supercontents;
1084 brush.q3surfaceflags = q3surfaceflags;
1085 brush.texture = texture;
1086 for (i = 0;i < brush.numplanes;i++)
1088 brush.planes[i].q3surfaceflags = q3surfaceflags;
1089 brush.planes[i].texture = texture;
1091 VectorCopy(v0, points[0].v);
1092 VectorCopy(v1, points[1].v);
1093 VectorCopy(v2, points[2].v);
1094 Collision_SnapCopyPoints(brush.numpoints, points, points, COLLISION_SNAPSCALE, COLLISION_SNAP);
1095 Collision_CalcEdgeDirsForPolygonBrushFloat(&brush);
1096 Collision_CalcPlanesForTriangleBrushFloat(&brush);
1097 //Collision_PrintBrushAsQHull(&brush, "brush");
1098 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &brush, &brush);
1101 void Collision_BrushForBox(colboxbrushf_t *boxbrush, const vec3_t mins, const vec3_t maxs, int supercontents, int q3surfaceflags, const texture_t *texture)
1104 memset(boxbrush, 0, sizeof(*boxbrush));
1105 boxbrush->brush.isaabb = true;
1106 boxbrush->brush.hasaabbplanes = true;
1107 boxbrush->brush.points = boxbrush->points;
1108 boxbrush->brush.edgedirs = boxbrush->edgedirs;
1109 boxbrush->brush.planes = boxbrush->planes;
1110 boxbrush->brush.supercontents = supercontents;
1111 boxbrush->brush.q3surfaceflags = q3surfaceflags;
1112 boxbrush->brush.texture = texture;
1113 if (VectorCompare(mins, maxs))
1116 boxbrush->brush.numpoints = 1;
1117 boxbrush->brush.numedgedirs = 0;
1118 boxbrush->brush.numplanes = 0;
1119 VectorCopy(mins, boxbrush->brush.points[0].v);
1123 boxbrush->brush.numpoints = 8;
1124 boxbrush->brush.numedgedirs = 3;
1125 boxbrush->brush.numplanes = 6;
1126 // there are 8 points on a box
1127 // there are 3 edgedirs on a box (both signs are tested in collision)
1128 // there are 6 planes on a box
1129 VectorSet(boxbrush->brush.points[0].v, mins[0], mins[1], mins[2]);
1130 VectorSet(boxbrush->brush.points[1].v, maxs[0], mins[1], mins[2]);
1131 VectorSet(boxbrush->brush.points[2].v, mins[0], maxs[1], mins[2]);
1132 VectorSet(boxbrush->brush.points[3].v, maxs[0], maxs[1], mins[2]);
1133 VectorSet(boxbrush->brush.points[4].v, mins[0], mins[1], maxs[2]);
1134 VectorSet(boxbrush->brush.points[5].v, maxs[0], mins[1], maxs[2]);
1135 VectorSet(boxbrush->brush.points[6].v, mins[0], maxs[1], maxs[2]);
1136 VectorSet(boxbrush->brush.points[7].v, maxs[0], maxs[1], maxs[2]);
1137 VectorSet(boxbrush->brush.edgedirs[0].v, 1, 0, 0);
1138 VectorSet(boxbrush->brush.edgedirs[1].v, 0, 1, 0);
1139 VectorSet(boxbrush->brush.edgedirs[2].v, 0, 0, 1);
1140 VectorSet(boxbrush->brush.planes[0].normal, -1, 0, 0);boxbrush->brush.planes[0].dist = -mins[0];
1141 VectorSet(boxbrush->brush.planes[1].normal, 1, 0, 0);boxbrush->brush.planes[1].dist = maxs[0];
1142 VectorSet(boxbrush->brush.planes[2].normal, 0, -1, 0);boxbrush->brush.planes[2].dist = -mins[1];
1143 VectorSet(boxbrush->brush.planes[3].normal, 0, 1, 0);boxbrush->brush.planes[3].dist = maxs[1];
1144 VectorSet(boxbrush->brush.planes[4].normal, 0, 0, -1);boxbrush->brush.planes[4].dist = -mins[2];
1145 VectorSet(boxbrush->brush.planes[5].normal, 0, 0, 1);boxbrush->brush.planes[5].dist = maxs[2];
1146 for (i = 0;i < 6;i++)
1148 boxbrush->brush.planes[i].q3surfaceflags = q3surfaceflags;
1149 boxbrush->brush.planes[i].texture = texture;
1152 boxbrush->brush.supercontents = supercontents;
1153 boxbrush->brush.q3surfaceflags = q3surfaceflags;
1154 boxbrush->brush.texture = texture;
1155 VectorSet(boxbrush->brush.mins, mins[0] - 1, mins[1] - 1, mins[2] - 1);
1156 VectorSet(boxbrush->brush.maxs, maxs[0] + 1, maxs[1] + 1, maxs[2] + 1);
1157 //Collision_ValidateBrush(&boxbrush->brush);
1160 //pseudocode for detecting line/sphere overlap without calculating an impact point
1161 //linesphereorigin = sphereorigin - linestart;linediff = lineend - linestart;linespherefrac = DotProduct(linesphereorigin, linediff) / DotProduct(linediff, linediff);return VectorLength2(linesphereorigin - bound(0, linespherefrac, 1) * linediff) >= sphereradius*sphereradius;
1163 // LordHavoc: currently unused, but tested
1164 // note: this can be used for tracing a moving sphere vs a stationary sphere,
1165 // by simply adding the moving sphere's radius to the sphereradius parameter,
1166 // all the results are correct (impactpoint, impactnormal, and fraction)
1167 float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double *sphereorigin, double sphereradius, double *impactpoint, double *impactnormal)
1169 double dir[3], scale, v[3], deviationdist2, impactdist, linelength;
1170 // make sure the impactpoint and impactnormal are valid even if there is
1172 VectorCopy(lineend, impactpoint);
1173 VectorClear(impactnormal);
1174 // calculate line direction
1175 VectorSubtract(lineend, linestart, dir);
1176 // normalize direction
1177 linelength = VectorLength(dir);
1180 scale = 1.0 / linelength;
1181 VectorScale(dir, scale, dir);
1183 // this dotproduct calculates the distance along the line at which the
1184 // sphere origin is (nearest point to the sphere origin on the line)
1185 impactdist = DotProduct(sphereorigin, dir) - DotProduct(linestart, dir);
1186 // calculate point on line at that distance, and subtract the
1187 // sphereorigin from it, so we have a vector to measure for the distance
1188 // of the line from the sphereorigin (deviation, how off-center it is)
1189 VectorMA(linestart, impactdist, dir, v);
1190 VectorSubtract(v, sphereorigin, v);
1191 deviationdist2 = sphereradius * sphereradius - VectorLength2(v);
1192 // if squared offset length is outside the squared sphere radius, miss
1193 if (deviationdist2 < 0)
1194 return 1; // miss (off to the side)
1195 // nudge back to find the correct impact distance
1196 impactdist -= sqrt(deviationdist2);
1197 if (impactdist >= linelength)
1198 return 1; // miss (not close enough)
1200 return 1; // miss (linestart is past or inside sphere)
1201 // calculate new impactpoint
1202 VectorMA(linestart, impactdist, dir, impactpoint);
1203 // calculate impactnormal (surface normal at point of impact)
1204 VectorSubtract(impactpoint, sphereorigin, impactnormal);
1205 // normalize impactnormal
1206 VectorNormalize(impactnormal);
1207 // return fraction of movement distance
1208 return impactdist / linelength;
1211 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, const texture_t *texture)
1213 float d1, d2, d, f, f2, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, faceplanenormallength2, edge01[3], edge21[3], edge02[3];
1215 // this function executes:
1216 // 32 ops when line starts behind triangle
1217 // 38 ops when line ends infront of triangle
1218 // 43 ops when line fraction is already closer than this triangle
1219 // 72 ops when line is outside edge 01
1220 // 92 ops when line is outside edge 21
1221 // 115 ops when line is outside edge 02
1222 // 123 ops when line impacts triangle and updates trace results
1224 // this code is designed for clockwise triangles, conversion to
1225 // counterclockwise would require swapping some things around...
1226 // it is easier to simply swap the point0 and point2 parameters to this
1227 // function when calling it than it is to rewire the internals.
1229 // calculate the faceplanenormal of the triangle, this represents the front side
1231 VectorSubtract(point0, point1, edge01);
1232 VectorSubtract(point2, point1, edge21);
1233 CrossProduct(edge01, edge21, faceplanenormal);
1234 // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
1236 faceplanenormallength2 = DotProduct(faceplanenormal, faceplanenormal);
1237 if (faceplanenormallength2 < 0.0001f)
1239 // calculate the distance
1241 faceplanedist = DotProduct(point0, faceplanenormal);
1243 // if start point is on the back side there is no collision
1244 // (we don't care about traces going through the triangle the wrong way)
1246 // calculate the start distance
1248 d1 = DotProduct(faceplanenormal, linestart);
1249 if (d1 <= faceplanedist)
1252 // calculate the end distance
1254 d2 = DotProduct(faceplanenormal, lineend);
1255 // if both are in front, there is no collision
1256 if (d2 >= faceplanedist)
1259 // from here on we know d1 is >= 0 and d2 is < 0
1260 // this means the line starts infront and ends behind, passing through it
1262 // calculate the recipricol of the distance delta,
1263 // so we can use it multiple times cheaply (instead of division)
1265 d = 1.0f / (d1 - d2);
1266 // calculate the impact fraction by taking the start distance (> 0)
1267 // and subtracting the face plane distance (this is the distance of the
1268 // triangle along that same normal)
1269 // then multiply by the recipricol distance delta
1271 f = (d1 - faceplanedist) * d;
1272 f2 = f - collision_impactnudge.value * d;
1273 // skip out if this impact is further away than previous ones
1275 if (f2 >= trace->fraction)
1277 // calculate the perfect impact point for classification of insidedness
1279 impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
1280 impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
1281 impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
1283 // calculate the edge normal and reject if impact is outside triangle
1284 // (an edge normal faces away from the triangle, to get the desired normal
1285 // a crossproduct with the faceplanenormal is used, and because of the way
1286 // the insidedness comparison is written it does not need to be normalized)
1288 // first use the two edges from the triangle plane math
1289 // the other edge only gets calculated if the point survives that long
1292 CrossProduct(edge01, faceplanenormal, edgenormal);
1293 if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
1297 CrossProduct(faceplanenormal, edge21, edgenormal);
1298 if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
1302 VectorSubtract(point0, point2, edge02);
1303 CrossProduct(faceplanenormal, edge02, edgenormal);
1304 if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
1309 // store the new trace fraction
1310 trace->fraction = f2;
1312 // store the new trace plane (because collisions only happen from
1313 // the front this is always simply the triangle normal, never flipped)
1314 d = 1.0 / sqrt(faceplanenormallength2);
1315 VectorScale(faceplanenormal, d, trace->plane.normal);
1316 trace->plane.dist = faceplanedist * d;
1318 trace->hitsupercontents = supercontents;
1319 trace->hitq3surfaceflags = q3surfaceflags;
1320 trace->hittexture = texture;
1323 void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac)
1326 colpointf_t *ps, *pe;
1327 float tempstart[3], tempend[3];
1328 VectorLerp(start->points[0].v, startfrac, end->points[0].v, mins);
1329 VectorCopy(mins, maxs);
1330 for (i = 0, ps = start->points, pe = end->points;i < start->numpoints;i++, ps++, pe++)
1332 VectorLerp(ps->v, startfrac, pe->v, tempstart);
1333 VectorLerp(ps->v, endfrac, pe->v, tempend);
1334 mins[0] = min(mins[0], min(tempstart[0], tempend[0]));
1335 mins[1] = min(mins[1], min(tempstart[1], tempend[1]));
1336 mins[2] = min(mins[2], min(tempstart[2], tempend[2]));
1337 maxs[0] = min(maxs[0], min(tempstart[0], tempend[0]));
1338 maxs[1] = min(maxs[1], min(tempstart[1], tempend[1]));
1339 maxs[2] = min(maxs[2], min(tempstart[2], tempend[2]));
1349 //===========================================
1351 static void Collision_TranslateBrush(const vec3_t shift, colbrushf_t *brush)
1354 // now we can transform the data
1355 for(i = 0; i < brush->numplanes; ++i)
1357 brush->planes[i].dist += DotProduct(shift, brush->planes[i].normal);
1359 for(i = 0; i < brush->numpoints; ++i)
1361 VectorAdd(brush->points[i].v, shift, brush->points[i].v);
1363 VectorAdd(brush->mins, shift, brush->mins);
1364 VectorAdd(brush->maxs, shift, brush->maxs);
1367 static void Collision_TransformBrush(const matrix4x4_t *matrix, colbrushf_t *brush)
1371 // we're breaking any AABB properties here...
1372 brush->isaabb = false;
1373 brush->hasaabbplanes = false;
1374 // now we can transform the data
1375 for(i = 0; i < brush->numplanes; ++i)
1377 Matrix4x4_TransformPositivePlane(matrix, brush->planes[i].normal[0], brush->planes[i].normal[1], brush->planes[i].normal[2], brush->planes[i].dist, brush->planes[i].normal);
1379 for(i = 0; i < brush->numedgedirs; ++i)
1381 Matrix4x4_Transform(matrix, brush->edgedirs[i].v, v);
1382 VectorCopy(v, brush->edgedirs[i].v);
1384 for(i = 0; i < brush->numpoints; ++i)
1386 Matrix4x4_Transform(matrix, brush->points[i].v, v);
1387 VectorCopy(v, brush->points[i].v);
1389 VectorCopy(brush->points[0].v, brush->mins);
1390 VectorCopy(brush->points[0].v, brush->maxs);
1391 for(i = 1; i < brush->numpoints; ++i)
1393 if(brush->points[i].v[0] < brush->mins[0]) brush->mins[0] = brush->points[i].v[0];
1394 if(brush->points[i].v[1] < brush->mins[1]) brush->mins[1] = brush->points[i].v[1];
1395 if(brush->points[i].v[2] < brush->mins[2]) brush->mins[2] = brush->points[i].v[2];
1396 if(brush->points[i].v[0] > brush->maxs[0]) brush->maxs[0] = brush->points[i].v[0];
1397 if(brush->points[i].v[1] > brush->maxs[1]) brush->maxs[1] = brush->points[i].v[1];
1398 if(brush->points[i].v[2] > brush->maxs[2]) brush->maxs[2] = brush->points[i].v[2];
1402 typedef struct collision_cachedtrace_parameters_s
1407 int hitsupercontentsmask;
1410 collision_cachedtrace_parameters_t;
1412 typedef struct collision_cachedtrace_s
1415 collision_cachedtrace_parameters_t p;
1418 collision_cachedtrace_t;
1420 static mempool_t *collision_cachedtrace_mempool;
1421 static collision_cachedtrace_t *collision_cachedtrace_array;
1422 static int collision_cachedtrace_firstfree;
1423 static int collision_cachedtrace_lastused;
1424 static int collision_cachedtrace_max;
1425 static int collision_cachedtrace_sequence;
1426 static int collision_cachedtrace_hashsize;
1427 static int *collision_cachedtrace_hash;
1428 static unsigned int *collision_cachedtrace_arrayfullhashindex;
1429 static unsigned int *collision_cachedtrace_arrayhashindex;
1430 static unsigned int *collision_cachedtrace_arraynext;
1431 static unsigned char *collision_cachedtrace_arrayused;
1432 static qboolean collision_cachedtrace_rebuildhash;
1434 void Collision_Cache_Reset(qboolean resetlimits)
1436 if (collision_cachedtrace_hash)
1437 Mem_Free(collision_cachedtrace_hash);
1438 if (collision_cachedtrace_array)
1439 Mem_Free(collision_cachedtrace_array);
1440 if (collision_cachedtrace_arrayfullhashindex)
1441 Mem_Free(collision_cachedtrace_arrayfullhashindex);
1442 if (collision_cachedtrace_arrayhashindex)
1443 Mem_Free(collision_cachedtrace_arrayhashindex);
1444 if (collision_cachedtrace_arraynext)
1445 Mem_Free(collision_cachedtrace_arraynext);
1446 if (collision_cachedtrace_arrayused)
1447 Mem_Free(collision_cachedtrace_arrayused);
1448 if (resetlimits || !collision_cachedtrace_max)
1449 collision_cachedtrace_max = collision_cache.integer ? 128 : 1;
1450 collision_cachedtrace_firstfree = 1;
1451 collision_cachedtrace_lastused = 0;
1452 collision_cachedtrace_hashsize = collision_cachedtrace_max;
1453 collision_cachedtrace_array = (collision_cachedtrace_t *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(collision_cachedtrace_t));
1454 collision_cachedtrace_hash = (int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_hashsize * sizeof(int));
1455 collision_cachedtrace_arrayfullhashindex = (unsigned int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned int));
1456 collision_cachedtrace_arrayhashindex = (unsigned int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned int));
1457 collision_cachedtrace_arraynext = (unsigned int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned int));
1458 collision_cachedtrace_arrayused = (unsigned char *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned char));
1459 collision_cachedtrace_sequence = 1;
1460 collision_cachedtrace_rebuildhash = false;
1463 void Collision_Cache_Init(mempool_t *mempool)
1465 collision_cachedtrace_mempool = mempool;
1466 Collision_Cache_Reset(true);
1469 static void Collision_Cache_RebuildHash(void)
1472 int range = collision_cachedtrace_lastused + 1;
1473 int sequence = collision_cachedtrace_sequence;
1474 int firstfree = collision_cachedtrace_max;
1476 int *hash = collision_cachedtrace_hash;
1477 unsigned int hashindex;
1478 unsigned int *arrayhashindex = collision_cachedtrace_arrayhashindex;
1479 unsigned int *arraynext = collision_cachedtrace_arraynext;
1480 collision_cachedtrace_rebuildhash = false;
1481 memset(collision_cachedtrace_hash, 0, collision_cachedtrace_hashsize * sizeof(int));
1482 for (index = 1;index < range;index++)
1484 if (collision_cachedtrace_arrayused[index] == sequence)
1486 hashindex = arrayhashindex[index];
1487 arraynext[index] = hash[hashindex];
1488 hash[hashindex] = index;
1493 if (firstfree > index)
1495 collision_cachedtrace_arrayused[index] = 0;
1498 collision_cachedtrace_firstfree = firstfree;
1499 collision_cachedtrace_lastused = lastused;
1502 void Collision_Cache_NewFrame(void)
1504 if (collision_cache.integer)
1506 if (collision_cachedtrace_max < 128)
1507 Collision_Cache_Reset(true);
1511 if (collision_cachedtrace_max > 1)
1512 Collision_Cache_Reset(true);
1514 // rebuild hash if sequence would overflow byte, otherwise increment
1515 if (collision_cachedtrace_sequence == 255)
1517 Collision_Cache_RebuildHash();
1518 collision_cachedtrace_sequence = 1;
1522 collision_cachedtrace_rebuildhash = true;
1523 collision_cachedtrace_sequence++;
1527 static unsigned int Collision_Cache_HashIndexForArray(unsigned int *array, unsigned int size)
1530 unsigned int hashindex = 0;
1531 // this is a super-cheesy checksum, designed only for speed
1532 for (i = 0;i < size;i++)
1533 hashindex += array[i] * (1 + i);
1537 static collision_cachedtrace_t *Collision_Cache_Lookup(dp_model_t *model, const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
1540 unsigned int fullhashindex;
1543 int sequence = collision_cachedtrace_sequence;
1544 int *hash = collision_cachedtrace_hash;
1545 unsigned int *arrayfullhashindex = collision_cachedtrace_arrayfullhashindex;
1546 unsigned int *arraynext = collision_cachedtrace_arraynext;
1547 collision_cachedtrace_t *cached = collision_cachedtrace_array + index;
1548 collision_cachedtrace_parameters_t params;
1549 // all non-cached traces use the same index
1550 if (!collision_cache.integer)
1551 r_refdef.stats[r_stat_photoncache_traced]++;
1554 // cached trace lookup
1555 memset(¶ms, 0, sizeof(params));
1556 params.model = model;
1557 VectorCopy(start, params.start);
1558 VectorCopy(end, params.end);
1559 params.hitsupercontentsmask = hitsupercontentsmask;
1560 params.matrix = *matrix;
1561 fullhashindex = Collision_Cache_HashIndexForArray((unsigned int *)¶ms, sizeof(params) / sizeof(unsigned int));
1562 hashindex = (int)(fullhashindex % (unsigned int)collision_cachedtrace_hashsize);
1563 for (index = hash[hashindex];index;index = arraynext[index])
1565 if (arrayfullhashindex[index] != fullhashindex)
1567 cached = collision_cachedtrace_array + index;
1568 //if (memcmp(&cached->p, ¶ms, sizeof(params)))
1569 if (cached->p.model != params.model
1570 || cached->p.end[0] != params.end[0]
1571 || cached->p.end[1] != params.end[1]
1572 || cached->p.end[2] != params.end[2]
1573 || cached->p.start[0] != params.start[0]
1574 || cached->p.start[1] != params.start[1]
1575 || cached->p.start[2] != params.start[2]
1576 || cached->p.hitsupercontentsmask != params.hitsupercontentsmask
1577 || cached->p.matrix.m[0][0] != params.matrix.m[0][0]
1578 || cached->p.matrix.m[0][1] != params.matrix.m[0][1]
1579 || cached->p.matrix.m[0][2] != params.matrix.m[0][2]
1580 || cached->p.matrix.m[0][3] != params.matrix.m[0][3]
1581 || cached->p.matrix.m[1][0] != params.matrix.m[1][0]
1582 || cached->p.matrix.m[1][1] != params.matrix.m[1][1]
1583 || cached->p.matrix.m[1][2] != params.matrix.m[1][2]
1584 || cached->p.matrix.m[1][3] != params.matrix.m[1][3]
1585 || cached->p.matrix.m[2][0] != params.matrix.m[2][0]
1586 || cached->p.matrix.m[2][1] != params.matrix.m[2][1]
1587 || cached->p.matrix.m[2][2] != params.matrix.m[2][2]
1588 || cached->p.matrix.m[2][3] != params.matrix.m[2][3]
1589 || cached->p.matrix.m[3][0] != params.matrix.m[3][0]
1590 || cached->p.matrix.m[3][1] != params.matrix.m[3][1]
1591 || cached->p.matrix.m[3][2] != params.matrix.m[3][2]
1592 || cached->p.matrix.m[3][3] != params.matrix.m[3][3]
1595 // found a matching trace in the cache
1596 r_refdef.stats[r_stat_photoncache_cached]++;
1597 cached->valid = true;
1598 collision_cachedtrace_arrayused[index] = collision_cachedtrace_sequence;
1601 r_refdef.stats[r_stat_photoncache_traced]++;
1602 // find an unused cache entry
1603 for (index = collision_cachedtrace_firstfree, range = collision_cachedtrace_max;index < range;index++)
1604 if (collision_cachedtrace_arrayused[index] == 0)
1608 // all claimed, but probably some are stale...
1609 for (index = 1, range = collision_cachedtrace_max;index < range;index++)
1610 if (collision_cachedtrace_arrayused[index] != sequence)
1614 // found a stale one, rebuild the hash
1615 Collision_Cache_RebuildHash();
1619 // we need to grow the cache
1620 collision_cachedtrace_max *= 2;
1621 Collision_Cache_Reset(false);
1625 // link the new cache entry into the hash bucket
1626 collision_cachedtrace_firstfree = index + 1;
1627 if (collision_cachedtrace_lastused < index)
1628 collision_cachedtrace_lastused = index;
1629 cached = collision_cachedtrace_array + index;
1630 collision_cachedtrace_arraynext[index] = collision_cachedtrace_hash[hashindex];
1631 collision_cachedtrace_hash[hashindex] = index;
1632 collision_cachedtrace_arrayhashindex[index] = hashindex;
1633 cached->valid = false;
1635 collision_cachedtrace_arrayfullhashindex[index] = fullhashindex;
1636 collision_cachedtrace_arrayused[index] = collision_cachedtrace_sequence;
1641 void Collision_Cache_ClipLineToGenericEntitySurfaces(trace_t *trace, dp_model_t *model, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
1643 collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, matrix, inversematrix, start, end, hitsupercontentsmask);
1646 *trace = cached->result;
1650 Collision_ClipLineToGenericEntity(trace, model, NULL, NULL, vec3_origin, vec3_origin, 0, matrix, inversematrix, start, end, hitsupercontentsmask, collision_extendmovelength.value, true);
1652 cached->result = *trace;
1655 void Collision_Cache_ClipLineToWorldSurfaces(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents)
1657 collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, &identitymatrix, &identitymatrix, start, end, hitsupercontents);
1660 *trace = cached->result;
1664 Collision_ClipLineToWorld(trace, model, start, end, hitsupercontents, collision_extendmovelength.value, true);
1666 cached->result = *trace;
1669 typedef struct extendtraceinfo_s
1675 float extendstart[3];
1677 float extenddelta[3];
1680 float scaletoextend;
1685 static void Collision_ClipExtendPrepare(extendtraceinfo_t *extendtraceinfo, trace_t *trace, const vec3_t tstart, const vec3_t tend, float textend)
1687 memset(trace, 0, sizeof(*trace));
1688 trace->fraction = 1;
1690 extendtraceinfo->trace = trace;
1691 VectorCopy(tstart, extendtraceinfo->realstart);
1692 VectorCopy(tend, extendtraceinfo->realend);
1693 VectorSubtract(extendtraceinfo->realend, extendtraceinfo->realstart, extendtraceinfo->realdelta);
1694 VectorCopy(extendtraceinfo->realstart, extendtraceinfo->extendstart);
1695 VectorCopy(extendtraceinfo->realend, extendtraceinfo->extendend);
1696 VectorCopy(extendtraceinfo->realdelta, extendtraceinfo->extenddelta);
1697 extendtraceinfo->reallength = VectorLength(extendtraceinfo->realdelta);
1698 extendtraceinfo->extendlength = extendtraceinfo->reallength;
1699 extendtraceinfo->scaletoextend = 1.0f;
1700 extendtraceinfo->extend = textend;
1702 // make the trace longer according to the extend parameter
1703 if (extendtraceinfo->reallength && extendtraceinfo->extend)
1705 extendtraceinfo->extendlength = extendtraceinfo->reallength + extendtraceinfo->extend;
1706 extendtraceinfo->scaletoextend = extendtraceinfo->extendlength / extendtraceinfo->reallength;
1707 VectorMA(extendtraceinfo->realstart, extendtraceinfo->scaletoextend, extendtraceinfo->realdelta, extendtraceinfo->extendend);
1708 VectorSubtract(extendtraceinfo->extendend, extendtraceinfo->extendstart, extendtraceinfo->extenddelta);
1712 static void Collision_ClipExtendFinish(extendtraceinfo_t *extendtraceinfo)
1714 trace_t *trace = extendtraceinfo->trace;
1716 if (trace->fraction != 1.0f)
1718 // undo the extended trace length
1719 trace->fraction *= extendtraceinfo->scaletoextend;
1721 // if the extended trace hit something that the unextended trace did not hit (even considering the collision_impactnudge), then we have to clear the hit information
1722 if (trace->fraction > 1.0f)
1724 // note that ent may refer to either startsolid or fraction<1, we can't restore the startsolid ent unfortunately
1726 trace->hitq3surfaceflags = 0;
1727 trace->hitsupercontents = 0;
1728 trace->hittexture = NULL;
1729 VectorClear(trace->plane.normal);
1730 trace->plane.dist = 0.0f;
1735 trace->fraction = bound(0, trace->fraction, 1);
1737 // calculate the end position
1738 VectorMA(extendtraceinfo->realstart, trace->fraction, extendtraceinfo->realdelta, trace->endpos);
1741 void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t tstart, const vec3_t mins, const vec3_t maxs, const vec3_t tend, int hitsupercontentsmask, float extend)
1743 vec3_t starttransformed, endtransformed;
1744 extendtraceinfo_t extendtraceinfo;
1745 Collision_ClipExtendPrepare(&extendtraceinfo, trace, tstart, tend, extend);
1747 Matrix4x4_Transform(inversematrix, extendtraceinfo.extendstart, starttransformed);
1748 Matrix4x4_Transform(inversematrix, extendtraceinfo.extendend, endtransformed);
1749 #if COLLISIONPARANOID >= 3
1750 Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", extendtraceinfo.extendstart[0], extendtraceinfo.extendstart[1], extendtraceinfo.extendstart[2], starttransformed[0], starttransformed[1], starttransformed[2], extendtraceinfo.extendend[0], extendtraceinfo.extendend[1], extendtraceinfo.extendend[2], endtransformed[0], endtransformed[1], endtransformed[2]);
1753 if (model && model->TraceBox)
1755 if(model->TraceBrush && (inversematrix->m[0][1] || inversematrix->m[0][2] || inversematrix->m[1][0] || inversematrix->m[1][2] || inversematrix->m[2][0] || inversematrix->m[2][1]))
1757 // we get here if TraceBrush exists, AND we have a rotation component (SOLID_BSP case)
1758 // using starttransformed, endtransformed is WRONG in this case!
1759 // should rather build a brush and trace using it
1760 colboxbrushf_t thisbrush_start, thisbrush_end;
1761 Collision_BrushForBox(&thisbrush_start, mins, maxs, 0, 0, NULL);
1762 Collision_BrushForBox(&thisbrush_end, mins, maxs, 0, 0, NULL);
1763 Collision_TranslateBrush(extendtraceinfo.extendstart, &thisbrush_start.brush);
1764 Collision_TranslateBrush(extendtraceinfo.extendend, &thisbrush_end.brush);
1765 Collision_TransformBrush(inversematrix, &thisbrush_start.brush);
1766 Collision_TransformBrush(inversematrix, &thisbrush_end.brush);
1767 //Collision_TranslateBrush(starttransformed, &thisbrush_start.brush);
1768 //Collision_TranslateBrush(endtransformed, &thisbrush_end.brush);
1769 model->TraceBrush(model, frameblend, skeleton, trace, &thisbrush_start.brush, &thisbrush_end.brush, hitsupercontentsmask);
1771 else // this is only approximate if rotated, quite useless
1772 model->TraceBox(model, frameblend, skeleton, trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask);
1774 else // and this requires that the transformation matrix doesn't have angles components, like SV_TraceBox ensures; FIXME may get called if a model is SOLID_BSP but has no TraceBox function
1775 Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
1777 Collision_ClipExtendFinish(&extendtraceinfo);
1780 // NOTE: this relies on plane.dist being directly after plane.normal
1781 Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
1784 void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t tstart, const vec3_t mins, const vec3_t maxs, const vec3_t tend, int hitsupercontents, float extend)
1786 extendtraceinfo_t extendtraceinfo;
1787 Collision_ClipExtendPrepare(&extendtraceinfo, trace, tstart, tend, extend);
1788 // ->TraceBox: TraceBrush not needed here, as worldmodel is never rotated
1789 if (model && model->TraceBox)
1790 model->TraceBox(model, NULL, NULL, trace, extendtraceinfo.extendstart, mins, maxs, extendtraceinfo.extendend, hitsupercontents);
1791 Collision_ClipExtendFinish(&extendtraceinfo);
1794 void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t tstart, const vec3_t tend, int hitsupercontentsmask, float extend, qboolean hitsurfaces)
1796 vec3_t starttransformed, endtransformed;
1797 extendtraceinfo_t extendtraceinfo;
1798 Collision_ClipExtendPrepare(&extendtraceinfo, trace, tstart, tend, extend);
1800 Matrix4x4_Transform(inversematrix, extendtraceinfo.extendstart, starttransformed);
1801 Matrix4x4_Transform(inversematrix, extendtraceinfo.extendend, endtransformed);
1802 #if COLLISIONPARANOID >= 3
1803 Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", extendtraceinfo.extendstart[0], extendtraceinfo.extendstart[1], extendtraceinfo.extendstart[2], starttransformed[0], starttransformed[1], starttransformed[2], extendtraceinfo.extendend[0], extendtraceinfo.extendend[1], extendtraceinfo.extendend[2], endtransformed[0], endtransformed[1], endtransformed[2]);
1806 if (model && model->TraceLineAgainstSurfaces && hitsurfaces)
1807 model->TraceLineAgainstSurfaces(model, frameblend, skeleton, trace, starttransformed, endtransformed, hitsupercontentsmask);
1808 else if (model && model->TraceLine)
1809 model->TraceLine(model, frameblend, skeleton, trace, starttransformed, endtransformed, hitsupercontentsmask);
1811 Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
1813 Collision_ClipExtendFinish(&extendtraceinfo);
1816 // NOTE: this relies on plane.dist being directly after plane.normal
1817 Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
1820 void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t tstart, const vec3_t tend, int hitsupercontents, float extend, qboolean hitsurfaces)
1822 extendtraceinfo_t extendtraceinfo;
1823 Collision_ClipExtendPrepare(&extendtraceinfo, trace, tstart, tend, extend);
1825 if (model && model->TraceLineAgainstSurfaces && hitsurfaces)
1826 model->TraceLineAgainstSurfaces(model, NULL, NULL, trace, extendtraceinfo.extendstart, extendtraceinfo.extendend, hitsupercontents);
1827 else if (model && model->TraceLine)
1828 model->TraceLine(model, NULL, NULL, trace, extendtraceinfo.extendstart, extendtraceinfo.extendend, hitsupercontents);
1830 Collision_ClipExtendFinish(&extendtraceinfo);
1833 void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask)
1835 float starttransformed[3];
1836 memset(trace, 0, sizeof(*trace));
1837 trace->fraction = 1;
1839 Matrix4x4_Transform(inversematrix, start, starttransformed);
1840 #if COLLISIONPARANOID >= 3
1841 Con_Printf("trans(%f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2]);
1844 if (model && model->TracePoint)
1845 model->TracePoint(model, NULL, NULL, trace, starttransformed, hitsupercontentsmask);
1847 Collision_ClipTrace_Point(trace, bodymins, bodymaxs, starttransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
1849 VectorCopy(start, trace->endpos);
1851 // NOTE: this relies on plane.dist being directly after plane.normal
1852 Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
1855 void Collision_ClipPointToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, int hitsupercontents)
1857 memset(trace, 0, sizeof(*trace));
1858 trace->fraction = 1;
1859 if (model && model->TracePoint)
1860 model->TracePoint(model, NULL, NULL, trace, start, hitsupercontents);
1861 VectorCopy(start, trace->endpos);
1864 void Collision_CombineTraces(trace_t *cliptrace, const trace_t *trace, void *touch, qboolean isbmodel)
1866 // take the 'best' answers from the new trace and combine with existing data
1867 if (trace->allsolid)
1868 cliptrace->allsolid = true;
1869 if (trace->startsolid)
1872 cliptrace->bmodelstartsolid = true;
1873 cliptrace->startsolid = true;
1874 if (cliptrace->fraction == 1)
1875 cliptrace->ent = touch;
1876 if (cliptrace->startdepth > trace->startdepth)
1878 cliptrace->startdepth = trace->startdepth;
1879 VectorCopy(trace->startdepthnormal, cliptrace->startdepthnormal);
1882 // don't set this except on the world, because it can easily confuse
1883 // monsters underwater if there's a bmodel involved in the trace
1884 // (inopen && inwater is how they check water visibility)
1885 //if (trace->inopen)
1886 // cliptrace->inopen = true;
1888 cliptrace->inwater = true;
1889 if ((trace->fraction < cliptrace->fraction) && (VectorLength2(trace->plane.normal) > 0))
1891 cliptrace->fraction = trace->fraction;
1892 VectorCopy(trace->endpos, cliptrace->endpos);
1893 cliptrace->plane = trace->plane;
1894 cliptrace->ent = touch;
1895 cliptrace->hitsupercontents = trace->hitsupercontents;
1896 cliptrace->hitq3surfaceflags = trace->hitq3surfaceflags;
1897 cliptrace->hittexture = trace->hittexture;
1899 cliptrace->startsupercontents |= trace->startsupercontents;