]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
fixed Mod_Q3BSP_BoxTouchingPVS to handle unvised maps properly
[xonotic/darkplaces.git] / model_brush.c
index 641c1eddbf097865fa8a3fa4dc3ec02520bdfca6..bcea9fabd661338ba4c5b2449c6de18ab3b4729e 100644 (file)
@@ -36,8 +36,10 @@ cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
 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", "0"};
+cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
+cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"};
 
+static void Mod_Q1BSP_Collision_Init (void);
 void Mod_BrushInit(void)
 {
 //     Cvar_RegisterVariable(&r_subdivide_size);
@@ -49,7 +51,9 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
+       Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
        memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
+       Mod_Q1BSP_Collision_Init();
 }
 
 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
@@ -89,45 +93,51 @@ static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p,
                memset(out, 0, outsize);
 }
 
-
-static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
+static int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
 {
-       int leafnum;
-loc0:
-       if (node->contents < 0)
-       {
-               // leaf
-               if (node->contents == CONTENTS_SOLID)
-                       return false;
-               leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
-               return pvs[leafnum >> 3] & (1 << (leafnum & 7));
-       }
-
-       // node - recurse down the BSP tree
-       switch (BoxOnPlaneSide(mins, maxs, node->plane))
+       int clusterindex, side, nodestackindex = 0;
+       mnode_t *node, *nodestack[1024];
+       node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
+       for (;;)
        {
-       case 1: // front
-               node = node->children[0];
-               goto loc0;
-       case 2: // back
-               node = node->children[1];
-               goto loc0;
-       default: // crossing
-               if (node->children[0]->contents != CONTENTS_SOLID)
-                       if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
+               if (node->plane)
+               {
+                       // node - recurse down the BSP tree
+                       side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
+                       if (side < 2)
+                       {
+                               // box is on one side of plane, take that path
+                               node = node->children[side];
+                       }
+                       else
+                       {
+                               // box crosses plane, take one path and remember the other
+                               nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                       }
+               }
+               else
+               {
+                       // leaf - check cluster bit
+                       clusterindex = (mleaf_t *)node - model->brushq1.leafs - 1;
+                       if (clusterindex >= 0 && pvs[clusterindex >> 3] & (1 << (clusterindex & 7)))
+                       {
+                               // it is visible, return immediately with the news
                                return true;
-               node = node->children[1];
-               goto loc0;
+                       }
+                       else
+                       {
+                               // nothing to see here, try another path we didn't take earlier
+                               if (nodestackindex == 0)
+                                       break;
+                               node = nodestack[--nodestackindex];
+                       }
+               }
        }
-       // never reached
+       // it is not visible
        return false;
 }
 
