]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - gl_rsurf.c
changed rtlight compiled cluster list/pvs to leaf list/pvs to enable slightly more...
[xonotic/darkplaces.git] / gl_rsurf.c
index 007e6bbae707b466e9b1a16200dc2ea8d29a1034..16e50846b19cbc90e9624b40f92fa603a18c978e 100644 (file)
@@ -24,15 +24,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #define MAX_LIGHTMAP_SIZE 256
 
-static unsigned int intblocklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*3]; // LordHavoc: *3 for colored lighting
-static float floatblocklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*3]; // LordHavoc: *3 for colored lighting
-
-static qbyte templight[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*4];
-
 cvar_t r_ambient = {0, "r_ambient", "0"};
 cvar_t r_drawportals = {0, "r_drawportals", "0"};
 cvar_t r_testvis = {0, "r_testvis", "0"};
-cvar_t r_floatbuildlightmap = {0, "r_floatbuildlightmap", "0"};
 cvar_t r_detailtextures = {CVAR_SAVE, "r_detailtextures", "1"};
 cvar_t r_surfaceworldnode = {0, "r_surfaceworldnode", "0"};
 cvar_t r_drawcollisionbrushes_polygonfactor = {0, "r_drawcollisionbrushes_polygonfactor", "-1"};
@@ -58,154 +52,79 @@ Combine and scale multiple lightmaps into the 8.8 format in blocklights
 */
 static void R_BuildLightMap (const entity_render_t *ent, msurface_t *surface)
 {
-       if (!r_floatbuildlightmap.integer)
+       int smax, tmax, i, j, size, size3, maps, stride, l;
+       unsigned int *bl, scale;
+       qbyte *lightmap, *out, *stain;
+       static unsigned int intblocklights[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*3]; // LordHavoc: *3 for colored lighting
+       static qbyte templight[MAX_LIGHTMAP_SIZE*MAX_LIGHTMAP_SIZE*4];
+
+       // update cached lighting info
+       surface->cached_dlight = 0;
+
+       smax = (surface->lightmapinfo->extents[0]>>4)+1;
+       tmax = (surface->lightmapinfo->extents[1]>>4)+1;
+       size = smax*tmax;
+       size3 = size*3;
+       lightmap = surface->lightmapinfo->samples;
+
+// set to full bright if no light data
+       bl = intblocklights;
+       if (!ent->model->brushq1.lightdata)
        {
-               int smax, tmax, i, j, size, size3, maps, stride, l;
-               unsigned int *bl, scale;
-               qbyte *lightmap, *out, *stain;
-
-               // update cached lighting info
-               surface->cached_dlight = 0;
-
-               smax = (surface->extents[0]>>4)+1;
-               tmax = (surface->extents[1]>>4)+1;
-               size = smax*tmax;
-               size3 = size*3;
-               lightmap = surface->samples;
-
-       // set to full bright if no light data
-               bl = intblocklights;
-               if (!ent->model->brushq1.lightdata)
-               {
-                       for (i = 0;i < size3;i++)
-                               bl[i] = 255*256;
-               }
-               else
-               {
-       // clear to no light
-                       memset(bl, 0, size*3*sizeof(unsigned int));
-
-       // add all the lightmaps
-                       if (lightmap)
-                       {
-                               bl = intblocklights;
-                               for (maps = 0;maps < MAXLIGHTMAPS && surface->styles[maps] != 255;maps++, lightmap += size3)
-                                       for (scale = d_lightstylevalue[surface->styles[maps]], i = 0;i < size3;i++)
-                                               bl[i] += lightmap[i] * scale;
-                       }
-               }
-
-               stain = surface->stainsamples;
-               bl = intblocklights;
-               out = templight;
-               // the >> 16 shift adjusts down 8 bits to account for the stainmap
-               // scaling, and remaps the 0-65536 (2x overbright) to 0-256, it will
-               // be doubled during rendering to achieve 2x overbright
-               // (0 = 0.0, 128 = 1.0, 256 = 2.0)
-               if (ent->model->brushq1.lightmaprgba)
-               {
-                       stride = (surface->lightmaptexturestride - smax) * 4;
-                       for (i = 0;i < tmax;i++, out += stride)
-                       {
-                               for (j = 0;j < smax;j++)
-                               {
-                                       l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
-                                       l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
-                                       l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
-                                       *out++ = 255;
-                               }
-                       }
-               }
-               else
-               {
-                       stride = (surface->lightmaptexturestride - smax) * 3;
-                       for (i = 0;i < tmax;i++, out += stride)
-                       {
-                               for (j = 0;j < smax;j++)
-                               {
-                                       l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
-                                       l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
-                                       l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
-                               }
-                       }
-               }
-
-               R_UpdateTexture(surface->lightmaptexture, templight);
+               for (i = 0;i < size3;i++)
+                       bl[i] = 255*256;
        }
        else
        {
-               int smax, tmax, i, j, size, size3, maps, stride, l;
-               float *bl, scale;
-               qbyte *lightmap, *out, *stain;
-
-               // update cached lighting info
-               surface->cached_dlight = 0;
-
-               smax = (surface->extents[0]>>4)+1;
-               tmax = (surface->extents[1]>>4)+1;
-               size = smax*tmax;
-               size3 = size*3;
-               lightmap = surface->samples;
-
-       // set to full bright if no light data
-               bl = floatblocklights;
-               if (!ent->model->brushq1.lightdata)
-               {
-                       for (i = 0;i < size3;i++)
-                               bl[i] = 255*256;
-               }
-               else
-               {
-                       memset(bl, 0, size*3*sizeof(float));
+// clear to no light
+               memset(bl, 0, size*3*sizeof(unsigned int));
 
-                       // add all the lightmaps
-                       if (lightmap)
-                       {
-                               bl = floatblocklights;
-                               for (maps = 0;maps < MAXLIGHTMAPS && surface->styles[maps] != 255;maps++, lightmap += size3)
-                                       for (scale = d_lightstylevalue[surface->styles[maps]], i = 0;i < size3;i++)
-                                               bl[i] += lightmap[i] * scale;
-                       }
+// add all the lightmaps
+               if (lightmap)
+               {
+                       bl = intblocklights;
+                       for (maps = 0;maps < MAXLIGHTMAPS && surface->lightmapinfo->styles[maps] != 255;maps++, lightmap += size3)
+                               for (scale = d_lightstylevalue[surface->lightmapinfo->styles[maps]], i = 0;i < size3;i++)
+                                       bl[i] += lightmap[i] * scale;
                }
+       }
 
-               stain = surface->stainsamples;
-               bl = floatblocklights;
-               out = templight;
-               // this scaling adjusts down 8 bits to account for the stainmap
-               // scaling, and remaps the 0.0-2.0 (2x overbright) to 0-256, it will
-               // be doubled during rendering to achieve 2x overbright
-               // (0 = 0.0, 128 = 1.0, 256 = 2.0)
-               scale = 1.0f / (1 << 16);
-               if (ent->model->brushq1.lightmaprgba)
+       stain = surface->lightmapinfo->stainsamples;
+       bl = intblocklights;
+       out = templight;
+       // the >> 16 shift adjusts down 8 bits to account for the stainmap
+       // scaling, and remaps the 0-65536 (2x overbright) to 0-256, it will
+       // be doubled during rendering to achieve 2x overbright
+       // (0 = 0.0, 128 = 1.0, 256 = 2.0)
+       if (ent->model->brushq1.lightmaprgba)
+       {
+               stride = (surface->lightmapinfo->lightmaptexturestride - smax) * 4;
+               for (i = 0;i < tmax;i++, out += stride)
                {
-                       stride = (surface->lightmaptexturestride - smax) * 4;
-                       for (i = 0;i < tmax;i++, out += stride)
+                       for (j = 0;j < smax;j++)
                        {
-                               for (j = 0;j < smax;j++)
-                               {
-                                       l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
-                                       l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
-                                       l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
-                                       *out++ = 255;
-                               }
+                               l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
+                               l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
+                               l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
+                               *out++ = 255;
                        }
                }
-               else
+       }
+       else
+       {
+               stride = (surface->lightmapinfo->lightmaptexturestride - smax) * 3;
+               for (i = 0;i < tmax;i++, out += stride)
                {
-                       stride = (surface->lightmaptexturestride - smax) * 3;
-                       for (i = 0;i < tmax;i++, out += stride)
+                       for (j = 0;j < smax;j++)
                        {
-                               for (j = 0;j < smax;j++)
-                               {
-                                       l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
-                                       l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
-                                       l = *bl++ * *stain++ * scale;*out++ = min(l, 255);
-                               }
+                               l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
+                               l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
+                               l = (*bl++ * *stain++) >> 16;*out++ = min(l, 255);
                        }
                }
-
-               R_UpdateTexture(surface->lightmaptexture, templight);
        }
+
+       R_UpdateTexture(surface->lightmaptexture, templight);
 }
 
 void R_StainNode (mnode_t *node, model_t *model, const vec3_t origin, float radius, const float fcolor[8])
@@ -251,13 +170,13 @@ loc0:
 
        for (surface = model->brush.data_surfaces + node->firstsurface, endsurface = surface + node->numsurfaces;surface < endsurface;surface++)
        {
-               if (surface->stainsamples)
+               if (surface->lightmapinfo->stainsamples)
                {
-                       smax = (surface->extents[0] >> 4) + 1;
-                       tmax = (surface->extents[1] >> 4) + 1;
+                       smax = (surface->lightmapinfo->extents[0] >> 4) + 1;
+                       tmax = (surface->lightmapinfo->extents[1] >> 4) + 1;
 
-                       impacts = DotProduct (impact, surface->texinfo->vecs[0]) + surface->texinfo->vecs[0][3] - surface->texturemins[0];
-                       impactt = DotProduct (impact, surface->texinfo->vecs[1]) + surface->texinfo->vecs[1][3] - surface->texturemins[1];
+                       impacts = DotProduct (impact, surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3] - surface->lightmapinfo->texturemins[0];
+                       impactt = DotProduct (impact, surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3] - surface->lightmapinfo->texturemins[1];
 
                        s = bound(0, impacts, smax * 16) - impacts;
                        t = bound(0, impactt, tmax * 16) - impactt;
@@ -269,7 +188,7 @@ loc0:
                        for (s = 0, i = impacts; s < smax; s++, i -= 16)
                                sdtable[s] = i * i + dist2;
 
-                       bl = surface->stainsamples;
+                       bl = surface->lightmapinfo->stainsamples;
                        smax3 = smax * 3;
                        stained = false;
 
@@ -337,7 +256,7 @@ void R_Stain (const vec3_t origin, float radius, int cr1, int cg1, int cb1, int
        entity_render_t *ent;
        model_t *model;
        vec3_t org;
-       if (r_refdef.worldmodel == NULL || !r_refdef.worldmodel->brush.data_nodes)
+       if (r_refdef.worldmodel == NULL || !r_refdef.worldmodel->brush.data_nodes || !r_refdef.worldmodel->brushq1.lightdata)
                return;
        fcolor[0] = cr1;
        fcolor[1] = cg1;
@@ -353,7 +272,7 @@ void R_Stain (const vec3_t origin, float radius, int cr1, int cg1, int cb1, int
        // look for embedded bmodels
        for (n = 0;n < cl_num_brushmodel_entities;n++)
        {
-               ent = cl_brushmodel_entities[n];
+               ent = &cl_entities[cl_brushmodel_entities[n]].render;
                model = ent->model;
                if (model && model->name[0] == '*')
                {
@@ -376,55 +295,78 @@ void R_Stain (const vec3_t origin, float radius, int cr1, int cg1, int cb1, int
 =============================================================
 */
 
-static float *RSurf_GetVertexPointer(const entity_render_t *ent, const msurface_t *surface)
+static void RSurf_DeformVertices(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg)
 {
-       if (surface->texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
+       int i, j;
+       float center[3], forward[3], right[3], up[3], v[4][3];
+       matrix4x4_t matrix1, imatrix1;
+       if (texture->textureflags & Q3TEXTUREFLAG_AUTOSPRITE2)
        {
-               texture_t *texture = surface->texture;
-               int i, j;
-               float center[3], center2[3], forward[3], right[3], up[3], v[4][3];
-               matrix4x4_t matrix1, imatrix1;
-               R_Mesh_Matrix(&r_identitymatrix);
                // a single autosprite surface can contain multiple sprites...
-               for (j = 0;j < surface->mesh.num_vertices - 3;j += 4)
+               VectorClear(forward);
+               VectorClear(right);
+               VectorSet(up, 0, 0, 1);
+               for (j = 0;j < surface->num_vertices - 3;j += 4)
                {
                        VectorClear(center);
                        for (i = 0;i < 4;i++)
-                               VectorAdd(center, surface->mesh.data_vertex3f + (j+i) * 3, center);
+                               VectorAdd(center, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
                        VectorScale(center, 0.25f, center);
-                       Matrix4x4_Transform(&ent->matrix, center, center2);
                        // FIXME: calculate vectors from triangle edges instead of using texture vectors as an easy way out?
-                       Matrix4x4_FromVectors(&matrix1, surface->mesh.data_normal3f + j*3, surface->mesh.data_svector3f + j*3, surface->mesh.data_tvector3f + j*3, center);
+                       Matrix4x4_FromVectors(&matrix1, (surface->groupmesh->data_normal3f + 3 * surface->num_firstvertex) + j*3, (surface->groupmesh->data_svector3f + 3 * surface->num_firstvertex) + j*3, (surface->groupmesh->data_tvector3f + 3 * surface->num_firstvertex) + j*3, center);
                        Matrix4x4_Invert_Simple(&imatrix1, &matrix1);
                        for (i = 0;i < 4;i++)
-                               Matrix4x4_Transform(&imatrix1, surface->mesh.data_vertex3f + (j+i)*3, v[i]);
-                       if (texture->textureflags & Q3TEXTUREFLAG_AUTOSPRITE2)
-                       {
-                               forward[0] = r_vieworigin[0] - center2[0];
-                               forward[1] = r_vieworigin[1] - center2[1];
-                               forward[2] = 0;
-                               VectorNormalize(forward);
-                               right[0] = forward[1];
-                               right[1] = -forward[0];
-                               right[2] = 0;
-                               up[0] = 0;
-                               up[1] = 0;
-                               up[2] = 1;
-                       }
-                       else
-                       {
-                               VectorCopy(r_viewforward, forward);
-                               VectorCopy(r_viewright, right);
-                               VectorCopy(r_viewup, up);
-                       }
+                               Matrix4x4_Transform(&imatrix1, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, v[i]);
+                       forward[0] = modelorg[0] - center[0];
+                       forward[1] = modelorg[1] - center[1];
+                       VectorNormalize(forward);
+                       right[0] = forward[1];
+                       right[1] = -forward[0];
+                       for (i = 0;i < 4;i++)
+                               VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, varray_vertex3f + (surface->num_firstvertex+i+j) * 3);
+               }
+       }
+       else if (texture->textureflags & Q3TEXTUREFLAG_AUTOSPRITE)
+       {
+               Matrix4x4_Transform(&ent->inversematrix, r_viewforward, forward);
+               Matrix4x4_Transform(&ent->inversematrix, r_viewright, right);
+               Matrix4x4_Transform(&ent->inversematrix, r_viewup, up);
+               // a single autosprite surface can contain multiple sprites...
+               for (j = 0;j < surface->num_vertices - 3;j += 4)
+               {
+                       VectorClear(center);
+                       for (i = 0;i < 4;i++)
+                               VectorAdd(center, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + (j+i) * 3, center);
+                       VectorScale(center, 0.25f, center);
+                       // FIXME: calculate vectors from triangle edges instead of using texture vectors as an easy way out?
+                       Matrix4x4_FromVectors(&matrix1, (surface->groupmesh->data_normal3f + 3 * surface->num_firstvertex) + j*3, (surface->groupmesh->data_svector3f + 3 * surface->num_firstvertex) + j*3, (surface->groupmesh->data_tvector3f + 3 * surface->num_firstvertex) + j*3, center);
+                       Matrix4x4_Invert_Simple(&imatrix1, &matrix1);
                        for (i = 0;i < 4;i++)
-                               VectorMAMAMAM(1, center2, v[i][0], forward, v[i][1], right, v[i][2], up, varray_vertex3f + (i+j) * 3);
+                               Matrix4x4_Transform(&imatrix1, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + (j+i)*3, v[i]);
+                       for (i = 0;i < 4;i++)
+                               VectorMAMAMAM(1, center, v[i][0], forward, v[i][1], right, v[i][2], up, varray_vertex3f + (surface->num_firstvertex+i+j) * 3);
                }
+       }
+       else
+               memcpy((varray_vertex3f + 3 * surface->num_firstvertex), (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), sizeof(float[3]) * surface->num_vertices);
+}
+
+// any sort of deformvertices call is *VERY* rare, so this must be optimized
+// to skip deformvertices quickly!
+#if 1
+#define RSurf_GetVertexPointer(ent, texture, surface, modelorg) ((texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2)) ? (RSurf_DeformVertices(ent, texture, surface, modelorg), varray_vertex3f) : surface->groupmesh->data_vertex3f)
+#else
+static float *RSurf_GetVertexPointer(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg)
+{
+       if (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
+       {
+               RSurf_DeformVertices(ent, texture, surface, modelorg);
                return varray_vertex3f;
        }
        else
-               return surface->mesh.data_vertex3f;
+               return surface->groupmesh->data_vertex3f;
 }
+#endif
 
 void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
 {
@@ -462,7 +404,7 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
 {
        int i;
        int texturesurfaceindex;
-       const float *v;
+       const float *v, *vertex3f;
        float *c;
        float diff[3];
        float f, r, g, b, a, base, colorscale;
@@ -475,6 +417,8 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
        qboolean dofogpass;
        qboolean fogallpasses;
        qboolean waterscrolling;
+       surfmesh_t *groupmesh;
+       rtexture_t *lightmaptexture;
        rmeshstate_t m;
        texture = texture->currentframe;
        if (texture->currentmaterialflags & MATERIALFLAG_NODRAW)
@@ -489,22 +433,16 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                qglDisable(GL_CULL_FACE);
                GL_Color(1, 1, 1, 1);
                memset(&m, 0, sizeof(m));
+               R_Mesh_State(&m);
                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                {
                        surface = texturesurfacelist[texturesurfaceindex];
-                       m.tex[0] = R_GetTexture(surface->lightmaptexture);
-                       m.pointer_texcoord[0] = surface->mesh.data_texcoordlightmap2f;
-                       if (surface->lightmaptexture)
-                       {
-                               GL_Color(1, 1, 1, 1);
-                               m.pointer_color = NULL;
-                       }
-                       else
-                               m.pointer_color = surface->mesh.data_lightmapcolor4f;
-                       m.pointer_vertex = surface->mesh.data_vertex3f;
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, surface->mesh.num_vertices);
-                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                       R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
+                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                       R_Mesh_ColorPointer(surface->lightmaptexture ? NULL : surface->groupmesh->data_lightmapcolor4f);
+                       R_Mesh_VertexPointer(surface->groupmesh->data_vertex3f);
+                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                        GL_LockArrays(0, 0);
                }
                qglEnable(GL_CULL_FACE);
@@ -531,7 +469,7 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                R_Sky();
                }
                // LordHavoc: HalfLife maps have freaky skypolys...
-               if (!ent->model->brush.ishlbsp)
+               //if (!ent->model->brush.ishlbsp)
                {
                        R_Mesh_Matrix(&ent->matrix);
                        GL_Color(fogcolor[0], fogcolor[1], fogcolor[2], 1);
@@ -551,13 +489,13 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                        GL_DepthMask(true);
                        GL_DepthTest(true);
                        memset(&m, 0, sizeof(m));
+                       R_Mesh_State(&m);
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
                                surface = texturesurfacelist[texturesurfaceindex];
-                               m.pointer_vertex = surface->mesh.data_vertex3f;
-                               R_Mesh_State(&m);
-                               GL_LockArrays(0, surface->mesh.num_vertices);
-                               R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                               R_Mesh_VertexPointer(surface->groupmesh->data_vertex3f);
+                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                GL_LockArrays(0, 0);
                        }
                        GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
@@ -574,7 +512,9 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                m.texcombinergb[1] = GL_REPLACE;
                Matrix4x4_CreateFromQuakeEntity(&m.texmatrix[0], 0, 0, 0, 0, 0, 0, r_watershader.value);
                m.texmatrix[1] = r_surf_waterscrollmatrix;
+               R_Mesh_State(&m);
 
+               GL_Color(1, 1, 1, texture->currentalpha);
                GL_ActiveTexture(0);
                qglTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_TEXTURE_2D);
                GL_ActiveTexture(1);
@@ -586,12 +526,11 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                {
                        surface = texturesurfacelist[texturesurfaceindex];
-                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                       m.pointer_texcoord[1] = surface->mesh.data_texcoordtexture2f;
-                       R_Mesh_State(&m);
-                       GL_LockArrays(0, surface->mesh.num_vertices);
-                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                       R_Mesh_VertexPointer(RSurf_GetVertexPointer(ent, texture, surface, modelorg));
+                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                       R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
+                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                        GL_LockArrays(0, 0);
                }
 
@@ -605,7 +544,7 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                dobase = true;
                dolightmap = !(texture->currentmaterialflags & MATERIALFLAG_FULLBRIGHT);
                doambient = r_ambient.value >= (1/64.0f);
-               dodetail = texture->skin.detail != NULL && !(texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT);
+               dodetail = r_detailtextures.integer && texture->skin.detail != NULL && !(texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT);
                doglow = texture->skin.glow != NULL;
                dofogpass = fogenabled && !(texture->currentmaterialflags & MATERIALFLAG_ADD);
                fogallpasses = fogenabled && !(texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT);
@@ -620,25 +559,37 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                        m.texmatrix[1] = r_surf_waterscrollmatrix;
                                m.texrgbscale[1] = 2;
                                m.pointer_color = varray_color4f;
+                               R_Mesh_State(&m);
                                colorscale = 1;
                                r = ent->colormod[0] * colorscale;
                                g = ent->colormod[1] * colorscale;
                                b = ent->colormod[2] * colorscale;
                                a = texture->currentalpha;
                                base = r_ambient.value * (1.0f / 64.0f);
+                               // q3bsp has no lightmap updates, so the lightstylevalue that
+                               // would normally be baked into the lightmaptexture must be
+                               // applied to the color
+                               if (ent->model->brushq1.lightdata)
+                               {
+                                       float scale = d_lightstylevalue[0] * (1.0f / 128.0f);
+                                       r *= scale;
+                                       g *= scale;
+                                       b *= scale;
+                               }
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
                                        surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordlightmap2f;
-                                       m.pointer_texcoord[1] = surface->mesh.data_texcoordtexture2f;
+                                       vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                                       R_Mesh_VertexPointer(vertex3f);
+                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                                       R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
                                        if (surface->lightmaptexture)
                                        {
-                                               m.tex[0] = R_GetTexture(surface->lightmaptexture);
+                                               R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
                                                if (fogallpasses)
                                                {
-                                                       m.pointer_color = varray_color4f;
-                                                       for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                                       R_Mesh_ColorPointer(varray_color4f);
+                                                       for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                        {
                                                                VectorSubtract(v, modelorg, diff);
                                                                f = 1 - exp(fogdensity/DotProduct(diff, diff));
@@ -650,91 +601,41 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                }
                                                else
                                                {
-                                                       m.pointer_color = NULL;
+                                                       R_Mesh_ColorPointer(NULL);
                                                        GL_Color(r, g, b, a);
                                                }
                                        }
                                        else
                                        {
-                                               m.tex[0] = R_GetTexture(r_texture_white);
-                                               m.pointer_color = varray_color4f;
-                                               if (surface->styles[0] != 255)
+                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                               R_Mesh_ColorPointer(varray_color4f);
+                                               if (!surface->lightmaptexture)
                                                {
-                                                       for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                                       for (i = 0, c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4)
                                                        {
-                                                               c[0] = 0;
-                                                               c[1] = 0;
-                                                               c[2] = 0;
-                                                               if (surface->styles[0] != 255)
-                                                               {
-                                                                       if (surface->mesh.data_lightmapcolor4f)
-                                                                       {
-                                                                               float scale = d_lightstylevalue[surface->styles[0]] * (1.0f / 128.0f);
-                                                                               VectorMA(c, scale, surface->mesh.data_lightmapcolor4f + i*4, c);
-                                                                       }
-                                                                       else if (surface->mesh.data_lightmapoffsets)
-                                                                       {
-                                                                               const qbyte *lm = surface->samples + surface->mesh.data_lightmapoffsets[i];
-                                                                               float scale = d_lightstylevalue[surface->styles[0]] * (1.0f / 32768.0f);
-                                                                               VectorMA(c, scale, lm, c);
-                                                                               if (surface->styles[1] != 255)
-                                                                               {
-                                                                                       int size3 = ((surface->extents[0]>>4)+1)*((surface->extents[1]>>4)+1)*3;
-                                                                                       lm += size3;
-                                                                                       scale = d_lightstylevalue[surface->styles[1]] * (1.0f / 32768.0f);
-                                                                                       VectorMA(c, scale, lm, c);
-                                                                                       if (surface->styles[2] != 255)
-                                                                                       {
-                                                                                               lm += size3;
-                                                                                               scale = d_lightstylevalue[surface->styles[2]] * (1.0f / 32768.0f);
-                                                                                               VectorMA(c, scale, lm, c);
-                                                                                               if (surface->styles[3] != 255)
-                                                                                               {
-                                                                                                       lm += size3;
-                                                                                                       scale = d_lightstylevalue[surface->styles[3]] * (1.0f / 32768.0f);
-                                                                                                       VectorMA(c, scale, lm, c);
-                                                                                               }
-                                                                                       }
-                                                                               }
-                                                                       }
-                                                               }
-                                                               c[0] *= r;
-                                                               c[1] *= g;
-                                                               c[2] *= b;
-                                                               if (fogallpasses)
+                                                               c[0] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+0] * r;
+                                                               c[1] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+1] * g;
+                                                               c[2] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+2] * b;
+                                                               c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
+                                                       }
+                                                       if (fogallpasses)
+                                                       {
+                                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                                {
                                                                        VectorSubtract(v, modelorg, diff);
                                                                        f = 1 - exp(fogdensity/DotProduct(diff, diff));
                                                                        VectorScale(c, f, c);
                                                                }
-                                                               if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
-                                                                       c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
-                                                               else
-                                                                       c[3] = a;
                                                        }
                                                }
                                                else
                                                {
-                                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
-                                                       {
-                                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
-                                                               {
-                                                                       c[0] = 0;
-                                                                       c[1] = 0;
-                                                                       c[2] = 0;
-                                                                       c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
-                                                               }
-                                                       }
-                                                       else
-                                                       {
-                                                               m.pointer_color = NULL;
-                                                               GL_Color(0, 0, 0, a);
-                                                       }
+                                                       R_Mesh_ColorPointer(NULL);
+                                                       GL_Color(0, 0, 0, a);
                                                }
                                        }
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                        GL_LockArrays(0, 0);
                                }
                        }
@@ -745,7 +646,6 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                m.tex[0] = R_GetTexture(texture->skin.base);
                                if (waterscrolling)
                                        m.texmatrix[0] = r_surf_waterscrollmatrix;
-                               m.texmatrix[0] = r_surf_waterscrollmatrix;
                                m.pointer_color = varray_color4f;
                                colorscale = 1;
                                if (gl_combine.integer)
@@ -753,51 +653,57 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                        m.texrgbscale[0] = 4;
                                        colorscale *= 0.25f;
                                }
+                               R_Mesh_State(&m);
                                r = ent->colormod[0] * colorscale;
                                g = ent->colormod[1] * colorscale;
                                b = ent->colormod[2] * colorscale;
                                a = texture->currentalpha;
                                if (dolightmap)
                                {
+                                       // q3bsp has no lightmap updates, so the lightstylevalue that
+                                       // would normally be baked into the lightmaptexture must be
+                                       // applied to the color
+                                       if (!ent->model->brushq1.lightdata)
+                                       {
+                                               float scale = d_lightstylevalue[0] * (1.0f / 128.0f);
+                                               r *= scale;
+                                               g *= scale;
+                                               b *= scale;
+                                       }
                                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                        {
                                                surface = texturesurfacelist[texturesurfaceindex];
-                                               m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                               m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                               vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                                               R_Mesh_VertexPointer(vertex3f);
+                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                {
                                                        c[0] = 0;
                                                        c[1] = 0;
                                                        c[2] = 0;
-                                                       if (surface->styles[0] != 255)
+                                                       if (!surface->lightmapinfo)
+                                                               VectorCopy((surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex) + i*4, c);
+                                                       else //if (surface->lightmapinfo)
                                                        {
-                                                               if (surface->mesh.data_lightmapcolor4f)
+                                                               const qbyte *lm = surface->lightmapinfo->samples + (surface->groupmesh->data_lightmapoffsets + surface->num_firstvertex)[i];
+                                                               float scale = d_lightstylevalue[surface->lightmapinfo->styles[0]] * (1.0f / 32768.0f);
+                                                               VectorMA(c, scale, lm, c);
+                                                               if (surface->lightmapinfo->styles[1] != 255)
                                                                {
-                                                                       float scale = d_lightstylevalue[surface->styles[0]] * (1.0f / 128.0f);
-                                                                       VectorMA(c, scale, surface->mesh.data_lightmapcolor4f + i*4, c);
-                                                               }
-                                                               else if (surface->mesh.data_lightmapoffsets)
-                                                               {
-                                                                       const qbyte *lm = surface->samples + surface->mesh.data_lightmapoffsets[i];
-                                                                       float scale = d_lightstylevalue[surface->styles[0]] * (1.0f / 32768.0f);
+                                                                       int size3 = ((surface->lightmapinfo->extents[0]>>4)+1)*((surface->lightmapinfo->extents[1]>>4)+1)*3;
+                                                                       lm += size3;
+                                                                       scale = d_lightstylevalue[surface->lightmapinfo->styles[1]] * (1.0f / 32768.0f);
                                                                        VectorMA(c, scale, lm, c);
-                                                                       if (surface->styles[1] != 255)
+                                                                       if (surface->lightmapinfo->styles[2] != 255)
                                                                        {
-                                                                               int size3 = ((surface->extents[0]>>4)+1)*((surface->extents[1]>>4)+1)*3;
                                                                                lm += size3;
-                                                                               scale = d_lightstylevalue[surface->styles[1]] * (1.0f / 32768.0f);
+                                                                               scale = d_lightstylevalue[surface->lightmapinfo->styles[2]] * (1.0f / 32768.0f);
                                                                                VectorMA(c, scale, lm, c);
-                                                                               if (surface->styles[2] != 255)
+                                                                               if (surface->lightmapinfo->styles[3] != 255)
                                                                                {
                                                                                        lm += size3;
-                                                                                       scale = d_lightstylevalue[surface->styles[2]] * (1.0f / 32768.0f);
+                                                                                       scale = d_lightstylevalue[surface->lightmapinfo->styles[3]] * (1.0f / 32768.0f);
                                                                                        VectorMA(c, scale, lm, c);
-                                                                                       if (surface->styles[3] != 255)
-                                                                                       {
-                                                                                               lm += size3;
-                                                                                               scale = d_lightstylevalue[surface->styles[3]] * (1.0f / 32768.0f);
-                                                                                               VectorMA(c, scale, lm, c);
-                                                                                       }
                                                                                }
                                                                        }
                                                                }
@@ -811,14 +717,13 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                                f = 1 - exp(fogdensity/DotProduct(diff, diff));
                                                                VectorScale(c, f, c);
                                                        }
-                                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
-                                                               c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
+                                                       if (!surface->lightmapinfo && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                                                               c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
                                                        else
                                                                c[3] = a;
                                                }
-                                               R_Mesh_State(&m);
-                                               GL_LockArrays(0, surface->mesh.num_vertices);
-                                               R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                                GL_LockArrays(0, 0);
                                        }
                                }
@@ -829,27 +734,26 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                                {
                                                        surface = texturesurfacelist[texturesurfaceindex];
-                                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                                       if (m.tex[1])
-                                                               m.pointer_texcoord[1] = surface->mesh.data_texcoordtexture2f;
-                                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                                                       vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                                                       R_Mesh_VertexPointer(vertex3f);
+                                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                                       if (!surface->lightmapinfo && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
                                                        {
-                                                               m.pointer_color = varray_color4f;
-                                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                                               R_Mesh_ColorPointer(varray_color4f);
+                                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                                {
                                                                        VectorSubtract(v, modelorg, diff);
                                                                        f = 1 - exp(fogdensity/DotProduct(diff, diff));
                                                                        c[0] = r * f;
                                                                        c[1] = g * f;
                                                                        c[2] = b * f;
-                                                                       c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
+                                                                       c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
                                                                }
                                                        }
                                                        else
                                                        {
-                                                               m.pointer_color = varray_color4f;
-                                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                                               R_Mesh_ColorPointer(varray_color4f);
+                                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                                {
                                                                        VectorSubtract(v, modelorg, diff);
                                                                        f = 1 - exp(fogdensity/DotProduct(diff, diff));
@@ -859,9 +763,8 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                                        c[3] = a;
                                                                }
                                                        }
-                                                       R_Mesh_State(&m);
-                                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                                        GL_LockArrays(0, 0);
                                                }
                                        }
@@ -870,29 +773,27 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                                {
                                                        surface = texturesurfacelist[texturesurfaceindex];
-                                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                                       if (m.tex[1])
-                                                               m.pointer_texcoord[1] = surface->mesh.data_texcoordtexture2f;
-                                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                                                       vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                                                       R_Mesh_VertexPointer(vertex3f);
+                                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                                       if (!surface->lightmaptexture && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
                                                        {
-                                                               m.pointer_color = varray_color4f;
-                                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                                               R_Mesh_ColorPointer(varray_color4f);
+                                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                                {
                                                                        c[0] = r;
                                                                        c[1] = g;
                                                                        c[2] = b;
-                                                                       c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
+                                                                       c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
                                                                }
                                                        }
                                                        else
                                                        {
-                                                               m.pointer_color = NULL;
+                                                               R_Mesh_ColorPointer(NULL);
                                                                GL_Color(r, g, b, a);
                                                        }
-                                                       R_Mesh_State(&m);
-                                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                                        GL_LockArrays(0, 0);
                                                }
                                        }
@@ -910,14 +811,14 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                m.tex[0] = R_GetTexture(texture->skin.base);
                                if (waterscrolling)
                                        m.texmatrix[0] = r_surf_waterscrollmatrix;
+                               R_Mesh_State(&m);
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
                                        surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                       R_Mesh_VertexPointer(RSurf_GetVertexPointer(ent, texture, surface, modelorg));
+                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                        GL_LockArrays(0, 0);
                                }
                        }
@@ -927,13 +828,13 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                dobase = false;
                                GL_Color(0, 0, 0, 1);
                                memset(&m, 0, sizeof(m));
+                               R_Mesh_State(&m);
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
                                        surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                       R_Mesh_VertexPointer(RSurf_GetVertexPointer(ent, texture, surface, modelorg));
+                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                        GL_LockArrays(0, 0);
                                }
                        }
@@ -949,47 +850,169 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                if (waterscrolling)
                                        m.texmatrix[1] = r_surf_waterscrollmatrix;
                                m.texrgbscale[1] = 2;
+                               R_Mesh_State(&m);
                                r = ent->colormod[0] * r_lightmapintensity;
                                g = ent->colormod[1] * r_lightmapintensity;
                                b = ent->colormod[2] * r_lightmapintensity;
-                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               GL_Color(r, g, b, 1);
+                               if (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
                                {
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       memset(&m, 0, sizeof(m));
-                                       m.tex[1] = R_GetTexture(texture->skin.base);
-                                       if (waterscrolling)
-                                               m.texmatrix[1] = r_surf_waterscrollmatrix;
-                                       m.texrgbscale[1] = 2;
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordlightmap2f;
-                                       m.pointer_texcoord[1] = surface->mesh.data_texcoordtexture2f;
-                                       if (surface->lightmaptexture)
+                                       R_Mesh_VertexPointer(varray_vertex3f);
+                                       if (r == 1 && g == 1 && b == 1)
                                        {
-                                               m.tex[0] = R_GetTexture(surface->lightmaptexture);
-                                               m.pointer_color = NULL;
-                                               GL_Color(r, g, b, 1);
+                                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                               {
+                                                       surface = texturesurfacelist[texturesurfaceindex];
+                                                       RSurf_DeformVertices(ent, texture, surface, modelorg);
+                                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                                                       R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
+                                                       if (surface->lightmaptexture)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
+                                                               R_Mesh_ColorPointer(NULL);
+                                                       }
+                                                       else //if (r == 1 && g == 1 && b == 1)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                               R_Mesh_ColorPointer(surface->groupmesh->data_lightmapcolor4f);
+                                                       }
+                                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                                       GL_LockArrays(0, 0);
+                                               }
                                        }
-                                       else if (r == 1 && g == 1 && b == 1)
+                                       else
                                        {
-                                               m.tex[0] = R_GetTexture(r_texture_white);
-                                               m.pointer_color = surface->mesh.data_lightmapcolor4f;
+                                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                               {
+                                                       surface = texturesurfacelist[texturesurfaceindex];
+                                                       RSurf_DeformVertices(ent, texture, surface, modelorg);
+                                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                                                       R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
+                                                       if (surface->lightmaptexture)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
+                                                               R_Mesh_ColorPointer(NULL);
+                                                       }
+                                                       else
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                               R_Mesh_ColorPointer(varray_color4f);
+                                                               for (i = 0, c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4)
+                                                               {
+                                                                       c[0] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+0] * r;
+                                                                       c[1] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+1] * g;
+                                                                       c[2] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+2] * b;
+                                                                       c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3];
+                                                               }
+                                                       }
+                                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                                       GL_LockArrays(0, 0);
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       if (r == 1 && g == 1 && b == 1)
+                                       {
+#if 0
+                                               // experimental direct state calls for measuring
+                                               // R_Mesh_ call overhead, do not use!
+                                               R_Mesh_VertexPointer(varray_vertex3f);
+                                               R_Mesh_TexCoordPointer(0, 2, varray_texcoord2f[0]);
+                                               R_Mesh_TexCoordPointer(1, 2, varray_texcoord2f[1]);
+                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                               R_Mesh_ColorPointer(varray_color4f);
+                                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                               {
+                                                       surface = texturesurfacelist[texturesurfaceindex];
+                                                       qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), surface->groupmesh->data_vertex3f);
+                                                       qglClientActiveTexture(GL_TEXTURE0_ARB);
+                                                       qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), surface->groupmesh->data_texcoordlightmap2f);
+                                                       qglClientActiveTexture(GL_TEXTURE1_ARB);
+                                                       qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), surface->groupmesh->data_texcoordtexture2f);
+                                                       if (surface->lightmaptexture)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
+                                                               qglDisableClientState(GL_COLOR_ARRAY);
+                                                               qglColor4f(r, g, b, 1);
+                                                       }
+                                                       else //if (r == 1 && g == 1 && b == 1)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                               qglEnableClientState(GL_COLOR_ARRAY);
+                                                               qglColorPointer(4, GL_FLOAT, sizeof(float[4]), surface->groupmesh->data_lightmapcolor4f);
+                                                       }
+                                                       qglLockArraysEXT(0, surface->num_vertices);
+                                                       qglDrawRangeElements(GL_TRIANGLES, surface->num_firstvertex, surface->num_firstvertex + surface->num_vertices, surface->num_triangles * 3, GL_UNSIGNED_INT, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                                       qglUnlockArraysEXT();
+                                               }
+#else
+                                               groupmesh = NULL;
+                                               lightmaptexture = NULL;
+                                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                               {
+                                                       surface = texturesurfacelist[texturesurfaceindex];
+                                                       if (groupmesh != surface->groupmesh)
+                                                       {
+                                                               groupmesh = surface->groupmesh;
+                                                               R_Mesh_VertexPointer(groupmesh->data_vertex3f);
+                                                               R_Mesh_TexCoordPointer(0, 2, groupmesh->data_texcoordlightmap2f);
+                                                               R_Mesh_TexCoordPointer(1, 2, groupmesh->data_texcoordtexture2f);
+                                                               if (!lightmaptexture)
+                                                                       R_Mesh_ColorPointer(groupmesh->data_lightmapcolor4f);
+                                                       }
+                                                       if (lightmaptexture != surface->lightmaptexture)
+                                                       {
+                                                               lightmaptexture = surface->lightmaptexture;
+                                                               if (lightmaptexture)
+                                                               {
+                                                                       R_Mesh_TexBind(0, R_GetTexture(lightmaptexture));
+                                                                       R_Mesh_ColorPointer(NULL);
+                                                               }
+                                                               else //if (r == 1 && g == 1 && b == 1)
+                                                               {
+                                                                       R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                                       R_Mesh_ColorPointer(surface->groupmesh->data_lightmapcolor4f);
+                                                               }
+                                                       }
+                                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                                       GL_LockArrays(0, 0);
+                                               }
+#endif
                                        }
                                        else
                                        {
-                                               m.tex[0] = R_GetTexture(r_texture_white);
-                                               m.pointer_color = varray_color4f;
-                                               for (i = 0;i < surface->mesh.num_vertices;i++)
+                                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                                {
-                                                       varray_color4f[i*4+0] = surface->mesh.data_lightmapcolor4f[i*4+0] * r;
-                                                       varray_color4f[i*4+1] = surface->mesh.data_lightmapcolor4f[i*4+1] * g;
-                                                       varray_color4f[i*4+2] = surface->mesh.data_lightmapcolor4f[i*4+2] * b;
-                                                       varray_color4f[i*4+3] = surface->mesh.data_lightmapcolor4f[i*4+3];
+                                                       surface = texturesurfacelist[texturesurfaceindex];
+                                                       R_Mesh_VertexPointer(surface->groupmesh->data_vertex3f);
+                                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                                                       R_Mesh_TexCoordPointer(1, 2, surface->groupmesh->data_texcoordtexture2f);
+                                                       if (surface->lightmaptexture)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
+                                                               R_Mesh_ColorPointer(NULL);
+                                                       }
+                                                       else
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                               R_Mesh_ColorPointer(varray_color4f);
+                                                               for (i = 0, c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, c += 4)
+                                                               {
+                                                                       c[0] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+0] * r;
+                                                                       c[1] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+1] * g;
+                                                                       c[2] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+2] * b;
+                                                                       c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3];
+                                                               }
+                                                       }
+                                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                                       GL_LockArrays(0, 0);
                                                }
                                        }
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
-                                       GL_LockArrays(0, 0);
                                }
                        }
                        // single texture
