]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - collision.c
reduce number of varying variables required in shaders by 1
[xonotic/darkplaces.git] / collision.c
index 56666afdf5532da0131f46c899a4cf3bcbfc2c01..fbc8a1cbbe5ba99999de5c5ed7994cb21e8c0805 100644 (file)
@@ -19,6 +19,9 @@ cvar_t collision_prefernudgedfraction = {0, "collision_prefernudgedfraction", "1
 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)
 {
@@ -32,6 +35,9 @@ void Collision_Init (void)
        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);
 }
 
 
@@ -725,10 +731,6 @@ void Collision_TraceBrushBrushFloat(trace_t *trace, const colbrushf_t *trace_sta
                        VectorCopy(startplane, startdepthnormal);
                }
 
-               if (startdist >= -collision_impactnudge.value && enddist >= startdist)
-                       return;
-               if (startdist <= 0 && enddist <= 0)
-                       continue;
                if (startdist > enddist)
                {
                        // moving into brush
@@ -901,10 +903,6 @@ void Collision_TraceLineBrushFloat(trace_t *trace, const vec3_t linestart, const
                        VectorCopy(startplane, startdepthnormal);
                }
 
-               if (startdist >= -collision_impactnudge.value && enddist >= startdist)
-                       return;
-               if (startdist <= 0 && enddist <= 0)
-                       continue;
                if (startdist > enddist)
                {
                        // moving into brush
@@ -1246,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)
@@ -1617,9 +1615,223 @@ 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_s
+{
+       int next;
+       int sequence;
+       dp_model_t *model;
+//     const frameblend_t *frameblend;
+//     const skeleton_t *skeleton;
+       vec3_t bodymins;
+       vec3_t bodymaxs;
+       int bodysupercontents;
+       matrix4x4_t matrix;
+       matrix4x4_t inversematrix;
+       vec3_t start;
+       vec3_t mins;
+       vec3_t maxs;
+       vec3_t end;
+       int hitsupercontentsmask;
+       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_hash;
+
+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 (resetlimits || !collision_cachedtrace_max)
+               collision_cachedtrace_max = 1024;
+       collision_cachedtrace_firstfree = 1;
+       collision_cachedtrace_lastused = 0;
+       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_max * sizeof(int));
+       collision_cachedtrace_sequence = 1;
+}
+
+void Collision_Cache_Init(mempool_t *mempool)
+{
+       collision_cachedtrace_mempool = mempool;
+       Collision_Cache_Reset(true);
+}
+
+void Collision_Cache_NewFrame(void)
+{
+       int hashindex;
+       int index;
+       int *p;
+       // unlink all stale traces
+       for (hashindex = 0;hashindex < collision_cachedtrace_max;hashindex++)
+       {
+               if (!collision_cachedtrace_hash[hashindex])
+                       continue;
+               p = &collision_cachedtrace_hash[hashindex];
+               while ((index = *p))
+               {
+                       if (collision_cachedtrace_array[index].sequence != collision_cachedtrace_sequence)
+                       {
+                               if (collision_cachedtrace_firstfree > index)
+                                       collision_cachedtrace_firstfree = index;
+                               *p = collision_cachedtrace_array[index].next;
+                               collision_cachedtrace_array[index].sequence = 0;
+                               //memset(&collision_cachedtrace_array[index], 0, sizeof(collision_cachedtrace_array[index]));
+                       }
+                       else
+                               p = &collision_cachedtrace_array[index].next;
+               }
+       }
+       // shrink used range if possible
+       index = collision_cachedtrace_lastused;
+       while (index && collision_cachedtrace_array[index].sequence == 0)
+               index--;
+       collision_cachedtrace_lastused = index;
+       // increment sequence
+       collision_cachedtrace_sequence++;
+       // do not allow sequence to wrap to 0
+       if (collision_cachedtrace_sequence >= 1<<30)
+               collision_cachedtrace_sequence = 1;
+}
+
+static collision_cachedtrace_t *Collision_Cache_Lookup(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, const matrix4x4_t *matrix, const matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
+{
+       int hashindex = 0;
+       int index = 0;
+       collision_cachedtrace_t *cached = collision_cachedtrace_array + index;
+       // all non-cached traces use the same index
+       if ((frameblend && frameblend[0].lerp != 1) || (skeleton && skeleton->relativetransforms))
+               r_refdef.stats.collisioncache_animated++;
+       else if (!collision_cache.integer)
+               r_refdef.stats.collisioncache_traced++;
+       else
+       {
+               // cached trace lookup
+               hashindex = (int)(((size_t)model + (size_t) + (size_t)(VectorLength2(bodymins) + VectorLength2(bodymaxs) + start[0] + start[1] + start[2] + end[0] + end[1] + end[2]) + bodysupercontents + hitsupercontentsmask) % collision_cachedtrace_max);
+               for (index = collision_cachedtrace_hash[hashindex];index;index = cached->next)
+               {
+                       cached = collision_cachedtrace_array + index;
+                       if (cached->model == model
+                        && VectorCompare(cached->bodymins, bodymins)
+                        && VectorCompare(cached->bodymaxs, bodymaxs)
+                        && cached->bodysupercontents == bodysupercontents
+                        && VectorCompare(cached->start, start)
+                        && VectorCompare(cached->mins, mins)
+                        && VectorCompare(cached->maxs, maxs)
+                        && VectorCompare(cached->end, end)
+                        && cached->hitsupercontentsmask == hitsupercontentsmask
+                        && !memcmp(&cached->matrix, matrix, sizeof(*matrix)))
+                       {
+                               r_refdef.stats.collisioncache_cached++;
+                               return cached; // found a match
+                       }
+               }
+               r_refdef.stats.collisioncache_traced++;
+               // find an unused cache entry
+               for (index = collision_cachedtrace_firstfree;index <= collision_cachedtrace_lastused;index++)
+                       if (!collision_cachedtrace_array[index].sequence)
+                               break;
+               collision_cachedtrace_firstfree = index;
+               if (index > collision_cachedtrace_lastused)
+               {
+                       // see if we need to reset the cache for growth
+                       if (collision_cachedtrace_max <= index)
+                       {
+                               collision_cachedtrace_max *= 2;
+                               Collision_Cache_Reset(false);
+                               collision_cachedtrace_firstfree = index = 1;
+                       }
+                       collision_cachedtrace_lastused = index;
+               }
+               // link the new cache entry into the hash bucket
+               cached = collision_cachedtrace_array + index;
+               cached->next = collision_cachedtrace_hash[hashindex];
+               collision_cachedtrace_hash[hashindex] = index;
+               cached->model = model;
+               VectorCopy(bodymins, cached->bodymins);
+               VectorCopy(bodymaxs, cached->bodymaxs);
+               cached->bodysupercontents = bodysupercontents;
+               VectorCopy(start, cached->start);
+               VectorCopy(mins,  cached->mins);
+               VectorCopy(maxs,  cached->maxs);
+               VectorCopy(end,   cached->end);
+               cached->hitsupercontentsmask = hitsupercontentsmask;
+               cached->matrix = *matrix;
+               cached->inversematrix = *inversematrix;
+       }
+       cached->sequence = 0;
+       return cached;
+}
+
 void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask)
 {
        float starttransformed[3], endtransformed[3];
+       collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, frameblend, skeleton, bodymins, bodymaxs, bodysupercontents, matrix, inversematrix, start, mins, maxs, end, hitsupercontentsmask);
+       if (cached->sequence)
+       {
+               cached->sequence = collision_cachedtrace_sequence;
+               *trace = cached->result;
+               return;
+       }
 
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
@@ -1631,8 +1843,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);
@@ -1641,22 +1872,44 @@ void Collision_ClipToGenericEntity(trace_t *trace, dp_model_t *model, const fram
        // transform plane
        // NOTE: this relies on plane.dist being directly after plane.normal
        Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
+
+       cached->sequence = collision_cachedtrace_sequence;
+       cached->result = *trace;
 }
 
 void Collision_ClipToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontents)
 {
+       collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, NULL, NULL, vec3_origin, vec3_origin, 0, &identitymatrix, &identitymatrix, start, mins, maxs, end, hitsupercontents);
+       if (cached->sequence)
+       {
+               cached->sequence = collision_cachedtrace_sequence;
+               *trace = cached->result;
+               return;
+       }
+
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
+       // ->TraceBox: TraceBrush not needed here, as worldmodel is never rotated
        if (model && model->TraceBox)
                model->TraceBox(model, NULL, NULL, trace, start, mins, maxs, end, hitsupercontents);
        trace->fraction = bound(0, trace->fraction, 1);
        trace->realfraction = bound(0, trace->realfraction, 1);
        VectorLerp(start, trace->fraction, end, trace->endpos);
