rendering lighting everywhere but shadow.
In our case we use a biased stencil clear of 128 to avoid requiring the
-stencil wrap extension (but probably should support it).
+stencil wrap extension (but probably should support it), and to address
+Creative's patent on this sort of technology we also draw the frontfaces
+first, and backfaces second (decrement, increment).
+
+Patent warning:
+This algorithm may be covered by Creative's patent (US Patent #6384822)
+on Carmack's Reverse paper (which I have not read), however that patent
+seems to be about drawing a stencil shadow from a model in an otherwise
+unshadowed scene, where as realtime lighting technology draws light where
+shadows do not lie.
cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
cvar_t r_shadow_shadows = {CVAR_SAVE, "r_shadow_shadows", "1"};
+cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
int c_rt_lights, c_rt_clears, c_rt_scissored;
int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
Cvar_RegisterVariable(&r_shadow_texture3d);
Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
Cvar_RegisterVariable(&r_shadow_shadows);
+ Cvar_RegisterVariable(&r_shadow_showtris);
Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
R_Shadow_EditLights_Init();
R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
GL_VertexPointer(varray_vertex3f2);
if (r_shadowstage == SHADOWSTAGE_STENCIL)
{
- // increment stencil if backface is behind depthbuffer
- qglCullFace(GL_BACK); // quake is backwards, this culls front faces
- qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
- R_Mesh_Draw(outverts, tris, shadowelements);
- c_rt_shadowmeshes++;
- c_rt_shadowtris += numtris;
// decrement stencil if frontface is behind depthbuffer
qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+ R_Mesh_Draw(outverts, tris, shadowelements);
+ c_rt_shadowmeshes++;
+ c_rt_shadowtris += numtris;
+ // increment stencil if backface is behind depthbuffer
+ qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+ qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
}
R_Mesh_Draw(outverts, tris, shadowelements);
c_rt_shadowmeshes++;
shadowmesh_t *mesh;
if (r_shadowstage == SHADOWSTAGE_STENCIL)
{
- // increment stencil if backface is behind depthbuffer
- qglCullFace(GL_BACK); // quake is backwards, this culls front faces
- qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+ // decrement stencil if frontface is behind depthbuffer
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+ qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
for (mesh = firstmesh;mesh;mesh = mesh->next)
{
GL_VertexPointer(mesh->vertex3f);
c_rtcached_shadowmeshes++;
c_rtcached_shadowtris += mesh->numtriangles;
}
- // decrement stencil if frontface is behind depthbuffer
- qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
- qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+ // increment stencil if backface is behind depthbuffer
+ qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+ qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
}
for (mesh = firstmesh;mesh;mesh = mesh->next)
{
GL_DepthTest(true);
R_Mesh_State_Texture(&m);
GL_Color(0, 0, 0, 1);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglDisable(GL_SCISSOR_TEST);
r_shadowstage = SHADOWSTAGE_NONE;
else
qglDisable(GL_POLYGON_OFFSET_FILL);
qglDepthFunc(GL_LESS);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglEnable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
qglStencilFunc(GL_ALWAYS, 128, 0xFF);
GL_Color(1, 1, 1, 1);
qglColorMask(1, 1, 1, 1);
qglDepthFunc(GL_EQUAL);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglDisable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
qglStencilFunc(GL_EQUAL, 128, 0xFF);
GL_Color(1, 1, 1, 1);
qglColorMask(1, 1, 1, 1);
qglDepthFunc(GL_EQUAL);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglEnable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// only draw light where this geometry was already rendered AND the
qglColorMask(1, 1, 1, 1);
qglDisable(GL_SCISSOR_TEST);
qglDepthFunc(GL_LEQUAL);
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
qglDisable(GL_STENCIL_TEST);
qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
qglStencilFunc(GL_ALWAYS, 128, 0xFF);
// if view is inside the box, just say yes it's visible
// LordHavoc: for some odd reason scissor seems broken without stencil
// (?!? seems like a driver bug) so abort if gl_stencil is false
- if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
+ if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
{
qglDisable(GL_SCISSOR_TEST);
return false;
}
for (i = 0;i < 3;i++)
{
- if (vpn[i] >= 0)
+ if (r_viewforward[i] >= 0)
{
v[i] = mins[i];
v2[i] = maxs[i];
v2[i] = mins[i];
}
}
- f = DotProduct(vpn, r_origin) + 1;
- if (DotProduct(vpn, v2) <= f)
+ f = DotProduct(r_viewforward, r_vieworigin) + 1;
+ if (DotProduct(r_viewforward, v2) <= f)
{
// entirely behind nearclip plane
return true;
}
- if (DotProduct(vpn, v) >= f)
+ if (DotProduct(r_viewforward, v) >= f)
{
// entirely infront of nearclip plane
x1 = y1 = x2 = y2 = 0;
// create viewspace bbox
for (i = 0;i < 8;i++)
{
- v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
- v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
- v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
- v2[0] = DotProduct(v, vright);
- v2[1] = DotProduct(v, vup);
- v2[2] = DotProduct(v, vpn);
+ v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
+ v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
+ v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
+ v2[0] = -DotProduct(v, r_viewleft);
+ v2[1] = DotProduct(v, r_viewup);
+ v2[2] = DotProduct(v, r_viewforward);
if (i)
{
if (smins[0] > v2[0]) smins[0] = v2[0];
v2[0] = (i & 1) ? smins[0] : smaxs[0];
v2[1] = (i & 2) ? smins[1] : smaxs[1];
v2[2] = (i & 4) ? smins[2] : smaxs[2];
- v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
- v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
- v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
+ v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
+ v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
+ v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
v[3] = 1.0f;
GL_TransformToScreen(v, v2);
//Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
}
}
-void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
+void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
{
R_Mesh_Matrix(matrix);
- R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
+ if (r_shadow_showtris.integer)
+ {
+ shadowmesh_t *mesh;
+ rmeshstate_t m;
+ int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
+ int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
+ qglDisable(GL_DEPTH_TEST);
+ qglDisable(GL_STENCIL_TEST);
+ //qglDisable(GL_CULL_FACE);
+ qglColorMask(1,1,1,1);
+ memset(&m, 0, sizeof(m));
+ R_Mesh_State_Texture(&m);
+ GL_Color(0,0.1,0,1);
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+ for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
+ {
+ GL_VertexPointer(mesh->vertex3f);
+ R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
+ }
+ //qglEnable(GL_CULL_FACE);
+ if (depthenabled)
+ qglEnable(GL_DEPTH_TEST);
+ if (stencilenabled)
+ {
+ qglEnable(GL_STENCIL_TEST);
+ qglColorMask(0,0,0,0);
+ }
+ }
+ R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
+}
+
+void R_Shadow_DrawStaticWorldLight_Light(worldlight_t *light, matrix4x4_t *matrix, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz)
+{
+ shadowmesh_t *mesh;
+ R_Mesh_Matrix(matrix);
+ if (r_shadow_showtris.integer)
+ {
+ rmeshstate_t m;
+ int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
+ int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
+ qglDisable(GL_DEPTH_TEST);
+ qglDisable(GL_STENCIL_TEST);
+ //qglDisable(GL_CULL_FACE);
+ memset(&m, 0, sizeof(m));
+ R_Mesh_State_Texture(&m);
+ GL_Color(0.2,0,0,1);
+ GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
+ for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
+ {
+ GL_VertexPointer(mesh->vertex3f);
+ R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
+ }
+ //qglEnable(GL_CULL_FACE);
+ if (depthenabled)
+ qglEnable(GL_DEPTH_TEST);
+ if (stencilenabled)
+ qglEnable(GL_STENCIL_TEST);
+ }
+ for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
+ {
+ R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, NULL);
+ R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, NULL);
+ }
}
cvar_t r_editlights = {0, "r_editlights", "0"};
static int lightpvsbytes;
static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
-static int castshadowcount = 1;
void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
{
- int i, j, k, l, maxverts = 256, *mark, tris, numsurfaces;
+ int i, j, k, l, maxverts = 256, tris;
float *vertex3f = NULL, mins[3], maxs[3];
worldlight_t *e;
- shadowmesh_t *mesh, *castmesh;
- mleaf_t *leaf;
- msurface_t *surf;
+ shadowmesh_t *mesh, *castmesh = NULL;
if (radius < 15 || DotProduct(color, color) < 0.03)
{
strcpy(e->cubemapname, cubemapname);
// FIXME: add cubemap loading (and don't load a cubemap twice)
}
+ // FIXME: rewrite this to store ALL geometry into a cache in the light
+ if (e->castshadows)
+ castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
+ e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
if (cl.worldmodel)
{
- castshadowcount++;
- VectorCopy(e->origin, e->mins);
- VectorCopy(e->origin, e->maxs);
- i = CL_PointQ1Contents(e->origin);
- if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
+ if (cl.worldmodel->brushq3.num_leafs)
{
- //qbyte *byteleafpvs;
- qbyte *bytesurfacepvs;
-
- //byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
- bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
-
- Portal_Visibility(cl.worldmodel, e->origin, NULL/*byteleafpvs*/, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
-
- /*
- for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
+ q3mleaf_t *leaf;
+ q3mface_t *face;
+ lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
+ VectorCopy(e->origin, e->mins);
+ VectorCopy(e->origin, e->maxs);
+ for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
+ face->lighttemp_castshadow = false;
+ for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
{
- if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
+ if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
{
for (k = 0;k < 3;k++)
{
if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
}
+ for (j = 0;j < leaf->numleaffaces;j++)
+ {
+ face = leaf->firstleafface[j];
+ if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
+ face->lighttemp_castshadow = true;
+ }
}
}
- */
-
- for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
- if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
- surf->castshadow = castshadowcount;
- //Mem_Free(byteleafpvs);
- Mem_Free(bytesurfacepvs);
- }
- else
- {
- lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
- for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
+ // add surfaces to shadow casting mesh and light mesh
+ for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
{
- if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
+ if (face->lighttemp_castshadow)
{
- for (k = 0;k < 3;k++)
- {
- if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
- if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
- }
- for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
+ face->lighttemp_castshadow = false;
+ if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
{
- surf = cl.worldmodel->brushq1.surfaces + *mark;
- if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
- surf->castshadow = castshadowcount;
+ if (e->castshadows)
+ if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
+ Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
+ if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
+ Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
}
}
}
}
-
- for (k = 0;k < 3;k++)
- {
- if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
- if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
- }
- e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
-
- numsurfaces = 0;
- for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
- if (surf->castshadow == castshadowcount)
- numsurfaces++;
- if (numsurfaces)
- e->surfaces = Mem_Alloc(r_shadow_mempool, numsurfaces * sizeof(msurface_t *));
- e->numsurfaces = 0;
- for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
- if (surf->castshadow == castshadowcount)
- e->surfaces[e->numsurfaces++] = surf;
-
- if (e->castshadows)
+ else if (cl.worldmodel->brushq1.numleafs)
{
- castshadowcount++;
- for (j = 0;j < e->numsurfaces;j++)
+ mleaf_t *leaf;
+ msurface_t *surf;
+ VectorCopy(e->origin, e->mins);
+ VectorCopy(e->origin, e->maxs);
+ i = CL_PointQ1Contents(e->origin);
+
+ for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
+ surf->lighttemp_castshadow = false;
+
+ if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
{
- surf = e->surfaces[j];
- if (surf->flags & SURF_SHADOWCAST)
+ qbyte *byteleafpvs;
+ qbyte *bytesurfacepvs;
+
+ byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
+ bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
+
+ Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
+
+ for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
{
- surf->castshadow = castshadowcount;
- if (maxverts < surf->poly_numverts)
- maxverts = surf->poly_numverts;
+ if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
+ {
+ for (k = 0;k < 3;k++)
+ {
+ if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
+ if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
+ }
+ }
}
+
+ for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
+ if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
+ surf->lighttemp_castshadow = true;
+
+ Mem_Free(byteleafpvs);
+ Mem_Free(bytesurfacepvs);
}
- e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
- // make a mesh to cast a shadow volume from
- castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
- for (j = 0;j < e->numsurfaces;j++)
- if ((surf = e->surfaces[j])->castshadow == castshadowcount)
- Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surf->mesh.data_vertex3f, surf->mesh.num_triangles, surf->mesh.data_element3i);
- castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
-
- // cast shadow volume from castmesh
- for (mesh = castmesh;mesh;mesh = mesh->next)
+ else
{
- R_Shadow_ResizeShadowElements(castmesh->numtriangles);
+ lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
+ for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
+ {
+ if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
+ {
+ for (k = 0;k < 3;k++)
+ {
+ if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
+ if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
+ }
+ for (j = 0;j < leaf->nummarksurfaces;j++)
+ {
+ surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
+ if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
+ surf->lighttemp_castshadow = true;
+ }
+ }
+ }
+ }
- if (maxverts < castmesh->numverts * 2)
+ // add surfaces to shadow casting mesh and light mesh
+ for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
+ {
+ if (surf->lighttemp_castshadow)
{
- maxverts = castmesh->numverts * 2;
- if (vertex3f)
- Mem_Free(vertex3f);
- vertex3f = NULL;
+ surf->lighttemp_castshadow = false;
+ if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
+ Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
+ if (!(surf->flags & SURF_DRAWSKY))
+ Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
}
- if (vertex3f == NULL && maxverts > 0)
- vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
+ }
+ }
+ }
- // now that we have the buffers big enough, construct and add
- // the shadow volume mesh
+ // limit box to light bounds (in case it grew larger)
+ for (k = 0;k < 3;k++)
+ {
+ if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
+ if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
+ }
+ e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
+
+ // cast shadow volume from castmesh
+ castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
+ if (castmesh)
+ {
+ maxverts = 0;
+ for (mesh = castmesh;mesh;mesh = mesh->next)
+ {
+ R_Shadow_ResizeShadowElements(mesh->numtriangles);
+ maxverts = max(maxverts, mesh->numverts * 2);
+ }
+
+ if (maxverts > 0)
+ {
+ vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
+ // now that we have the buffers big enough, construct and add
+ // the shadow volume mesh
+ if (e->castshadows)
+ e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
+ for (mesh = castmesh;mesh;mesh = mesh->next)
+ {
+ Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
if ((tris = R_Shadow_ConstructShadowVolume(castmesh->numverts, 0, castmesh->numtriangles, castmesh->element3i, castmesh->neighbor3i, castmesh->vertex3f, NULL, shadowelements, vertex3f, e->origin, r_shadow_projectdistance.value)))
- Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
+ Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
}
- if (vertex3f)
- Mem_Free(vertex3f);
+ Mem_Free(vertex3f);
vertex3f = NULL;
- // we're done with castmesh now
- Mod_ShadowMesh_Free(castmesh);
- e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
- for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
- l += mesh->numtriangles;
- Con_Printf("static shadow volume built containing %i triangles\n", l);
}
- }
- Con_Printf("%f %f %f, %f %f %f, %f, %f, %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->numsurfaces);
+ // we're done with castmesh now
+ Mod_ShadowMesh_Free(castmesh);
+ }
+
+ e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
+ e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
+
+ k = 0;
+ if (e->meshchain_shadow)
+ for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
+ k += mesh->numtriangles;
+ l = 0;
+ if (e->meshchain_light)
+ for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
+ l += mesh->numtriangles;
+ Con_Printf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], k, l);
}
void R_Shadow_FreeWorldLight(worldlight_t *light)
*lightpointer = light->next;
if (light->cubemapname)
Mem_Free(light->cubemapname);
- if (light->shadowvolume)
- Mod_ShadowMesh_Free(light->shadowvolume);
- if (light->surfaces)
- Mem_Free(light->surfaces);
+ if (light->meshchain_shadow)
+ Mod_ShadowMesh_Free(light->meshchain_shadow);
+ if (light->meshchain_light)
+ Mod_ShadowMesh_Free(light->meshchain_light);
Mem_Free(light);
}
void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
{
float scale = r_editlights_cursorgrid.value * 0.5f;
- R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
+ R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
}
void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
intensity = 0.5;
if (light->selected)
intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
- if (!light->shadowvolume)
+ if (!light->meshchain_shadow)
intensity *= 0.5f;
- R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
+ R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
}
void R_Shadow_DrawLightSprites(void)
bestrating = 0;
for (light = r_shadow_worldlightchain;light;light = light->next)
{
- VectorSubtract(light->origin, r_refdef.vieworg, temp);
- rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
+ VectorSubtract(light->origin, r_vieworigin, temp);
+ rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
if (rating >= 0.95)
{
rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
- if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
+ if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
{
bestrating = rating;
best = light;
{
int entnum, style, islight;
char key[256], value[1024];
- float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
+ float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
const char *data;
if (cl.worldmodel == NULL)
originhack[0] = originhack[1] = originhack[2] = 0;
color[0] = color[1] = color[2] = 1;
overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
- scale = 1;
+ fadescale = 1;
+ lightscale = 1;
style = 0;
islight = false;
while (1)
else if (!strcmp("color", key))
sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
else if (!strcmp("wait", key))
- scale = atof(value);
+ fadescale = atof(value);
else if (!strcmp("classname", key))
{
if (!strncmp(value, "light", 5))
}
else if (!strcmp("style", key))
style = atoi(value);
+ else if (cl.worldmodel->type == mod_brushq3)
+ {
+ if (!strcmp("scale", key))
+ lightscale = atof(value);
+ if (!strcmp("fade", key))
+ fadescale = atof(value);
+ }
}
if (light <= 0 && islight)
light = 300;
- radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
+ if (lightscale <= 0)
+ lightscale = 1;
+ if (fadescale <= 0)
+ fadescale = 1;
+ radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
if (color[0] == 1 && color[1] == 1 && color[2] == 1)
VectorCopy(overridecolor, color);
{
vec_t dist, push, frac;
vec3_t dest, endpos, normal;
- VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
- frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
+ VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
+ frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
if (frac < 1)
{
dist = frac * r_editlights_cursordistance.value;
if (push > dist)
push = dist;
push = -push;
- VectorMA(endpos, push, vpn, endpos);
+ VectorMA(endpos, push, r_viewforward, endpos);
VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
}
r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;