@@ -999,20 +1022,63 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                GL_DepthMask(true);
                                GL_Color(1, 1, 1, 1);
                                memset(&m, 0, sizeof(m));
-                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               R_Mesh_State(&m);
+                               if (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
                                {
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.tex[0] = R_GetTexture(surface->lightmaptexture);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordlightmap2f;
-                                       if (surface->lightmaptexture)
-                                               m.pointer_color = NULL;
-                                       else
-                                               m.pointer_color = surface->mesh.data_lightmapcolor4f;
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
-                                       GL_LockArrays(0, 0);
+                                       R_Mesh_VertexPointer(varray_vertex3f);
+                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                       {
+                                               surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DeformVertices(ent, texture, surface, modelorg);
+                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordlightmap2f);
+                                               if (surface->lightmaptexture)
+                                               {
+                                                       R_Mesh_TexBind(0, R_GetTexture(surface->lightmaptexture));
+                                                       R_Mesh_ColorPointer(NULL);
+                                               }
+                                               else //if (r == 1 && g == 1 && b == 1)
+                                               {
+                                                       R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                       R_Mesh_ColorPointer(surface->groupmesh->data_lightmapcolor4f);
+                                               }
+                                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                               GL_LockArrays(0, 0);
+                                       }
+                               }
+                               else
+                               {
+                                       groupmesh = NULL;
+                                       lightmaptexture = NULL;
+                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                       {
+                                               surface = texturesurfacelist[texturesurfaceindex];
+                                               if (groupmesh != surface->groupmesh)
+                                               {
+                                                       groupmesh = surface->groupmesh;
+                                                       R_Mesh_VertexPointer(groupmesh->data_vertex3f);
+                                                       R_Mesh_TexCoordPointer(0, 2, groupmesh->data_texcoordlightmap2f);
+                                                       if (!lightmaptexture)
+                                                               R_Mesh_ColorPointer(groupmesh->data_lightmapcolor4f);
+                                               }
+                                               if (lightmaptexture != surface->lightmaptexture)
+                                               {
+                                                       lightmaptexture = surface->lightmaptexture;
+                                                       if (lightmaptexture)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(lightmaptexture));
+                                                               R_Mesh_ColorPointer(NULL);
+                                                       }
+                                                       else //if (r == 1 && g == 1 && b == 1)
+                                                       {
+                                                               R_Mesh_TexBind(0, R_GetTexture(r_texture_white));
+                                                               R_Mesh_ColorPointer(surface->groupmesh->data_lightmapcolor4f);
+                                                       }
+                                               }
+                                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                               GL_LockArrays(0, 0);
+                                       }
                                }
                        }
                        if (dobase)
