+ GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+ }
+ else
+ {
+ // 2/2/2 2D combine path (any dot3 card)
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.pointer_texcoord3f[0] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+ m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.pointer_texcoord3f[1] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[1] = r_shadow_entitytoattenuationz;
+ R_Mesh_TextureState(&m);
+ GL_ColorMask(0,0,0,1);
+ GL_BlendFunc(GL_ONE, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // second pass
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(normalmaptexture);
+ m.texcombinergb[0] = GL_REPLACE;
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+ m.pointer_texcoord_bufferobject[1] = 0;
+ m.pointer_texcoord_bufferoffset[1] = 0;
+ R_Mesh_TextureState(&m);
+ GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // second pass
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(basetexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+ {
+ m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+ m.pointer_texcoord3f[1] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[1] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[1] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[1] = r_shadow_entitytolight;
+ }
+ GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+ }
+ // this final code is shared
+ R_Mesh_TextureState(&m);
+ R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
+}
+
+static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
+{
+ float glossexponent;
+ rmeshstate_t m;
+ // FIXME: detect blendsquare!
+ //if (!gl_support_blendsquare)
+ // return;
+ GL_Color(1,1,1,1);
+ // generate normalization cubemap texcoords
+ R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
+ if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+ {
+ // 2/0/0/1/2 3D combine blendsquare path
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(normalmaptexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+ m.pointer_texcoord_bufferobject[1] = 0;
+ m.pointer_texcoord_bufferoffset[1] = 0;
+ R_Mesh_TextureState(&m);
+ GL_ColorMask(0,0,0,1);
+ // this squares the result
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // second and third pass
+ R_Mesh_ResetTextureState();
+ // square alpha in framebuffer a few times to make it shiny
+ GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
+ for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // fourth pass
+ memset(&m, 0, sizeof(m));
+ m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
+ m.pointer_texcoord3f[0] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+ R_Mesh_TextureState(&m);
+ GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // fifth pass
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(glosstexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+ {
+ m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+ m.pointer_texcoord3f[1] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[1] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[1] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[1] = r_shadow_entitytolight;
+ }
+ GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+ }
+ else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
+ {
+ // 2/0/0/2 3D combine blendsquare path
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(normalmaptexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+ m.pointer_texcoord_bufferobject[1] = 0;
+ m.pointer_texcoord_bufferoffset[1] = 0;
+ R_Mesh_TextureState(&m);
+ GL_ColorMask(0,0,0,1);
+ // this squares the result
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // second and third pass
+ R_Mesh_ResetTextureState();
+ // square alpha in framebuffer a few times to make it shiny
+ GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
+ for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // fourth pass
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(glosstexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
+ m.pointer_texcoord3f[1] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[1] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[1] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
+ GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+ }
+ else
+ {
+ // 2/0/0/2/2 2D combine blendsquare path
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(normalmaptexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
+ m.pointer_texcoord_bufferobject[1] = 0;
+ m.pointer_texcoord_bufferoffset[1] = 0;
+ R_Mesh_TextureState(&m);
+ GL_ColorMask(0,0,0,1);
+ // this squares the result
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // second and third pass
+ R_Mesh_ResetTextureState();
+ // square alpha in framebuffer a few times to make it shiny
+ GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
+ for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // fourth pass
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.pointer_texcoord3f[0] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
+ m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.pointer_texcoord3f[1] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[1] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[1] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[1] = r_shadow_entitytoattenuationz;
+ R_Mesh_TextureState(&m);
+ GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
+ R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
+
+ // fifth pass
+ memset(&m, 0, sizeof(m));
+ m.tex[0] = R_GetTexture(glosstexture);
+ m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
+ m.pointer_texcoord_bufferobject[0] = rsurface_model->surfmesh.vbo;
+ m.pointer_texcoord_bufferoffset[0] = rsurface_model->surfmesh.vbooffset_texcoordtexture2f;
+ m.texmatrix[0] = rsurface_texture->currenttexmatrix;
+ if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
+ {
+ m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
+ m.pointer_texcoord3f[1] = rsurface_vertex3f;
+ m.pointer_texcoord_bufferobject[1] = rsurface_vertex3f_bufferobject;
+ m.pointer_texcoord_bufferoffset[1] = rsurface_vertex3f_bufferoffset;
+ m.texmatrix[1] = r_shadow_entitytolight;
+ }
+ GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
+ }
+ // this final code is shared
+ R_Mesh_TextureState(&m);
+ R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
+}
+
+static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
+{
+ // ARB path (any Geforce, any Radeon)
+ qboolean doambient = ambientscale > 0;
+ qboolean dodiffuse = diffusescale > 0;
+ qboolean dospecular = specularscale > 0;
+ if (!doambient && !dodiffuse && !dospecular)
+ return;
+ R_Mesh_ColorPointer(NULL, 0, 0);
+ if (doambient)
+ R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, ambientscale * r_view.colorscale);
+ if (dodiffuse)
+ R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_view.colorscale);
+ if (dopants)
+ {
+ if (doambient)
+ R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, ambientscale * r_view.colorscale);
+ if (dodiffuse)
+ R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_view.colorscale);
+ }
+ if (doshirt)
+ {
+ if (doambient)
+ R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, ambientscale * r_view.colorscale);
+ if (dodiffuse)
+ R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_view.colorscale);
+ }
+ if (dospecular)
+ R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
+}
+
+void R_Shadow_RenderLighting_Light_Vertex_Pass(const model_t *model, int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, vec3_t diffusecolor2, vec3_t ambientcolor2)
+{
+ int renders;
+ int i;
+ int stop;
+ int newfirstvertex;
+ int newlastvertex;
+ int newnumtriangles;
+ int *newe;
+ const int *e;
+ float *c;
+ int newelements[4096*3];
+ R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
+ for (renders = 0;renders < 64;renders++)
+ {
+ stop = true;
+ newfirstvertex = 0;
+ newlastvertex = 0;
+ newnumtriangles = 0;
+ newe = newelements;
+ // due to low fillrate on the cards this vertex lighting path is
+ // designed for, we manually cull all triangles that do not
+ // contain a lit vertex
+ // this builds batches of triangles from multiple surfaces and
+ // renders them at once
+ for (i = 0, e = element3i;i < numtriangles;i++, e += 3)