+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)
+{
+#if 1
+ // more optimized
+ float d1, d2, d, f, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, faceplanenormallength2, edge01[3], edge21[3], edge02[3];
+
+ // this function executes:
+ // 32 ops when line starts behind triangle
+ // 38 ops when line ends infront of triangle
+ // 43 ops when line fraction is already closer than this triangle
+ // 72 ops when line is outside edge 01
+ // 92 ops when line is outside edge 21
+ // 115 ops when line is outside edge 02
+ // 123 ops when line impacts triangle and updates trace results
+
+ // this code is designed for clockwise triangles, conversion to
+ // counterclockwise would require swapping some things around...
+ // it is easier to simply swap the point0 and point2 parameters to this
+ // function when calling it than it is to rewire the internals.
+
+ // calculate the faceplanenormal of the triangle, this represents the front side
+ // 15 ops
+ VectorSubtract(point0, point1, edge01);
+ VectorSubtract(point2, point1, edge21);
+ CrossProduct(edge01, edge21, faceplanenormal);
+ // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out)
+ // 6 ops
+ faceplanenormallength2 = DotProduct(faceplanenormal, faceplanenormal);
+ if (faceplanenormallength2 < 0.0001f)
+ return;
+ // calculate the distance
+ // 5 ops
+ faceplanedist = DotProduct(point0, faceplanenormal);
+
+ // if start point is on the back side there is no collision
+ // (we don't care about traces going through the triangle the wrong way)
+
+ // calculate the start distance
+ // 6 ops
+ d1 = DotProduct(faceplanenormal, linestart);
+ if (d1 <= faceplanedist)
+ return;
+
+ // calculate the end distance
+ // 6 ops
+ d2 = DotProduct(faceplanenormal, lineend);
+ // if both are in front, there is no collision
+ if (d2 >= faceplanedist)
+ return;
+
+ // from here on we know d1 is >= 0 and d2 is < 0
+ // this means the line starts infront and ends behind, passing through it
+
+ // calculate the recipricol of the distance delta,
+ // so we can use it multiple times cheaply (instead of division)
+ // 2 ops
+ d = 1.0f / (d1 - d2);
+ // calculate the impact fraction by taking the start distance (> 0)
+ // and subtracting the face plane distance (this is the distance of the
+ // triangle along that same normal)
+ // then multiply by the recipricol distance delta
+ // 2 ops
+ f = (d1 - faceplanedist) * d;
+ // skip out if this impact is further away than previous ones
+ // 1 ops
+ if (f > trace->realfraction)
+ return;
+ // calculate the perfect impact point for classification of insidedness
+ // 9 ops
+ impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
+ impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
+ impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
+
+ // calculate the edge normal and reject if impact is outside triangle
+ // (an edge normal faces away from the triangle, to get the desired normal
+ // a crossproduct with the faceplanenormal is used, and because of the way
+ // the insidedness comparison is written it does not need to be normalized)
+
+ // first use the two edges from the triangle plane math
+ // the other edge only gets calculated if the point survives that long
+
+ // 20 ops
+ CrossProduct(edge01, faceplanenormal, edgenormal);
+ if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
+ return;
+
+ // 20 ops
+ CrossProduct(faceplanenormal, edge21, edgenormal);
+ if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
+ return;
+
+ // 23 ops
+ VectorSubtract(point0, point2, edge02);
+ CrossProduct(faceplanenormal, edge02, edgenormal);
+ if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
+ return;
+
+ // 8 ops (rare)
+
+ // store the new trace fraction
+ trace->realfraction = f;
+
+ // calculate a nudged fraction to keep it out of the surface
+ // (the main fraction remains perfect)
+ trace->fraction = f - collision_impactnudge.value * d;
+
+ if (collision_prefernudgedfraction.integer)
+ trace->realfraction = trace->fraction;
+
+ // store the new trace plane (because collisions only happen from
+ // the front this is always simply the triangle normal, never flipped)
+ d = 1.0 / sqrt(faceplanenormallength2);
+ VectorScale(faceplanenormal, d, trace->plane.normal);
+ trace->plane.dist = faceplanedist * d;
+
+ trace->hitsupercontents = supercontents;
+ trace->hitq3surfaceflags = q3surfaceflags;
+ trace->hittexture = texture;
+#else
+ float d1, d2, d, f, fnudged, impact[3], edgenormal[3], faceplanenormal[3], faceplanedist, edge[3];
+
+ // this code is designed for clockwise triangles, conversion to
+ // counterclockwise would require swapping some things around...
+ // it is easier to simply swap the point0 and point2 parameters to this
+ // function when calling it than it is to rewire the internals.
+
+ // calculate the unnormalized faceplanenormal of the triangle,
+ // this represents the front side
+ TriangleNormal(point0, point1, point2, faceplanenormal);
+ // there's no point in processing a degenerate triangle
+ // (GIGO - Garbage In, Garbage Out)
+ if (DotProduct(faceplanenormal, faceplanenormal) < 0.0001f)
+ return;
+ // calculate the unnormalized distance
+ faceplanedist = DotProduct(point0, faceplanenormal);
+
+ // calculate the unnormalized start distance
+ d1 = DotProduct(faceplanenormal, linestart) - faceplanedist;
+ // if start point is on the back side there is no collision
+ // (we don't care about traces going through the triangle the wrong way)
+ if (d1 <= 0)
+ return;
+
+ // calculate the unnormalized end distance
+ d2 = DotProduct(faceplanenormal, lineend) - faceplanedist;
+ // if both are in front, there is no collision
+ if (d2 >= 0)
+ return;
+
+ // from here on we know d1 is >= 0 and d2 is < 0
+ // this means the line starts infront and ends behind, passing through it
+
+ // calculate the recipricol of the distance delta,
+ // so we can use it multiple times cheaply (instead of division)
+ d = 1.0f / (d1 - d2);
+ // calculate the impact fraction by taking the start distance (> 0)
+ // and subtracting the face plane distance (this is the distance of the
+ // triangle along that same normal)
+ // then multiply by the recipricol distance delta
+ f = d1 * d;
+ // skip out if this impact is further away than previous ones
+ if (f > trace->realfraction)
+ return;
+ // calculate the perfect impact point for classification of insidedness
+ impact[0] = linestart[0] + f * (lineend[0] - linestart[0]);
+ impact[1] = linestart[1] + f * (lineend[1] - linestart[1]);
+ impact[2] = linestart[2] + f * (lineend[2] - linestart[2]);
+
+ // calculate the edge normal and reject if impact is outside triangle
+ // (an edge normal faces away from the triangle, to get the desired normal
+ // a crossproduct with the faceplanenormal is used, and because of the way
+ // the insidedness comparison is written it does not need to be normalized)
+
+ VectorSubtract(point2, point0, edge);
+ CrossProduct(edge, faceplanenormal, edgenormal);
+ if (DotProduct(impact, edgenormal) > DotProduct(point0, edgenormal))
+ return;
+
+ VectorSubtract(point0, point1, edge);
+ CrossProduct(edge, faceplanenormal, edgenormal);
+ if (DotProduct(impact, edgenormal) > DotProduct(point1, edgenormal))
+ return;
+
+ VectorSubtract(point1, point2, edge);
+ CrossProduct(edge, faceplanenormal, edgenormal);
+ if (DotProduct(impact, edgenormal) > DotProduct(point2, edgenormal))
+ return;
+
+ // store the new trace fraction
+ trace->realfraction = bound(0, f, 1);
+
+ // store the new trace plane (because collisions only happen from
+ // the front this is always simply the triangle normal, never flipped)
+ VectorNormalize(faceplanenormal);
+ VectorCopy(faceplanenormal, trace->plane.normal);
+ trace->plane.dist = DotProduct(point0, faceplanenormal);
+
+ // calculate the normalized start and end distances
+ d1 = DotProduct(trace->plane.normal, linestart) - trace->plane.dist;
+ d2 = DotProduct(trace->plane.normal, lineend) - trace->plane.dist;
+
+ // calculate a nudged fraction to keep it out of the surface
+ // (the main fraction remains perfect)
+ fnudged = (d1 - collision_impactnudge.value) / (d1 - d2);
+ trace->fraction = bound(0, fnudged, 1);
+
+ // store the new trace endpos
+ // not needed, it's calculated later when the trace is finished
+ //trace->endpos[0] = linestart[0] + fnudged * (lineend[0] - linestart[0]);
+ //trace->endpos[1] = linestart[1] + fnudged * (lineend[1] - linestart[1]);
+ //trace->endpos[2] = linestart[2] + fnudged * (lineend[2] - linestart[2]);
+ trace->hitsupercontents = supercontents;
+ trace->hitq3surfaceflags = q3surfaceflags;
+ trace->hittexture = texture;
+#endif
+}
+
+typedef struct colbspnode_s
+{
+ mplane_t plane;
+ struct colbspnode_s *children[2];
+ // the node is reallocated or split if max is reached
+ int numcolbrushf;
+ int maxcolbrushf;
+ colbrushf_t **colbrushflist;
+ //int numcolbrushd;
+ //int maxcolbrushd;
+ //colbrushd_t **colbrushdlist;
+}
+colbspnode_t;
+
+typedef struct colbsp_s
+{
+ mempool_t *mempool;
+ colbspnode_t *nodes;
+}
+colbsp_t;
+
+colbsp_t *Collision_CreateCollisionBSP(mempool_t *mempool)
+{
+ colbsp_t *bsp;
+ bsp = (colbsp_t *)Mem_Alloc(mempool, sizeof(colbsp_t));
+ bsp->mempool = mempool;
+ bsp->nodes = (colbspnode_t *)Mem_Alloc(bsp->mempool, sizeof(colbspnode_t));
+ return bsp;
+}
+
+void Collision_FreeCollisionBSPNode(colbspnode_t *node)
+{
+ if (node->children[0])
+ Collision_FreeCollisionBSPNode(node->children[0]);
+ if (node->children[1])
+ Collision_FreeCollisionBSPNode(node->children[1]);
+ while (--node->numcolbrushf)
+ Mem_Free(node->colbrushflist[node->numcolbrushf]);
+ //while (--node->numcolbrushd)
+ // Mem_Free(node->colbrushdlist[node->numcolbrushd]);
+ Mem_Free(node);
+}
+
+void Collision_FreeCollisionBSP(colbsp_t *bsp)
+{
+ Collision_FreeCollisionBSPNode(bsp->nodes);
+ Mem_Free(bsp);
+}
+
+void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac)
+{
+ int i;
+ colpointf_t *ps, *pe;
+ float tempstart[3], tempend[3];
+ VectorLerp(start->points[0].v, startfrac, end->points[0].v, mins);
+ VectorCopy(mins, maxs);
+ for (i = 0, ps = start->points, pe = end->points;i < start->numpoints;i++, ps++, pe++)
+ {
+ VectorLerp(ps->v, startfrac, pe->v, tempstart);
+ VectorLerp(ps->v, endfrac, pe->v, tempend);
+ mins[0] = min(mins[0], min(tempstart[0], tempend[0]));
+ mins[1] = min(mins[1], min(tempstart[1], tempend[1]));
+ mins[2] = min(mins[2], min(tempstart[2], tempend[2]));
+ maxs[0] = min(maxs[0], min(tempstart[0], tempend[0]));
+ maxs[1] = min(maxs[1], min(tempstart[1], tempend[1]));
+ maxs[2] = min(maxs[2], min(tempstart[2], tempend[2]));
+ }
+ mins[0] -= 1;
+ mins[1] -= 1;
+ mins[2] -= 1;
+ maxs[0] += 1;
+ maxs[1] += 1;
+ maxs[2] += 1;
+}
+
+//===========================================
+
+void Collision_TranslateBrush(const vec3_t shift, colbrushf_t *brush)
+{
+ int i;
+ // now we can transform the data
+ for(i = 0; i < brush->numplanes; ++i)
+ {
+ brush->planes[i].dist += DotProduct(shift, brush->planes[i].normal);
+ }
+ for(i = 0; i < brush->numpoints; ++i)
+ {
+ VectorAdd(brush->points[i].v, shift, brush->points[i].v);
+ }
+ VectorAdd(brush->mins, shift, brush->mins);
+ VectorAdd(brush->maxs, shift, brush->maxs);
+}
+
+void Collision_TransformBrush(const matrix4x4_t *matrix, colbrushf_t *brush)
+{
+ int i;
+ vec3_t v;
+ // we're breaking any AABB properties here...
+ brush->isaabb = false;
+ brush->hasaabbplanes = false;
+ // now we can transform the data
+ for(i = 0; i < brush->numplanes; ++i)
+ {
+ 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);
+ }
+ for(i = 0; i < brush->numedgedirs; ++i)
+ {
+ Matrix4x4_Transform(matrix, brush->edgedirs[i].v, v);
+ VectorCopy(v, brush->edgedirs[i].v);
+ }
+ for(i = 0; i < brush->numpoints; ++i)
+ {
+ Matrix4x4_Transform(matrix, brush->points[i].v, v);
+ VectorCopy(v, brush->points[i].v);
+ }
+ VectorCopy(brush->points[0].v, brush->mins);
+ VectorCopy(brush->points[0].v, brush->maxs);
+ for(i = 1; i < brush->numpoints; ++i)
+ {
+ if(brush->points[i].v[0] < brush->mins[0]) brush->mins[0] = brush->points[i].v[0];
+ if(brush->points[i].v[1] < brush->mins[1]) brush->mins[1] = brush->points[i].v[1];
+ if(brush->points[i].v[2] < brush->mins[2]) brush->mins[2] = brush->points[i].v[2];
+ if(brush->points[i].v[0] > brush->maxs[0]) brush->maxs[0] = brush->points[i].v[0];
+ if(brush->points[i].v[1] > brush->maxs[1]) brush->maxs[1] = brush->points[i].v[1];
+ if(brush->points[i].v[2] > brush->maxs[2]) brush->maxs[2] = brush->points[i].v[2];
+ }
+}
+
+typedef struct collision_cachedtrace_parameters_s
+{
+ dp_model_t *model;
+ vec3_t end;
+ vec3_t start;
+ vec3_t mins;
+ vec3_t maxs;
+// const frameblend_t *frameblend;
+// const skeleton_t *skeleton;
+// matrix4x4_t inversematrix;
+ int hitsupercontentsmask;
+ int type; // which type of query produced this cache entry
+ matrix4x4_t matrix;
+ vec3_t bodymins;
+ vec3_t bodymaxs;
+ int bodysupercontents;
+}
+collision_cachedtrace_parameters_t;
+
+typedef struct collision_cachedtrace_s
+{
+ qboolean valid;
+ collision_cachedtrace_parameters_t p;
+ trace_t result;
+}
+collision_cachedtrace_t;
+
+static mempool_t *collision_cachedtrace_mempool;
+static collision_cachedtrace_t *collision_cachedtrace_array;
+static int collision_cachedtrace_firstfree;
+static int collision_cachedtrace_lastused;
+static int collision_cachedtrace_max;
+static int collision_cachedtrace_sequence;
+static int collision_cachedtrace_hashsize;
+static int *collision_cachedtrace_hash;
+static unsigned int *collision_cachedtrace_arrayfullhashindex;
+static unsigned int *collision_cachedtrace_arrayhashindex;
+static unsigned int *collision_cachedtrace_arraynext;
+static unsigned char *collision_cachedtrace_arrayused;
+static qboolean collision_cachedtrace_rebuildhash;
+
+void Collision_Cache_Reset(qboolean resetlimits)
+{
+ if (collision_cachedtrace_hash)
+ Mem_Free(collision_cachedtrace_hash);
+ if (collision_cachedtrace_array)
+ Mem_Free(collision_cachedtrace_array);
+ if (collision_cachedtrace_arrayfullhashindex)
+ Mem_Free(collision_cachedtrace_arrayfullhashindex);
+ if (collision_cachedtrace_arrayhashindex)
+ Mem_Free(collision_cachedtrace_arrayhashindex);
+ if (collision_cachedtrace_arraynext)
+ Mem_Free(collision_cachedtrace_arraynext);
+ if (collision_cachedtrace_arrayused)
+ Mem_Free(collision_cachedtrace_arrayused);
+ if (resetlimits || !collision_cachedtrace_max)
+ collision_cachedtrace_max = collision_cache.integer ? 128 : 1;
+ collision_cachedtrace_firstfree = 1;
+ collision_cachedtrace_lastused = 0;
+ collision_cachedtrace_hashsize = collision_cachedtrace_max;
+ collision_cachedtrace_array = (collision_cachedtrace_t *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(collision_cachedtrace_t));
+ collision_cachedtrace_hash = (int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_hashsize * sizeof(int));
+ collision_cachedtrace_arrayfullhashindex = (unsigned int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned int));
+ collision_cachedtrace_arrayhashindex = (unsigned int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned int));
+ collision_cachedtrace_arraynext = (unsigned int *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned int));
+ collision_cachedtrace_arrayused = (unsigned char *)Mem_Alloc(collision_cachedtrace_mempool, collision_cachedtrace_max * sizeof(unsigned char));
+ collision_cachedtrace_sequence = 1;
+ collision_cachedtrace_rebuildhash = false;
+}
+
+void Collision_Cache_Init(mempool_t *mempool)
+{
+ collision_cachedtrace_mempool = mempool;
+ Collision_Cache_Reset(true);
+}
+
+void Collision_Cache_RebuildHash(void)
+{
+ int index;
+ int range = collision_cachedtrace_lastused + 1;
+ int sequence = collision_cachedtrace_sequence;
+ int firstfree = collision_cachedtrace_max;
+ int lastused = 0;
+ int *hash = collision_cachedtrace_hash;
+ unsigned int hashindex;
+ unsigned int *arrayhashindex = collision_cachedtrace_arrayhashindex;
+ unsigned int *arraynext = collision_cachedtrace_arraynext;
+ collision_cachedtrace_rebuildhash = false;
+ memset(collision_cachedtrace_hash, 0, collision_cachedtrace_hashsize * sizeof(int));
+ for (index = 1;index < range;index++)
+ {
+ if (collision_cachedtrace_arrayused[index] == sequence)
+ {
+ hashindex = arrayhashindex[index];
+ arraynext[index] = hash[hashindex];
+ hash[hashindex] = index;
+ lastused = index;
+ }
+ else
+ {
+ if (firstfree > index)
+ firstfree = index;
+ collision_cachedtrace_arrayused[index] = 0;
+ }
+ }
+ collision_cachedtrace_firstfree = firstfree;
+ collision_cachedtrace_lastused = lastused;
+}
+
+void Collision_Cache_NewFrame(void)
+{
+ if (collision_cache.integer)
+ {
+ if (collision_cachedtrace_max < 128)
+ Collision_Cache_Reset(true);
+ }
+ else
+ {
+ if (collision_cachedtrace_max > 1)
+ Collision_Cache_Reset(true);
+ }
+ // rebuild hash if sequence would overflow byte, otherwise increment
+ if (collision_cachedtrace_sequence == 255)
+ {
+ Collision_Cache_RebuildHash();
+ collision_cachedtrace_sequence = 1;
+ }
+ else
+ {
+ collision_cachedtrace_rebuildhash = true;
+ collision_cachedtrace_sequence++;
+ }
+}
+
+static unsigned int Collision_Cache_HashIndexForArray(unsigned int *array, unsigned int size)
+{
+ unsigned int i;
+ unsigned int hashindex = 0;
+ // this is a super-cheesy checksum, designed only for speed
+ for (i = 0;i < size;i++)
+ hashindex += array[i] * (1 + i);
+ return hashindex;
+}
+
+static collision_cachedtrace_t *Collision_Cache_Lookup(int type, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
+{
+ int hashindex = 0;
+ unsigned int fullhashindex;
+ int index = 0;
+ int range;
+ int sequence = collision_cachedtrace_sequence;
+ int *hash = collision_cachedtrace_hash;
+ unsigned int *arrayfullhashindex = collision_cachedtrace_arrayfullhashindex;
+ unsigned int *arraynext = collision_cachedtrace_arraynext;
+ collision_cachedtrace_t *cached = collision_cachedtrace_array + index;
+ collision_cachedtrace_parameters_t params;
+ // all non-cached traces use the same index
+ if ((frameblend && frameblend[0].lerp != 1) || (skeleton && skeleton->relativetransforms))
+ r_refdef.stats.collisioncache_animated++;
+ else if (!collision_cache.integer)
+ r_refdef.stats.collisioncache_traced++;
+ else
+ {
+ // cached trace lookup
+ memset(¶ms, 0, sizeof(params));
+ params.type = type;
+ params.model = model;
+ VectorCopy(bodymins, params.bodymins);
+ VectorCopy(bodymaxs, params.bodymaxs);
+ params.bodysupercontents = bodysupercontents;
+ VectorCopy(start, params.start);
+ VectorCopy(mins, params.mins);
+ VectorCopy(maxs, params.maxs);
+ VectorCopy(end, params.end);
+ params.hitsupercontentsmask = hitsupercontentsmask;
+ params.matrix = *matrix;
+ //params.inversematrix = *inversematrix;
+ fullhashindex = Collision_Cache_HashIndexForArray((unsigned int *)¶ms, sizeof(params) / sizeof(unsigned int));
+ //fullhashindex = Collision_Cache_HashIndexForArray((unsigned int *)¶ms, 10);
+ hashindex = (int)(fullhashindex % (unsigned int)collision_cachedtrace_hashsize);
+ for (index = hash[hashindex];index;index = arraynext[index])
+ {
+ if (arrayfullhashindex[index] != fullhashindex)
+ continue;
+ cached = collision_cachedtrace_array + index;
+ //if (memcmp(&cached->p, ¶ms, sizeof(params)))
+ if (cached->p.model != params.model
+ || cached->p.end[0] != params.end[0]
+ || cached->p.end[1] != params.end[1]
+ || cached->p.end[2] != params.end[2]
+ || cached->p.start[0] != params.start[0]
+ || cached->p.start[1] != params.start[1]
+ || cached->p.start[2] != params.start[2]
+ || cached->p.mins[0] != params.mins[0]
+ || cached->p.mins[1] != params.mins[1]
+ || cached->p.mins[2] != params.mins[2]
+ || cached->p.maxs[0] != params.maxs[0]
+ || cached->p.maxs[1] != params.maxs[1]
+ || cached->p.maxs[2] != params.maxs[2]
+ || cached->p.type != params.type
+ || cached->p.bodysupercontents != params.bodysupercontents
+ || cached->p.bodymins[0] != params.bodymins[0]
+ || cached->p.bodymins[1] != params.bodymins[1]
+ || cached->p.bodymins[2] != params.bodymins[2]
+ || cached->p.bodymaxs[0] != params.bodymaxs[0]
+ || cached->p.bodymaxs[1] != params.bodymaxs[1]
+ || cached->p.bodymaxs[2] != params.bodymaxs[2]
+ || cached->p.hitsupercontentsmask != params.hitsupercontentsmask
+ || cached->p.matrix.m[0][0] != params.matrix.m[0][0]
+ || cached->p.matrix.m[0][1] != params.matrix.m[0][1]
+ || cached->p.matrix.m[0][2] != params.matrix.m[0][2]
+ || cached->p.matrix.m[0][3] != params.matrix.m[0][3]
+ || cached->p.matrix.m[1][0] != params.matrix.m[1][0]
+ || cached->p.matrix.m[1][1] != params.matrix.m[1][1]
+ || cached->p.matrix.m[1][2] != params.matrix.m[1][2]
+ || cached->p.matrix.m[1][3] != params.matrix.m[1][3]
+ || cached->p.matrix.m[2][0] != params.matrix.m[2][0]
+ || cached->p.matrix.m[2][1] != params.matrix.m[2][1]
+ || cached->p.matrix.m[2][2] != params.matrix.m[2][2]
+ || cached->p.matrix.m[2][3] != params.matrix.m[2][3]
+ || cached->p.matrix.m[3][0] != params.matrix.m[3][0]
+ || cached->p.matrix.m[3][1] != params.matrix.m[3][1]
+ || cached->p.matrix.m[3][2] != params.matrix.m[3][2]
+ || cached->p.matrix.m[3][3] != params.matrix.m[3][3]
+ )
+ continue;
+ // found a matching trace in the cache
+ r_refdef.stats.collisioncache_cached++;
+ cached->valid = true;
+ collision_cachedtrace_arrayused[index] = collision_cachedtrace_sequence;
+ return cached;
+ }
+ r_refdef.stats.collisioncache_traced++;
+ // find an unused cache entry
+ for (index = collision_cachedtrace_firstfree, range = collision_cachedtrace_max;index < range;index++)
+ if (collision_cachedtrace_arrayused[index] == 0)
+ break;
+ if (index == range)
+ {
+ // all claimed, but probably some are stale...
+ for (index = 1, range = collision_cachedtrace_max;index < range;index++)
+ if (collision_cachedtrace_arrayused[index] != sequence)
+ break;
+ if (index < range)
+ {
+ // found a stale one, rebuild the hash
+ Collision_Cache_RebuildHash();
+ }
+ else
+ {
+ // we need to grow the cache
+ collision_cachedtrace_max *= 2;
+ Collision_Cache_Reset(false);
+ index = 1;
+ }
+ }
+ // link the new cache entry into the hash bucket
+ collision_cachedtrace_firstfree = index + 1;
+ if (collision_cachedtrace_lastused < index)
+ collision_cachedtrace_lastused = index;
+ cached = collision_cachedtrace_array + index;
+ collision_cachedtrace_arraynext[index] = collision_cachedtrace_hash[hashindex];
+ collision_cachedtrace_hash[hashindex] = index;
+ collision_cachedtrace_arrayhashindex[index] = hashindex;
+ cached->valid = false;
+ cached->p = params;
+ collision_cachedtrace_arrayfullhashindex[index] = fullhashindex;
+ collision_cachedtrace_arrayused[index] = collision_cachedtrace_sequence;
+ }
+ return cached;
+}
+
+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 start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
+{
+ float starttransformed[3], endtransformed[3];
+ collision_cachedtrace_t *cached = Collision_Cache_Lookup(3, model, frameblend, skeleton, bodymins, bodymaxs, bodysupercontents, matrix, inversematrix, start, mins, maxs, end, hitsupercontentsmask);
+ if (cached->valid)
+ {
+ *trace = cached->result;
+ return;
+ }
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+
+ Matrix4x4_Transform(inversematrix, start, starttransformed);
+ Matrix4x4_Transform(inversematrix, end, endtransformed);
+#if COLLISIONPARANOID >= 3
+ 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]);
+#endif
+
+ if (model && model->TraceBox)
+ {
+ 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]))
+ {
+ // we get here if TraceBrush exists, AND we have a rotation component (SOLID_BSP case)
+ // using starttransformed, endtransformed is WRONG in this case!
+ // should rather build a brush and trace using it
+ colboxbrushf_t thisbrush_start, thisbrush_end;
+ Collision_BrushForBox(&thisbrush_start, mins, maxs, 0, 0, NULL);
+ Collision_BrushForBox(&thisbrush_end, mins, maxs, 0, 0, NULL);
+ Collision_TranslateBrush(start, &thisbrush_start.brush);
+ Collision_TranslateBrush(end, &thisbrush_end.brush);
+ Collision_TransformBrush(inversematrix, &thisbrush_start.brush);
+ Collision_TransformBrush(inversematrix, &thisbrush_end.brush);
+ //Collision_TranslateBrush(starttransformed, &thisbrush_start.brush);
+ //Collision_TranslateBrush(endtransformed, &thisbrush_end.brush);
+ model->TraceBrush(model, frameblend, skeleton, trace, &thisbrush_start.brush, &thisbrush_end.brush, hitsupercontentsmask);
+ }
+ else // this is only approximate if rotated, quite useless
+ model->TraceBox(model, frameblend, skeleton, trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask);
+ }
+ 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
+ Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
+ trace->fraction = bound(0, trace->fraction, 1);
+ trace->realfraction = bound(0, trace->realfraction, 1);
+
+ VectorLerp(start, trace->fraction, end, trace->endpos);
+ // transform plane
+ // NOTE: this relies on plane.dist being directly after plane.normal
+ Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
+
+ cached->result = *trace;
+}
+
+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)
+{
+ collision_cachedtrace_t *cached = Collision_Cache_Lookup(3, model, NULL, NULL, vec3_origin, vec3_origin, 0, &identitymatrix, &identitymatrix, start, mins, maxs, end, hitsupercontents);
+ if (cached->valid)
+ {
+ *trace = cached->result;
+ return;
+ }
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+ // ->TraceBox: TraceBrush not needed here, as worldmodel is never rotated
+ if (model && model->TraceBox)
+ model->TraceBox(model, NULL, NULL, trace, start, mins, maxs, end, hitsupercontents);
+ trace->fraction = bound(0, trace->fraction, 1);
+ trace->realfraction = bound(0, trace->realfraction, 1);
+ VectorLerp(start, trace->fraction, end, trace->endpos);
+
+ cached->result = *trace;
+}
+
+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 start, const vec3_t end, int hitsupercontentsmask, qboolean hitsurfaces)
+{
+ float starttransformed[3], endtransformed[3];
+ collision_cachedtrace_t *cached = Collision_Cache_Lookup(2, model, frameblend, skeleton, bodymins, bodymaxs, bodysupercontents, matrix, inversematrix, start, vec3_origin, vec3_origin, end, hitsupercontentsmask);
+ if (cached->valid)
+ {
+ *trace = cached->result;
+ return;
+ }
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+
+ Matrix4x4_Transform(inversematrix, start, starttransformed);
+ Matrix4x4_Transform(inversematrix, end, endtransformed);
+#if COLLISIONPARANOID >= 3
+ 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]);
+#endif
+
+ if (model && model->TraceLineAgainstSurfaces && hitsurfaces)
+ model->TraceLineAgainstSurfaces(model, frameblend, skeleton, trace, starttransformed, endtransformed, hitsupercontentsmask);
+ else if (model && model->TraceLine)
+ model->TraceLine(model, frameblend, skeleton, trace, starttransformed, endtransformed, hitsupercontentsmask);
+ else
+ Collision_ClipTrace_Box(trace, bodymins, bodymaxs, starttransformed, vec3_origin, vec3_origin, endtransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
+ trace->fraction = bound(0, trace->fraction, 1);
+ trace->realfraction = bound(0, trace->realfraction, 1);
+
+ VectorLerp(start, trace->fraction, end, trace->endpos);
+ // transform plane
+ // NOTE: this relies on plane.dist being directly after plane.normal
+ Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
+
+ cached->result = *trace;
+}
+
+void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents, qboolean hitsurfaces)
+{
+ collision_cachedtrace_t *cached = Collision_Cache_Lookup(2, model, NULL, NULL, vec3_origin, vec3_origin, 0, &identitymatrix, &identitymatrix, start, vec3_origin, vec3_origin, end, hitsupercontents);
+ if (cached->valid)
+ {
+ *trace = cached->result;
+ return;
+ }
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+ if (model && model->TraceLineAgainstSurfaces && hitsurfaces)
+ model->TraceLineAgainstSurfaces(model, NULL, NULL, trace, start, end, hitsupercontents);
+ else if (model && model->TraceLine)
+ model->TraceLine(model, NULL, NULL, trace, start, end, hitsupercontents);
+ trace->fraction = bound(0, trace->fraction, 1);
+ trace->realfraction = bound(0, trace->realfraction, 1);
+ VectorLerp(start, trace->fraction, end, trace->endpos);
+
+ cached->result = *trace;
+}
+
+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)
+{
+ float starttransformed[3];
+ collision_cachedtrace_t *cached = Collision_Cache_Lookup(1, model, frameblend, skeleton, bodymins, bodymaxs, bodysupercontents, matrix, inversematrix, start, vec3_origin, vec3_origin, start, hitsupercontentsmask);
+ if (cached->valid)
+ {
+ *trace = cached->result;
+ return;
+ }
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+
+ Matrix4x4_Transform(inversematrix, start, starttransformed);
+#if COLLISIONPARANOID >= 3
+ Con_Printf("trans(%f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2]);
+#endif
+
+ if (model && model->TracePoint)
+ model->TracePoint(model, NULL, NULL, trace, starttransformed, hitsupercontentsmask);
+ else
+ Collision_ClipTrace_Point(trace, bodymins, bodymaxs, starttransformed, hitsupercontentsmask, bodysupercontents, 0, NULL);
+
+ VectorCopy(start, trace->endpos);
+ // transform plane
+ // NOTE: this relies on plane.dist being directly after plane.normal
+ Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
+
+ cached->result = *trace;
+}
+
+void Collision_ClipPointToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, int hitsupercontents)
+{
+ collision_cachedtrace_t *cached = Collision_Cache_Lookup(1, model, NULL, NULL, vec3_origin, vec3_origin, 0, &identitymatrix, &identitymatrix, start, vec3_origin, vec3_origin, start, hitsupercontents);
+ if (cached->valid)
+ {
+ *trace = cached->result;
+ return;
+ }
+
+ memset(trace, 0, sizeof(*trace));
+ trace->fraction = trace->realfraction = 1;
+ if (model && model->TracePoint)
+ model->TracePoint(model, NULL, NULL, trace, start, hitsupercontents);
+ VectorCopy(start, trace->endpos);
+
+ cached->result = *trace;
+}
+
+void Collision_CombineTraces(trace_t *cliptrace, const trace_t *trace, void *touch, qboolean isbmodel)
+{
+ // take the 'best' answers from the new trace and combine with existing data
+ if (trace->allsolid)
+ cliptrace->allsolid = true;
+ if (trace->startsolid)
+ {
+ if (isbmodel)
+ cliptrace->bmodelstartsolid = true;
+ cliptrace->startsolid = true;
+ if (cliptrace->realfraction == 1)
+ cliptrace->ent = touch;
+ if (cliptrace->startdepth > trace->startdepth)
+ {
+ cliptrace->startdepth = trace->startdepth;
+ VectorCopy(trace->startdepthnormal, cliptrace->startdepthnormal);
+ }
+ }
+ // don't set this except on the world, because it can easily confuse
+ // monsters underwater if there's a bmodel involved in the trace
+ // (inopen && inwater is how they check water visibility)
+ //if (trace->inopen)
+ // cliptrace->inopen = true;
+ if (trace->inwater)
+ cliptrace->inwater = true;
+ if ((trace->realfraction <= cliptrace->realfraction) && (VectorLength2(trace->plane.normal) > 0))
+ {
+ cliptrace->fraction = trace->fraction;
+ cliptrace->realfraction = trace->realfraction;
+ VectorCopy(trace->endpos, cliptrace->endpos);
+ cliptrace->plane = trace->plane;
+ cliptrace->ent = touch;
+ cliptrace->hitsupercontents = trace->hitsupercontents;
+ cliptrace->hitq3surfaceflags = trace->hitq3surfaceflags;
+ cliptrace->hittexture = trace->hittexture;
+ }
+ cliptrace->startsupercontents |= trace->startsupercontents;
+}
+
+void Collision_ShortenTrace(trace_t *trace, float shorten_factor, const vec3_t end)
+{
+ // now undo our moving end 1 qu farther...
+ trace->fraction = bound(trace->fraction, trace->fraction / shorten_factor - 1e-6, 1); // we subtract 1e-6 to guard for roundoff errors
+ trace->realfraction = bound(trace->realfraction, trace->realfraction / shorten_factor - 1e-6, 1); // we subtract 1e-6 to guard for roundoff errors
+ if(trace->fraction >= 1) // trace would NOT hit if not expanded!
+ {
+ trace->fraction = 1;
+ trace->realfraction = 1;
+ VectorCopy(end, trace->endpos);
+ memset(&trace->plane, 0, sizeof(trace->plane));
+ trace->ent = NULL;
+ trace->hitsupercontentsmask = 0;
+ trace->hitsupercontents = 0;
+ trace->hitq3surfaceflags = 0;
+ trace->hittexture = NULL;
+ }
+}