-int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
-{
-       return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
-}
-
 /*
 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
 {
@@ -416,7 +426,7 @@ loc0:
                        // if the first leaf is solid, set startsolid
                        if (t->trace->allsolid)
                                t->trace->startsolid = true;
-#ifdef COLLISIONPARANOID
+#if COLLISIONPARANOID >= 3
                        Con_Printf("S");
 #endif
                        return HULLCHECKSTATE_SOLID;
@@ -424,7 +434,7 @@ loc0:
                else
                {
                        t->trace->allsolid = false;
-#ifdef COLLISIONPARANOID
+#if COLLISIONPARANOID >= 3
                        Con_Printf("E");
 #endif
                        return HULLCHECKSTATE_EMPTY;
@@ -450,7 +460,7 @@ loc0:
        {
                if (t2 < 0)
                {
-#ifdef COLLISIONPARANOID
+#if COLLISIONPARANOID >= 3
                        Con_Printf("<");
 #endif
                        num = node->children[1];
@@ -462,7 +472,7 @@ loc0:
        {
                if (t2 >= 0)
                {
-#ifdef COLLISIONPARANOID
+#if COLLISIONPARANOID >= 3
                        Con_Printf(">");
 #endif
                        num = node->children[0];
@@ -473,7 +483,7 @@ loc0:
 
        // the line intersects, find intersection point
        // LordHavoc: this uses the original trace for maximum accuracy
-#ifdef COLLISIONPARANOID
+#if COLLISIONPARANOID >= 3
        Con_Printf("M");
 #endif
        if (plane->type < 3)
@@ -514,20 +524,23 @@ loc0:
                VectorCopy (plane->normal, t->trace->plane.normal);
        }
 
-       // bias away from surface a bit
-       t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
-       t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
-
+       // calculate the true fraction
+       t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
+       t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
        midf = t1 / (t1 - t2);
-       t->trace->fraction = bound(0.0f, midf, 1.0);
+       t->trace->realfraction = bound(0, midf, 1);
 
-#ifdef COLLISIONPARANOID
+       // calculate the return fraction which is nudged off the surface a bit
+       midf = (t1 - DIST_EPSILON) / (t1 - t2);
+       t->trace->fraction = bound(0, midf, 1);
+
+#if COLLISIONPARANOID >= 3
        Con_Printf("D");
 #endif
        return HULLCHECKSTATE_DONE;
 }
 
-#ifndef COLLISIONPARANOID
+#if COLLISIONPARANOID < 2
 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
 {
        while (num >= 0)
@@ -562,6 +575,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace,
        rhc.trace = trace;
        rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
        rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
        rhc.trace->allsolid = true;
        VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
        if (boxsize[0] < 3)
@@ -592,7 +606,7 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace,
        VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
        VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
        VectorSubtract(rhc.end, rhc.start, rhc.dist);
-#ifdef COLLISIONPARANOID
+#if COLLISIONPARANOID >= 2
        Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
        Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
        Con_Printf("\n");
@@ -604,6 +618,105 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace,
 #endif
 }
 
+static hull_t box_hull;
+static dclipnode_t box_clipnodes[6];
+static mplane_t box_planes[6];
+
+static void Mod_Q1BSP_Collision_Init (void)
+{
+       int             i;
+       int             side;
+
+       //Set up the planes and clipnodes so that the six floats of a bounding box
+       //can just be stored out and get a proper hull_t structure.
+
+       box_hull.clipnodes = box_clipnodes;
+       box_hull.planes = box_planes;
+       box_hull.firstclipnode = 0;
+       box_hull.lastclipnode = 5;
+
+       for (i = 0;i < 6;i++)
+       {
+               box_clipnodes[i].planenum = i;
+
+               side = i&1;
+
+               box_clipnodes[i].children[side] = CONTENTS_EMPTY;
+               if (i != 5)
+                       box_clipnodes[i].children[side^1] = i + 1;
+               else
+                       box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
+
+               box_planes[i].type = i>>1;
+               box_planes[i].normal[i>>1] = 1;
+       }
+}
+
+void Collision_ClipTrace_Box(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 boxsupercontents)
+{
+#if 1
+       colbrushf_t cbox;
+       colplanef_t cbox_planes[6];
+       cbox.supercontents = boxsupercontents;
+       cbox.numplanes = 6;
+       cbox.numpoints = 0;
+       cbox.numtriangles = 0;
+       cbox.planes = cbox_planes;
+       cbox.points = NULL;
+       cbox.elements = NULL;
+       cbox.markframe = 0;
+       cbox.mins[0] = 0;
+       cbox.mins[1] = 0;
+       cbox.mins[2] = 0;
+       cbox.maxs[0] = 0;
+       cbox.maxs[1] = 0;
+       cbox.maxs[2] = 0;
+       cbox_planes[0].normal[0] =  1;cbox_planes[0].normal[1] =  0;cbox_planes[0].normal[2] =  0;cbox_planes[0].dist = cmaxs[0] - mins[0];
+       cbox_planes[1].normal[0] = -1;cbox_planes[1].normal[1] =  0;cbox_planes[1].normal[2] =  0;cbox_planes[1].dist = maxs[0] - cmins[0];
+       cbox_planes[2].normal[0] =  0;cbox_planes[2].normal[1] =  1;cbox_planes[2].normal[2] =  0;cbox_planes[2].dist = cmaxs[1] - mins[1];
+       cbox_planes[3].normal[0] =  0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] =  0;cbox_planes[3].dist = maxs[1] - cmins[1];
+       cbox_planes[4].normal[0] =  0;cbox_planes[4].normal[1] =  0;cbox_planes[4].normal[2] =  1;cbox_planes[4].dist = cmaxs[2] - mins[2];
+       cbox_planes[5].normal[0] =  0;cbox_planes[5].normal[1] =  0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2];
+       memset(trace, 0, sizeof(trace_t));
+       trace->hitsupercontentsmask = hitsupercontentsmask;
+       trace->fraction = 1;
+       trace->realfraction = 1;
+       Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
+#else
+       RecursiveHullCheckTraceInfo_t rhc;
+       // fill in a default trace
+       memset(&rhc, 0, sizeof(rhc));
+       memset(trace, 0, sizeof(trace_t));
+       //To keep everything totally uniform, bounding boxes are turned into small
+       //BSP trees instead of being compared directly.
+       // create a temp hull from bounding box sizes
+       box_planes[0].dist = cmaxs[0] - mins[0];
+       box_planes[1].dist = cmins[0] - maxs[0];
+       box_planes[2].dist = cmaxs[1] - mins[1];
+       box_planes[3].dist = cmins[1] - maxs[1];
+       box_planes[4].dist = cmaxs[2] - mins[2];
+       box_planes[5].dist = cmins[2] - maxs[2];
+#if COLLISIONPARANOID >= 3
+       Con_Printf("box_planes %f:%f %f:%f %f:%f\ncbox %f %f %f:%f %f %f\nbox %f %f %f:%f %f %f\n", box_planes[0].dist, box_planes[1].dist, box_planes[2].dist, box_planes[3].dist, box_planes[4].dist, box_planes[5].dist, cmins[0], cmins[1], cmins[2], cmaxs[0], cmaxs[1], cmaxs[2], mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
+#endif
+       // trace a line through the generated clipping hull
+       //rhc.boxsupercontents = boxsupercontents;
+       rhc.hull = &box_hull;
+       rhc.trace = trace;
+       rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
+       rhc.trace->fraction = 1;
+       rhc.trace->realfraction = 1;
+       rhc.trace->allsolid = true;
+       VectorCopy(start, rhc.start);
+       VectorCopy(end, rhc.end);
+       VectorSubtract(rhc.end, rhc.start, rhc.dist);
+       Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
+       //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
+       if (rhc.trace->startsupercontents)
+               rhc.trace->startsupercontents = boxsupercontents;
+#endif
+}
+
 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
 {
        int side, distz = endz - startz;
@@ -752,11 +865,12 @@ void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, v
 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
 {
        int c;
+       qbyte *outstart = out;
        while (out < outend)
        {
                if (in == inend)
                {
-                       Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
+                       Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
                        return;
                }
                c = *in++;
@@ -766,14 +880,14 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
                {
                        if (in == inend)
                        {
-                               Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
+                               Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
                                return;
                        }
                        for (c = *in++;c > 0;c--)
                        {
                                if (out == outend)
                                {
-                                       Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
+                                       Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
                                        return;
                                }
                                *out++ = 0;
@@ -1871,7 +1985,8 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l)
 
        loadmodel->brushq1.leafs = out;
        loadmodel->brushq1.numleafs = count;
-       pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
+       // get visleafs from the submodel data
+       pvschainbytes = (loadmodel->brushq1.submodels[0].visleafs+7)>>3;
        loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
 
        for ( i=0 ; i<count ; i++, in++, out++)
@@ -1900,7 +2015,7 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l)
                pvs += pvschainbytes;
 
                p = LittleLong(in->visofs);
-               if (p >= 0)
+               if (p >= 0 && i > 0) // ignore visofs errors on leaf 0 (solid)
                {
                        if (p >= loadmodel->brushq1.num_compressedpvs)
                                Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
@@ -2633,7 +2748,7 @@ static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org,
        }
        // FIXME: code!
        // if this is a leaf, accumulate the pvs bits
-       if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
+       if (/*node->contents != CONTENTS_SOLID && */((mleaf_t *)node)->pvsdata)
                for (i = 0;i < pvsbytes;i++)
                        pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
 }