@@ -1024,15 +1090,36 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                m.tex[0] = R_GetTexture(texture->skin.base);
                                if (waterscrolling)
                                        m.texmatrix[0] = r_surf_waterscrollmatrix;
-                               for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                               R_Mesh_State(&m);
+                               if (texture->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
                                {
-                                       surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
-                                       GL_LockArrays(0, 0);
+                                       R_Mesh_VertexPointer(varray_vertex3f);
+                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                       {
+                                               surface = texturesurfacelist[texturesurfaceindex];
+                                               RSurf_DeformVertices(ent, texture, surface, modelorg);
+                                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                               GL_LockArrays(0, 0);
+                                       }
+                               }
+                               else
+                               {
+                                       groupmesh = NULL;
+                                       for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
+                                       {
+                                               surface = texturesurfacelist[texturesurfaceindex];
+                                               if (groupmesh != surface->groupmesh)
+                                               {
+                                                       groupmesh = surface->groupmesh;
+                                                       R_Mesh_VertexPointer(groupmesh->data_vertex3f);
+                                                       R_Mesh_TexCoordPointer(0, 2, groupmesh->data_texcoordtexture2f);
+                                               }
+                                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
+                                               GL_LockArrays(0, 0);
+                                       }
                                }
                        }
                }
@@ -1052,6 +1139,7 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                m.texrgbscale[0] = 4;
                                colorscale *= 0.25f;
                        }
