]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
fixed fiends jumping through player bug, huge thanks to Tomaz for days of help tracki...
[xonotic/darkplaces.git] / model_brush.c
index 52c6fb5888c87a2f2f90794ac6e8679e775f8af0..13b4df27a227aedfe6ac3ede1aba90fcf226a8a4 100644 (file)
@@ -36,8 +36,9 @@ 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", "1"};
+cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "0"};
 
+static void Mod_Q1BSP_Collision_Init (void);
 void Mod_BrushInit(void)
 {
 //     Cvar_RegisterVariable(&r_subdivide_size);
@@ -50,6 +51,7 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
        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)
@@ -416,11 +418,17 @@ loc0:
                        // if the first leaf is solid, set startsolid
                        if (t->trace->allsolid)
                                t->trace->startsolid = true;
+#if COLLISIONPARANOID >= 3
+                       Con_Printf("S");
+#endif
                        return HULLCHECKSTATE_SOLID;
                }
                else
                {
                        t->trace->allsolid = false;
+#if COLLISIONPARANOID >= 3
+                       Con_Printf("E");
+#endif
                        return HULLCHECKSTATE_EMPTY;
                }
        }
@@ -444,6 +452,9 @@ loc0:
        {
                if (t2 < 0)
                {
+#if COLLISIONPARANOID >= 3
+                       Con_Printf("<");
+#endif
                        num = node->children[1];
                        goto loc0;
                }
@@ -453,6 +464,9 @@ loc0:
        {
                if (t2 >= 0)
                {
+#if COLLISIONPARANOID >= 3
+                       Con_Printf(">");
+#endif
                        num = node->children[0];
                        goto loc0;
                }
@@ -461,6 +475,9 @@ loc0:
 
        // the line intersects, find intersection point
        // LordHavoc: this uses the original trace for maximum accuracy
+#if COLLISIONPARANOID >= 3
+       Con_Printf("M");
+#endif
        if (plane->type < 3)
        {
                t1 = t->start[plane->type] - plane->dist;
@@ -506,10 +523,37 @@ loc0:
        midf = t1 / (t1 - t2);
        t->trace->fraction = bound(0.0f, midf, 1.0);
 
+#if COLLISIONPARANOID >= 3
+       Con_Printf("D");
+#endif
        return HULLCHECKSTATE_DONE;
 }
 
-static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
+#if COLLISIONPARANOID < 2
+static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
+{
+       while (num >= 0)
+               num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
+       num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
+       t->trace->startsupercontents |= num;
+       if (num & SUPERCONTENTS_LIQUIDSMASK)
+               t->trace->inwater = true;
+       if (num == 0)
+               t->trace->inopen = true;
+       if (num & t->trace->hitsupercontentsmask)
+       {
+               t->trace->allsolid = t->trace->startsolid = true;
+               return HULLCHECKSTATE_SOLID;
+       }
+       else
+       {
+               t->trace->allsolid = t->trace->startsolid = false;
+               return HULLCHECKSTATE_EMPTY;
+       }
+}
+#endif
+
+static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
 {
        // this function currently only supports same size start and end
        double boxsize[3];
@@ -526,7 +570,9 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3
                rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
        else if (model->brush.ishlbsp)
        {
-               if (boxsize[0] <= 32)
+               // LordHavoc: this has to have a minor tolerance (the .1) because of
+               // minor float precision errors from the box being transformed around
+               if (boxsize[0] < 32.1)
                {
                        if (boxsize[2] < 54) // pick the nearest of 36 or 72
                                rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
@@ -538,7 +584,9 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3
        }
        else
        {
-               if (boxsize[0] <= 32)
+               // LordHavoc: this has to have a minor tolerance (the .1) because of
+               // minor float precision errors from the box being transformed around
+               if (boxsize[0] < 32.1)
                        rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
                else
                        rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
@@ -546,7 +594,113 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3
        VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
        VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
        VectorSubtract(rhc.end, rhc.start, rhc.dist);
+#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");
+#else
+       if (DotProduct(rhc.dist, 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 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;
+       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->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)
@@ -701,7 +855,7 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
        {
                if (in == inend)
                {
-                       Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
+                       Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
                        return;
                }
                c = *in++;
@@ -709,11 +863,16 @@ static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *
                        *out++ = c;
                else
                {
+                       if (in == inend)
+                       {
+                               Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
+                               return;
+                       }
                        for (c = *in++;c > 0;c--)
                        {
                                if (out == outend)
                                {
-                                       Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
+                                       Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
                                        return;
                                }
                                *out++ = 0;
@@ -2660,6 +2819,8 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
                Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
        mod->brush.ishlbsp = i == 30;
 
+       mod->soundfromcenter = true;
+       mod->TraceBox = Mod_Q1BSP_TraceBox;
        mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
        mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
        mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
@@ -2667,7 +2828,6 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer)
        mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
        mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
        mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
-       mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
        mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
        mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
        mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
@@ -3303,6 +3463,12 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
        q3dtexture_t *in;
        q3mtexture_t *out;
        int i, count;
+       int j, c;
+       fssearch_t *search;
+       char *f;
+       const char *text;
+       int flags;
+       char shadername[Q3PATHLENGTH];
 
        in = (void *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
@@ -3315,19 +3481,157 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
 
        for (i = 0;i < count;i++, in++, out++)
        {
+               out->number = i;
                strlcpy (out->name, in->name, sizeof (out->name));
                out->surfaceflags = LittleLong(in->surfaceflags);
                out->nativecontents = LittleLong(in->contents);
                out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
-               out->renderflags = 0;
-               if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
-                       out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
-               if (!strncmp(out->name, "textures/skies/", 15))
-                       out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
-
-               out->number = i;
                Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
+               out->surfaceparms = -1;
        }
+
+       // do a quick parse of shader files to get surfaceparms
+       if ((search = FS_Search("scripts/*.shader", true, false)))
+       {
+               for (i = 0;i < search->numfilenames;i++)
+               {
+                       if ((f = FS_LoadFile(search->filenames[i], false)))
+                       {
+                               text = f;
+                               while (COM_ParseToken(&text, false))
+                               {
+                                       snprintf(shadername, sizeof(shadername), "%s", com_token);
+                                       flags = 0;
+                                       if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
+                                       {
+                                               while (COM_ParseToken(&text, false))
+                                               {
+                                                       if (!strcmp(com_token, "}"))
+                                                               break;
+                                                       else if (!strcmp(com_token, "{"))
+                                                       {
+                                                               while (COM_ParseToken(&text, false))
+                                                               {
+                                                                       if (!strcmp(com_token, "}"))
+                                                                               break;
+                                                               }
+                                                       }
+                                                       else if (!strcmp(com_token, "surfaceparm"))
+                                                       {
+                                                               if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
+                                                               {
+                                                                       if (!strcmp(com_token, "alphashadow"))
+                                                                               flags |= Q3SURFACEPARM_ALPHASHADOW;
+                                                                       else if (!strcmp(com_token, "areaportal"))
+                                                                               flags |= Q3SURFACEPARM_AREAPORTAL;
+                                                                       else if (!strcmp(com_token, "clusterportal"))
+                                                                               flags |= Q3SURFACEPARM_CLUSTERPORTAL;
+                                                                       else if (!strcmp(com_token, "detail"))
+                                                                               flags |= Q3SURFACEPARM_DETAIL;
+                                                                       else if (!strcmp(com_token, "donotenter"))
+                                                                               flags |= Q3SURFACEPARM_DONOTENTER;
+                                                                       else if (!strcmp(com_token, "fog"))
+                                                                               flags |= Q3SURFACEPARM_FOG;
+                                                                       else if (!strcmp(com_token, "lava"))
+                                                                               flags |= Q3SURFACEPARM_LAVA;
+                                                                       else if (!strcmp(com_token, "lightfilter"))
+                                                                               flags |= Q3SURFACEPARM_LIGHTFILTER;
+                                                                       else if (!strcmp(com_token, "metalsteps"))
+                                                                               flags |= Q3SURFACEPARM_METALSTEPS;
+                                                                       else if (!strcmp(com_token, "nodamage"))
+                                                                               flags |= Q3SURFACEPARM_NODAMAGE;
+                                                                       else if (!strcmp(com_token, "nodlight"))
+                                                                               flags |= Q3SURFACEPARM_NODLIGHT;
+                                                                       else if (!strcmp(com_token, "nodraw"))
+                                                                               flags |= Q3SURFACEPARM_NODRAW;
+                                                                       else if (!strcmp(com_token, "nodrop"))
+                                                                               flags |= Q3SURFACEPARM_NODROP;
+                                                                       else if (!strcmp(com_token, "noimpact"))
+                                                                               flags |= Q3SURFACEPARM_NOIMPACT;
+                                                                       else if (!strcmp(com_token, "nolightmap"))
+                                                                               flags |= Q3SURFACEPARM_NOLIGHTMAP;
+                                                                       else if (!strcmp(com_token, "nomarks"))
+                                                                               flags |= Q3SURFACEPARM_NOMARKS;
+                                                                       else if (!strcmp(com_token, "nomipmaps"))
+                                                                               flags |= Q3SURFACEPARM_NOMIPMAPS;
+                                                                       else if (!strcmp(com_token, "nonsolid"))
+                                                                               flags |= Q3SURFACEPARM_NONSOLID;
+                                                                       else if (!strcmp(com_token, "origin"))
+                                                                               flags |= Q3SURFACEPARM_ORIGIN;
+                                                                       else if (!strcmp(com_token, "playerclip"))
+                                                                               flags |= Q3SURFACEPARM_PLAYERCLIP;
+                                                                       else if (!strcmp(com_token, "sky"))
+                                                                               flags |= Q3SURFACEPARM_SKY;
+                                                                       else if (!strcmp(com_token, "slick"))
+                                                                               flags |= Q3SURFACEPARM_SLICK;
+                                                                       else if (!strcmp(com_token, "slime"))
+                                                                               flags |= Q3SURFACEPARM_SLIME;
+                                                                       else if (!strcmp(com_token, "structural"))
+                                                                               flags |= Q3SURFACEPARM_STRUCTURAL;
+                                                                       else if (!strcmp(com_token, "trans"))
+                                                                               flags |= Q3SURFACEPARM_TRANS;
+                                                                       else if (!strcmp(com_token, "water"))
+                                                                               flags |= Q3SURFACEPARM_WATER;
+                                                                       else
+                                                                               Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
+                                                                       if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n"))
+                                                                       {
+                                                                               Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
+                                                                               goto parseerror;
+                                                                       }
+                                                               }
+                                                               else
+                                                               {
+                                                                       Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
+                                                                       goto parseerror;
+                                                               }
+                                                       }
+                                                       else
+                                                       {
+                                                               // look for linebreak or }
+                                                               while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}"));
+                                                               // break out to top level if it was }
+                                                               if (!strcmp(com_token, "}"))
+                                                                       break;
+                                                       }
+                                               }
+                                               // add shader to list (shadername and flags)
+                                               // actually here we just poke into the texture settings
+                                               for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
+                                                       if (!strcmp(out->name, shadername))
+                                                               out->surfaceparms = flags;
+                                       }
+                                       else
+                                       {
+                                               Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
+                                               goto parseerror;
+                                       }
+                               }
+parseerror:
+                               Mem_Free(f);
+                       }
+               }
+       }
+
+       c = 0;
+       for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
+       {
+               if (out->surfaceparms == -1)
+               {
+                       c++;
+                       Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
+                       out->surfaceparms = 0;
+                       // these are defaults
+                       if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
+                        || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
+                               out->surfaceparms |= Q3SURFACEPARM_NODRAW;
+                       if (!strncmp(out->name, "textures/skies/", 15))
+                               out->surfaceparms |= Q3SURFACEPARM_SKY;
+                       if (R_TextureHasAlpha(out->skin.base))
+                               out->surfaceparms |= Q3SURFACEPARM_TRANS;
+               }
+       }
+       Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
 }
 
 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
@@ -3389,6 +3693,7 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
        q3mbrush_t *out;
        int i, j, n, c, count, maxplanes;
        mplane_t *planes;
+       winding_t *temp1, *temp2;
 
        in = (void *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
@@ -3399,6 +3704,9 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
        loadmodel->brushq3.data_brushes = out;
        loadmodel->brushq3.num_brushes = count;
 
+       temp1 = Winding_New(64);
+       temp2 = Winding_New(64);
+
        maxplanes = 0;
        planes = NULL;
 
@@ -3429,10 +3737,12 @@ static void Mod_Q3BSP_LoadBrushes(lump_t *l)
                        planes[j].dist = out->firstbrushside[j].plane->dist;
                }
                // make the colbrush from the planes
-               out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
+               out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
        }
        if (planes)
                Mem_Free(planes);
+       Winding_Free(temp1);
+       Winding_Free(temp2);
 }
 
 static void Mod_Q3BSP_LoadEffects(lump_t *l)
@@ -3470,13 +3780,13 @@ static void Mod_Q3BSP_LoadVertices(lump_t *l)
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
        loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
-       loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
-       loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
-       loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
-       loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
+       loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
+       loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
+       loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
+       loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
+       loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
+       loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
+       loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
 
        for (i = 0;i < count;i++, in++)
        {
@@ -3514,11 +3824,11 @@ static void Mod_Q3BSP_LoadTriangles(lump_t *l)
        if (l->filelen % sizeof(int[3]))
                Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
 
        loadmodel->brushq3.num_triangles = count / 3;
        loadmodel->brushq3.data_element3i = out;
-       loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
+       loadmodel->brushq3.data_neighbor3i = out + count;
 
        for (i = 0;i < count;i++, in++, out++)
        {
@@ -3537,6 +3847,8 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
        rtexture_t **out;
        int i, count;
 
+       if (!l->filelen)
+               return;
        in = (void *)(mod_base + l->fileofs);
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
@@ -3585,6 +3897,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                 && out->type != Q3FACETYPE_FLARE)
                {
                        Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
+                       out->num_vertices = 0;
+                       out->num_triangles = 0;
                        out->type = 0; // error
                        continue;
                }
@@ -3593,6 +3907,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                if (n < 0 || n >= loadmodel->brushq3.num_textures)
                {
                        Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
+                       out->num_vertices = 0;
+                       out->num_triangles = 0;
                        out->type = 0; // error
                        continue;
                        n = 0;
@@ -3626,35 +3942,41 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                if (out->num_triangles * 3 != LittleLong(in->numelements))
                {
                        Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
+                       out->num_vertices = 0;
+                       out->num_triangles = 0;
                        out->type = 0; // error
                        continue;
                }
                if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
                {
                        Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
+                       out->num_vertices = 0;
+                       out->num_triangles = 0;
                        out->type = 0; // error
                        continue;
                }
                if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
                {
                        Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
+                       out->num_vertices = 0;
+                       out->num_triangles = 0;
                        out->type = 0; // error
                        continue;
                }
-               out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
-               out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
-               out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
-               out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
-               out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
-               out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
-               out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
-               out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
-               out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
                switch(out->type)
                {
                case Q3FACETYPE_POLYGON:
                case Q3FACETYPE_MESH:
                        // no processing necessary
+                       out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
+                       out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
+                       out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
+                       out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
+                       out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
+                       out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
+                       out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
+                       out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
+                       out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
                        break;
                case Q3FACETYPE_PATCH:
                        patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
@@ -3662,6 +3984,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        if (patchsize[0] < 1 || patchsize[1] < 1)
                        {
                                Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
+                               out->num_vertices = 0;
+                               out->num_triangles = 0;
                                out->type = 0; // error
                                continue;
                        }
@@ -3672,6 +3996,16 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        finalheight = ((patchsize[1] - 1) << ylevel) + 1;
                        finalvertices = finalwidth * finalheight;
                        finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
+                       originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
+                       //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
+                       //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
+                       //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
+                       originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
+                       originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
+                       originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
+                       //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
+                       //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
+                       /*
                        originalvertex3f = out->data_vertex3f;
                        //originalsvector3f = out->data_svector3f;
                        //originaltvector3f = out->data_tvector3f;
@@ -3681,14 +4015,15 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
                        //originalelement3i = out->data_element3i;
                        //originalneighbor3i = out->data_neighbor3i;
-                       out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
+                       */
+                       out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
                        out->data_svector3f = out->data_vertex3f + finalvertices * 3;
                        out->data_tvector3f = out->data_svector3f + finalvertices * 3;
                        out->data_normal3f = out->data_tvector3f + finalvertices * 3;
                        out->data_color4f = out->data_normal3f + finalvertices * 3;
                        out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
                        out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
-                       out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
+                       out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
                        out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
                        out->type = Q3FACETYPE_MESH;
                        out->firstvertex = -1;
@@ -3733,7 +4068,9 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                case Q3FACETYPE_FLARE:
                        Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
                        // don't render it
+                       out->num_vertices = 0;
                        out->num_triangles = 0;
+                       out->type = 0;
                        break;
                }
                for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
@@ -3778,6 +4115,95 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                        out->maxs[2] += 1.0f;
                }
        }
+
+       // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
+       /*
+       {
+       int totalverts, totaltris;
+       int originalnum_vertices;
+       float *originaldata_vertex3f;
+       float *originaldata_texcoordtexture2f;
+       float *originaldata_texcoordlightmap2f;
+       float *originaldata_svector3f;
+       float *originaldata_tvector3f;
+       float *originaldata_normal3f;
+       float *originaldata_color4f;
+       int originalnum_triangles;
+       int *originaldata_element3i;
+       int *originaldata_neighbor3i;
+
+       totalverts = 0;
+       totaltris = 0;
+       for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
+       {
+               if (!out->type)
+                       continue;
+               totalverts += out->num_vertices;
+               totaltris += out->num_triangles;
+       }
+
+       originalnum_vertices = loadmodel->brushq3.num_vertices;
+       originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
+       originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
+       originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
+       originaldata_svector3f = loadmodel->brushq3.data_svector3f;
+       originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
+       originaldata_normal3f = loadmodel->brushq3.data_normal3f;
+       originaldata_color4f = loadmodel->brushq3.data_color4f;
+       originalnum_triangles = loadmodel->brushq3.num_triangles;
+       originaldata_element3i = loadmodel->brushq3.data_element3i;
+       originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
+       loadmodel->brushq3.num_vertices = totalverts;
+       loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
+       loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
+       loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
+       loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
+       loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
+       loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
+       loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
+       loadmodel->brushq3.num_triangles = totaltris;
+       loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
+       loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
+       totalverts = 0;
+       totaltris = 0;
+       for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
+       {
+               if (!out->type)
+                       continue;
+               Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
+               memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
+               memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
+               memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
+               if (out->firstvertex == -1)
+                       Mem_Free(out->data_vertex3f);
+               if (out->firstelement == -1)
+                       Mem_Free(out->data_element3i);
+               out->firstvertex = totalverts;
+               out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
+               out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
+               out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
+               out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
+               out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
+               out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
+               out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
+               out->firstelement = totaltris * 3;
+               out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
+               out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
+               //for (j = 0;j < out->numtriangles * 3;j++)
+               //      out->data_element3i[j] += totalverts - out->firstvertex;
+               totalverts += out->num_vertices;
+               totaltris += out->num_triangles;
+       }
+       Mem_Free(originaldata_vertex3f);
+       Mem_Free(originaldata_element3i);
+       }
+       */
 }
 
 static void Mod_Q3BSP_LoadModels(lump_t *l)
@@ -4050,9 +4476,9 @@ static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientc
        // FIXME: write this
        if (!model->brushq3.num_lightgrid)
        {
-               ambientcolor[0] += 128;
-               ambientcolor[1] += 128;
-               ambientcolor[2] += 128;
+               ambientcolor[0] = 1;
+               ambientcolor[1] = 1;
+               ambientcolor[2] = 1;
                return;
        }
        Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
@@ -4255,7 +4681,7 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod
        }
 }
 
-static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
+static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
 {
        int i;
        float segmentmins[3], segmentmaxs[3];
@@ -4441,13 +4867,13 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int superco
        return nativecontents;
 }
 
-//extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
+extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
 extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
 {
-       int i;
+       int i, j;
        q3dheader_t *header;
        float corner[3], yawradius, modelradius;
 
@@ -4467,6 +4893,8 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                R_ResetQuakeSky();
        }
 
+       mod->soundfromcenter = true;
+       mod->TraceBox = Mod_Q3BSP_TraceBox;
        mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
        mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
        mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
@@ -4474,7 +4902,6 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
        mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
        mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
        mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
-       mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
        //mod->DrawSky = R_Q3BSP_DrawSky;
        mod->Draw = R_Q3BSP_Draw;
        mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
@@ -4543,6 +4970,12 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer)
                mod->yawmaxs[2] = mod->normalmaxs[2];
                mod->radius = modelradius;
                mod->radius2 = modelradius * modelradius;
+
+               for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
+                       if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
+                               break;
+               if (j < mod->brushq3.data_thismodel->numfaces)
+                       mod->DrawSky = R_Q3BSP_DrawSky;
        }
 }