@@ -2644,6 +2759,11 @@ static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyt
 {
        int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
        bytes = min(bytes, pvsbufferlength);
+       if (r_novis.integer)
+       {
+               memset(pvsbuffer, 0xFF, bytes);
+               return bytes;
+       }
        memset(pvsbuffer, 0, bytes);
        Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
        return bytes;
@@ -2763,10 +2883,11 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
        Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
        Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
+       // load submodels before leafs because they contain the number of vis leafs
+       Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
        Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
        Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
        Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
-       Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
 
        if (mod->brushq1.data_compressedpvs)
                Mem_Free(mod->brushq1.data_compressedpvs);
@@ -2776,9 +2897,6 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        Mod_Q1BSP_MakeHull0();
        Mod_Q1BSP_MakePortals();
 
-       if (developer.integer)
-               Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals);
-
        mod->numframes = 2;             // regular and alternate animation
 
        mainmempool = mod->mempool;
@@ -2884,6 +3002,9 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
 
        loadmodel = originalloadmodel;
        //Mod_Q1BSP_ProcessLightList();
+
+       if (developer.integer)
+               Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.visleafs, loadmodel->brushq1.numportals);
 }
 
 static void Mod_Q2BSP_LoadEntities(lump_t *l)
@@ -4209,15 +4330,15 @@ static void Mod_Q3BSP_LoadLeafs(lump_t *l)
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               out->isnode = false;
                out->parent = NULL;
