X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=collision.c;h=e0cc37dd817c1644a0dbac94750ed75ab328fcf2;hp=e5b07548788bbd28ab5c1f0ca275c766accb1d65;hb=ab9e8a37699390a5dc199e4def9cdbaaa59d82cf;hpb=4e402f83fc7aa1989f298c38847661bdf6d7883d diff --git a/collision.c b/collision.c index e5b07548..e0cc37dd 100644 --- a/collision.c +++ b/collision.c @@ -15,6 +15,13 @@ cvar_t collision_endnudge = {0, "collision_endnudge", "0", "how much to bias col cvar_t collision_enternudge = {0, "collision_enternudge", "0", "how much to bias collision entry fraction"}; cvar_t collision_leavenudge = {0, "collision_leavenudge", "0", "how much to bias collision exit fraction"}; cvar_t collision_prefernudgedfraction = {0, "collision_prefernudgedfraction", "1", "whether to sort collision events by nudged fraction (1) or real fraction (0)"}; +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND +cvar_t collision_endposnudge = {0, "collision_endposnudge", "0", "workaround to fix trace_endpos sometimes being returned where it would be inside solid by making that collision hit (recommended: values like 1)"}; +#endif +cvar_t collision_debug_tracelineasbox = {0, "collision_debug_tracelineasbox", "0", "workaround for any bugs in Collision_TraceLineBrushFloat by using Collision_TraceBrushBrushFloat"}; +cvar_t collision_cache = {0, "collision_cache", "1", "store results of collision traces for next frame to reuse if possible (optimization)"}; + +mempool_t *collision_mempool; void Collision_Init (void) { @@ -24,6 +31,13 @@ void Collision_Init (void) Cvar_RegisterVariable(&collision_enternudge); Cvar_RegisterVariable(&collision_leavenudge); Cvar_RegisterVariable(&collision_prefernudgedfraction); +#ifdef COLLISION_STUPID_TRACE_ENDPOS_IN_SOLID_WORKAROUND + Cvar_RegisterVariable(&collision_endposnudge); +#endif + Cvar_RegisterVariable(&collision_debug_tracelineasbox); + Cvar_RegisterVariable(&collision_cache); + collision_mempool = Mem_AllocPool("collision cache", 0, NULL); + Collision_Cache_Init(collision_mempool); } @@ -149,7 +163,7 @@ void Collision_CalcEdgeDirsForPolygonBrushFloat(colbrushf_t *brush) VectorSubtract(brush->points[i].v, brush->points[j].v, brush->edgedirs[j].v); } -colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const colplanef_t *originalplanes, int supercontents, int q3surfaceflags, texture_t *texture, int hasaabbplanes) +colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const colplanef_t *originalplanes, int supercontents, int q3surfaceflags, const texture_t *texture, int hasaabbplanes) { // TODO: planesbuf could be replaced by a remapping table int j, k, l, m, w, xyzflags; @@ -508,7 +522,7 @@ void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush) brush->planes[3].dist = DotProduct(brush->points[0].v, brush->planes[3].normal); brush->planes[4].dist = DotProduct(brush->points[1].v, brush->planes[4].normal); - if (developer.integer >= 100) + if (developer_extra.integer) { // validation code #if 0 @@ -520,34 +534,34 @@ void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush) VectorNormalize(normal); VectorSubtract(normal, brush->planes[0].normal, temp); if (VectorLength(temp) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: TriangleNormal gave wrong answer (%f %f %f != correct answer %f %f %f)\n", brush->planes->normal[0], brush->planes->normal[1], brush->planes->normal[2], normal[0], normal[1], normal[2]); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: TriangleNormal gave wrong answer (%f %f %f != correct answer %f %f %f)\n", brush->planes->normal[0], brush->planes->normal[1], brush->planes->normal[2], normal[0], normal[1], normal[2]); if (fabs(DotProduct(brush->planes[1].normal, brush->planes[0].normal) - -1.0f) > 0.01f || fabs(brush->planes[1].dist - -brush->planes[0].dist) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 1 (%f %f %f %f) is not opposite plane 0 (%f %f %f %f)\n", brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 1 (%f %f %f %f) is not opposite plane 0 (%f %f %f %f)\n", brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist); #if 0 if (fabs(DotProduct(brush->planes[2].normal, brush->planes[0].normal)) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[2].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[2].dist); if (fabs(DotProduct(brush->planes[3].normal, brush->planes[0].normal)) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[3].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[3].dist); if (fabs(DotProduct(brush->planes[4].normal, brush->planes[0].normal)) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[4].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to plane 0 (%f %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[4].dist); if (fabs(DotProduct(brush->planes[2].normal, edge0)) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to edge 0 (%f %f %f to %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2]); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 2 (%f %f %f %f) is not perpendicular to edge 0 (%f %f %f to %f %f %f)\n", brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist, brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2]); if (fabs(DotProduct(brush->planes[3].normal, edge1)) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to edge 1 (%f %f %f to %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2]); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 3 (%f %f %f %f) is not perpendicular to edge 1 (%f %f %f to %f %f %f)\n", brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist, brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2]); if (fabs(DotProduct(brush->planes[4].normal, edge2)) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to edge 2 (%f %f %f to %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2]); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: plane 4 (%f %f %f %f) is not perpendicular to edge 2 (%f %f %f to %f %f %f)\n", brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist, brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2]); #endif #endif if (fabs(DotProduct(brush->points[0].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[0].normal) - brush->planes[0].dist) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off front plane 0 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off front plane 0 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[0].normal[0], brush->planes[0].normal[1], brush->planes[0].normal[2], brush->planes[0].dist); if (fabs(DotProduct(brush->points[0].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[1].normal) - brush->planes[1].dist) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off back plane 1 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: edges (%f %f %f to %f %f %f to %f %f %f) off back plane 1 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[1].normal[0], brush->planes[1].normal[1], brush->planes[1].normal[2], brush->planes[1].dist); if (fabs(DotProduct(brush->points[2].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f || fabs(DotProduct(brush->points[0].v, brush->planes[2].normal) - brush->planes[2].dist) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->planes[2].normal[0], brush->planes[2].normal[1], brush->planes[2].normal[2], brush->planes[2].dist); if (fabs(DotProduct(brush->points[0].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f || fabs(DotProduct(brush->points[1].v, brush->planes[3].normal) - brush->planes[3].dist) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[0].v[0], brush->points[0].v[1], brush->points[0].v[2], brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->planes[3].normal[0], brush->planes[3].normal[1], brush->planes[3].normal[2], brush->planes[3].dist); if (fabs(DotProduct(brush->points[1].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f || fabs(DotProduct(brush->points[2].v, brush->planes[4].normal) - brush->planes[4].dist) > 0.01f) - Con_Printf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist); + Con_DPrintf("Collision_CalcPlanesForPolygonBrushFloat: edge 0 (%f %f %f to %f %f %f) off front plane 2 (%f %f %f %f)\n", brush->points[1].v[0], brush->points[1].v[1], brush->points[1].v[2], brush->points[2].v[0], brush->points[2].v[1], brush->points[2].v[2], brush->planes[4].normal[0], brush->planes[4].normal[1], brush->planes[4].normal[2], brush->planes[4].dist); } } } @@ -593,7 +607,7 @@ void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush) } } - if (developer.integer >= 100) + if (developer_extra.integer) { // validity check - will be disabled later Collision_ValidateBrush(brush); @@ -602,12 +616,12 @@ void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush) int j; for (j = 0, p = brush->points;j < brush->numpoints;j++, p++) if (DotProduct(p->v, brush->planes[i].normal) > brush->planes[i].dist + COLLISION_PLANE_DIST_EPSILON) - Con_Printf("Error in brush plane generation, plane %i\n", i); + Con_DPrintf("Error in brush plane generation, plane %i\n", i); } } } -colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents, int q3surfaceflags, texture_t *texture) +colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents, int q3surfaceflags, const texture_t *texture) { colbrushf_t *brush; brush = (colbrushf_t *)Mem_Alloc(mempool, sizeof(colbrushf_t) + sizeof(colplanef_t) * (numpoints + 2) + sizeof(colpointf_t) * numpoints); @@ -641,7 +655,7 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *trace_sta vec4_t startplane; vec4_t endplane; vec4_t newimpactplane; - texture_t *hittexture = NULL; + const texture_t *hittexture = NULL; vec_t startdepth = 1; vec3_t startdepthnormal; @@ -711,7 +725,7 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *trace_sta //Con_Printf("%c%i: startdist = %f, enddist = %f, startdist / (startdist - enddist) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, startdist, enddist, startdist / (startdist - enddist)); // aside from collisions, this is also used for error correction - if (startdist < 0 && (startdepth < startdist || startdepth == 1)) + if (startdist < collision_impactnudge.value && nplane < numplanes1 && (startdepth < startdist || startdepth == 1)) { startdepth = startdist; VectorCopy(startplane, startdepthnormal); @@ -846,10 +860,19 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec4_t startplane; vec4_t endplane; vec4_t newimpactplane; - texture_t *hittexture = NULL; + const texture_t *hittexture = NULL; vec_t startdepth = 1; vec3_t startdepthnormal; + if (collision_debug_tracelineasbox.integer) + { + colboxbrushf_t thisbrush_start, thisbrush_end; + Collision_BrushForBox(&thisbrush_start, linestart, linestart, 0, 0, NULL); + Collision_BrushForBox(&thisbrush_end, lineend, lineend, 0, 0, NULL); + Collision_TraceBrushBrushFloat(trace, &thisbrush_start.brush, &thisbrush_end.brush, other_start, other_end); + return; + } + VectorClear(startdepthnormal); Vector4Clear(newimpactplane); @@ -874,7 +897,7 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const //Con_Printf("%c%i: startdist = %f, enddist = %f, startdist / (startdist - enddist) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, startdist, enddist, startdist / (startdist - enddist)); // aside from collisions, this is also used for error correction - if (startdist < 0 && (startdepth < startdist || startdepth == 1)) + if (startdist < collision_impactnudge.value && (startdepth < startdist || startdepth == 1)) { startdepth = startdist; VectorCopy(startplane, startdepthnormal); @@ -1020,7 +1043,7 @@ void Collision_SnapCopyPoints(int numpoints, const colpointf_t *in, colpointf_t } } -void Collision_TraceBrushTriangleMeshFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numtriangles, const int *element3i, const float *vertex3f, int stride, float *bbox6f, int supercontents, int q3surfaceflags, texture_t *texture, const vec3_t segmentmins, const vec3_t segmentmaxs) +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) { int i; colpointf_t points[3]; @@ -1102,7 +1125,7 @@ void Collision_TraceBrushTriangleMeshFloat(trace_t *trace, const colbrushf_t *th } } -void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numtriangles, const int *element3i, const float *vertex3f, int stride, float *bbox6f, int supercontents, int q3surfaceflags, texture_t *texture, const vec3_t segmentmins, const vec3_t segmentmaxs) +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) { int i; // FIXME: snap vertices? @@ -1131,7 +1154,41 @@ void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart } } -void Collision_BrushForBox(colboxbrushf_t *boxbrush, const vec3_t mins, const vec3_t maxs, int supercontents, int q3surfaceflags, texture_t *texture) +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) +{ + int i; + colpointf_t points[3]; + colpointf_t edgedirs[3]; + colplanef_t planes[5]; + colbrushf_t brush; + memset(&brush, 0, sizeof(brush)); + brush.isaabb = false; + brush.hasaabbplanes = false; + brush.numpoints = 3; + brush.numedgedirs = 3; + brush.numplanes = 5; + brush.points = points; + brush.edgedirs = edgedirs; + brush.planes = planes; + brush.supercontents = supercontents; + brush.q3surfaceflags = q3surfaceflags; + brush.texture = texture; + for (i = 0;i < brush.numplanes;i++) + { + brush.planes[i].q3surfaceflags = q3surfaceflags; + brush.planes[i].texture = texture; + } + VectorCopy(v0, points[0].v); + VectorCopy(v1, points[1].v); + VectorCopy(v2, points[2].v); + Collision_SnapCopyPoints(brush.numpoints, points, points, COLLISION_SNAPSCALE, COLLISION_SNAP); + Collision_CalcEdgeDirsForPolygonBrushFloat(&brush); + Collision_CalcPlanesForPolygonBrushFloat(&brush); + //Collision_PrintBrushAsQHull(&brush, "brush"); + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &brush, &brush); +} + +void Collision_BrushForBox(colboxbrushf_t *boxbrush, const vec3_t mins, const vec3_t maxs, int supercontents, int q3surfaceflags, const texture_t *texture) { int i; memset(boxbrush, 0, sizeof(*boxbrush)); @@ -1187,7 +1244,7 @@ void Collision_BrushForBox(colboxbrushf_t *boxbrush, const vec3_t mins, const ve boxbrush->brush.texture = texture; VectorSet(boxbrush->brush.mins, mins[0] - 1, mins[1] - 1, mins[2] - 1); VectorSet(boxbrush->brush.maxs, maxs[0] + 1, maxs[1] + 1, maxs[2] + 1); - Collision_ValidateBrush(&boxbrush->brush); + //Collision_ValidateBrush(&boxbrush->brush); } void Collision_ClipTrace_BrushBox(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int supercontents, int q3surfaceflags, texture_t *texture) @@ -1264,7 +1321,7 @@ float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double return impactdist / linelength; } -void Collision_TraceLineTriangleFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const float *point0, const float *point1, const float *point2, int supercontents, int q3surfaceflags, texture_t *texture) +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 @@ -1558,6 +1615,324 @@ void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const co //=========================================== +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; + int hitsupercontentsmask; + matrix4x4_t matrix; +} +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(dp_model_t *model, const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, const vec3_t start, 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 (!collision_cache.integer) + r_refdef.stats.collisioncache_traced++; + else + { + // cached trace lookup + memset(¶ms, 0, sizeof(params)); + params.model = model; + VectorCopy(start, params.start); + VectorCopy(end, params.end); + params.hitsupercontentsmask = hitsupercontentsmask; + params.matrix = *matrix; + fullhashindex = Collision_Cache_HashIndexForArray((unsigned int *)¶ms, sizeof(params) / sizeof(unsigned int)); + 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.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_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) +{ + collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, matrix, inversematrix, start, end, hitsupercontentsmask); + if (cached->valid) + { + *trace = cached->result; + return; + } + + Collision_ClipLineToGenericEntity(trace, model, NULL, NULL, vec3_origin, vec3_origin, 0, matrix, inversematrix, start, end, hitsupercontentsmask, true); + + cached->result = *trace; +} + +void Collision_Cache_ClipLineToWorldSurfaces(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents) +{ + collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, &identitymatrix, &identitymatrix, start, end, hitsupercontents); + if (cached->valid) + { + *trace = cached->result; + return; + } + + Collision_ClipLineToWorld(trace, model, start, end, hitsupercontents, true); + + cached->result = *trace; +} + 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]; @@ -1572,8 +1947,27 @@ void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, const fram #endif if (model && model->TraceBox) - model->TraceBox(model, frameblend, skeleton, trace, starttransformed, mins, maxs, endtransformed, hitsupercontentsmask); - else + { + 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); @@ -1588,6 +1982,7 @@ void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t start { 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); @@ -1595,10 +1990,9 @@ void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t start VectorLerp(start, trace->fraction, end, trace->endpos); } -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) +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]; - memset(trace, 0, sizeof(*trace)); trace->fraction = trace->realfraction = 1; @@ -1608,7 +2002,9 @@ void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const 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->TraceLine) + 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); @@ -1621,11 +2017,13 @@ void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal); } -void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents) +void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t end, int hitsupercontents, qboolean hitsurfaces) { memset(trace, 0, sizeof(*trace)); trace->fraction = trace->realfraction = 1; - if (model && model->TraceLine) + 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); @@ -1635,7 +2033,6 @@ void Collision_ClipLineToWorld(trace_t *trace, dp_model_t *model, const vec3_t s 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]; - memset(trace, 0, sizeof(*trace)); trace->fraction = trace->realfraction = 1;