+ // 2 texture no3D combine path, two pass
+ m.tex[0] = R_GetTexture(bumptexture);
+ m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
+ m.texcombinergb[0] = GL_REPLACE;
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ m.texalphascale[1] = 1;
+ R_Mesh_TextureState(&m);
+ qglColorMask(0,0,0,1);
+ qglDisable(GL_BLEND);
+ GL_Color(1,1,1,1);
+ memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+ R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = R_GetTexture(basetexture);
+ m.tex3d[1] = 0;
+ m.texcubemap[1] = R_GetTexture(lightcubemap);
+ m.texcombinergb[0] = GL_MODULATE;
+ m.texcombinergb[1] = GL_MODULATE;
+ m.texrgbscale[1] = 1;
+ m.texalphascale[1] = 1;
+ R_Mesh_TextureState(&m);
+ qglColorMask(1,1,1,1);
+ qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+ qglEnable(GL_BLEND);
+ if (lightcubemap)
+ R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+ colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
+ for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+ colorscale *= scale;
+ GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+ for (renders = 0;renders < mult;renders++)
+ R_Mesh_Draw(numverts, numtriangles, elements);
+ }
+ }
+ else if (r_textureunits.integer >= 4)
+ {
+ // 4 texture no3D combine path, two pass
+ m.tex[0] = R_GetTexture(bumptexture);
+ m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
+ m.texcombinergb[0] = GL_REPLACE;
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
+ R_Mesh_TextureState(&m);
+ qglColorMask(0,0,0,1);
+ qglDisable(GL_BLEND);
+ GL_Color(1,1,1,1);
+ memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+ R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
+ R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[2], varray_texcoord[3], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = R_GetTexture(basetexture);
+ m.texcubemap[1] = R_GetTexture(lightcubemap);
+ m.texcombinergb[0] = GL_MODULATE;
+ m.texcombinergb[1] = GL_MODULATE;
+ m.tex[2] = 0;
+ m.tex[3] = 0;
+ R_Mesh_TextureState(&m);
+ qglColorMask(1,1,1,1);
+ qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+ qglEnable(GL_BLEND);
+ if (lightcubemap)
+ R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+ colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
+ for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+ colorscale *= scale;
+ GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+ for (renders = 0;renders < mult;renders++)
+ R_Mesh_Draw(numverts, numtriangles, elements);
+ }
+ else
+ {
+ // 2 texture no3D combine path, three pass
+ m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+ R_Mesh_TextureState(&m);
+ qglColorMask(0,0,0,1);
+ qglDisable(GL_BLEND);
+ GL_Color(1,1,1,1);
+ R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = R_GetTexture(bumptexture);
+ m.tex[1] = 0;
+ m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ R_Mesh_TextureState(&m);
+ qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
+ qglEnable(GL_BLEND);
+ memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+ R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = R_GetTexture(basetexture);
+ m.texcubemap[1] = R_GetTexture(lightcubemap);
+ m.texcombinergb[1] = GL_MODULATE;
+ R_Mesh_TextureState(&m);
+ qglColorMask(1,1,1,1);
+ qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+ if (lightcubemap)
+ R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+ colorscale = r_colorscale * 1.0f * r_shadow_lightintensityscale.value;
+ for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+ colorscale *= scale;
+ GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+ for (renders = 0;renders < mult;renders++)
+ R_Mesh_Draw(numverts, numtriangles, elements);
+ }
+}
+
+void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
+{
+ int renders, mult;
+ float scale, colorscale;
+ rmeshstate_t m;
+ memset(&m, 0, sizeof(m));
+ if (!bumptexture)
+ bumptexture = r_shadow_blankbumptexture;
+ if (!glosstexture)
+ glosstexture = r_shadow_blankglosstexture;
+ if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
+ {
+ // 2 texture no3D combine path, five pass
+ memset(&m, 0, sizeof(m));
+
+ m.tex[0] = R_GetTexture(bumptexture);
+ m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
+ m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
+ R_Mesh_TextureState(&m);
+ qglColorMask(0,0,0,1);
+ qglDisable(GL_BLEND);
+ GL_Color(1,1,1,1);
+ memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+ R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = 0;
+ m.texcubemap[1] = 0;
+ m.texcombinergb[1] = GL_MODULATE;
+ R_Mesh_TextureState(&m);
+ // square alpha in framebuffer a few times to make it shiny
+ qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
+ qglEnable(GL_BLEND);
+ // these comments are a test run through this math for intensity 0.5
+ // 0.5 * 0.5 = 0.25
+ R_Mesh_Draw(numverts, numtriangles, elements);
+ // 0.25 * 0.25 = 0.0625
+ R_Mesh_Draw(numverts, numtriangles, elements);
+ // 0.0625 * 0.0625 = 0.00390625
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
+ m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
+ R_Mesh_TextureState(&m);
+ qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
+ R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
+ R_Mesh_Draw(numverts, numtriangles, elements);
+
+ m.tex[0] = R_GetTexture(glosstexture);
+ m.texcubemap[1] = R_GetTexture(lightcubemap);
+ R_Mesh_TextureState(&m);
+ qglColorMask(1,1,1,1);
+ qglBlendFunc(GL_DST_ALPHA, GL_ONE);
+ memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
+ if (lightcubemap)
+ R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
+
+ // the 0.25f makes specular lighting much dimmer than diffuse (intentionally)
+ colorscale = r_colorscale * 0.25f * r_shadow_lightintensityscale.value;
+ for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
+ colorscale *= scale;
+ GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
+ for (renders = 0;renders < mult;renders++)
+ R_Mesh_Draw(numverts, numtriangles, elements);
+ }
+}
+
+#define PRECOMPUTEDSHADOWVOLUMES 1
+void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
+{
+#if PRECOMPUTEDSHADOWVOLUMES
+ R_Mesh_Matrix(matrix);
+ R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
+#else
+ shadowmesh_t *mesh;
+ R_Mesh_Matrix(matrix);
+ for (mesh = light->shadowvolume;mesh;mesh = mesh->next)
+ {
+ R_Mesh_ResizeCheck(mesh->numverts * 2);
+ memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
+ R_Shadow_Volume(mesh->numverts, mesh->numtriangles, varray_vertex, mesh->elements, mesh->neighbors, light->origin, light->lightradius, light->lightradius);
+ }
+#endif
+}
+
+cvar_t r_editlights = {0, "r_editlights", "0"};
+cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
+cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
+cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
+cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
+worldlight_t *r_shadow_worldlightchain;
+worldlight_t *r_shadow_selectedlight;
+vec3_t r_editlights_cursorlocation;
+
+static int castshadowcount = 1;
+void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname)
+{
+ int i, j, k, l, maxverts, *mark;
+ float *verts, *v, *v0, *v1, f, projectdistance, temp[3], temp2[3], temp3[3], radius2;
+ worldlight_t *e;
+ shadowmesh_t *mesh;
+ mleaf_t *leaf;
+ msurface_t *surf;
+ qbyte *pvs;
+
+ e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
+ VectorCopy(origin, e->origin);
+ VectorCopy(color, e->light);
+ e->lightradius = radius;
+ VectorCopy(origin, e->mins);
+ VectorCopy(origin, e->maxs);
+ e->cullradius = 0;
+ e->style = style;
+ e->next = r_shadow_worldlightchain;
+ r_shadow_worldlightchain = e;
+ if (cubemapname)
+ {
+ e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
+ strcpy(e->cubemapname, cubemapname);
+ // FIXME: add cubemap loading (and don't load a cubemap twice)
+ }
+ if (cl.worldmodel)
+ {
+ castshadowcount++;
+ leaf = Mod_PointInLeaf(origin, cl.worldmodel);
+ pvs = Mod_LeafPVS(leaf, cl.worldmodel);
+ for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+ {
+ if (pvs[i >> 3] & (1 << (i & 7)))
+ {
+ VectorCopy(origin, temp);
+ if (temp[0] < leaf->mins[0]) temp[0] = leaf->mins[0];
+ if (temp[0] > leaf->maxs[0]) temp[0] = leaf->maxs[0];
+ if (temp[1] < leaf->mins[1]) temp[1] = leaf->mins[1];
+ if (temp[1] > leaf->maxs[1]) temp[1] = leaf->maxs[1];
+ if (temp[2] < leaf->mins[2]) temp[2] = leaf->mins[2];
+ if (temp[2] > leaf->maxs[2]) temp[2] = leaf->maxs[2];
+ VectorSubtract(temp, origin, temp);
+ if (DotProduct(temp, temp) < e->lightradius * e->lightradius)
+ {
+ leaf->worldnodeframe = castshadowcount;
+ for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
+ {
+ surf = cl.worldmodel->surfaces + *mark;
+ if (surf->castshadow != castshadowcount)
+ {
+ f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
+ if (surf->flags & SURF_PLANEBACK)
+ f = -f;
+ if (f > 0 && f < e->lightradius)
+ {
+ VectorSubtract(e->origin, surf->poly_center, temp);
+ if (DotProduct(temp, temp) - surf->poly_radius2 < e->lightradius * e->lightradius)
+ surf->castshadow = castshadowcount;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ e->numleafs = 0;
+ for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+ if (leaf->worldnodeframe == castshadowcount)
+ e->numleafs++;
+ e->numsurfaces = 0;
+ for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
+ if (surf->castshadow == castshadowcount)
+ e->numsurfaces++;
+
+ if (e->numleafs)
+ e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
+ if (e->numsurfaces)
+ e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
+ e->numleafs = 0;
+ for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
+ if (leaf->worldnodeframe == castshadowcount)
+ e->leafs[e->numleafs++] = leaf;
+ e->numsurfaces = 0;
+ for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
+ if (surf->castshadow == castshadowcount)
+ e->surfaces[e->numsurfaces++] = surf;
+ // find bounding box and sphere of lit surfaces
+ // (these will be used for creating a shape to clip the light)
+ radius2 = 0;
+ VectorCopy(e->origin, e->mins);
+ VectorCopy(e->origin, e->maxs);
+ for (j = 0;j < e->numsurfaces;j++)
+ {
+ surf = e->surfaces[j];
+ for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
+ {
+ if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
+ if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
+ if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
+ VectorSubtract(v, e->origin, temp);
+ f = DotProduct(temp, temp);
+ if (radius2 < f)
+ radius2 = f;
+ }
+ }
+ e->cullradius = sqrt(radius2);
+ if (e->cullradius > e->lightradius)
+ e->cullradius = e->lightradius;
+ if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
+ if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
+ if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
+ if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
+ if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
+ if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
+ Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
+ // clip shadow volumes against eachother to remove unnecessary
+ // polygons (and sections of polygons)
+ maxverts = 256;
+ verts = NULL;
+ castshadowcount++;
+ for (j = 0;j < e->numsurfaces;j++)
+ {
+ surf = e->surfaces[j];
+ if (surf->flags & SURF_SHADOWCAST)
+ {
+ surf->castshadow = castshadowcount;
+ if (maxverts < surf->poly_numverts)
+ maxverts = surf->poly_numverts;
+ }
+ }
+ e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 32768);
+#if !PRECOMPUTEDSHADOWVOLUMES
+ // make a mesh to cast a shadow volume from
+ for (j = 0;j < e->numsurfaces;j++)
+ if (e->surfaces[j]->castshadow == castshadowcount)
+ Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, e->surfaces[j]->poly_numverts, e->surfaces[j]->poly_verts);
+#else
+#if 1
+ {
+ int tris;
+ shadowmesh_t *castmesh, *mesh;
+ surfmesh_t *surfmesh;
+ // make a mesh to cast a shadow volume from
+ castmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, 32768);
+ for (j = 0;j < e->numsurfaces;j++)
+ if (e->surfaces[j]->castshadow == castshadowcount)
+ for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
+ Mod_ShadowMesh_AddMesh(loadmodel->mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
+ castmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, castmesh);
+
+ // cast shadow volume from castmesh
+ for (mesh = castmesh;mesh;mesh = mesh->next)
+ {
+ R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
+ R_Shadow_ResizeShadowElements(castmesh->numtriangles);
+
+ if (maxverts < castmesh->numverts * 2)
+ {
+ maxverts = castmesh->numverts * 2;
+ if (verts)
+ Mem_Free(verts);
+ verts = NULL;
+ }
+ if (verts == NULL && maxverts > 0)
+ verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[4]));
+
+ // now that we have the buffers big enough, construct shadow volume mesh
+ memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
+ R_Shadow_ProjectVertices(verts, verts + castmesh->numverts * 4, castmesh->numverts, e->origin, e->lightradius);
+ R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, e->origin, e->lightradius);
+ tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numtriangles, castmesh->numverts, trianglefacinglight, shadowelements);
+ // add the constructed shadow volume mesh
+ Mod_ShadowMesh_AddMesh(loadmodel->mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
+ }
+ // we're done with castmesh now
+ Mod_ShadowMesh_Free(castmesh);
+ }
+#else
+ // make a shadow volume mesh
+ if (verts == NULL && maxverts > 0)
+ verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[4]));
+ for (j = 0;j < e->numsurfaces;j++)
+ {
+ surf = e->surfaces[j];
+ if (surf->castshadow != castshadowcount)
+ continue;
+ projectdistance = 1000000.0f;//e->lightradius;
+ // copy the original polygon, for the front cap of the volume
+ for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
+ VectorCopy(v0, v1);
+ Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
+ // project the original polygon, reversed, for the back cap of the volume
+ for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)