+               out->plane = NULL;
                out->clusterindex = LittleLong(in->clusterindex);
                out->areaindex = LittleLong(in->areaindex);
                for (j = 0;j < 3;j++)
                {
                        // yes the mins/maxs are ints
-                       out->mins[j] = LittleLong(in->mins[j]);
-                       out->maxs[j] = LittleLong(in->maxs[j]);
+                       out->mins[j] = LittleLong(in->mins[j]) - 1;
+                       out->maxs[j] = LittleLong(in->maxs[j]) + 1;
                }
                n = LittleLong(in->firstleafface);
                c = LittleLong(in->numleaffaces);
@@ -4239,7 +4360,7 @@ static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *p
        if (node->parent)
                Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
        node->parent = parent;
-       if (node->isnode)
+       if (node->plane)
        {
                Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
                Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
@@ -4263,7 +4384,6 @@ static void Mod_Q3BSP_LoadNodes(lump_t *l)
 
        for (i = 0;i < count;i++, in++, out++)
        {
-               out->isnode = true;
                out->parent = NULL;
                n = LittleLong(in->planeindex);
                if (n < 0 || n >= loadmodel->brushq3.num_planes)
@@ -4289,8 +4409,8 @@ static void Mod_Q3BSP_LoadNodes(lump_t *l)
                for (j = 0;j < 3;j++)
                {
                        // yes the mins/maxs are ints
-                       out->mins[j] = LittleLong(in->mins[j]);
-                       out->maxs[j] = LittleLong(in->maxs[j]);
+                       out->mins[j] = LittleLong(in->mins[j]) - 1;
+                       out->maxs[j] = LittleLong(in->maxs[j]) + 1;
                }
        }
 
@@ -4427,72 +4547,105 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc
        //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
 }
 
