From: havoc Date: Wed, 27 Aug 2003 12:22:00 +0000 (+0000) Subject: q3bsp curve collisions (technically it can collide against any triangle mesh) X-Git-Tag: xonotic-v0.1.0preview~6414 X-Git-Url: https://git.xonotic.org/?a=commitdiff_plain;h=0db6270f9fdf1da680391fe0389d7ad6cfc48517;p=xonotic%2Fdarkplaces.git q3bsp curve collisions (technically it can collide against any triangle mesh) fixed some severe bugs in the q3bsp traceline code optimized q3bsp tracebrush code - now uses a bbox for the trace, and reduces it to fit each node it encounters, this caused *IMMENSE* speedups! optimized q3bsp curve collisions to use bbox culling of triangles (to avoid the cost of generating collision brushes most of the time) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@3417 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cl_collision.c b/cl_collision.c index 657a0ff4..ce613001 100644 --- a/cl_collision.c +++ b/cl_collision.c @@ -42,7 +42,7 @@ float CL_TraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t n float tempnormal[3], starttransformed[3], endtransformed[3]; if (hitent) - *hitent = NULL; + *hitent = &cl_entities[0].render; Mod_CheckLoaded(cl.worldmodel); if (cl.worldmodel && cl.worldmodel->brush.TraceBox) cl.worldmodel->brush.TraceBox(cl.worldmodel, &trace, start, start, end, end, hitsupercontentsmask); @@ -53,8 +53,6 @@ float CL_TraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t n VectorCopy(trace.plane.normal, normal); cl_traceline_startsupercontents = trace.startsupercontents; maxfrac = trace.fraction; - if (hitent && trace.fraction < 1) - *hitent = &cl_entities[0].render; if (hitbmodels && cl_num_brushmodel_entities) { @@ -82,21 +80,18 @@ float CL_TraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t n if (ent->model && ent->model->brush.TraceBox) ent->model->brush.TraceBox(ent->model, &trace, starttransformed, starttransformed, endtransformed, endtransformed, hitsupercontentsmask); - if (trace.allsolid || trace.startsolid || maxfrac > trace.fraction) + cl_traceline_startsupercontents |= trace.startsupercontents; + if (maxfrac > trace.fraction) { - cl_traceline_startsupercontents = trace.startsupercontents; if (hitent) *hitent = ent; - if (maxfrac > trace.fraction) + maxfrac = trace.fraction; + if (impact) + VectorLerp(start, trace.fraction, end, impact); + if (normal) { - maxfrac = trace.fraction; - if (impact) - VectorLerp(start, trace.fraction, end, impact); - if (normal) - { - VectorCopy(trace.plane.normal, tempnormal); - Matrix4x4_Transform3x3(&matrix, tempnormal, normal); - } + VectorCopy(trace.plane.normal, tempnormal); + Matrix4x4_Transform3x3(&matrix, tempnormal, normal); } } } diff --git a/collision.c b/collision.c index 20e1e767..1c4a4fe6 100644 --- a/collision.c +++ b/collision.c @@ -289,32 +289,77 @@ void Collision_PrintBrushAsQHull(colbrushf_t *brush, const char *name) void Collision_ValidateBrush(colbrushf_t *brush) { - int j, k; + int j, k, pointsoffplanes, printbrush; + float d; + printbrush = false; if (!brush->numpoints) { Con_Printf("Collision_ValidateBrush: brush with no points!\n"); - Collision_PrintBrushAsQHull(brush, "unnamed"); - return; + printbrush = true; } +#if 0 // it's ok for a brush to have one point and no planes... if (brush->numplanes == 0 && brush->numpoints != 1) { Con_Printf("Collision_ValidateBrush: brush with no planes and more than one point!\n"); - Collision_PrintBrushAsQHull(brush, "unnamed"); - return; + printbrush = true; } - for (k = 0;k < brush->numplanes;k++) +#endif + if (brush->numplanes) { - for (j = 0;j < brush->numpoints;j++) + pointsoffplanes = 0; + for (k = 0;k < brush->numplanes;k++) { - if (DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist > (1.0f / 8.0f)) + if (DotProduct(brush->planes[k].normal, brush->planes[k].normal) < 0.0001f) + 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); + for (j = 0;j < brush->numpoints;j++) { - Con_Printf("Collision_NewBrushFromPlanes: 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); - Collision_PrintBrushAsQHull(brush, "unnamed"); - return; + d = DotProduct(brush->points[j].v, brush->planes[k].normal) - brush->planes[k].dist; + if (d > (1.0f / 8.0f)) + { + 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); + printbrush = true; + } + if (fabs(d) > 0.01f) + pointsoffplanes++; } } + if (pointsoffplanes == 0) // all points are on all planes + { + Con_Printf("Collision_ValidateBrush: all points lie on all planes (degenerate, no brush volume!)\n"); + printbrush = true; + } + } + if (printbrush) + Collision_PrintBrushAsQHull(brush, "unnamed"); +} + +float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) +{ + float dist, bestdist; + bestdist = DotProduct(points->v, normal); + points++; + while(--numpoints) + { + dist = DotProduct(points->v, normal); + bestdist = min(bestdist, dist); + points++; + } + return bestdist; +} + +float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) +{ + float dist, bestdist; + bestdist = DotProduct(points->v, normal); + points++; + while(--numpoints) + { + dist = DotProduct(points->v, normal); + bestdist = max(bestdist, dist); + points++; } + return bestdist; } @@ -328,6 +373,7 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla colplanef_t planesbuf[256]; int elementsbuf[1024]; int polypointbuf[256]; + float mins[3], maxs[3]; // construct a collision brush (points, planes, and renderable mesh) from // a set of planes, this also optimizes out any unnecessary planes (ones // whose polygon is clipped away by the other planes) @@ -429,6 +475,25 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla numplanes++; } + // recalc distances + for (j = 0;j < numplanes;j++) + planesbuf[j].dist = furthestplanedist_float(planesbuf[j].normal, pointsbuf, numpoints); + + if (numpoints) + { + VectorCopy(pointsbuf[0].v, mins); + VectorCopy(pointsbuf[0].v, maxs); + for (j = 1;j < numpoints;j++) + { + mins[0] = min(mins[0], pointsbuf[j].v[0]); + mins[1] = min(mins[1], pointsbuf[j].v[1]); + mins[2] = min(mins[2], pointsbuf[j].v[2]); + maxs[0] = max(maxs[0], pointsbuf[j].v[0]); + maxs[1] = max(maxs[1], pointsbuf[j].v[1]); + maxs[2] = max(maxs[2], pointsbuf[j].v[2]); + } + } + // if nothing is left, there's nothing to allocate if (numtriangles < 4 || numplanes < 4 || numpoints < 4) return NULL; @@ -438,6 +503,8 @@ colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalpla memcpy(brush->points, pointsbuf, numpoints * sizeof(colpointf_t)); memcpy(brush->planes, planesbuf, numplanes * sizeof(colplanef_t)); memcpy(brush->elements, elementsbuf, numtriangles * sizeof(int[3])); + VectorCopy(mins, brush->mins); + VectorCopy(maxs, brush->maxs); Collision_ValidateBrush(brush); return brush; } @@ -461,48 +528,130 @@ colbrushf_t *Collision_AllocBrushFloat(mempool_t *mempool, int numpoints, int nu void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush) { int i; - float edge0[3], edge1[3], normal[3], dist, bestdist; + float edge0[3], edge1[3], edge2[3], normal[3], dist, bestdist, temp[3]; colpointf_t *p, *p2; - // choose best surface normal for polygon's plane - bestdist = 0; - for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++) + if (brush->numpoints == 3) { - VectorSubtract(p[-1].v, p[0].v, edge0); - VectorSubtract(p[1].v, p[0].v, edge1); - CrossProduct(edge0, edge1, normal); - dist = DotProduct(normal, normal); - if (i == 0 || bestdist < dist) + // optimized triangle case + TriangleNormal(brush->points[0].v, brush->points[1].v, brush->points[2].v, brush->planes[0].normal); + if (DotProduct(brush->planes[0].normal, brush->planes[0].normal) < 0.0001f) { - bestdist = dist; - VectorCopy(normal, brush->planes->normal); + // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out) + brush->numplanes = 0; + return; + } + else + { + brush->numplanes = 5; + VectorNormalize(brush->planes[0].normal); + brush->planes[0].dist = DotProduct(brush->points->v, brush->planes[0].normal); + VectorNegate(brush->planes[0].normal, brush->planes[1].normal); + brush->planes[1].dist = -brush->planes[0].dist; + VectorSubtract(brush->points[2].v, brush->points[0].v, edge0); + VectorSubtract(brush->points[0].v, brush->points[1].v, edge1); + VectorSubtract(brush->points[1].v, brush->points[2].v, edge2); + CrossProduct(edge0, brush->planes->normal, brush->planes[2].normal); + CrossProduct(edge1, brush->planes->normal, brush->planes[3].normal); + CrossProduct(edge2, brush->planes->normal, brush->planes[4].normal); + VectorNormalize(brush->planes[2].normal); + VectorNormalize(brush->planes[3].normal); + VectorNormalize(brush->planes[4].normal); + brush->planes[2].dist = DotProduct(brush->points[2].v, brush->planes[2].normal); + 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) + { + // validation code + //VectorSubtract(brush->points[0].v, brush->points[1].v, edge0); + //VectorSubtract(brush->points[2].v, brush->points[1].v, edge1); + CrossProduct(edge1, edge0, normal); + 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]); + 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); + 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); + 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); + 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); + 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]); + 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]); + 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]); + 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); + 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); + 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); + 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); + 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); + } } } - - VectorNormalize(brush->planes->normal); - brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal); - - // negate plane to create other side - VectorNegate(brush->planes[0].normal, brush->planes[1].normal); - brush->planes[1].dist = -brush->planes[0].dist; - for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++) + else { - VectorSubtract(p->v, p2->v, edge0); - CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal); - VectorNormalize(brush->planes[i + 2].normal); - brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal); + // choose best surface normal for polygon's plane + bestdist = 0; + for (i = 0, p = brush->points + 1;i < brush->numpoints - 2;i++, p++) + { + VectorSubtract(p[-1].v, p[0].v, edge0); + VectorSubtract(p[1].v, p[0].v, edge1); + CrossProduct(edge0, edge1, normal); + //TriangleNormal(p[-1].v, p[0].v, p[1].v, normal); + dist = DotProduct(normal, normal); + if (i == 0 || bestdist < dist) + { + bestdist = dist; + VectorCopy(normal, brush->planes->normal); + } + } + if (bestdist < 0.0001f) + { + // there's no point in processing a degenerate triangle (GIGO - Garbage In, Garbage Out) + brush->numplanes = 0; + return; + } + else + { + brush->numplanes = brush->numpoints + 2; + VectorNormalize(brush->planes->normal); + brush->planes->dist = DotProduct(brush->points->v, brush->planes->normal); + + // negate plane to create other side + VectorNegate(brush->planes[0].normal, brush->planes[1].normal); + brush->planes[1].dist = -brush->planes[0].dist; + for (i = 0, p = brush->points + (brush->numpoints - 1), p2 = brush->points;i < brush->numpoints;i++, p = p2, p2++) + { + VectorSubtract(p->v, p2->v, edge0); + CrossProduct(edge0, brush->planes->normal, brush->planes[i + 2].normal); + VectorNormalize(brush->planes[i + 2].normal); + brush->planes[i + 2].dist = DotProduct(p->v, brush->planes[i + 2].normal); + } + } } -#if 1 - // validity check - will be disabled later - for (i = 0;i < brush->numplanes;i++) + if (developer.integer) { - 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 + (1.0 / 32.0)) - Con_Printf("Error in brush plane generation, plane %i\n", i); + // validity check - will be disabled later + Collision_ValidateBrush(brush); + for (i = 0;i < brush->numplanes;i++) + { + 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 + (1.0 / 32.0)) + Con_Printf("Error in brush plane generation, plane %i\n", i); + } } -#endif } colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents) @@ -518,34 +667,6 @@ colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, i return brush; } -float nearestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) -{ - float dist, bestdist; - bestdist = DotProduct(points->v, normal); - points++; - while(--numpoints) - { - dist = DotProduct(points->v, normal); - bestdist = min(bestdist, dist); - points++; - } - return bestdist; -} - -float furthestplanedist_float(const float *normal, const colpointf_t *points, int numpoints) -{ - float dist, bestdist; - bestdist = DotProduct(points->v, normal); - points++; - while(--numpoints) - { - dist = DotProduct(points->v, normal); - bestdist = max(bestdist, dist); - points++; - } - return bestdist; -} - #define COLLISIONEPSILON (1.0f / 32.0f) #define COLLISIONEPSILON2 0//(1.0f / 32.0f) @@ -569,14 +690,40 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush nplane2 -= thatbrush_start->numplanes; startplane = thisbrush_start->planes + nplane2; endplane = thisbrush_end->planes + nplane2; + if (developer.integer) + { + // any brush with degenerate planes is not worth handling + if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f) + { + Con_Printf("Collision_TraceBrushBrushFloat: degenerate thisbrush plane!\n"); + return; + } + f = furthestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints); + if (fabs(f - startplane->dist) > 0.01f) + Con_Printf("startplane->dist %f != calculated %f (thisbrush_start)\n", startplane->dist, f); + } + d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints); + d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - furthestplanedist_float(endplane->normal, thatbrush_end->points, thatbrush_end->numpoints) - COLLISIONEPSILON2; } else { startplane = thatbrush_start->planes + nplane2; endplane = thatbrush_end->planes + nplane2; + if (developer.integer) + { + // any brush with degenerate planes is not worth handling + if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f) + { + Con_Printf("Collision_TraceBrushBrushFloat: degenerate thatbrush plane!\n"); + return; + } + f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints); + if (fabs(f - startplane->dist) > 0.01f) + Con_Printf("startplane->dist %f != calculated %f (thatbrush_start)\n", startplane->dist, f); + } + d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - startplane->dist; + d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - startplane->dist - COLLISIONEPSILON2; } - d1 = nearestplanedist_float(startplane->normal, thisbrush_start->points, thisbrush_start->numpoints) - furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints); - d2 = nearestplanedist_float(endplane->normal, thisbrush_end->points, thisbrush_end->numpoints) - furthestplanedist_float(endplane->normal, thatbrush_end->points, thatbrush_end->numpoints) - COLLISIONEPSILON2; //Con_Printf("%c%i: d1 = %f, d2 = %f, d1 / (d1 - d2) = %f\n", nplane2 != nplane ? 'b' : 'a', nplane2, d1, d2, d1 / (d1 - d2)); f = d1 - d2; @@ -594,10 +741,10 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush if (enterfrac < f) { enterfrac = f; - VectorBlend(startplane->normal, endplane->normal, enterfrac, newimpactnormal); + VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal); } } - else if (f < 0) + else { // moving out of brush if (d1 > 0) @@ -636,7 +783,7 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush } } -// NOTE: start and end of brush pair must have same numplanes/numpoints +// NOTE: start and end brush pair must have same numplanes/numpoints void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end) { int nplane, fstartsolid, fendsolid, brushsolid; @@ -653,7 +800,19 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const startplane = thatbrush_start->planes + nplane; endplane = thatbrush_end->planes + nplane; d1 = DotProduct(startplane->normal, linestart) - startplane->dist; - d2 = DotProduct(endplane->normal, lineend) - endplane->dist; + d2 = DotProduct(endplane->normal, lineend) - endplane->dist - COLLISIONEPSILON2; + if (developer.integer) + { + // any brush with degenerate planes is not worth handling + if (DotProduct(startplane->normal, startplane->normal) < 0.9f || DotProduct(endplane->normal, endplane->normal) < 0.9f) + { + Con_Printf("Collision_TraceLineBrushFloat: degenerate plane!\n"); + return; + } + f = furthestplanedist_float(startplane->normal, thatbrush_start->points, thatbrush_start->numpoints); + if (fabs(f - startplane->dist) > 0.01f) + Con_Printf("startplane->dist %f != calculated %f\n", startplane->dist, f); + } f = d1 - d2; if (f >= 0) @@ -670,10 +829,10 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const if (enterfrac < f) { enterfrac = f; - VectorBlend(startplane->normal, endplane->normal, enterfrac, newimpactnormal); + VectorLerp(startplane->normal, enterfrac, endplane->normal, newimpactnormal); } } - else if (f < 0) + else { // moving out of brush if (d1 > 0) @@ -712,10 +871,11 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const } } +static colpointf_t polyf_points[256]; static colplanef_t polyf_planes[256 + 2]; static colbrushf_t polyf_brush; -void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points) +void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents) { if (numpoints > 256) { @@ -726,16 +886,93 @@ void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbru polyf_brush.numplanes = numpoints + 2; polyf_brush.points = (colpointf_t *)points; polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush); } +void Collision_TraceBrushTriangleMeshFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs) +{ + int i; + float facemins[3], facemaxs[3]; + polyf_brush.numpoints = 3; + polyf_brush.numplanes = 5; + polyf_brush.points = polyf_points; + polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; + for (i = 0;i < numtriangles;i++, element3i += 3) + { + VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v); + VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v); + VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v); + facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])); + facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])); + facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])); + facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])); + facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])); + facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])); + if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs)) + { + Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); + //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, &polyf_brush, &polyf_brush); + } + } +} + +void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents) +{ + if (numpoints > 256) + { + Con_Printf("Polygon with more than 256 points not supported yet (fixme!)\n"); + return; + } + polyf_brush.numpoints = numpoints; + polyf_brush.numplanes = numpoints + 2; + polyf_brush.points = (colpointf_t *)points; + polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; + Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); + //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); + Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush); +} + +void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs) +{ + int i; + float facemins[3], facemaxs[3]; + polyf_brush.numpoints = 3; + polyf_brush.numplanes = 5; + polyf_brush.points = polyf_points; + polyf_brush.planes = polyf_planes; + polyf_brush.supercontents = supercontents; + for (i = 0;i < numtriangles;i++, element3i += 3) + { + VectorCopy(vertex3f + element3i[0] * 3, polyf_points[0].v); + VectorCopy(vertex3f + element3i[1] * 3, polyf_points[1].v); + VectorCopy(vertex3f + element3i[2] * 3, polyf_points[2].v); + facemins[0] = min(polyf_points[0].v[0], min(polyf_points[1].v[0], polyf_points[2].v[0])); + facemins[1] = min(polyf_points[0].v[1], min(polyf_points[1].v[1], polyf_points[2].v[1])); + facemins[2] = min(polyf_points[0].v[2], min(polyf_points[1].v[2], polyf_points[2].v[2])); + facemaxs[0] = max(polyf_points[0].v[0], max(polyf_points[1].v[0], polyf_points[2].v[0])); + facemaxs[1] = max(polyf_points[0].v[1], max(polyf_points[1].v[1], polyf_points[2].v[1])); + facemaxs[2] = max(polyf_points[0].v[2], max(polyf_points[1].v[2], polyf_points[2].v[2])); + if (BoxesOverlap(segmentmins, segmentmaxs, facemins, facemaxs)) + { + Collision_CalcPlanesForPolygonBrushFloat(&polyf_brush); + //Collision_PrintBrushAsQHull(&polyf_brush, "polyf_brush"); + Collision_TraceLineBrushFloat(trace, linestart, lineend, &polyf_brush, &polyf_brush); + } + } +} + + static colpointf_t polyf_pointsstart[256], polyf_pointsend[256]; static colplanef_t polyf_planesstart[256 + 2], polyf_planesend[256 + 2]; static colbrushf_t polyf_brushstart, polyf_brushend; -void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend) +void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend, int supercontents) { int i; if (numpoints > 256) @@ -747,12 +984,14 @@ void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t polyf_brushstart.numplanes = numpoints + 2; polyf_brushstart.points = polyf_pointsstart;//(colpointf_t *)points; polyf_brushstart.planes = polyf_planesstart; + polyf_brushstart.supercontents = supercontents; for (i = 0;i < numpoints;i++) Matrix4x4_Transform(polygonmatrixstart, points + i * 3, polyf_brushstart.points[i].v); polyf_brushend.numpoints = numpoints; polyf_brushend.numplanes = numpoints + 2; polyf_brushend.points = polyf_pointsend;//(colpointf_t *)points; polyf_brushend.planes = polyf_planesend; + polyf_brushend.supercontents = supercontents; for (i = 0;i < numpoints;i++) Matrix4x4_Transform(polygonmatrixend, points + i * 3, polyf_brushend.points[i].v); Collision_CalcPlanesForPolygonBrushFloat(&polyf_brushstart); @@ -771,6 +1010,7 @@ static int brushforbox_index = 0; static colpointf_t brushforbox_point[MAX_BRUSHFORBOX*8]; static colplanef_t brushforbox_plane[MAX_BRUSHFORBOX*6]; static colbrushf_t brushforbox_brush[MAX_BRUSHFORBOX]; +static colbrushf_t brushforpoint_brush[MAX_BRUSHFORBOX]; void Collision_InitBrushForBox(void) { @@ -782,6 +1022,11 @@ void Collision_InitBrushForBox(void) brushforbox_brush[i].numplanes = 6; brushforbox_brush[i].points = brushforbox_point + i * 8; brushforbox_brush[i].planes = brushforbox_plane + i * 6; + brushforpoint_brush[i].supercontents = SUPERCONTENTS_SOLID; + brushforpoint_brush[i].numpoints = 1; + brushforpoint_brush[i].numplanes = 0; + brushforpoint_brush[i].points = brushforbox_point + i * 8; + brushforpoint_brush[i].planes = brushforbox_plane + i * 6; } } @@ -792,23 +1037,32 @@ colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, colbrushf_t *brush; if (brushforbox_brush[0].numpoints == 0) Collision_InitBrushForBox(); - brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX); - // FIXME: optimize - for (i = 0;i < 8;i++) + if (VectorCompare(mins, maxs)) { - v[0] = i & 1 ? maxs[0] : mins[0]; - v[1] = i & 2 ? maxs[1] : mins[1]; - v[2] = i & 4 ? maxs[2] : mins[2]; - Matrix4x4_Transform(matrix, v, brush->points[i].v); + // point brush + brush = brushforpoint_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX); + VectorCopy(mins, brush->points->v); } - // FIXME: optimize! - for (i = 0;i < 6;i++) + else { - VectorClear(v); - v[i >> 1] = i & 1 ? 1 : -1; - Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal); - VectorNormalize(brush->planes[i].normal); - brush->planes[i].dist = furthestplanedist_float(brush->planes[i].normal, brush->points, brush->numpoints); + brush = brushforbox_brush + ((brushforbox_index++) % MAX_BRUSHFORBOX); + // FIXME: optimize + for (i = 0;i < 8;i++) + { + v[0] = i & 1 ? maxs[0] : mins[0]; + v[1] = i & 2 ? maxs[1] : mins[1]; + v[2] = i & 4 ? maxs[2] : mins[2]; + Matrix4x4_Transform(matrix, v, brush->points[i].v); + } + // FIXME: optimize! + for (i = 0;i < 6;i++) + { + VectorClear(v); + v[i >> 1] = i & 1 ? 1 : -1; + Matrix4x4_Transform3x3(matrix, v, brush->planes[i].normal); + VectorNormalize(brush->planes[i].normal); + brush->planes[i].dist = furthestplanedist_float(brush->planes[i].normal, brush->points, brush->numpoints); + } } Collision_ValidateBrush(brush); return brush; @@ -906,3 +1160,72 @@ float Collision_ClipTrace_Line_Sphere(double *linestart, double *lineend, double return impactdist / linelength; } +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 = Mem_Alloc(mempool, sizeof(colbsp_t)); + bsp->mempool = mempool; + bsp->nodes = 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])); + } +} + diff --git a/collision.h b/collision.h index 49dde748..ad391c66 100644 --- a/collision.h +++ b/collision.h @@ -76,6 +76,9 @@ typedef struct colbrushf_s int *elements; // used to avoid tracing against the same brush more than once int markframe; + // culling box + vec3_t mins; + vec3_t maxs; } colbrushf_t; @@ -84,10 +87,16 @@ void Collision_CalcPlanesForPolygonBrushFloat(colbrushf_t *brush); colbrushf_t *Collision_AllocBrushFromPermanentPolygonFloat(mempool_t *mempool, int numpoints, float *points, int supercontents); colbrushf_t *Collision_NewBrushFromPlanes(mempool_t *mempool, int numoriginalplanes, const mplane_t *originalplanes, int supercontents); void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end); +void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, int supercontents); +void Collision_TraceBrushTriangleMeshFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs); void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, const colbrushf_t *thatbrush_start, const colbrushf_t *thatbrush_end); -void Collision_TraceBrushPolygonFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points); -void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend); +void Collision_TraceLinePolygonFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numpoints, const float *points, int supercontents); +void Collision_TraceLineTriangleMeshFloat(trace_t *trace, const vec3_t linestart, const vec3_t lineend, int numtriangles, const int *element3i, const float *vertex3f, int supercontents, const vec3_t segmentmins, const vec3_t segmentmaxs); + +void Collision_TraceBrushPolygonTransformFloat(trace_t *trace, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int numpoints, const float *points, const matrix4x4_t *polygonmatrixstart, const matrix4x4_t *polygonmatrixend, int supercontents); colbrushf_t *Collision_BrushForBox(const matrix4x4_t *matrix, const vec3_t mins, const vec3_t maxs); +void Collision_BoundingBoxOfBrushTraceSegment(const colbrushf_t *start, const colbrushf_t *end, vec3_t mins, vec3_t maxs, float startfrac, float endfrac); + #endif diff --git a/gl_rsurf.c b/gl_rsurf.c index 2262d66f..8f3af75a 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -37,6 +37,7 @@ cvar_t r_testvis = {0, "r_testvis", "0"}; cvar_t r_floatbuildlightmap = {0, "r_floatbuildlightmap", "0"}; cvar_t r_detailtextures = {CVAR_SAVE, "r_detailtextures", "1"}; cvar_t r_surfaceworldnode = {0, "r_surfaceworldnode", "1"}; +cvar_t r_drawcollisionbrushes_polygonoffset = {0, "r_drawcollisionbrushes_polygonoffset", "-4"}; static int dlightdivtable[32768]; @@ -1914,7 +1915,7 @@ void R_DrawCollisionBrush(colbrushf_t *brush) void R_Q3BSP_DrawFace(entity_render_t *ent, q3mface_t *face) { rmeshstate_t m; - if ((face->texture->renderflags & Q3MTEXTURERENDERFLAGS_NODRAW) || !face->numtriangles) + if ((face->texture->renderflags & Q3MTEXTURERENDERFLAGS_NODRAW) || !face->numtriangles || R_CullBox(face->mins, face->maxs)) return; memset(&m, 0, sizeof(m)); GL_BlendFunc(GL_ONE, GL_ZERO); @@ -1949,6 +1950,7 @@ void R_Q3BSP_RecursiveWorldNode(entity_render_t *ent, q3mnode_t *node, const vec { int i; q3mleaf_t *leaf; + q3mface_t *face; while (node->isnode) { if (R_CullBox(node->mins, node->maxs)) @@ -1963,10 +1965,11 @@ void R_Q3BSP_RecursiveWorldNode(entity_render_t *ent, q3mnode_t *node, const vec { for (i = 0;i < leaf->numleaffaces;i++) { - if (leaf->firstleafface[i]->markframe != markframe) + face = leaf->firstleafface[i]; + if (face->markframe != markframe) { - leaf->firstleafface[i]->markframe = markframe; - R_Q3BSP_DrawFace(ent, leaf->firstleafface[i]); + face->markframe = markframe; + R_Q3BSP_DrawFace(ent, face); } } } @@ -1982,10 +1985,12 @@ void R_Q3BSP_Draw(entity_render_t *ent) model_t *model; qbyte *pvs; static int markframe = 0; + qglEnable(GL_POLYGON_OFFSET_FILL); R_Mesh_Matrix(&ent->matrix); model = ent->model; if (r_drawcollisionbrushes.integer < 2) { + qglPolygonOffset(1.0f, 0); if (ent == &cl_entities[0].render && model->brushq3.num_pvsclusters && !r_novis.integer) { Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg); @@ -2004,10 +2009,12 @@ void R_Q3BSP_Draw(entity_render_t *ent) GL_DepthMask(false); GL_DepthTest(true); R_Mesh_State_Texture(&m); + qglPolygonOffset(1.0f, r_drawcollisionbrushes_polygonoffset.value); for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) if (model->brushq3.data_thismodel->firstbrush[i].colbrushf && model->brushq3.data_thismodel->firstbrush[i].colbrushf->numtriangles) R_DrawCollisionBrush(model->brushq3.data_thismodel->firstbrush[i].colbrushf); } + qglDisable(GL_POLYGON_OFFSET_FILL); } /* @@ -2022,11 +2029,41 @@ void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, } */ -/* void R_Q3BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz) { + int i; + q3mface_t *face; + vec3_t modelorg; + model_t *model; + qbyte *pvs; + static int markframe = 0; + R_Mesh_Matrix(&ent->matrix); + model = ent->model; + if (r_drawcollisionbrushes.integer < 2) + { + if (ent == &cl_entities[0].render && model->brushq3.num_pvsclusters && !r_novis.integer) + { + Matrix4x4_Transform(&ent->inversematrix, r_origin, modelorg); + pvs = model->brush.GetPVS(model, modelorg); + R_Q3BSP_RecursiveWorldNode(ent, model->brushq3.data_nodes, modelorg, pvs, ++markframe); + } + else + for (i = 0, face = model->brushq3.data_thismodel->firstface;i < model->brushq3.data_thismodel->numfaces;i++, face++) + R_Q3BSP_DrawFace(ent, face); + } + if (r_drawcollisionbrushes.integer >= 1) + { + rmeshstate_t m; + memset(&m, 0, sizeof(m)); + GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); + GL_DepthMask(false); + GL_DepthTest(true); + R_Mesh_State_Texture(&m); + for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) + if (model->brushq3.data_thismodel->firstbrush[i].colbrushf && model->brushq3.data_thismodel->firstbrush[i].colbrushf->numtriangles) + R_DrawCollisionBrush(model->brushq3.data_thismodel->firstbrush[i].colbrushf); + } } -*/ static void gl_surf_start(void) { @@ -2055,6 +2092,7 @@ void GL_Surf_Init(void) Cvar_RegisterVariable(&r_floatbuildlightmap); Cvar_RegisterVariable(&r_detailtextures); Cvar_RegisterVariable(&r_surfaceworldnode); + Cvar_RegisterVariable(&r_drawcollisionbrushes_polygonoffset); R_RegisterModule("GL_Surf", gl_surf_start, gl_surf_shutdown, gl_surf_newmap); } diff --git a/mathlib.h b/mathlib.h index a005cc2b..c5a67abd 100644 --- a/mathlib.h +++ b/mathlib.h @@ -71,7 +71,8 @@ extern vec3_t vec3_origin; #define VectorNormalizeDouble(v) {double ilength = sqrt(DotProduct(v,v));if (ilength) ilength = 1.0 / ilength;v[0] *= ilength;v[1] *= ilength;v[2] *= ilength;} #define VectorDistance2(a, b) (((a)[0] - (b)[0]) * ((a)[0] - (b)[0]) + ((a)[1] - (b)[1]) * ((a)[1] - (b)[1]) + ((a)[2] - (b)[2]) * ((a)[2] - (b)[2])) #define VectorDistance(a, b) (sqrt(VectorDistance2(a,b))) -#define VectorLength(a) sqrt(DotProduct(a, a)) +#define VectorLength(a) (sqrt(DotProduct(a, a))) +#define VectorLength2(a) (DotProduct(a, a)) #define VectorScale(in, scale, out) ((out)[0] = (in)[0] * (scale),(out)[1] = (in)[1] * (scale),(out)[2] = (in)[2] * (scale)) #define VectorCompare(a,b) (((a)[0]==(b)[0])&&((a)[1]==(b)[1])&&((a)[2]==(b)[2])) #define VectorMA(a, scale, b, c) ((c)[0] = (a)[0] + (scale) * (b)[0],(c)[1] = (a)[1] + (scale) * (b)[1],(c)[2] = (a)[2] + (scale) * (b)[2]) @@ -91,10 +92,11 @@ extern vec3_t vec3_origin; }\ } #define VectorRandom(v) do{(v)[0] = lhrandom(-1, 1);(v)[1] = lhrandom(-1, 1);(v)[2] = lhrandom(-1, 1);}while(DotProduct(v, v) > 1) -#define VectorBlend(b1, b2, blend, c) do{float iblend = 1 - (blend);VectorMAM(iblend, b1, blend, b2, c);}while(0) #define VectorLerp(v1,lerp,v2,c) ((c)[0] = (v1)[0] + (lerp) * ((v2)[0] - (v1)[0]), (c)[1] = (v1)[1] + (lerp) * ((v2)[1] - (v1)[1]), (c)[2] = (v1)[2] + (lerp) * ((v2)[2] - (v1)[2])) #define BoxesOverlap(a,b,c,d) ((a)[0] <= (d)[0] && (b)[0] >= (c)[0] && (a)[1] <= (d)[1] && (b)[1] >= (c)[1] && (a)[2] <= (d)[2] && (b)[2] >= (c)[2]) +#define TriangleNormal(a,b,c,n) ((n)[0] = ((a)[1] - (b)[1]) * ((c)[2] - (b)[2]) - ((a)[2] - (b)[2]) * ((c)[1] - (b)[1]), (n)[1] = ((a)[2] - (b)[2]) * ((c)[0] - (b)[0]) - ((a)[0] - (b)[0]) * ((c)[2] - (b)[2]), (n)[2] = ((a)[0] - (b)[0]) * ((c)[1] - (b)[1]) - ((a)[1] - (b)[1]) * ((c)[0] - (b)[0])) + // fast PointInfrontOfTriangle // subtracts v1 from v0 and v2, combined into a crossproduct, combined with a // dotproduct of the light location relative to the first point of the diff --git a/model_brush.c b/model_brush.c index 582e4ab7..4ead7ce6 100644 --- a/model_brush.c +++ b/model_brush.c @@ -34,8 +34,9 @@ cvar_t r_novis = {0, "r_novis", "0"}; cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"}; cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"}; cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"}; -cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"}; -cvar_t r_curves_subdivide_level = {0, "r_curves_subdivide_level", "2"}; +cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"}; +cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"}; +cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"}; void Mod_BrushInit(void) { @@ -45,8 +46,9 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&r_miplightmaps); Cvar_RegisterVariable(&r_lightmaprgba); Cvar_RegisterVariable(&r_nosurftextures); - Cvar_RegisterVariable(&r_sortsurfaces); - Cvar_RegisterVariable(&r_curves_subdivide_level); + Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level); + Cvar_RegisterVariable(&mod_q3bsp_curves_collisions); + Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline); memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis)); } @@ -3553,6 +3555,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) float *originalcolor4f; float *originaltexcoordtexture2f; float *originaltexcoordlightmap2f; + float *v; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -3655,8 +3658,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) continue; } // convert patch to Q3FACETYPE_MESH - xlevel = r_curves_subdivide_level.integer; - ylevel = r_curves_subdivide_level.integer; + xlevel = mod_q3bsp_curves_subdivide_level.integer; + ylevel = mod_q3bsp_curves_subdivide_level.integer; finalwidth = ((patchsize[0] - 1) << xlevel) + 1; finalheight = ((patchsize[1] - 1) << ylevel) + 1; finalvertices = finalwidth * finalheight; @@ -3709,6 +3712,16 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) row1++; } } + out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f); + if (developer.integer) + { + if (out->numtriangles < finaltriangles) + Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->numvertices, finaltriangles, finaltriangles - out->numtriangles, out->numtriangles); + else + Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles); + } + // q3map does not put in collision brushes for curves... ugh + out->collisions = true; break; case Q3FACETYPE_FLARE: Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name); @@ -3734,6 +3747,29 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles); // for per pixel lighting Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f); + // calculate a bounding box + VectorClear(out->mins); + VectorClear(out->maxs); + if (out->numvertices) + { + VectorCopy(out->data_vertex3f, out->mins); + VectorCopy(out->data_vertex3f, out->maxs); + for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3) + { + out->mins[0] = min(out->mins[0], v[0]); + out->maxs[0] = max(out->maxs[0], v[0]); + out->mins[1] = min(out->mins[1], v[1]); + out->maxs[1] = max(out->maxs[1], v[1]); + out->mins[2] = min(out->mins[2], v[2]); + out->maxs[2] = max(out->maxs[2], v[2]); + } + out->mins[0] -= 1.0f; + out->mins[1] -= 1.0f; + out->mins[2] -= 1.0f; + out->maxs[0] += 1.0f; + out->maxs[1] += 1.0f; + out->maxs[2] += 1.0f; + } } } @@ -4051,8 +4087,12 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe) { int i, startside, endside; - float dist1, dist2, midfrac, mid[3]; + float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3]; q3mleaf_t *leaf; + q3mface_t *face; + colbrushf_t *brush; + if (startfrac >= trace->fraction) + return; // note: all line fragments past first impact fraction are ignored while (node->isnode) { @@ -4068,60 +4108,103 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node } else { + // line crosses node plane, split the line + midfrac = dist1 / (dist1 - dist2); + VectorLerp(linestart, midfrac, lineend, mid); // take the near side first - if (startfrac < trace->fraction) - { - // line crosses node plane, split the line - midfrac = dist1 / (dist1 - dist2); - VectorLerp(linestart, midfrac, lineend, mid); - Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe); - if (midfrac < trace->fraction) - Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe); - } + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe); + if (midfrac < trace->fraction) + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe); return; } } // hit a leaf + segmentmins[0] = min(start[0], end[0]); + segmentmins[1] = min(start[1], end[1]); + segmentmins[2] = min(start[2], end[2]); + segmentmaxs[0] = max(start[0], end[0]); + segmentmaxs[1] = max(start[1], end[1]); + segmentmaxs[2] = max(start[2], end[2]); leaf = (q3mleaf_t *)node; for (i = 0;i < leaf->numleafbrushes;i++) { - if (endfrac >= trace->fraction) + if (startfrac >= trace->fraction) return; - if (leaf->firstleafbrush[i]->colbrushf && leaf->firstleafbrush[i]->colbrushf->markframe != markframe) + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe) { - leaf->firstleafbrush[i]->colbrushf->markframe = markframe; - Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + brush->markframe = markframe; + if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs)) + Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + } + } + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < leaf->numleaffaces;i++) + { + if (startfrac >= trace->fraction) + return; + face = leaf->firstleafface[i]; + if (face->collisions && face->collisionmarkframe != markframe) + { + face->collisionmarkframe = markframe; + if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs)) + Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } } } } -static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe) +static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs) { - int i; - float dist; - colpointf_t *ps, *pe; + int i, sides; + float nodesegmentmins[3], nodesegmentmaxs[3]; q3mleaf_t *leaf; + colbrushf_t *brush; + q3mface_t *face; + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; if (node->isnode) { // recurse down node sides + sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane); + if (sides == 3) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); + } + else if (sides == 2) + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + else // sides == 1 + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + /* dist = node->plane->dist - (1.0f / 8.0f); for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++) { if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist) { - Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); break; } } + */ + /* dist = node->plane->dist + (1.0f / 8.0f); for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++) { if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist) { - Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); break; } } + */ /* sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane); if (sides & 1) @@ -4136,27 +4219,46 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod leaf = (q3mleaf_t *)node; for (i = 0;i < leaf->numleafbrushes;i++) { - if (leaf->firstleafbrush[i]->colbrushf && leaf->firstleafbrush[i]->colbrushf->markframe != markframe) + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs)) { - leaf->firstleafbrush[i]->colbrushf->markframe = markframe; + brush->markframe = markframe; Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); } } + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < leaf->numleaffaces;i++) + { + face = leaf->firstleafface[i]; + // note: this can not be optimized with a face->collisionmarkframe because each triangle of the face would need to be marked as done individually (because each one is bbox culled individually), and if all are marked, then the face could be marked as done + if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs)) + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } } } static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) { int i; + float segmentmins[3], segmentmaxs[3]; colbrushf_t *thisbrush_start, *thisbrush_end; matrix4x4_t startmatrix, endmatrix; static int markframe = 0; + q3mface_t *face; memset(trace, 0, sizeof(*trace)); trace->fraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; Matrix4x4_CreateIdentity(&startmatrix); Matrix4x4_CreateIdentity(&endmatrix); - if (VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs)) + segmentmins[0] = min(boxstartmins[0], boxendmins[0]); + segmentmins[1] = min(boxstartmins[1], boxendmins[1]); + segmentmins[2] = min(boxstartmins[2], boxendmins[2]); + segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]); + segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]); + segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]); + if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs)) { // line trace if (model->brushq3.submodel) @@ -4164,13 +4266,22 @@ static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxs for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++) + { + face = model->brushq3.data_thismodel->firstface + i; + if (face->collisions) + Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } } else Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe); } else { - // box trace, performed as box trace + // box trace, performed as brush trace thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs); thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs); if (model->brushq3.submodel) @@ -4178,9 +4289,18 @@ static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxs for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++) + { + face = model->brushq3.data_thismodel->firstface + i; + if (face->collisions) + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } } else - Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs); } } @@ -4311,6 +4431,8 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer) float corner[3], yawradius, modelradius; mod->type = mod_brushq3; + mod->numframes = 1; + mod->numskins = 1; header = (q3dheader_t *)buffer; diff --git a/model_shared.c b/model_shared.c index 393e7bb3..2d519e2c 100644 --- a/model_shared.c +++ b/model_shared.c @@ -1039,4 +1039,30 @@ int Mod_CountSkinFiles(skinfile_t *skinfile) return i; } +int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f) +{ + int i, outtriangles; + float d, edgedir[3], temp[3]; + // a degenerate triangle is one with no width (thickness, surface area) + // these are characterized by having all 3 points colinear (along a line) + // or having two points identical + for (i = 0, outtriangles = 0;i < numtriangles;i++, inelement3i += 3) + { + // calculate first edge + VectorSubtract(vertex3f + inelement3i[1] * 3, vertex3f + inelement3i[0] * 3, edgedir); + if (VectorLength2(edgedir) < 0.0001f) + continue; // degenerate first edge (no length) + VectorNormalize(edgedir); + // check if third point is on the edge (colinear) + d = -DotProduct(vertex3f + inelement3i[2] * 3, edgedir); + VectorMA(vertex3f + inelement3i[2] * 3, d, edgedir, temp); + if (VectorLength2(temp) < 0.0001f) + continue; // third point colinear with first edge + // valid triangle (no colinear points, no duplicate points) + VectorCopy(inelement3i, outelement3i); + outelement3i += 3; + outtriangles++; + } + return outtriangles; +} diff --git a/model_shared.h b/model_shared.h index 6e6605ff..bd73c39a 100644 --- a/model_shared.h +++ b/model_shared.h @@ -353,6 +353,8 @@ typedef struct q3mface_s struct q3mtexture_s *texture; struct q3meffect_s *effect; rtexture_t *lightmaptexture; + int collisions; // performs per triangle collisions on this surface + int collisionmarkframe; // don't collide twice in one trace int type; int firstvertex; int numvertices; @@ -360,6 +362,9 @@ typedef struct q3mface_s int numelements; int patchsize[2]; int markframe; + // bounding box for culling + float mins[3]; + float maxs[3]; float *data_vertex3f; float *data_texcoordtexture2f; @@ -602,5 +607,7 @@ skinfile_t *Mod_LoadSkinFiles(void); void Mod_FreeSkinFiles(skinfile_t *skinfile); int Mod_CountSkinFiles(skinfile_t *skinfile); +int Mod_RemoveDegenerateTriangles(int numtriangles, const int *inelement3i, int *outelement3i, const float *vertex3f); + #endif // __MODEL__ diff --git a/sv_main.c b/sv_main.c index cc999fe1..ada5adbc 100644 --- a/sv_main.c +++ b/sv_main.c @@ -513,7 +513,8 @@ void SV_WriteEntitiesToClient (client_t *client, edict_t *clent, sizebuf_t *msg) testorigin[1] = lhrandom(entmins[1], entmaxs[1]); testorigin[2] = lhrandom(entmins[2], entmaxs[2]); - if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL).fraction == 1) + sv.worldmodel->brush.TraceBox(sv.worldmodel, &trace, testeye, testeye, testorigin, testorigin, SUPERCONTENTS_SOLID); + if (trace.fraction == 1) client->visibletime[e] = realtime + 1; else { @@ -522,7 +523,8 @@ void SV_WriteEntitiesToClient (client_t *client, edict_t *clent, sizebuf_t *msg) testorigin[1] = bound(entmins[1], testeye[1], entmaxs[1]); testorigin[2] = bound(entmins[2], testeye[2], entmaxs[2]); - if (SV_Move(testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL).fraction == 1) + sv.worldmodel->brush.TraceBox(sv.worldmodel, &trace, testeye, testeye, testorigin, testorigin, SUPERCONTENTS_SOLID); + if (trace.fraction == 1) client->visibletime[e] = realtime + 1; else if (realtime > client->visibletime[e]) { @@ -679,16 +681,16 @@ void SV_WriteEntitiesToClient (client_t *client, edict_t *clent, sizebuf_t *msg) MSG_WriteShort (msg,e); else MSG_WriteByte (msg,e); - if (bits & U_MODEL) MSG_WriteByte (msg, ent->v->modelindex); - if (bits & U_FRAME) MSG_WriteByte (msg, ent->v->frame); - if (bits & U_COLORMAP) MSG_WriteByte (msg, ent->v->colormap); - if (bits & U_SKIN) MSG_WriteByte (msg, ent->v->skin); - if (bits & U_EFFECTS) MSG_WriteByte (msg, ent->v->effects); - if (bits & U_ORIGIN1) MSG_WriteDPCoord (msg, origin[0]); + if (bits & U_MODEL) MSG_WriteByte(msg, ent->v->modelindex); + if (bits & U_FRAME) MSG_WriteByte(msg, ent->v->frame); + if (bits & U_COLORMAP) MSG_WriteByte(msg, ent->v->colormap); + if (bits & U_SKIN) MSG_WriteByte(msg, ent->v->skin); + if (bits & U_EFFECTS) MSG_WriteByte(msg, ent->v->effects); + if (bits & U_ORIGIN1) MSG_WriteDPCoord(msg, origin[0]); if (bits & U_ANGLE1) MSG_WriteAngle(msg, angles[0]); - if (bits & U_ORIGIN2) MSG_WriteDPCoord (msg, origin[1]); + if (bits & U_ORIGIN2) MSG_WriteDPCoord(msg, origin[1]); if (bits & U_ANGLE2) MSG_WriteAngle(msg, angles[1]); - if (bits & U_ORIGIN3) MSG_WriteDPCoord (msg, origin[2]); + if (bits & U_ORIGIN3) MSG_WriteDPCoord(msg, origin[2]); if (bits & U_ANGLE3) MSG_WriteAngle(msg, angles[2]); // LordHavoc: new stuff @@ -928,7 +930,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) testorigin[0] = (entmins[0] + entmaxs[0]) * 0.5f; testorigin[1] = (entmins[1] + entmaxs[1]) * 0.5f; testorigin[2] = (entmins[2] + entmaxs[2]) * 0.5f; - trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL); + sv.worldmodel->brush.TraceBox(sv.worldmodel, &trace, sv_writeentitiestoclient_testeye, sv_writeentitiestoclient_testeye, testorigin, testorigin, SUPERCONTENTS_SOLID); if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs)) sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1; else @@ -937,7 +939,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) testorigin[0] = lhrandom(entmins[0], entmaxs[0]); testorigin[1] = lhrandom(entmins[1], entmaxs[1]); testorigin[2] = lhrandom(entmins[2], entmaxs[2]); - trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL); + sv.worldmodel->brush.TraceBox(sv.worldmodel, &trace, sv_writeentitiestoclient_testeye, sv_writeentitiestoclient_testeye, testorigin, testorigin, SUPERCONTENTS_SOLID); if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs)) sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1; else @@ -948,7 +950,7 @@ void SV_MarkWriteEntityStateToClient(entity_state_t *s) testorigin[0] = lhrandom(lightmins[0], lightmaxs[0]); testorigin[1] = lhrandom(lightmins[1], lightmaxs[1]); testorigin[2] = lhrandom(lightmins[2], lightmaxs[2]); - trace = SV_Move(sv_writeentitiestoclient_testeye, vec3_origin, vec3_origin, testorigin, MOVE_WORLDONLY, NULL); + sv.worldmodel->brush.TraceBox(sv.worldmodel, &trace, sv_writeentitiestoclient_testeye, sv_writeentitiestoclient_testeye, testorigin, testorigin, SUPERCONTENTS_SOLID); if (trace.fraction == 1 || BoxesOverlap(trace.endpos, trace.endpos, entmins, entmaxs)) sv_writeentitiestoclient_client->visibletime[s->number] = realtime + 1; } diff --git a/sv_phys.c b/sv_phys.c index 2bae3238..6cfa187e 100644 --- a/sv_phys.c +++ b/sv_phys.c @@ -521,7 +521,7 @@ trace_t SV_PushEntity (edict_t *ent, vec3_t push, vec3_t pushangles) ent->v->angles[1] += trace.fraction * pushangles[1]; SV_LinkEdict (ent, true); - if (trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent))) + if (trace.fraction < 1 && trace.ent && (!((int)ent->v->flags & FL_ONGROUND) || ent->v->groundentity != EDICT_TO_PROG(trace.ent))) SV_Impact (ent, trace.ent); return trace; } diff --git a/todo b/todo index 1d0499a6..c33a80ba 100644 --- a/todo +++ b/todo @@ -3,6 +3,16 @@ -n darkplaces: fix a crash when changing level while using qe1 textures (Todd) -n darkplaces: revert noclip movement to match nq for compatibility with mods that trap movement as input (MauveBib) -n dpmod: make grapple off-hand (joe hill) +2 darkplaces: write a readme (Antti) +0 darkplaces: add graphics options menu and put realtime lighting stuff in it (Antti) +0 darkplaces: add cl_particles_particleffect_bloodhack cvar to menu (Alex Boveri) +0 darkplaces: add cl_particles_particleffect_bloodhack cvar to enable converting id1 blood effects to TE_BLOOD style (Alex Boveri) +0 darkplaces: add slowmo to options menu (Cristian Beltramo) +0 darkplaces: noclipping out the ceiling of q3dm17 crashes (Static_Fiend) +0 dpmod: add support for info_player_deathmatch in singleplayer for q3 compatibility (Static_Fiend) +0 dpmod: add target_position entity for a touch of q3 compatibility on jumppads (Static_Fiend) +0 darkplaces: add support for multiple -game's (note: this needs an enhanced COM_CheckParm to find the multiple matches) (Static_Fiend) +0 darkplaces: dedicated server hosting prydon with multiple players exhibited severe networking bugs in tests, including failure to find acked network frames, and a segfault (Supajoe, Uffe, FrikaC, Harb) 0 darkplaces: add chase_pitch cvar to control pitch angle of chase camera, and chase_angle cvar to control yaw angle of chase camera, and add back chase_right cvar (Electro) 0 darkplaces: add a scr_screenshot_jpeg_quality cvar (Electro) 0 darkplaces: shadows are not working with model tag attachments (Electro) diff --git a/world.c b/world.c index 5b028c4d..7310d90d 100644 --- a/world.c +++ b/world.c @@ -53,9 +53,6 @@ typedef struct // size when clipping against monsters vec3_t mins2, maxs2; - // size when clipping against brush models - vec3_t hullmins, hullmaxs; - // start and end origin of move vec3_t start, end; @@ -484,22 +481,28 @@ trace_t SV_ClipMoveToEntity(edict_t *ent, const vec3_t start, const vec3_t mins, Matrix4x4_Invert_Simple(&imatrix, &matrix); Matrix4x4_Transform(&imatrix, start, starttransformed); Matrix4x4_Transform(&imatrix, end, endtransformed); - VectorAdd(starttransformed, maxs, starttransformedmaxs); - VectorAdd(endtransformed, maxs, endtransformedmaxs); - VectorAdd(starttransformed, mins, starttransformedmins); - VectorAdd(endtransformed, mins, endtransformedmins); if (model && model->brush.TraceBox) + { + VectorAdd(starttransformed, maxs, starttransformedmaxs); + VectorAdd(endtransformed, maxs, endtransformedmaxs); + VectorAdd(starttransformed, mins, starttransformedmins); + VectorAdd(endtransformed, mins, endtransformedmins); model->brush.TraceBox(model, &trace, starttransformedmins, starttransformedmaxs, endtransformedmins, endtransformedmaxs, SUPERCONTENTS_SOLID); + } else Collision_ClipTrace_Box(&trace, ent->v->mins, ent->v->maxs, starttransformed, mins, maxs, endtransformed, SUPERCONTENTS_SOLID, SUPERCONTENTS_SOLID); if (trace.fraction < 1 || trace.startsolid) + { trace.ent = ent; - VectorLerp(start, trace.fraction, end, trace.endpos); - VectorCopy(trace.plane.normal, tempnormal); - Matrix4x4_Transform3x3(&matrix, tempnormal, trace.plane.normal); - // FIXME: should recalc trace.plane.dist + VectorLerp(start, trace.fraction, end, trace.endpos); + VectorCopy(trace.plane.normal, tempnormal); + Matrix4x4_Transform3x3(&matrix, tempnormal, trace.plane.normal); + // FIXME: should recalc trace.plane.dist + } + else + VectorCopy(end, trace.endpos); return trace; } @@ -561,7 +564,7 @@ void SV_ClipToNode(moveclip_t *clip, link_t *list) // might interact, so do an exact clip if (touch->v->solid == SOLID_BSP) - trace = SV_ClipMoveToEntity (touch, clip->start, clip->hullmins, clip->hullmaxs, clip->end); + trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins, clip->maxs, clip->end); else if ((int)touch->v->flags & FL_MONSTER) trace = SV_ClipMoveToEntity (touch, clip->start, clip->mins2, clip->maxs2, clip->end); else @@ -600,7 +603,7 @@ SV_Move trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, edict_t *passedict) { moveclip_t clip; - vec3_t bigmins, bigmaxs; + vec3_t hullmins, hullmaxs; areagrid_t *grid; int i, igrid[3], igridmins[3], igridmaxs[3]; @@ -610,64 +613,46 @@ trace_t SV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const VectorCopy(end, clip.end); VectorCopy(mins, clip.mins); VectorCopy(maxs, clip.maxs); - VectorCopy(mins, clip.hullmins); - VectorCopy(maxs, clip.hullmaxs); + VectorCopy(mins, clip.mins2); + VectorCopy(maxs, clip.maxs2); clip.type = type; clip.passedict = passedict; - if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize) - sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clip.mins, clip.maxs, clip.hullmins, clip.hullmaxs); - // clip to world - clip.trace = SV_ClipMoveToEntity(sv.edicts, clip.start, clip.hullmins, clip.hullmaxs, clip.end); + clip.trace = SV_ClipMoveToEntity(sv.edicts, clip.start, clip.mins, clip.maxs, clip.end); if (clip.type == MOVE_WORLDONLY) //if (clip.trace.allsolid) return clip.trace; if (clip.type == MOVE_MISSILE) { - // LordHavoc: modified this, was = -15, now = clip.mins[i] - 15 - for (i=0 ; i<3 ; i++) + // LordHavoc: modified this, was = -15, now -= 15 + for (i = 0;i < 3;i++) { - clip.mins2[i] = clip.mins[i] - 15; - clip.maxs2[i] = clip.maxs[i] + 15; + clip.mins2[i] -= 15; + clip.maxs2[i] += 15; } } + + // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp + if (sv.worldmodel && sv.worldmodel->brush.RoundUpToHullSize) + sv.worldmodel->brush.RoundUpToHullSize(sv.worldmodel, clip.mins, clip.maxs, hullmins, hullmaxs); else { - VectorCopy (clip.mins, clip.mins2); - VectorCopy (clip.maxs, clip.maxs2); + VectorCopy(clip.mins, hullmins); + VectorCopy(clip.maxs, hullmaxs); } - bigmins[0] = min(clip.mins2[0], clip.hullmins[0]); - bigmaxs[0] = max(clip.maxs2[0], clip.hullmaxs[0]); - bigmins[1] = min(clip.mins2[1], clip.hullmins[1]); - bigmaxs[1] = max(clip.maxs2[1], clip.hullmaxs[1]); - bigmins[2] = min(clip.mins2[2], clip.hullmins[2]); - bigmaxs[2] = max(clip.maxs2[2], clip.hullmaxs[2]); - // create the bounding box of the entire move - if (!sv_debugmove.integer) + for (i = 0;i < 3;i++) { - int i; - - for (i=0 ; i<3 ; i++) - { - if (clip.trace.endpos[i] > clip.start[i]) - { - clip.boxmins[i] = clip.start[i] + bigmins[i] - 1; - clip.boxmaxs[i] = clip.trace.endpos[i] + bigmaxs[i] + 1; - } - else - { - clip.boxmins[i] = clip.trace.endpos[i] + bigmins[i] - 1; - clip.boxmaxs[i] = clip.start[i] + bigmaxs[i] + 1; - } - } + clip.boxmins[i] = min(clip.start[i], clip.trace.endpos[i]) + min(hullmins[i], clip.mins2[i]) - 1; + clip.boxmaxs[i] = max(clip.start[i], clip.trace.endpos[i]) + max(hullmaxs[i], clip.maxs2[i]) + 1; } - else + + // debug override to test against everything + if (sv_debugmove.integer) { - // debug to test against everything clip.boxmins[0] = clip.boxmins[1] = clip.boxmins[2] = -999999999; clip.boxmaxs[0] = clip.boxmaxs[1] = clip.boxmaxs[2] = 999999999; }