+
+       cached->sequence = collision_cachedtrace_sequence;
+       cached->result = *trace;
 }
 
-void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, const vec3_t end, int hitsupercontentsmask, qboolean hitsurfaces)
 {
        float starttransformed[3], endtransformed[3];
+       collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, frameblend, skeleton, bodymins, bodymaxs, bodysupercontents, matrix, inversematrix, start, vec3_origin, vec3_origin, end, hitsupercontentsmask);
+       if (cached->sequence)
+       {
+               cached->sequence = collision_cachedtrace_sequence;
+               *trace = cached->result;
+               return;
+       }
 
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
@@ -1667,7 +1920,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);
@@ -1678,22 +1933,45 @@ void Collision_ClipLineToGenericEntity(trace_t *trace, dp_model_t *model, const
        // transform plane
        // NOTE: this relies on plane.dist being directly after plane.normal
        Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
+
+       cached->sequence = collision_cachedtrace_sequence;
+       cached->result = *trace;
 }
 
-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)
 {
+       collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, NULL, NULL, vec3_origin, vec3_origin, 0, &identitymatrix, &identitymatrix, start, vec3_origin, vec3_origin, end, hitsupercontents);
+       if (cached->sequence)
+       {
+               cached->sequence = collision_cachedtrace_sequence;
+               *trace = cached->result;
+               return;
+       }
+
        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);
        VectorLerp(start, trace->fraction, end, trace->endpos);