-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)
+static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t point, int markframe)
+{
+       int i;
+       q3mleaf_t *leaf;
+       colbrushf_t *brush;
+       // find which leaf the point is in
+       while (node->plane)
+               node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
+       // point trace the brushes
+       leaf = (q3mleaf_t *)node;
+       for (i = 0;i < leaf->numleafbrushes;i++)
+       {
+               brush = leaf->firstleafbrush[i]->colbrushf;
+               if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
+               {
+                       brush->markframe = markframe;
+                       Collision_TracePointBrushFloat(trace, point, leaf->firstleafbrush[i]->colbrushf);
+               }
+       }
+       // can't do point traces on curves (they have no thickness)
+}
+
+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, const vec3_t segmentmins, const vec3_t segmentmaxs)
 {
        int i, startside, endside;
-       float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
+       float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
        q3mleaf_t *leaf;
        q3mface_t *face;
        colbrushf_t *brush;
-       if (startfrac >= trace->fraction)
+       if (startfrac > trace->realfraction)
                return;
        // note: all line fragments past first impact fraction are ignored
-       while (node->isnode)
-       {
-               // recurse down node sides
-               dist1 = PlaneDiff(start, node->plane);
-               dist2 = PlaneDiff(end, node->plane);
-               startside = dist1 < 0;
-               endside = dist2 < 0;
-               if (startside == endside)
-               {
-                       // most of the time the line fragment is on one side of the plane
-                       node = node->children[startside];
-               }
-               else
+       if (VectorCompare(start, end))
+       {
+               // find which leaf the point is in
+               while (node->plane)
+                       node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
+       }
+       else
+       {
+               // find which nodes the line is in and recurse for them
+               while (node->plane)
                {
-                       // line crosses node plane, split the line
-                       midfrac = dist1 / (dist1 - dist2);
-                       VectorLerp(linestart, midfrac, lineend, mid);
-                       // take the near side first
-                       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;
+                       // recurse down node sides
+                       dist1 = PlaneDiff(start, node->plane);
+                       dist2 = PlaneDiff(end, node->plane);
+                       startside = dist1 < 0;
+                       endside = dist2 < 0;
+                       if (startside == endside)
+                       {
+                               // most of the time the line fragment is on one side of the plane
+                               node = node->children[startside];
+                       }
+                       else
+                       {
+                               // line crosses node plane, split the line
+                               midfrac = dist1 / (dist1 - dist2);
+                               VectorLerp(start, midfrac, end, mid);
+                               // take the near side first
+                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
+                               if (midfrac <= trace->realfraction)
+                                       Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
+                               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]);
+       nodesegmentmins[0] = min(start[0], end[0]);
+       nodesegmentmins[1] = min(start[1], end[1]);
+       nodesegmentmins[2] = min(start[2], end[2]);
+       nodesegmentmaxs[0] = max(start[0], end[0]);
+       nodesegmentmaxs[1] = max(start[1], end[1]);
+       nodesegmentmaxs[2] = max(start[2], end[2]);
+       // line trace the brushes
        leaf = (q3mleaf_t *)node;
        for (i = 0;i < leaf->numleafbrushes;i++)
        {
-               if (startfrac >= trace->fraction)
-                       return;
                brush = leaf->firstleafbrush[i]->colbrushf;
-               if (brush && brush->markframe != markframe)
+               if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
                {
                        brush->markframe = markframe;
-                       if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
-                               Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
+                       Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
+                       if (startfrac > trace->realfraction)
+                               return;
                }
        }
-       if (mod_q3bsp_curves_collisions.integer)
+       // can't do point traces on curves (they have no thickness)
+       if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
        {
+               // line trace the curves
                for (i = 0;i < leaf->numleaffaces;i++)
                {
-                       if (startfrac >= trace->fraction)
-                               return;
                        face = leaf->firstleafface[i];
-                       if (face->collisions && face->collisionmarkframe != markframe)
+                       if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
                        {
                                face->collisionmarkframe = markframe;
-                               if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
-                                       Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+                               Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+                               if (startfrac > trace->realfraction)
+                                       return;
                        }
                }
        }
@@ -4505,78 +4658,295 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod
        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)
+       /*
+               // find which nodes the line is in and recurse for them
+               while (node->plane)
                {
-                       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);
+                       // recurse down node sides
+                       int startside, endside;
+                       float dist1near, dist1far, dist2near, dist2far;
+                       BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
+                       BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
+                       startside = dist1near < 0;
+                       startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
+                       endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
+                       if (startside == 2 || endside == 2)
+                       {
+                               // brushes cross plane
+                               // do not clip anything, just take both sides
+                               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                               node = node->children[1];
+                               continue;
+                       }
+                       if (startside == 0)
+                       {
+                               if (endside == 0)
+                               {
+                                       node = node->children[0];
+                                       continue;
+                               }
+                               else
+                               {
+                                       //midf0 = dist1near / (dist1near - dist2near);
+                                       //midf1 = dist1far / (dist1far - dist2far);
+                                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                                       node = node->children[1];
+                                       continue;
+                               }
+                       }
+                       else
+                       {
+                               if (endside == 0)
+                               {
+                                       //midf0 = dist1near / (dist1near - dist2near);
+                                       //midf1 = dist1far / (dist1far - dist2far);
+                                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                                       node = node->children[1];
+                                       continue;
+                               }
+                               else
+                               {
+                                       node = node->children[1];
+                                       continue;
+                               }
+                       }
+
+                       if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
+                       if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near <  0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near <  0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
+                       if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){}
+                       if (dist1near <  0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near <  0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
+                       if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){}
+                       if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
+                       if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
+                       if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
+                       if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
+                       {             
+                               if (dist2near < 0) // d1n<0 && d2n<0
+                               {
+                                       if (dist2near < 0) // d1n<0 && d2n<0
+                                       {
+                                               if (dist2near < 0) // d1n<0 && d2n<0
+                                               {
+                                               }
+                                               else // d1n<0 && d2n>0
+                                               {
+                                               }
+                                       }
+                                       else // d1n<0 && d2n>0
+                                       {
+                                               if (dist2near < 0) // d1n<0 && d2n<0
+                                               {
+                                               }
+                                               else // d1n<0 && d2n>0
+                                               {
+                                               }
+                                       }
+                               }
+                               else // d1n<0 && d2n>0
+                               {
+                               }
+                       }
+                       else // d1n>0
+                       {
+                               if (dist2near < 0) // d1n>0 && d2n<0
+                               {
+                               }
+                               else // d1n>0 && d2n>0
+                               {
+                               }
+                       }
+                       if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
+                       {
+                               node = node->children[startside];
+                               continue;
+                       }
+                       if (dist1near < dist2near)
+                       {
+                               // out
+                               if (dist1near >= 0)
+                               {
+                                       node = node->children[0];
+                                       continue;
+                               }
+                               if (dist2far < 0)
+                               {
+                                       node = node->children[1];
+                                       continue;
+                               }
+                               // dist1near < 0 && dist2far >= 0
+                       }
+                       else
+                       {
+                               // in
+                       }
+                       startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
+                       endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
+                       if (startside == 2 || endside == 2)
+                       {
+                               // brushes cross plane
+                               // do not clip anything, just take both sides
+                               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                               node = node->children[1];
+                       }
+                       else if (startside == endside)
+                               node = node->children[startside];
+                       else if (startside == 0) // endside = 1 (start infront, end behind)
+                       {
+                       }
+                       else // startside == 1 endside = 0 (start behind, end infront)
+                       {
+                       }
+                       == endside)
+                       {
+                               if (startside < 2)
+                                       node = node->children[startside];
+                               else
+                               {
+                                       // start and end brush cross plane
+                               }
+                       }
+                       else
+                       {
+                       }
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                               node = node->children[1];
+                       else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
+                       else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
+                       else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
+                               node = node->children[0];
+                       else
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                       {
+                       }
+                       else if (dist1near >= 0 && dist1far >= 0)
+                       {
+                       }
+                       else // mixed (lying on plane)
+                       {
+                       }
+                       {
+                               if (dist2near < 0 && dist2far < 0)
+                               {
+                               }
+                               else
+                                       node = node->children[1];
+                       }
+                       if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
+                               node = node->children[0];
+                       else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
+                               node = node->children[1];
+                       else
+                       {
+                               // both sides
+                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
+                               node = node->children[1];
+                       }
+                       sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
+                       startside = dist1 < 0;
+                       endside = dist2 < 0;
+                       if (startside == endside)
+                       {
+                               // most of the time the line fragment is on one side of the plane
+                               node = node->children[startside];
+                       }
+                       else
+                       {
+                               // line crosses node plane, split the line
+                               midfrac = dist1 / (dist1 - dist2);
+                               VectorLerp(start, midfrac, end, mid);
+                               // take the near side first
+                               Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
+                               if (midfrac <= trace->fraction)
+                                       Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
+                               return;
+                       }
                }
-               else if (sides == 2)
-                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
-               else // sides == 1
+       */
+       // FIXME: could be made faster by copying TraceLine code and making it use
+       // box plane distances...  (variant on the BoxOnPlaneSide code)
+       for (;;)
+       {
+               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->plane)
+                       break;
+               if (mod_q3bsp_debugtracebrush.integer == 2)
+               {
                        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++)
+                       node = node->children[1];
+               }
+               else if (mod_q3bsp_debugtracebrush.integer == 1)
                {
-                       if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
+                       // 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);
-                               break;
+                               // segment box crosses plane
+                               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                               node = node->children[1];
+                       }
+                       else
+                       {
+                               // take whichever side the segment box is on
+                               node = node->children[sides - 1];
                        }
                }