+                       R_Mesh_State(&m);
                        base = r_ambient.value * (1.0f / 64.0f);
                        r = ent->colormod[0] * colorscale * base;
                        g = ent->colormod[1] * colorscale * base;
@@ -1060,9 +1148,10 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
                                surface = texturesurfacelist[texturesurfaceindex];
-                               m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                               m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                               vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                               R_Mesh_VertexPointer(vertex3f);
+                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                {
                                        c[0] = r;
                                        c[1] = g;
@@ -1073,14 +1162,13 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                f = 1 - exp(fogdensity/DotProduct(diff, diff));
                                                VectorScale(c, f, c);
                                        }
-                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
-                                               c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
+                                       if (!surface->lightmaptexture && surface->groupmesh->data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                                               c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
                                        else
                                                c[3] = a;
                                }
-                               R_Mesh_State(&m);
-                               GL_LockArrays(0, surface->mesh.num_vertices);
-                               R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                GL_LockArrays(0, 0);
                        }
                }
@@ -1091,14 +1179,14 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                        GL_Color(1, 1, 1, 1);
                        memset(&m, 0, sizeof(m));
                        m.tex[0] = R_GetTexture(texture->skin.detail);
+                       R_Mesh_State(&m);
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
                                surface = texturesurfacelist[texturesurfaceindex];