+
+       cached->sequence = collision_cachedtrace_sequence;
+       cached->result = *trace;
 }
 
 void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, const vec3_t bodymins, const vec3_t bodymaxs, int bodysupercontents, matrix4x4_t *matrix, matrix4x4_t *inversematrix, const vec3_t start, int hitsupercontentsmask)
 {
        float starttransformed[3];
+       collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, frameblend, skeleton, bodymins, bodymaxs, bodysupercontents, matrix, inversematrix, start, vec3_origin, vec3_origin, start, hitsupercontentsmask);
+       if (cached->sequence)
+       {
+               cached->sequence = collision_cachedtrace_sequence;
+               *trace = cached->result;
+               return;
+       }
 
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
@@ -1712,15 +1990,29 @@ void Collision_ClipPointToGenericEntity(trace_t *trace, dp_model_t *model, const
        // transform plane
        // NOTE: this relies on plane.dist being directly after plane.normal
        Matrix4x4_TransformPositivePlane(matrix, trace->plane.normal[0], trace->plane.normal[1], trace->plane.normal[2], trace->plane.dist, trace->plane.normal);
+
+       cached->sequence = collision_cachedtrace_sequence;
+       cached->result = *trace;
 }
 
 void Collision_ClipPointToWorld(trace_t *trace, dp_model_t *model, const vec3_t start, int hitsupercontents)
 {
+       collision_cachedtrace_t *cached = Collision_Cache_Lookup(model, NULL, NULL, vec3_origin, vec3_origin, 0, &identitymatrix, &identitymatrix, start, vec3_origin, vec3_origin, start, hitsupercontents);
+       if (cached->sequence)
+       {
+               cached->sequence = collision_cachedtrace_sequence;
+               *trace = cached->result;
+               return;
+       }
+
        memset(trace, 0, sizeof(*trace));
        trace->fraction = trace->realfraction = 1;
        if (model && model->TracePoint)
                model->TracePoint(model, NULL, NULL, trace, start, hitsupercontents);
        VectorCopy(start, trace->endpos);
+
+       cached->sequence = collision_cachedtrace_sequence;
+       cached->result = *trace;
 }
 
 void Collision_CombineTraces(trace_t *cliptrace, const trace_t *trace, void *touch, qboolean isbmodel)