-               */
-               /*
-               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++)
+               else
                {
-                       if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
+                       // recurse down node sides
+                       sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
+                       if (sides == 3)
                        {
-                               Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
-                               break;
+                               // segment box crosses plane
+                               // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
+                               sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
+                               if (sides == 3)
+                               {
+                                       Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
+                                       sides = 2;
+                               }
                        }
+                       // take whichever side the segment box is on
+                       node = node->children[sides - 1];
                }
-               */
-               /*
-               sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
-               if (sides & 1)
-                       Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
-               if (sides & 2)
-                       Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
-               */
        }
-       else
+       // hit a leaf
+       leaf = (q3mleaf_t *)node;
+       for (i = 0;i < leaf->numleafbrushes;i++)
        {
-               // hit a leaf
-               leaf = (q3mleaf_t *)node;
-               for (i = 0;i < leaf->numleafbrushes;i++)
+               brush = leaf->firstleafbrush[i]->colbrushf;
+               if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
                {
-                       brush = leaf->firstleafbrush[i]->colbrushf;
-                       if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
-                       {
-                               brush->markframe = markframe;
-                               Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
-                       }
+                       brush->markframe = markframe;
+                       Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
                }
-               if (mod_q3bsp_curves_collisions.integer)
+       }
+       if (mod_q3bsp_curves_collisions.integer)
+       {
+               for (i = 0;i < leaf->numleaffaces;i++)
                {
-                       for (i = 0;i < leaf->numleaffaces;i++)
+                       face = leaf->firstleafface[i];
+                       if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
                        {
-                               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->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+                               face->markframe = markframe;
+                               Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
                        }
                }
        }