-                               m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                               m.pointer_texcoord[0] = surface->mesh.data_texcoorddetail2f;
-                               R_Mesh_State(&m);
-                               GL_LockArrays(0, surface->mesh.num_vertices);
-                               R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                               R_Mesh_VertexPointer(RSurf_GetVertexPointer(ent, texture, surface, modelorg));
+                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoorddetail2f);
+                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                GL_LockArrays(0, 0);
                        }
                }
@@ -1112,6 +1200,7 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                        if (waterscrolling)
                                m.texmatrix[0] = r_surf_waterscrollmatrix;
                        m.pointer_color = varray_color4f;
+                       R_Mesh_State(&m);
                        colorscale = 1;
                        r = ent->colormod[0] * colorscale;
                        g = ent->colormod[1] * colorscale;
@@ -1122,25 +1211,25 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
                                        surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                                       vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                                       R_Mesh_VertexPointer(vertex3f);
+                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                       R_Mesh_ColorPointer(varray_color4f);
+                                       if (!surface->lightmaptexture && surface->groupmesh->data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
                                        {
-                                               m.pointer_color = varray_color4f;
-                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                {
                                                        VectorSubtract(v, modelorg, diff);
                                                        f = 1 - exp(fogdensity/DotProduct(diff, diff));
                                                        c[0] = f * r;
                                                        c[1] = f * g;
                                                        c[2] = f * b;
-                                                       c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
+                                                       c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
                                                }
                                        }
                                        else
                                        {
-                                               m.pointer_color = varray_color4f;
-                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                {
                                                        VectorSubtract(v, modelorg, diff);
                                                        f = 1 - exp(fogdensity/DotProduct(diff, diff));
@@ -1150,9 +1239,8 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                        c[3] = a;
                                                }
                                        }
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                        GL_LockArrays(0, 0);
                                }
                        }
