]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
use bounding boxes for shadowmap side culling instead of spheres for better accuracy
[xonotic/darkplaces.git] / model_brush.c
index 996bcda2f7e218becbc151f299e8947d368475e0..d30aca745cabf425037349fcc7002c1f0e16dc75 100644 (file)
@@ -39,6 +39,8 @@ cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess"
 cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"};
 cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"};
 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
+cvar_t mod_q3bsp_curves_collisions_stride = {0, "mod_q3bsp_curves_collisions_stride", "16", "collisions against curves: optimize performance by doing a combined collision check for this triangle amount first"};
+cvar_t mod_q3bsp_curves_stride = {0, "mod_q3bsp_curves_stride", "16", "particle effect collisions against curves: optimize performance by doing a combined collision check for this triangle amount first"};
 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
 cvar_t mod_q3bsp_lightmapmergepower = {CVAR_SAVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
@@ -65,6 +67,8 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
        Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_collisions_stride);
+       Cvar_RegisterVariable(&mod_q3bsp_curves_stride);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
        Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
        Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
@@ -411,6 +415,7 @@ static int Mod_Q1BSP_BoxTouchingVisibleLeafs(dp_model_t *model, const unsigned c
 typedef struct findnonsolidlocationinfo_s
 {
        vec3_t center;
+       vec3_t absmin, absmax;
        vec_t radius;
        vec3_t nudge;
        vec_t bestdist;
@@ -418,100 +423,128 @@ typedef struct findnonsolidlocationinfo_s
 }
 findnonsolidlocationinfo_t;
 
-static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
+static void Mod_Q1BSP_FindNonSolidLocation_r_Triangle(findnonsolidlocationinfo_t *info, msurface_t *surface, int k)
 {
-       int i, surfacenum, k, *tri, *mark;
+       int i, *tri;
        float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
+
+       tri = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle) + k * 3;
+       VectorCopy((info->model->surfmesh.data_vertex3f + tri[0] * 3), vert[0]);
+       VectorCopy((info->model->surfmesh.data_vertex3f + tri[1] * 3), vert[1]);
+       VectorCopy((info->model->surfmesh.data_vertex3f + tri[2] * 3), vert[2]);
+       VectorSubtract(vert[1], vert[0], edge[0]);
+       VectorSubtract(vert[2], vert[1], edge[1]);
+       CrossProduct(edge[1], edge[0], facenormal);
+       if (facenormal[0] || facenormal[1] || facenormal[2])
+       {
+               VectorNormalize(facenormal);
+               f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
+               if (f <= info->bestdist && f >= -info->bestdist)
+               {
+                       VectorSubtract(vert[0], vert[2], edge[2]);
+                       VectorNormalize(edge[0]);
+                       VectorNormalize(edge[1]);
+                       VectorNormalize(edge[2]);
+                       CrossProduct(facenormal, edge[0], edgenormal[0]);
+                       CrossProduct(facenormal, edge[1], edgenormal[1]);
+                       CrossProduct(facenormal, edge[2], edgenormal[2]);
+                       // face distance
+                       if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
+                                       && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
+                                       && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
+                       {
+                               // we got lucky, the center is within the face
+                               dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
+                               if (dist < 0)
+                               {
+                                       dist = -dist;
+                                       if (info->bestdist > dist)
+                                       {
+                                               info->bestdist = dist;
+                                               VectorScale(facenormal, (info->radius - -dist), info->nudge);
+                                       }
+                               }
+                               else
+                               {
+                                       if (info->bestdist > dist)
+                                       {
+                                               info->bestdist = dist;
+                                               VectorScale(facenormal, (info->radius - dist), info->nudge);
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               // check which edge or vertex the center is nearest
+                               for (i = 0;i < 3;i++)
+                               {
+                                       f = DotProduct(info->center, edge[i]);
+                                       if (f >= DotProduct(vert[0], edge[i])
+                                                       && f <= DotProduct(vert[1], edge[i]))
+                                       {
+                                               // on edge
+                                               VectorMA(info->center, -f, edge[i], point);
+                                               dist = sqrt(DotProduct(point, point));
+                                               if (info->bestdist > dist)
+                                               {
+                                                       info->bestdist = dist;
+                                                       VectorScale(point, (info->radius / dist), info->nudge);
+                                               }
+                                               // skip both vertex checks
+                                               // (both are further away than this edge)
+                                               i++;
+                                       }
+                                       else
+                                       {
+                                               // not on edge, check first vertex of edge
+                                               VectorSubtract(info->center, vert[i], point);
+                                               dist = sqrt(DotProduct(point, point));
+                                               if (info->bestdist > dist)
+                                               {
+                                                       info->bestdist = dist;
+                                                       VectorScale(point, (info->radius / dist), info->nudge);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
+{
+       int surfacenum, k, *mark;
        msurface_t *surface;
        for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
        {
                surface = info->model->data_surfaces + *mark;
                if (surface->texture->supercontents & SUPERCONTENTS_SOLID)
                {
-                       for (k = 0;k < surface->num_triangles;k++)
+                       if(surface->num_bboxstride)
                        {
-                               tri = (info->model->surfmesh.data_element3i + 3 * surface->num_firsttriangle) + k * 3;
-                               VectorCopy((info->model->surfmesh.data_vertex3f + tri[0] * 3), vert[0]);
-                               VectorCopy((info->model->surfmesh.data_vertex3f + tri[1] * 3), vert[1]);
-                               VectorCopy((info->model->surfmesh.data_vertex3f + tri[2] * 3), vert[2]);
-                               VectorSubtract(vert[1], vert[0], edge[0]);
-                               VectorSubtract(vert[2], vert[1], edge[1]);
-                               CrossProduct(edge[1], edge[0], facenormal);
-                               if (facenormal[0] || facenormal[1] || facenormal[2])
+                               int i, cnt, tri;
+                               cnt = (surface->num_triangles + surface->num_bboxstride - 1) / surface->num_bboxstride;
+                               for(i = 0; i < cnt; ++i)
                                {
-                                       VectorNormalize(facenormal);
-                                       f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
-                                       if (f <= info->bestdist && f >= -info->bestdist)
+                                       if(BoxesOverlap(surface->data_bbox6f + i * 6, surface->data_bbox6f + i * 6 + 3, info->absmin, info->absmax))
                                        {
-                                               VectorSubtract(vert[0], vert[2], edge[2]);
-                                               VectorNormalize(edge[0]);
-                                               VectorNormalize(edge[1]);
-                                               VectorNormalize(edge[2]);
-                                               CrossProduct(facenormal, edge[0], edgenormal[0]);
-                                               CrossProduct(facenormal, edge[1], edgenormal[1]);
-                                               CrossProduct(facenormal, edge[2], edgenormal[2]);
-                                               // face distance
-                                               if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
-                                                && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
-                                                && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
+                                               for(k = 0; k < surface->num_bboxstride; ++k)
                                                {
-                                                       // we got lucky, the center is within the face
-                                                       dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
-                                                       if (dist < 0)
-                                                       {
-                                                               dist = -dist;
-                                                               if (info->bestdist > dist)
-                                                               {
-                                                                       info->bestdist = dist;
-                                                                       VectorScale(facenormal, (info->radius - -dist), info->nudge);
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               if (info->bestdist > dist)
-                                                               {
-                                                                       info->bestdist = dist;
-                                                                       VectorScale(facenormal, (info->radius - dist), info->nudge);
-                                                               }
-                                                       }
-                                               }
-                                               else
-                                               {
-                                                       // check which edge or vertex the center is nearest
-                                                       for (i = 0;i < 3;i++)
-                                                       {
-                                                               f = DotProduct(info->center, edge[i]);
-                                                               if (f >= DotProduct(vert[0], edge[i])
-                                                                && f <= DotProduct(vert[1], edge[i]))
-                                                               {
-                                                                       // on edge
-                                                                       VectorMA(info->center, -f, edge[i], point);
-                                                                       dist = sqrt(DotProduct(point, point));
-                                                                       if (info->bestdist > dist)
-                                                                       {
-                                                                               info->bestdist = dist;
-                                                                               VectorScale(point, (info->radius / dist), info->nudge);
-                                                                       }
-                                                                       // skip both vertex checks
-                                                                       // (both are further away than this edge)
-                                                                       i++;
-                                                               }
-                                                               else
-                                                               {
-                                                                       // not on edge, check first vertex of edge
-                                                                       VectorSubtract(info->center, vert[i], point);
-                                                                       dist = sqrt(DotProduct(point, point));
-                                                                       if (info->bestdist > dist)
-                                                                       {
-                                                                               info->bestdist = dist;
-                                                                               VectorScale(point, (info->radius / dist), info->nudge);
-                                                                       }
-                                                               }
-                                                       }
+                                                       tri = i * surface->num_bboxstride + k;
+                                                       if(tri >= surface->num_triangles)
+                                                               break;
+                                                       Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, tri);
                                                }
                                        }
                                }
                        }
+                       else
+                       {
+                               for (k = 0;k < surface->num_triangles;k++)
+                               {
+                                       Mod_Q1BSP_FindNonSolidLocation_r_Triangle(info, surface, k);
+                               }
+                       }
                }
        }
 }
@@ -550,6 +583,14 @@ static void Mod_Q1BSP_FindNonSolidLocation(dp_model_t *model, const vec3_t in, v
        {
                VectorClear(info.nudge);
                info.bestdist = radius;
+               VectorCopy(info.center, info.absmin);
+               VectorCopy(info.center, info.absmax);
+               info.absmin[0] -= info.radius + 1;
+               info.absmin[1] -= info.radius + 1;
+               info.absmin[2] -= info.radius + 1;
+               info.absmax[0] += info.radius + 1;
+               info.absmax[1] += info.radius + 1;
+               info.absmax[2] += info.radius + 1;
                Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
                VectorAdd(info.center, info.nudge, info.center);
        }
@@ -564,7 +605,7 @@ int Mod_Q1BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativeconte
                case CONTENTS_EMPTY:
                        return 0;
                case CONTENTS_SOLID:
-                       return SUPERCONTENTS_SOLID;
+                       return SUPERCONTENTS_SOLID | SUPERCONTENTS_OPAQUE;
                case CONTENTS_WATER:
                        return SUPERCONTENTS_WATER;
                case CONTENTS_SLIME:
@@ -572,7 +613,7 @@ int Mod_Q1BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativeconte
                case CONTENTS_LAVA:
                        return SUPERCONTENTS_LAVA | SUPERCONTENTS_NODROP;
                case CONTENTS_SKY:
-                       return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP;
+                       return SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP | SUPERCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
        }
        return 0;
 }
@@ -810,12 +851,89 @@ static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, i
 }
 //#endif
 
+static void Mod_Q1BSP_TracePoint(struct model_s *model, int frame, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
+{
+       RecursiveHullCheckTraceInfo_t rhc;
+
+       memset(&rhc, 0, sizeof(rhc));
+       memset(trace, 0, sizeof(trace_t));
+       rhc.trace = trace;
+       rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
+       rhc.trace->allsolid = true;
+       rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
+       VectorCopy(start, rhc.start);
+       VectorCopy(start, rhc.end);
+       Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
+}
+
+static void Mod_Q1BSP_TraceLine(struct model_s *model, int frame, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
+{
+       RecursiveHullCheckTraceInfo_t rhc;
+
+       if (VectorCompare(start, end))
+       {
+               Mod_Q1BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+               return;
+       }
+
+       memset(&rhc, 0, sizeof(rhc));
+       memset(trace, 0, sizeof(trace_t));
+       rhc.trace = trace;
+       rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
+       rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
+       rhc.trace->allsolid = true;
+       rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
+       VectorCopy(start, rhc.start);
+       VectorCopy(end, rhc.end);
+       VectorSubtract(rhc.end, rhc.start, rhc.dist);
+#if COLLISIONPARANOID >= 2
+       Con_Printf("t(%f %f %f,%f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2]);
+       Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       {
+
+               double test[3];
+               trace_t testtrace;
+               VectorLerp(rhc.start, rhc.trace->fraction, rhc.end, test);
+               memset(&testtrace, 0, sizeof(trace_t));
+               rhc.trace = &testtrace;
+               rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
+               rhc.trace->fraction = 1;
+               rhc.trace->realfraction = 1;
+               rhc.trace->allsolid = true;
+               VectorCopy(test, rhc.start);
+               VectorCopy(test, rhc.end);
+               VectorClear(rhc.dist);
+               Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
+               //Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, test, test);
+               if (!trace->startsolid && testtrace.startsolid)
+                       Con_Printf(" - ended in solid!\n");
+       }
+       Con_Print("\n");
+#else
+       if (VectorLength2(rhc.dist))
+               Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       else
+               Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
+#endif
+}
+
 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
 {
        // this function currently only supports same size start and end
        double boxsize[3];
        RecursiveHullCheckTraceInfo_t rhc;
 
+       if (VectorCompare(boxmins, boxmaxs))
+       {
+               if (VectorCompare(start, end))
+                       Mod_Q1BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+               else
+                       Mod_Q1BSP_TraceLine(model, frame, trace, start, end, hitsupercontentsmask);
+               return;
+       }
+
        memset(&rhc, 0, sizeof(rhc));
        memset(trace, 0, sizeof(trace_t));
        rhc.trace = trace;
@@ -1001,6 +1119,22 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm
 #endif
 }
 
+void Collision_ClipTrace_Point(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, int hitsupercontentsmask, int boxsupercontents, int boxq3surfaceflags, texture_t *boxtexture)
+{
+       memset(trace, 0, sizeof(trace_t));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       if (BoxesOverlap(start, start, cmins, cmaxs))
+       {
+               trace->startsupercontents |= boxsupercontents;
+               if (hitsupercontentsmask & boxsupercontents)
+               {
+                       trace->startsolid = true;
+                       trace->allsolid = true;
+               }
+       }
+}
+
 static int Mod_Q1BSP_TraceLineOfSight_RecursiveNodeCheck(mnode_t *node, double p1[3], double p2[3])
 {
        double t1, t2;
@@ -1939,6 +2073,9 @@ static void Mod_Q1BSP_LoadEdges(lump_t *l)
                if (out->v[0] >= loadmodel->brushq1.numvertexes || out->v[1] >= loadmodel->brushq1.numvertexes)
                {
                        Con_Printf("Mod_Q1BSP_LoadEdges: %s has invalid vertex indices in edge %i (vertices %i %i >= numvertices %i)\n", loadmodel->name, i, out->v[0], out->v[1], loadmodel->brushq1.numvertexes);
+                       if(!loadmodel->brushq1.numvertexes)
+                               Host_Error("Mod_Q1BSP_LoadEdges: %s has edges but no vertexes, cannot fix\n", loadmodel->name);
+                               
                        out->v[0] = 0;
                        out->v[1] = 0;
                }
@@ -2284,7 +2421,8 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                {
                        int lindex = loadmodel->brushq1.surfedges[firstedge + i];
                        float s, t;
-                       if (lindex > 0)
+                       // note: the q1bsp format does not allow a 0 surfedge (it would have no negative counterpart)
+                       if (lindex >= 0)
                                VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
                        else
                                VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3);
@@ -2341,6 +2479,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                if (i == -1)
                {
                        surface->lightmapinfo->samples = NULL;
+#if 0
                        // give non-lightmapped water a 1x white lightmap
                        if (surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256)
                        {
@@ -2348,6 +2487,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                                surface->lightmapinfo->styles[0] = 0;
                                memset(surface->lightmapinfo->samples, 128, ssize * tsize * 3);
                        }
+#endif
                }
                else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
                        surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + i;
@@ -2394,6 +2534,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
        // now that we've decided the lightmap texture size, we can do the rest
        if (cls.state != ca_dedicated)
        {
+               int stainmapsize = 0;
                struct alloc_lm_state allocState;
 
                for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
@@ -2401,10 +2542,14 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                        int i, iu, iv, lightmapx = 0, lightmapy = 0;
                        float u, v, ubase, vbase, uscale, vscale;
 
+                       if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+                               continue;
+
                        smax = surface->lightmapinfo->extents[0] >> 4;
                        tmax = surface->lightmapinfo->extents[1] >> 4;
                        ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
                        tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
+                       stainmapsize += ssize * tsize * 3;
 
                        if (!lightmaptexture || !Mod_Q1BSP_AllocLightmapBlock(&allocState, lightmapsize, lightmapsize, ssize, tsize, &lightmapx, &lightmapy))
                        {
@@ -2440,26 +2585,19 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                                iv = (int) v;
                                (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
                        }
-
                }
 
                if (cl_stainmaps.integer)
                {
-                       // allocate stainmaps for permanent marks on walls
-                       int stainmapsize = 0;
+                       // allocate stainmaps for permanent marks on walls and clear white
                        unsigned char *stainsamples = NULL;
-                       for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
-                       {
-                               ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
-                               tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
-                               stainmapsize += ssize * tsize * 3;
-                       }
-                       // allocate and clear to white
                        stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, stainmapsize);
                        memset(stainsamples, 255, stainmapsize);
                        // assign pointers
                        for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
                        {
+                               if (!loadmodel->brushq1.lightmapupdateflags[surfacenum])
+                                       continue;
                                ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
                                tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
                                surface->lightmapinfo->stainsamples = stainsamples;
@@ -2611,7 +2749,7 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l)
 
                out->firstleafsurface = loadmodel->brush.data_leafsurfaces + (unsigned short)LittleShort(in->firstmarksurface);
                out->numleafsurfaces = (unsigned short)LittleShort(in->nummarksurfaces);
-               if (out->firstleafsurface < 0 || (unsigned short)LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
+               if ((unsigned short)LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
                {
                        Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", (int)(out->firstleafsurface - loadmodel->brush.data_leafsurfaces), (int)(out->firstleafsurface + out->numleafsurfaces - loadmodel->brush.data_leafsurfaces), 0, loadmodel->brush.num_leafsurfaces);
                        out->firstleafsurface = NULL;
@@ -3427,6 +3565,8 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        mod->soundfromcenter = true;
        mod->TraceBox = Mod_Q1BSP_TraceBox;
+       mod->TraceLine = Mod_Q1BSP_TraceLine;
+       mod->TracePoint = Mod_Q1BSP_TracePoint;
        mod->PointSuperContents = Mod_Q1BSP_PointSuperContents;
        mod->brush.TraceLineOfSight = Mod_Q1BSP_TraceLineOfSight;
        mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
@@ -3446,6 +3586,8 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->DrawDepth = R_Q1BSP_DrawDepth;
        mod->DrawDebug = R_Q1BSP_DrawDebug;
        mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+       mod->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        mod->DrawLight = R_Q1BSP_DrawLight;
@@ -4143,6 +4285,8 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
        loadmodel->brush.entities[l->filelen] = 0;
        data = loadmodel->brush.entities;
        // some Q3 maps override the lightgrid_cellsize with a worldspawn key
+       // VorteX: q3map2 FS-R generates tangentspace deluxemaps for q3bsp and sets 'deluxeMaps' key
+       loadmodel->brushq3.deluxemapping = false;
        if (data && COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{')
        {
                while (1)
@@ -4168,6 +4312,19 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
                                if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
                                        VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
                        }
+                       else if (!strcmp("deluxeMaps", key))
+                       {
+                               if (!strcmp(com_token, "1"))
+                               {
+                                       loadmodel->brushq3.deluxemapping = true;
+                                       loadmodel->brushq3.deluxemapping_modelspace = true;
+                               }
+                               else if (!strcmp(com_token, "2"))
+                               {
+                                       loadmodel->brushq3.deluxemapping = true;
+                                       loadmodel->brushq3.deluxemapping_modelspace = false;
+                               }
+                       }
                }
        }
 }
@@ -4425,6 +4582,8 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
                if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
                {
                        Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
+                       if(!loadmodel->brushq3.num_vertices)
+                               Host_Error("Mod_Q1BSP_LoadTrianglles: %s has triangles but no vertexes, cannot fix\n", loadmodel->name);
                        *out = 0;
                }
        }
@@ -4522,71 +4681,75 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
        // reason when only one lightmap is used, which can throw off the
        // deluxemapping detection method, so check 2-lightmap bsp's specifically
        // to see if the second lightmap is blank, if so it is not deluxemapped.
-       loadmodel->brushq3.deluxemapping = !(count & 1);
-       loadmodel->brushq3.deluxemapping_modelspace = true;
-       endlightmap = 0;
-       if (loadmodel->brushq3.deluxemapping)
-       {
-               int facecount = faceslump->filelen / sizeof(q3dface_t);
-               q3dface_t *faces = (q3dface_t *)(mod_base + faceslump->fileofs);
-               for (i = 0;i < facecount;i++)
-               {
-                       j = LittleLong(faces[i].lightmapindex);
-                       if (j >= 0)
+       // VorteX: autodetect only if previous attempt to find "deluxeMaps" key
+       // in Mod_Q3BSP_LoadEntities was failed
+       if (!loadmodel->brushq3.deluxemapping)
+       {
+               loadmodel->brushq3.deluxemapping = !(count & 1);
+               loadmodel->brushq3.deluxemapping_modelspace = true;
+               endlightmap = 0;
+               if (loadmodel->brushq3.deluxemapping)
+               {
+                       int facecount = faceslump->filelen / sizeof(q3dface_t);
+                       q3dface_t *faces = (q3dface_t *)(mod_base + faceslump->fileofs);
+                       for (i = 0;i < facecount;i++)
                        {
-                               endlightmap = max(endlightmap, j + 1);
-                               if ((j & 1) || j + 1 >= count)
+                               j = LittleLong(faces[i].lightmapindex);
+                               if (j >= 0)
                                {
-                                       loadmodel->brushq3.deluxemapping = false;
-                                       break;
+                                       endlightmap = max(endlightmap, j + 1);
+                                       if ((j & 1) || j + 1 >= count)
+                                       {
+                                               loadmodel->brushq3.deluxemapping = false;
+                                               break;
+                                       }
                                }
                        }
                }
-       }
 
-       // q3map2 sometimes (or always?) makes a second blank lightmap for no
-       // reason when only one lightmap is used, which can throw off the
-       // deluxemapping detection method, so check 2-lightmap bsp's specifically
-       // to see if the second lightmap is blank, if so it is not deluxemapped.
-       //
-       // further research has shown q3map2 sometimes creates a deluxemap and two
-       // blank lightmaps, which must be handled properly as well
-       if (endlightmap == 1 && count > 1)
-       {
-               c = inpixels[1];
-               for (i = 0;i < size*size;i++)
+               // q3map2 sometimes (or always?) makes a second blank lightmap for no
+               // reason when only one lightmap is used, which can throw off the
+               // deluxemapping detection method, so check 2-lightmap bsp's specifically
+               // to see if the second lightmap is blank, if so it is not deluxemapped.
+               //
+               // further research has shown q3map2 sometimes creates a deluxemap and two
+               // blank lightmaps, which must be handled properly as well
+               if (endlightmap == 1 && count > 1)
                {
-                       if (c[bytesperpixel*i + rgbmap[0]])
-                               break;
-                       if (c[bytesperpixel*i + rgbmap[1]])
-                               break;
-                       if (c[bytesperpixel*i + rgbmap[2]])
-                               break;
-               }
-               if (i == size*size)
-               {
-                       // all pixels in the unused lightmap were black...
-                       loadmodel->brushq3.deluxemapping = false;
+                       c = inpixels[1];
+                       for (i = 0;i < size*size;i++)
+                       {
+                               if (c[bytesperpixel*i + rgbmap[0]])
+                                       break;
+                               if (c[bytesperpixel*i + rgbmap[1]])
+                                       break;
+                               if (c[bytesperpixel*i + rgbmap[2]])
+                                       break;
+                       }
+                       if (i == size*size)
+                       {
+                               // all pixels in the unused lightmap were black...
+                               loadmodel->brushq3.deluxemapping = false;
+                       }
                }
        }
 
        Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
 
        // figure out what the most reasonable merge power is within limits
+
        loadmodel->brushq3.num_lightmapmergepower = 0;
-       for (power = 1;power <= mod_q3bsp_lightmapmergepower.integer && (128 << power) <= gl_max_texture_size && (1 << (power * 2)) < 4 * (count >> loadmodel->brushq3.deluxemapping);power++)
-               loadmodel->brushq3.num_lightmapmergepower = power;
 
-       // as the lightmap size may actually be another power of 2, adjust for this
-       // (and interpret it as the power for 128x128 lightmaps above)
        for(i = 0; (128 << i) < size; ++i)
-               loadmodel->brushq3.num_lightmapmergepower -= 1;
-       if(loadmodel->brushq3.num_lightmapmergepower < 0)
-               loadmodel->brushq3.num_lightmapmergepower = 0;
+               ;
+       // i is now 0 for 128, 1 for 256, etc
+
+       for (power = 1;power + i <= mod_q3bsp_lightmapmergepower.integer && (size << power) <= gl_max_texture_size && (1 << (power * 2)) < 4 * (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)); power++)
+               loadmodel->brushq3.num_lightmapmergepower = power;
 
        loadmodel->brushq3.num_lightmapmerge = 1 << loadmodel->brushq3.num_lightmapmergepower;
 
-       loadmodel->brushq3.num_mergedlightmaps = ((count >> loadmodel->brushq3.deluxemapping) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
+       loadmodel->brushq3.num_mergedlightmaps = ((count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
        loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
        if (loadmodel->brushq3.deluxemapping)
                loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
@@ -4620,7 +4783,7 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                                // all be full size except the last one which may be smaller
                                // because it only needs to the remaining blocks, and it will often
                                // be odd sizes like 2048x512 due to only being 25% full or so.
-                               j = (count >> loadmodel->brushq3.deluxemapping) - (lightmapindex << power2);
+                               j = (count >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) - (lightmapindex << power2);
                                for (mergewidth = 1;mergewidth < j && mergewidth < (1 << power);mergewidth *= 2)
                                        ;
                                for (mergeheight = 1;mergewidth*mergeheight < j && mergeheight < (1 << power);mergeheight *= 2)
@@ -4633,7 +4796,7 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                        }
                        mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
                        mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / size;
-                       j = (i >> loadmodel->brushq3.deluxemapping) & ((1 << power2) - 1);
+                       j = (i >> (loadmodel->brushq3.deluxemapping ? 1 : 0)) & ((1 << power2) - 1);
                        if (loadmodel->brushq3.deluxemapping && (i & 1))
                                R_UpdateTexture(loadmodel->brushq3.data_deluxemaps[lightmapindex], convertedpixels, (j % mergewidth) * size, (j / mergewidth) * size, size, size);
                        else
@@ -4657,6 +4820,56 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
        }
 }
 
+static void Mod_Q3BSP_BuildBBoxes(const int *element3i, int num_triangles, const float *vertex3f, float **collisionbbox6f, int *collisionstride, int stride)
+{
+       int j, k, cnt, tri;
+       float *mins, *maxs;
+       const float *vert;
+       if(stride > 0)
+       {
+               *collisionstride = stride;
+               cnt = (num_triangles + stride - 1) / stride;
+               *collisionbbox6f = (float *) Mem_Alloc(loadmodel->mempool, sizeof(float[6]) * cnt);
+               for(j = 0; j < cnt; ++j)
+               {
+                       mins = &((*collisionbbox6f)[6 * j + 0]);
+                       maxs = &((*collisionbbox6f)[6 * j + 3]);
+                       for(k = 0; k < stride; ++k)
+                       {
+                               tri = j * stride + k;
+                               if(tri >= num_triangles)
+                                       break;
+                               vert = &(vertex3f[element3i[3 * tri + 0] * 3]);
+                               if(!k || vert[0] < mins[0]) mins[0] = vert[0];
+                               if(!k || vert[1] < mins[1]) mins[1] = vert[1];
+                               if(!k || vert[2] < mins[2]) mins[2] = vert[2];
+                               if(!k || vert[0] > maxs[0]) maxs[0] = vert[0];
+                               if(!k || vert[1] > maxs[1]) maxs[1] = vert[1];
+                               if(!k || vert[2] > maxs[2]) maxs[2] = vert[2];
+                               vert = &(vertex3f[element3i[3 * tri + 1] * 3]);
+                               if(vert[0] < mins[0]) mins[0] = vert[0];
+                               if(vert[1] < mins[1]) mins[1] = vert[1];
+                               if(vert[2] < mins[2]) mins[2] = vert[2];
+                               if(vert[0] > maxs[0]) maxs[0] = vert[0];
+                               if(vert[1] > maxs[1]) maxs[1] = vert[1];
+                               if(vert[2] > maxs[2]) maxs[2] = vert[2];
+                               vert = &(vertex3f[element3i[3 * tri + 2] * 3]);
+                               if(vert[0] < mins[0]) mins[0] = vert[0];
+                               if(vert[1] < mins[1]) mins[1] = vert[1];
+                               if(vert[2] < mins[2]) mins[2] = vert[2];
+                               if(vert[0] > maxs[0]) maxs[0] = vert[0];
+                               if(vert[1] > maxs[1]) maxs[1] = vert[1];
+                               if(vert[2] > maxs[2]) maxs[2] = vert[2];
+                       }
+               }
+       }
+       else
+       {
+               *collisionstride = 0;
+               *collisionbbox6f = NULL;
+       }
+}
+
 typedef struct patchtess_s
 {
        patchinfo_t info;
@@ -4995,6 +5208,11 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        oldnumtriangles = out->num_triangles;
                        oldnumtriangles2 = out->num_collisiontriangles;
                        out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
+
+                       // now optimize the collision mesh by finding triangle bboxes...
+                       Mod_Q3BSP_BuildBBoxes(out->data_collisionelement3i, out->num_collisiontriangles, out->data_collisionvertex3f, &out->data_collisionbbox6f, &out->num_collisionbboxstride, mod_q3bsp_curves_collisions_stride.integer);
+                       Mod_Q3BSP_BuildBBoxes(loadmodel->surfmesh.data_element3i + 3 * out->num_firsttriangle, out->num_triangles, loadmodel->surfmesh.data_vertex3f, &out->data_bbox6f, &out->num_bboxstride, mod_q3bsp_curves_stride.integer);
+
                        if (developer.integer >= 100)
                                Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve became %i:%i vertices / %i:%i triangles (%i:%i degenerate)\n", patchsize[0], patchsize[1], out->num_vertices, out->num_collisionvertices, oldnumtriangles, oldnumtriangles2, oldnumtriangles - out->num_triangles, oldnumtriangles2 - out->num_collisiontriangles);
                        break;
@@ -5025,7 +5243,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        if (cls.state != ca_dedicated && out->lightmaptexture)
                        {
                                // figure out which part of the merged lightmap this fits into
-                               int lightmapindex = LittleLong(in->lightmapindex) >> loadmodel->brushq3.deluxemapping;
+                               int lightmapindex = LittleLong(in->lightmapindex) >> (loadmodel->brushq3.deluxemapping ? 1 : 0);
                                int mergewidth = R_TextureWidth(out->lightmaptexture) / loadmodel->brushq3.lightmapsize;
                                int mergeheight = R_TextureHeight(out->lightmaptexture) / loadmodel->brushq3.lightmapsize;
                                lightmapindex &= mergewidth * mergeheight - 1;
@@ -5528,7 +5746,7 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, dp_model_t *mod
                        if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
                                surface->collisionmarkframe = markframe;
-                               Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+                               Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
                }
        }
@@ -5609,90 +5827,122 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, dp_model_t *mo
                        if (surface->num_collisiontriangles && surface->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, surface->mins, surface->maxs))
                        {
                                surface->collisionmarkframe = markframe;
-                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
                        }
                }
        }
 }
 
-static void Mod_Q3BSP_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+static int markframe = 0;
+
+static void Mod_Q3BSP_TracePoint(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, int hitsupercontentsmask)
+{
+       int i;
+       q3mbrush_t *brush;
+       memset(trace, 0, sizeof(*trace));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       if (model->brush.submodel)
+       {
+               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                       if (brush->colbrushf)
+                               Collision_TracePointBrushFloat(trace, start, brush->colbrushf);
+       }
+       else
+               Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
+}
+
+static void Mod_Q3BSP_TraceLine(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t end, int hitsupercontentsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
-       static int markframe = 0;
        msurface_t *surface;
        q3mbrush_t *brush;
+
+       if (VectorCompare(start, end))
+       {
+               Mod_Q3BSP_TracePoint(model, frame, trace, start, hitsupercontentsmask);
+               return;
+       }
+
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
        trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
-       if (mod_q3bsp_optimizedtraceline.integer && VectorLength2(boxmins) + VectorLength2(boxmaxs) == 0)
+       segmentmins[0] = min(start[0], end[0]) - 1;
+       segmentmins[1] = min(start[1], end[1]) - 1;
+       segmentmins[2] = min(start[2], end[2]) - 1;
+       segmentmaxs[0] = max(start[0], end[0]) + 1;
+       segmentmaxs[1] = max(start[1], end[1]) + 1;
+       segmentmaxs[2] = max(start[2], end[2]) + 1;
+       if (model->brush.submodel)
        {
+               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                       if (brush->colbrushf)
+                               Collision_TraceLineBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
+               if (mod_q3bsp_curves_collisions.integer)
+                       for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                               if (surface->num_collisiontriangles)
+                                       Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
+       }
+       else
+               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
+}
+
+static void Mod_Q3BSP_TraceBox(dp_model_t *model, int frame, trace_t *trace, const vec3_t start, const vec3_t boxmins, const vec3_t boxmaxs, const vec3_t end, int hitsupercontentsmask)
+{
+       int i;
+       float segmentmins[3], segmentmaxs[3];
+       msurface_t *surface;
+       q3mbrush_t *brush;
+       colbrushf_t *thisbrush_start, *thisbrush_end;
+       vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
+
+       if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxmins, boxmaxs))
+       {
+               vec3_t shiftstart, shiftend;
+               VectorAdd(start, boxmins, shiftstart);
+               VectorAdd(end, boxmins, shiftend);
                if (VectorCompare(start, end))
-               {
-                       // point trace
-                       if (model->brush.submodel)
-                       {
-                               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
-                                       if (brush->colbrushf)
-                                               Collision_TracePointBrushFloat(trace, start, brush->colbrushf);
-                       }
-                       else
-                               Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, ++markframe);
-               }
+                       Mod_Q3BSP_TracePoint(model, frame, trace, shiftstart, hitsupercontentsmask);
                else
                {
-                       // line trace
-                       segmentmins[0] = min(start[0], end[0]) - 1;
-                       segmentmins[1] = min(start[1], end[1]) - 1;
-                       segmentmins[2] = min(start[2], end[2]) - 1;
-                       segmentmaxs[0] = max(start[0], end[0]) + 1;
-                       segmentmaxs[1] = max(start[1], end[1]) + 1;
-                       segmentmaxs[2] = max(start[2], end[2]) + 1;
-                       if (model->brush.submodel)
-                       {
-                               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
-                                       if (brush->colbrushf)
-                                               Collision_TraceLineBrushFloat(trace, start, end, brush->colbrushf, brush->colbrushf);
-                               if (mod_q3bsp_curves_collisions.integer)
-                                       for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
-                                               if (surface->num_collisiontriangles)
-                                                       Collision_TraceLineTriangleMeshFloat(trace, start, end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
-                       }
-                       else
-                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, model->brush.data_nodes, start, end, 0, 1, start, end, ++markframe, segmentmins, segmentmaxs);
+                       Mod_Q3BSP_TraceLine(model, frame, trace, shiftstart, shiftend, hitsupercontentsmask);
+                       VectorSubtract(trace->endpos, boxmins, trace->endpos);
                }
+               return;
        }
-       else
+
+       // box trace, performed as brush trace
+       memset(trace, 0, sizeof(*trace));
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
+       segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
+       segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
+       segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
+       segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
+       segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
+       VectorAdd(start, boxmins, boxstartmins);
+       VectorAdd(start, boxmaxs, boxstartmaxs);
+       VectorAdd(end, boxmins, boxendmins);
+       VectorAdd(end, boxmaxs, boxendmaxs);
+       thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL);
+       thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL);
+       if (model->brush.submodel)
        {
-               // box trace, performed as brush trace
-               colbrushf_t *thisbrush_start, *thisbrush_end;
-               vec3_t boxstartmins, boxstartmaxs, boxendmins, boxendmaxs;
-               segmentmins[0] = min(start[0], end[0]) + boxmins[0] - 1;
-               segmentmins[1] = min(start[1], end[1]) + boxmins[1] - 1;
-               segmentmins[2] = min(start[2], end[2]) + boxmins[2] - 1;
-               segmentmaxs[0] = max(start[0], end[0]) + boxmaxs[0] + 1;
-               segmentmaxs[1] = max(start[1], end[1]) + boxmaxs[1] + 1;
-               segmentmaxs[2] = max(start[2], end[2]) + boxmaxs[2] + 1;
-               VectorAdd(start, boxmins, boxstartmins);
-               VectorAdd(start, boxmaxs, boxstartmaxs);
-               VectorAdd(end, boxmins, boxendmins);
-               VectorAdd(end, boxmaxs, boxendmaxs);
-               thisbrush_start = Collision_BrushForBox(&identitymatrix, boxstartmins, boxstartmaxs, 0, 0, NULL);
-               thisbrush_end = Collision_BrushForBox(&identitymatrix, boxendmins, boxendmaxs, 0, 0, NULL);
-               if (model->brush.submodel)
-               {
-                       for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
-                               if (brush->colbrushf)
-                                       Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
-                       if (mod_q3bsp_curves_collisions.integer)
-                               for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
-                                       if (surface->num_collisiontriangles)
-                                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
-               }
-               else
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
+               for (i = 0, brush = model->brush.data_brushes + model->firstmodelbrush;i < model->nummodelbrushes;i++, brush++)
+                       if (brush->colbrushf)
+                               Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush->colbrushf, brush->colbrushf);
+               if (mod_q3bsp_curves_collisions.integer)
+                       for (i = 0, surface = model->data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
+                               if (surface->num_collisiontriangles)
+                                       Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, surface->num_collisiontriangles, surface->data_collisionelement3i, surface->data_collisionvertex3f, surface->num_collisionbboxstride, surface->data_collisionbbox6f, surface->texture->supercontents, surface->texture->surfaceflags, surface->texture, segmentmins, segmentmaxs);
        }
+       else
+               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model, model->brush.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
 }
 
 static int Mod_Q3BSP_PointSuperContents(struct model_s *model, int frame, const vec3_t point)
@@ -5750,6 +6000,10 @@ static int Mod_Q3BSP_SuperContentsFromNativeContents(dp_model_t *model, int nati
                supercontents |= SUPERCONTENTS_MONSTERCLIP;
        if (nativecontents & CONTENTSQ3_DONOTENTER)
                supercontents |= SUPERCONTENTS_DONOTENTER;
+       if (nativecontents & CONTENTSQ3_BOTCLIP)
+               supercontents |= SUPERCONTENTS_BOTCLIP;
+       if (!(nativecontents & CONTENTSQ3_TRANSLUCENT))
+               supercontents |= SUPERCONTENTS_OPAQUE;
        return supercontents;
 }
 
@@ -5776,6 +6030,10 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(dp_model_t *model, int supe
                nativecontents |= CONTENTSQ3_MONSTERCLIP;
        if (supercontents & SUPERCONTENTS_DONOTENTER)
                nativecontents |= CONTENTSQ3_DONOTENTER;
+       if (supercontents & SUPERCONTENTS_BOTCLIP)
+               nativecontents |= CONTENTSQ3_BOTCLIP;
+       if (!(supercontents & SUPERCONTENTS_OPAQUE))
+               nativecontents |= CONTENTSQ3_TRANSLUCENT;
        return nativecontents;
 }
 
@@ -5815,6 +6073,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        mod->soundfromcenter = true;
        mod->TraceBox = Mod_Q3BSP_TraceBox;
+       mod->TraceLine = Mod_Q3BSP_TraceLine;
+       mod->TracePoint = Mod_Q3BSP_TracePoint;
        mod->PointSuperContents = Mod_Q3BSP_PointSuperContents;
        mod->brush.TraceLineOfSight = Mod_Q1BSP_TraceLineOfSight;
        mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
@@ -5834,6 +6094,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->DrawDepth = R_Q1BSP_DrawDepth;
        mod->DrawDebug = R_Q1BSP_DrawDebug;
        mod->GetLightInfo = R_Q1BSP_GetLightInfo;
+       mod->CompileShadowMap = R_Q1BSP_CompileShadowMap;
+       mod->DrawShadowMap = R_Q1BSP_DrawShadowMap;
        mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        mod->DrawLight = R_Q1BSP_DrawLight;