@@ -4592,6 +4962,7 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
        q3mface_t *face;
        memset(trace, 0, sizeof(*trace));
        trace->fraction = 1;
+       trace->realfraction = 1;
        trace->hitsupercontentsmask = hitsupercontentsmask;
        Matrix4x4_CreateIdentity(&startmatrix);
        Matrix4x4_CreateIdentity(&endmatrix);
@@ -4603,24 +4974,39 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
        segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
        if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
        {
-               // line trace
-               if (model->brushq3.submodel)
+               if (VectorCompare(boxstartmins, boxendmins))
                {
-                       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)
+                       // point trace
+                       if (model->brushq3.submodel)
                        {
-                               for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
+                               for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
+                                       if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
+                                               Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
+                       }
+                       else
+                               Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, ++markframe);
+               }
+               else
+               {
+                       // line trace
+                       if (model->brushq3.submodel)
+                       {
+                               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)
                                {
-                                       face = model->brushq3.data_thismodel->firstface + i;
-                                       if (face->collisions)
-                                               Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
+                                       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->num_triangles, 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, segmentmins, segmentmaxs);
                }
-               else
-                       Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
        }
        else
        {
@@ -4647,42 +5033,60 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const
        }
 }
 
-
-static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
+static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
 {
-       int clusterindex;
-loc0:
-       if (!node->isnode)
-       {
-               // leaf
-               clusterindex = ((q3mleaf_t *)node)->clusterindex;
-               return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
-       }
-
-       // node - recurse down the BSP tree
-       switch (BoxOnPlaneSide(mins, maxs, node->plane))
+       int clusterindex, side, nodestackindex = 0;
+       q3mnode_t *node, *nodestack[1024];
+       node = model->brushq3.data_nodes;
+       if (!loadmodel->brushq3.num_pvsclusters)
+               return true;
+       for (;;)
        {
-       case 1: // front
-               node = node->children[0];
-               goto loc0;
-       case 2: // back
-               node = node->children[1];
-               goto loc0;
-       default: // crossing
-               if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
-                       return true;
-               node = node->children[1];
-               goto loc0;
+               if (node->plane)
+               {
+                       // node - recurse down the BSP tree
+                       side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
+                       if (side < 2)
+                       {
+                               // box is on one side of plane, take that path
+                               node = node->children[side];
+                       }
+                       else
+                       {
+                               // box crosses plane, take one path and remember the other
+                               nodestack[nodestackindex++] = node->children[0];
+                               node = node->children[1];
+                       }
+               }
+               else
+               {
+                       // leaf - check cluster bit
+                       clusterindex = ((q3mleaf_t *)node)->clusterindex;
+#if 0
+                       if (clusterindex >= loadmodel->brushq3.num_pvsclusters)
+                       {
+                               Con_Printf("%i >= %i\n", clusterindex, loadmodel->brushq3.num_pvsclusters);
+                               return true;
+                       }
+#endif
+                       if (clusterindex < 0 || (pvs[clusterindex >> 3] & (1 << (clusterindex & 7))))
+                       {
+                               // it is visible, return immediately with the news
+                               return true;
+                       }
+                       else
+                       {
+                               // nothing to see here, try another path we didn't take earlier
+                               if (nodestackindex == 0)
+                                       break;
+                               node = nodestack[--nodestackindex];
+                       }
+               }
        }
-       // never reached
+       // it is not visible
        return false;
 }
 