@@ -1161,27 +1249,27 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                                {
                                        surface = texturesurfacelist[texturesurfaceindex];
-                                       m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                                       m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                                       if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                                       vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                                       R_Mesh_VertexPointer(vertex3f);
+                                       R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                                       if (!surface->lightmaptexture && surface->groupmesh->data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
                                        {
-                                               m.pointer_color = varray_color4f;
-                                               for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                               R_Mesh_ColorPointer(varray_color4f);
+                                               for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                                {
                                                        c[0] = r;
                                                        c[1] = g;
                                                        c[2] = b;
-                                                       c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * a;
+                                                       c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * a;
                                                }
                                        }
                                        else
                                        {
-                                               m.pointer_color = NULL;
+                                               R_Mesh_ColorPointer(NULL);
                                                GL_Color(r, g, b, a);
                                        }
-                                       R_Mesh_State(&m);
-                                       GL_LockArrays(0, surface->mesh.num_vertices);
-                                       R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                       GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                                       R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                        GL_LockArrays(0, 0);
                                }
                        }
@@ -1208,6 +1296,7 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                        m.tex[0] = R_GetTexture(texture->skin.fog);
                        if (waterscrolling)
                                m.texmatrix[0] = r_surf_waterscrollmatrix;
+                       R_Mesh_State(&m);
                        r = fogcolor[0];
                        g = fogcolor[1];
                        b = fogcolor[2];