-static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
-{
-       return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
-}
-
 //Returns PVS data for a given point
 //(note: can return NULL)
 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
@@ -4690,7 +5094,7 @@ static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
        q3mnode_t *node;
        Mod_CheckLoaded(model);
        node = model->brushq3.data_nodes;
-       while (node->isnode)
+       while (node->plane)
                node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
        if (((q3mleaf_t *)node)->clusterindex >= 0)
                return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
@@ -4704,7 +5108,7 @@ static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org,
        float d;
        qbyte *pvs;
 
-       while (node->isnode)
+       while (node->plane)
        {
                d = PlaneDiff(org, node->plane);
                if (d > radius)
@@ -4725,6 +5129,8 @@ static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org,
                for (i = 0;i < pvsbytes;i++)
                        pvsbuffer[i] |= pvs[i];
        }
+       else
+               memset(pvsbuffer, 0xFF, pvsbytes);
        return;
 }
 
@@ -4734,6 +5140,11 @@ static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyt
 {
        int bytes = model->brushq3.num_pvschainlength;
        bytes = min(bytes, pvsbufferlength);
+       if (r_novis.integer || !loadmodel->brushq3.num_pvsclusters)
+       {
+               memset(pvsbuffer, 0xFF, bytes);
+               return bytes;
+       }
        memset(pvsbuffer, 0, bytes);
        Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
        return bytes;