@@ -1215,27 +1304,26 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                        for (texturesurfaceindex = 0;texturesurfaceindex < texturenumsurfaces;texturesurfaceindex++)
                        {
                                surface = texturesurfacelist[texturesurfaceindex];
-                               m.pointer_vertex = RSurf_GetVertexPointer(ent, surface);
-                               m.pointer_texcoord[0] = surface->mesh.data_texcoordtexture2f;
-                               m.pointer_color = varray_color4f;
-                               //RSurf_FogPassColors_Vertex3f_Color4f(surface->mesh.data_vertex3f, varray_color4f, fogcolor[0], fogcolor[1], fogcolor[2], texture->currentalpha, 1, surface->mesh.num_vertices, modelorg);
-                               if (surface->mesh.data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
+                               vertex3f = RSurf_GetVertexPointer(ent, texture, surface, modelorg);
+                               R_Mesh_VertexPointer(vertex3f);
+                               R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
+                               R_Mesh_ColorPointer(varray_color4f);
+                               //RSurf_FogPassColors_Vertex3f_Color4f((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), varray_color4f, fogcolor[0], fogcolor[1], fogcolor[2], texture->currentalpha, 1, surface->num_vertices, modelorg);
+                               if (!surface->lightmaptexture && surface->groupmesh->data_lightmapcolor4f && (texture->currentmaterialflags & MATERIALFLAG_TRANSPARENT))
                                {
-                                       m.pointer_color = varray_color4f;
-                                       for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                       for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                        {
                                                VectorSubtract(v, modelorg, diff);
                                                f = exp(fogdensity/DotProduct(diff, diff));
                                                c[0] = r;
                                                c[1] = g;
                                                c[2] = b;
-                                               c[3] = surface->mesh.data_lightmapcolor4f[i*4+3] * f * a;
+                                               c[3] = (surface->groupmesh->data_lightmapcolor4f + 4 * surface->num_firstvertex)[i*4+3] * f * a;
                                        }
                                }
                                else
                                {
-                                       m.pointer_color = varray_color4f;
-                                       for (i = 0, v = m.pointer_vertex, c = varray_color4f;i < surface->mesh.num_vertices;i++, v += 3, c += 4)
+                                       for (i = 0, v = (vertex3f + 3 * surface->num_firstvertex), c = (varray_color4f + 4 * surface->num_firstvertex);i < surface->num_vertices;i++, v += 3, c += 4)
                                        {
                                                VectorSubtract(v, modelorg, diff);
                                                f = exp(fogdensity/DotProduct(diff, diff));
@@ -1245,9 +1333,8 @@ static void R_DrawSurfaceList(const entity_render_t *ent, texture_t *texture, in
                                                c[3] = f * a;
                                        }
                                }
-                               R_Mesh_State(&m);
-                               GL_LockArrays(0, surface->mesh.num_vertices);
-                               R_Mesh_Draw(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                               GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
+                               R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                                GL_LockArrays(0, 0);
                        }
                }
@@ -1314,7 +1401,7 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
        Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, modelorg);
 
        // update light styles
-       if (!skysurfaces)
+       if (!skysurfaces && model->brushq1.light_styleupdatechains)
        {
                for (i = 0;i < model->brushq1.light_styles;i++)
                {
@@ -1334,11 +1421,42 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
        t = NULL;
        texture = NULL;
        numsurfacelist = 0;
-       for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
+       if (ent == r_refdef.worldentity)
+       {
+               for (i = 0, j = model->firstmodelsurface, surface = model->brush.data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
+               {
+                       if (!r_worldsurfacevisible[j])
+                               continue;
+                       if (t != surface->texture)
+                       {
+                               if (numsurfacelist)
+                               {
+                                       R_QueueSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
+                                       numsurfacelist = 0;
+                               }
+                               t = surface->texture;
+                               f = t->currentmaterialflags & flagsmask;
+                               texture = t->currentframe;
+                       }
+                       if (f && surface->num_triangles)
+                       {
+                               // if lightmap parameters changed, rebuild lightmap texture
+                               if (surface->cached_dlight && surface->lightmapinfo->samples)
+                                       R_BuildLightMap(ent, surface);
+                               // add face to draw list
+                               surfacelist[numsurfacelist++] = surface;
+                               if (numsurfacelist >= maxsurfacelist)
+                               {
+                                       R_QueueSurfaceList(ent, texture, numsurfacelist, surfacelist, modelorg);
+                                       numsurfacelist = 0;
+                               }
+                       }
+               }
+       }
+       else
        {
-               if (ent != r_refdef.worldentity || r_worldsurfacevisible[j])
+               for (i = 0, j = model->firstmodelsurface, surface = model->brush.data_surfaces + j;i < model->nummodelsurfaces;i++, j++, surface++)
                {
-                       surface = model->brush.data_surfaces + j;
                        if (t != surface->texture)
                        {
                                if (numsurfacelist)
@@ -1350,10 +1468,10 @@ void R_DrawSurfaces(entity_render_t *ent, qboolean skysurfaces)
                                f = t->currentmaterialflags & flagsmask;
                                texture = t->currentframe;
                        }
-                       if (f)
+                       if (f && surface->num_triangles)
                        {
                                // if lightmap parameters changed, rebuild lightmap texture
-                               if (surface->cached_dlight && surface->samples)
+                               if (surface->cached_dlight && surface->lightmapinfo->samples)
                                        R_BuildLightMap(ent, surface);
                                // add face to draw list
                                surfacelist[numsurfacelist++] = surface;
@@ -1398,30 +1516,38 @@ static void R_DrawPortal_Callback(const void *calldata1, int calldata2)
                for (i = 0, v = varray_vertex3f;i < portal->numpoints;i++, v += 3)
                        VectorCopy(portal->points[i].position, v);
        GL_LockArrays(0, portal->numpoints);
-       R_Mesh_Draw(portal->numpoints, portal->numpoints - 2, polygonelements);
+       R_Mesh_Draw(0, portal->numpoints, portal->numpoints - 2, polygonelements);
        GL_LockArrays(0, 0);
 }
 
 // LordHavoc: this is just a nice debugging tool, very slow
 static void R_DrawPortals(void)
 {
-       int i, portalnum;
+       int i, leafnum;//, portalnum;
        mportal_t *portal;
        float center[3], f;
        model_t *model = r_refdef.worldmodel;
        if (model == NULL)
                return;
-       for (portalnum = 0, portal = model->brush.data_portals;portalnum < model->brush.num_portals;portalnum++, portal++)
+       for (leafnum = 0;leafnum < r_refdef.worldmodel->brush.num_leafs;leafnum++)
        {
-               if (portal->numpoints <= POLYGONELEMENTS_MAXPOINTS)
-               if (!R_CullBox(portal->mins, portal->maxs))
+               if (r_worldleafvisible[leafnum])
                {
-                       VectorClear(center);
-                       for (i = 0;i < portal->numpoints;i++)
-                               VectorAdd(center, portal->points[i].position, center);
-                       f = ixtable[portal->numpoints];
-                       VectorScale(center, f, center);
-                       R_MeshQueue_AddTransparent(center, R_DrawPortal_Callback, portal, portalnum);
+                       //for (portalnum = 0, portal = model->brush.data_portals;portalnum < model->brush.num_portals;portalnum++, portal++)
+                       for (portal = r_refdef.worldmodel->brush.data_leafs[leafnum].portals;portal;portal = portal->next)
+                       {
+                               if (portal->numpoints <= POLYGONELEMENTS_MAXPOINTS)
+                               if (!R_CullBox(portal->mins, portal->maxs))
+                               {
+                                       VectorClear(center);
+                                       for (i = 0;i < portal->numpoints;i++)
+                                               VectorAdd(center, portal->points[i].position, center);
+                                       f = ixtable[portal->numpoints];
+                                       VectorScale(center, f, center);
+                                       //R_MeshQueue_AddTransparent(center, R_DrawPortal_Callback, portal, portalnum);
+                                       R_MeshQueue_AddTransparent(center, R_DrawPortal_Callback, portal, leafnum);
+                               }
+                       }
                }
        }
 }
@@ -1436,7 +1562,7 @@ static void R_DrawCollisionBrush(colbrushf_t *brush)
        i = (int)(((size_t)brush) / sizeof(colbrushf_t));
        GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
        GL_LockArrays(0, brush->numpoints);
-       R_Mesh_Draw(brush->numpoints, brush->numtriangles, brush->elements);
+       R_Mesh_Draw(0, brush->numpoints, brush->numtriangles, brush->elements);
        GL_LockArrays(0, 0);
 }
 
@@ -1444,15 +1570,15 @@ static void R_DrawCollisionSurface(entity_render_t *ent, msurface_t *surface)
 {
        int i;
        rmeshstate_t m;
-       if (!surface->mesh.num_collisiontriangles)
+       if (!surface->num_collisiontriangles)
                return;
        memset(&m, 0, sizeof(m));
-       m.pointer_vertex = surface->mesh.data_collisionvertex3f;
+       m.pointer_vertex = surface->data_collisionvertex3f;
        R_Mesh_State(&m);
        i = (int)(((size_t)surface) / sizeof(msurface_t));
        GL_Color((i & 31) * (1.0f / 32.0f), ((i >> 5) & 31) * (1.0f / 32.0f), ((i >> 10) & 31) * (1.0f / 32.0f), 0.2f);
-       GL_LockArrays(0, surface->mesh.num_collisionvertices);
-       R_Mesh_Draw(surface->mesh.num_collisionvertices, surface->mesh.num_collisiontriangles, surface->mesh.data_collisionelement3i);
+       GL_LockArrays(0, surface->num_collisionvertices);
+       R_Mesh_Draw(0, surface->num_collisionvertices, surface->num_collisiontriangles, surface->data_collisionelement3i);
        GL_LockArrays(0, 0);
 }
 
@@ -1467,7 +1593,7 @@ void R_WorldVisibility(void)
                return;
 
        // if possible find the leaf the view origin is in
-       viewleaf = model->brushq1.PointInLeaf ? model->brushq1.PointInLeaf(model, r_vieworigin) : NULL;
+       viewleaf = model->brush.PointInLeaf ? model->brush.PointInLeaf(model, r_vieworigin) : NULL;
        // if possible fetch the visible cluster bits
        if (model->brush.FatPVS)
                model->brush.FatPVS(model, r_vieworigin, 2, r_pvsbits, sizeof(r_pvsbits));
@@ -1563,86 +1689,93 @@ void R_Q1BSP_Draw(entity_render_t *ent)
                        if (brush->colbrushf && brush->colbrushf->numtriangles)
                                R_DrawCollisionBrush(brush->colbrushf);
                for (i = 0, surface = model->brush.data_surfaces + model->firstmodelsurface;i < model->nummodelsurfaces;i++, surface++)
-                       if (surface->mesh.num_collisiontriangles)
+                       if (surface->num_collisiontriangles)
                                R_DrawCollisionSurface(ent, surface);
                qglPolygonOffset(0, 0);
        }
 }
 
-void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outclusterlist, qbyte *outclusterpvs, int *outnumclusterspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer)
+typedef struct r_q1bsp_getlightinfo_s
 {
-       model_t *model = ent->model;
-       vec3_t lightmins, lightmaxs;
-       int t, leafindex, leafsurfaceindex, surfaceindex, triangleindex, outnumclusters = 0, outnumsurfaces = 0;
-       const int *e;
-       const float *v[3];
-       msurface_t *surface;
-       mleaf_t *leaf;
+       model_t *model;
+       vec3_t relativelightorigin;
+       float lightradius;
+       int *outleaflist;
+       qbyte *outleafpvs;
+       int outnumleafs;
+       int *outsurfacelist;
+       qbyte *outsurfacepvs;
+       int outnumsurfaces;
+       vec3_t outmins;
+       vec3_t outmaxs;
+       vec3_t lightmins;
+       vec3_t lightmaxs;
        const qbyte *pvs;
-       lightmins[0] = relativelightorigin[0] - lightradius;
-       lightmins[1] = relativelightorigin[1] - lightradius;
-       lightmins[2] = relativelightorigin[2] - lightradius;
-       lightmaxs[0] = relativelightorigin[0] + lightradius;
-       lightmaxs[1] = relativelightorigin[1] + lightradius;
-       lightmaxs[2] = relativelightorigin[2] + lightradius;
-       *outnumclusterspointer = 0;
-       *outnumsurfacespointer = 0;
-       memset(outclusterpvs, 0, model->brush.num_pvsclusterbytes);
-       memset(outsurfacepvs, 0, (model->nummodelsurfaces + 7) >> 3);
-       if (model == NULL)
+}
+r_q1bsp_getlightinfo_t;
+
+void R_Q1BSP_RecursiveGetLightInfo(r_q1bsp_getlightinfo_t *info, mnode_t *node)
+{
+       int sides;
+       mleaf_t *leaf;
+       for (;;)
        {
-               VectorCopy(lightmins, outmins);
-               VectorCopy(lightmaxs, outmaxs);
-               return;
+               if (!BoxesOverlap(info->lightmins, info->lightmaxs, node->mins, node->maxs))
+                       return;
+               if (!node->plane)
+                       break;
+               sides = BoxOnPlaneSide(info->lightmins, info->lightmaxs, node->plane) - 1;
+               if (sides == 2)
+               {
+                       R_Q1BSP_RecursiveGetLightInfo(info, node->children[0]);
+                       node = node->children[1];
+               }
+               else
+                       node = node->children[sides];
        }
-       VectorCopy(relativelightorigin, outmins);
-       VectorCopy(relativelightorigin, outmaxs);
-       if (model->brush.GetPVS)
-               pvs = model->brush.GetPVS(model, relativelightorigin);
-       else
-               pvs = NULL;
-       R_UpdateAllTextureInfo(ent);
-       // FIXME: use BSP recursion as lights are often small
-       for (leafindex = 0, leaf = model->brush.data_leafs;leafindex < model->brush.num_leafs;leafindex++, leaf++)
+       leaf = (mleaf_t *)node;
+       if (info->pvs == NULL || CHECKPVSBIT(info->pvs, leaf->clusterindex))
        {
-               if (BoxesOverlap(lightmins, lightmaxs, leaf->mins, leaf->maxs) && (pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
+               info->outmins[0] = min(info->outmins[0], leaf->mins[0]);
+               info->outmins[1] = min(info->outmins[1], leaf->mins[1]);
+               info->outmins[2] = min(info->outmins[2], leaf->mins[2]);
+               info->outmaxs[0] = max(info->outmaxs[0], leaf->maxs[0]);
+               info->outmaxs[1] = max(info->outmaxs[1], leaf->maxs[1]);
+               info->outmaxs[2] = max(info->outmaxs[2], leaf->maxs[2]);
+               if (info->outleafpvs)
                {
-                       outmins[0] = min(outmins[0], leaf->mins[0]);
-                       outmins[1] = min(outmins[1], leaf->mins[1]);
-                       outmins[2] = min(outmins[2], leaf->mins[2]);
-                       outmaxs[0] = max(outmaxs[0], leaf->maxs[0]);
-                       outmaxs[1] = max(outmaxs[1], leaf->maxs[1]);
-                       outmaxs[2] = max(outmaxs[2], leaf->maxs[2]);
-                       if (outclusterpvs)
+                       int leafindex = leaf - info->model->brush.data_leafs;
+                       if (!CHECKPVSBIT(info->outleafpvs, leafindex))
                        {
-                               if (!CHECKPVSBIT(outclusterpvs, leaf->clusterindex))
-                               {
-                                       SETPVSBIT(outclusterpvs, leaf->clusterindex);
-                                       outclusterlist[outnumclusters++] = leaf->clusterindex;
-                               }
+                               SETPVSBIT(info->outleafpvs, leafindex);
+                               info->outleaflist[info->outnumleafs++] = leafindex;
                        }
-                       if (outsurfacepvs)
+               }
+               if (info->outsurfacepvs)
+               {
+                       int leafsurfaceindex;
+                       for (leafsurfaceindex = 0;leafsurfaceindex < leaf->numleafsurfaces;leafsurfaceindex++)
                        {
-                               for (leafsurfaceindex = 0;leafsurfaceindex < leaf->numleafsurfaces;leafsurfaceindex++)
+                               int surfaceindex = leaf->firstleafsurface[leafsurfaceindex];
+                               if (!CHECKPVSBIT(info->outsurfacepvs, surfaceindex))
                                {
-                                       surfaceindex = leaf->firstleafsurface[leafsurfaceindex];
-                                       if (!CHECKPVSBIT(outsurfacepvs, surfaceindex))
+                                       msurface_t *surface = info->model->brush.data_surfaces + surfaceindex;
+                                       if (BoxesOverlap(info->lightmins, info->lightmaxs, surface->mins, surface->maxs))
+                                       if ((surface->texture->currentmaterialflags & (MATERIALFLAG_WALL | MATERIALFLAG_NODRAW | MATERIALFLAG_TRANSPARENT)) == MATERIALFLAG_WALL)
                                        {
-                                               surface = model->brush.data_surfaces + surfaceindex;
-                                               if (BoxesOverlap(lightmins, lightmaxs, surface->mins, surface->maxs))
-                                               if ((surface->texture->currentmaterialflags & (MATERIALFLAG_WALL | MATERIALFLAG_NODRAW | MATERIALFLAG_TRANSPARENT)) == MATERIALFLAG_WALL)
+                                               int triangleindex, t;
+                                               const int *e;
+                                               const vec_t *v[3];
+                                               for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = info->model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->num_triangles;triangleindex++, t++, e += 3)
                                                {
-                                                       for (triangleindex = 0, t = surface->num_firstshadowmeshtriangle, e = model->brush.shadowmesh->element3i + t * 3;triangleindex < surface->mesh.num_triangles;triangleindex++, t++, e += 3)
+                                                       v[0] = info->model->brush.shadowmesh->vertex3f + e[0] * 3;
+                                                       v[1] = info->model->brush.shadowmesh->vertex3f + e[1] * 3;
+                                                       v[2] = info->model->brush.shadowmesh->vertex3f + e[2] * 3;
+                                                       if (info->lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && info->lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && info->lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && info->lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && info->lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && info->lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
                                                        {
-                                                               v[0] = model->brush.shadowmesh->vertex3f + e[0] * 3;
-                                                               v[1] = model->brush.shadowmesh->vertex3f + e[1] * 3;
-                                                               v[2] = model->brush.shadowmesh->vertex3f + e[2] * 3;
-                                                               if (lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
-                                                               {
-                                                                       SETPVSBIT(outsurfacepvs, surfaceindex);
-                                                                       outsurfacelist[outnumsurfaces++] = surfaceindex;
-                                                                       break;
-                                                               }
+                                                               SETPVSBIT(info->outsurfacepvs, surfaceindex);
+                                                               info->outsurfacelist[info->outnumsurfaces++] = surfaceindex;
+                                                               break;
                                                        }
                                                }
                                        }
@@ -1650,17 +1783,65 @@ void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, floa
                        }
                }
        }
+}
+
+void R_Q1BSP_GetLightInfo(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, vec3_t outmins, vec3_t outmaxs, int *outleaflist, qbyte *outleafpvs, int *outnumleafspointer, int *outsurfacelist, qbyte *outsurfacepvs, int *outnumsurfacespointer)
+{
+       r_q1bsp_getlightinfo_t info;
+       VectorCopy(relativelightorigin, info.relativelightorigin);
+       info.lightradius = lightradius;
+       info.lightmins[0] = info.relativelightorigin[0] - info.lightradius;
+       info.lightmins[1] = info.relativelightorigin[1] - info.lightradius;
+       info.lightmins[2] = info.relativelightorigin[2] - info.lightradius;
+       info.lightmaxs[0] = info.relativelightorigin[0] + info.lightradius;
+       info.lightmaxs[1] = info.relativelightorigin[1] + info.lightradius;
+       info.lightmaxs[2] = info.relativelightorigin[2] + info.lightradius;
+       if (ent->model == NULL)
+       {
+               VectorCopy(info.lightmins, outmins);
+               VectorCopy(info.lightmaxs, outmaxs);
+               *outnumleafspointer = 0;
+               *outnumsurfacespointer = 0;
+               return;
+       }
+       info.model = ent->model;
+       info.outleaflist = outleaflist;
+       info.outleafpvs = outleafpvs;
+       info.outnumleafs = 0;
+       info.outsurfacelist = outsurfacelist;
+       info.outsurfacepvs = outsurfacepvs;
+       info.outnumsurfaces = 0;
+       VectorCopy(info.relativelightorigin, info.outmins);
+       VectorCopy(info.relativelightorigin, info.outmaxs);
+       memset(outleafpvs, 0, (info.model->brush.num_leafs + 7) >> 3);
+       memset(outsurfacepvs, 0, (info.model->nummodelsurfaces + 7) >> 3);
+       if (info.model->brush.GetPVS)
+               info.pvs = info.model->brush.GetPVS(info.model, info.relativelightorigin);
+       else
+               info.pvs = NULL;
+       R_UpdateAllTextureInfo(ent);
+       /*
+       if (r_shadow_compilingrtlight)
+       {
+               Portal_Visibility(info.model, info.relativelightorigin, leafmark, surfacemark, const mplane_t *frustumplanes, int numfrustumplanes, int exact, const float *boxmins, const float *boxmaxs, float *updateleafsmins, float *updateleafsmaxs)
+       }
+       else
+       */
+       {
+               // use BSP recursion as lights are often small
+               R_Q1BSP_RecursiveGetLightInfo(&info, info.model->brush.data_nodes);
+       }
 
        // limit combined leaf box to light boundaries
-       outmins[0] = max(outmins[0], lightmins[0]);
-       outmins[1] = max(outmins[1], lightmins[1]);
-       outmins[2] = max(outmins[2], lightmins[2]);
-       outmaxs[0] = min(outmaxs[0], lightmaxs[0]);
-       outmaxs[1] = min(outmaxs[1], lightmaxs[1]);
-       outmaxs[2] = min(outmaxs[2], lightmaxs[2]);
-
-       *outnumclusterspointer = outnumclusters;
-       *outnumsurfacespointer = outnumsurfaces;
+       outmins[0] = max(info.outmins[0], info.lightmins[0]);
+       outmins[1] = max(info.outmins[1], info.lightmins[1]);
+       outmins[2] = max(info.outmins[2], info.lightmins[2]);
+       outmaxs[0] = min(info.outmaxs[0], info.lightmaxs[0]);
+       outmaxs[1] = min(info.outmaxs[1], info.lightmaxs[1]);
+       outmaxs[2] = min(info.outmaxs[2], info.lightmaxs[2]);
+
+       *outnumleafspointer = info.outnumleafs;
+       *outnumsurfacespointer = info.outnumsurfaces;
 }
 
 void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist, const vec3_t lightmins, const vec3_t lightmaxs)
@@ -1681,13 +1862,13 @@ void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin,
                                continue;
                        if (surface->texture->textureflags & Q3TEXTUREFLAG_TWOSIDED)
                                continue;
-                       R_Shadow_MarkVolumeFromBox(surface->num_firstshadowmeshtriangle, surface->mesh.num_triangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, relativelightorigin, lightmins, lightmaxs, surface->mins, surface->maxs);
+                       R_Shadow_MarkVolumeFromBox(surface->num_firstshadowmeshtriangle, surface->num_triangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, relativelightorigin, lightmins, lightmaxs, surface->mins, surface->maxs);
                }
                R_Shadow_VolumeFromList(model->brush.shadowmesh->numverts, model->brush.shadowmesh->numtriangles, model->brush.shadowmesh->vertex3f, model->brush.shadowmesh->element3i, model->brush.shadowmesh->neighbor3i, relativelightorigin, lightradius + model->radius + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
        }
 }
 
-void R_Q1BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int numsurfaces, const int *surfacelist)
+void R_Q1BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int numsurfaces, const int *surfacelist, int visiblelighting)
 {
        model_t *model = ent->model;
        msurface_t *surface;
@@ -1701,14 +1882,14 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t
                for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
                {
                        surface = model->brush.data_surfaces + surfacelist[surfacelistindex];
-                       if (surface->texture->basematerialflags & MATERIALFLAG_NODRAW || !surface->mesh.num_triangles)
+                       if (surface->texture->basematerialflags & MATERIALFLAG_NODRAW || !surface->num_triangles)
                                continue;
                        if (r_shadow_compilingrtlight)
                        {
                                // if compiling an rtlight, capture the mesh
                                t = surface->texture;
                                if ((t->basematerialflags & (MATERIALFLAG_WALL | MATERIALFLAG_TRANSPARENT)) == MATERIALFLAG_WALL)
-                                       Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_light, surface->texture->skin.base, surface->texture->skin.gloss, surface->texture->skin.nmap, surface->mesh.data_vertex3f, surface->mesh.data_svector3f, surface->mesh.data_tvector3f, surface->mesh.data_normal3f, surface->mesh.data_texcoordtexture2f, surface->mesh.num_triangles, surface->mesh.data_element3i);
+                                       Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_light, surface->texture->skin.base, surface->texture->skin.gloss, surface->texture->skin.nmap, surface->groupmesh->data_vertex3f, surface->groupmesh->data_svector3f, surface->groupmesh->data_tvector3f, surface->groupmesh->data_normal3f, surface->groupmesh->data_texcoordtexture2f, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle));
                        }
                        else if (ent != r_refdef.worldentity || r_worldsurfacevisible[surfacelist[surfacelistindex]])
                        {
@@ -1718,7 +1899,7 @@ void R_Q1BSP_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t
                                {
                                        if (surface->texture->textureflags & Q3TEXTUREFLAG_TWOSIDED)
                                                qglDisable(GL_CULL_FACE);
-                                       R_Shadow_RenderLighting(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_element3i, surface->mesh.data_vertex3f, surface->mesh.data_svector3f, surface->mesh.data_tvector3f, surface->mesh.data_normal3f, surface->mesh.data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.base, t->skin.nmap, t->skin.gloss, lightcubemap, ambientscale, diffusescale, specularscale);
+                                       R_Shadow_RenderLighting(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle), surface->groupmesh->data_vertex3f, surface->groupmesh->data_svector3f, surface->groupmesh->data_tvector3f, surface->groupmesh->data_normal3f, surface->groupmesh->data_texcoordtexture2f, relativelightorigin, relativeeyeorigin, lightcolor, NULL, NULL, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, t->skin.base, NULL, NULL, t->skin.nmap, t->skin.gloss, lightcubemap, ambientscale, diffusescale, specularscale, visiblelighting);
                                        if (surface->texture->textureflags & Q3TEXTUREFLAG_TWOSIDED)
                                                qglEnable(GL_CULL_FACE);
                                }
@@ -1747,7 +1928,6 @@ void GL_Surf_Init(void)
        Cvar_RegisterVariable(&r_ambient);
        Cvar_RegisterVariable(&r_drawportals);
        Cvar_RegisterVariable(&r_testvis);
-       Cvar_RegisterVariable(&r_floatbuildlightmap);
        Cvar_RegisterVariable(&r_detailtextures);
        Cvar_RegisterVariable(&r_surfaceworldnode);
        Cvar_RegisterVariable(&r_drawcollisionbrushes_polygonfactor);