+// compiles rtlight geometry
+// (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
+void R_RTLight_Compile(rtlight_t *rtlight)
+{
+ int shadowmeshes, shadowtris, lightmeshes, lighttris, numleafs, numleafpvsbytes, numsurfaces;
+ entity_render_t *ent = r_refdef.worldentity;
+ model_t *model = r_refdef.worldmodel;
+ qbyte *data;
+
+ // compile the light
+ rtlight->compiled = true;
+ rtlight->static_numleafs = 0;
+ rtlight->static_numleafpvsbytes = 0;
+ rtlight->static_leaflist = NULL;
+ rtlight->static_leafpvs = NULL;
+ rtlight->static_numsurfaces = 0;
+ rtlight->static_surfacelist = NULL;
+ rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
+ rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
+ rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
+ rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
+ rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
+ rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
+
+ if (model && model->GetLightInfo)
+ {
+ // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
+ r_shadow_compilingrtlight = rtlight;
+ R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
+ model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+ numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
+ data = Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
+ rtlight->static_numleafs = numleafs;
+ rtlight->static_numleafpvsbytes = numleafpvsbytes;
+ rtlight->static_leaflist = (void *)data;data += sizeof(int) * numleafs;
+ rtlight->static_leafpvs = (void *)data;data += numleafpvsbytes;
+ rtlight->static_numsurfaces = numsurfaces;
+ rtlight->static_surfacelist = (void *)data;data += sizeof(int) * numsurfaces;
+ if (numleafs)
+ memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
+ if (numleafpvsbytes)
+ memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
+ if (numsurfaces)
+ memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
+ if (model->DrawShadowVolume && rtlight->shadow)
+ {
+ rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
+ model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist, rtlight->cullmins, rtlight->cullmaxs);
+ rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
+ }
+ if (model->DrawLight)
+ {
+ rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
+ model->DrawLight(ent, vec3_origin, numsurfaces, r_shadow_buffer_surfacelist);
+ rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
+ }
+ // switch back to rendering when DrawShadowVolume or DrawLight is called
+ r_shadow_compilingrtlight = NULL;
+ }
+
+
+ // use smallest available cullradius - box radius or light radius
+ //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
+ //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
+
+ shadowmeshes = 0;
+ shadowtris = 0;
+ if (rtlight->static_meshchain_shadow)
+ {
+ shadowmesh_t *mesh;
+ for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
+ {
+ shadowmeshes++;
+ shadowtris += mesh->numtriangles;
+ }
+ }
+
+ lightmeshes = 0;
+ lighttris = 0;
+ if (rtlight->static_meshchain_light)
+ {
+ shadowmesh_t *mesh;
+ for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
+ {
+ lightmeshes++;
+ lighttris += mesh->numtriangles;
+ }
+ }
+
+ Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes, lighttris, lightmeshes);
+}
+
+void R_RTLight_Uncompile(rtlight_t *rtlight)
+{
+ if (rtlight->compiled)
+ {
+ if (rtlight->static_meshchain_shadow)
+ Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
+ rtlight->static_meshchain_shadow = NULL;
+ if (rtlight->static_meshchain_light)
+ Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
+ rtlight->static_meshchain_light = NULL;
+ // these allocations are grouped
+ if (rtlight->static_leaflist)
+ Mem_Free(rtlight->static_leaflist);
+ rtlight->static_numleafs = 0;
+ rtlight->static_numleafpvsbytes = 0;
+ rtlight->static_leaflist = NULL;
+ rtlight->static_leafpvs = NULL;
+ rtlight->static_numsurfaces = 0;
+ rtlight->static_surfacelist = NULL;
+ rtlight->compiled = false;
+ }
+}
+
+void R_Shadow_UncompileWorldLights(void)
+{
+ dlight_t *light;
+ for (light = r_shadow_worldlightchain;light;light = light->next)
+ R_RTLight_Uncompile(&light->rtlight);
+}
+
+void R_Shadow_DrawEntityShadow(entity_render_t *ent, rtlight_t *rtlight, int numsurfaces, int *surfacelist)
+{
+ vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
+ vec_t relativeshadowradius;
+ if (ent == r_refdef.worldentity)
+ {
+ if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
+ {
+ shadowmesh_t *mesh;
+ R_Mesh_Matrix(&ent->matrix);
+ for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
+ {
+ R_Mesh_VertexPointer(mesh->vertex3f);
+ GL_LockArrays(0, mesh->numverts);
+ if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
+ {
+ // decrement stencil if backface is behind depthbuffer
+ qglCullFace(GL_BACK); // quake is backwards, this culls front faces
+ qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
+ R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
+ c_rtcached_shadowmeshes++;
+ c_rtcached_shadowtris += mesh->numtriangles;
+ // increment stencil if frontface is behind depthbuffer
+ qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
+ qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
+ }
+ R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
+ c_rtcached_shadowmeshes++;
+ c_rtcached_shadowtris += mesh->numtriangles;
+ GL_LockArrays(0, 0);
+ }
+ }
+ else if (numsurfaces)
+ {
+ R_Mesh_Matrix(&ent->matrix);
+ ent->model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, surfacelist, rtlight->cullmins, rtlight->cullmaxs);
+ }
+ }
+ else
+ {
+ Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativeshadoworigin);
+ relativeshadowradius = rtlight->radius / ent->scale;
+ relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
+ relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
+ relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
+ relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
+ relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
+ relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
+ R_Mesh_Matrix(&ent->matrix);
+ ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
+ }
+}
+
+void R_Shadow_DrawEntityLight(entity_render_t *ent, rtlight_t *rtlight, vec3_t lightcolor, int numsurfaces, int *surfacelist)
+{
+ shadowmesh_t *mesh;
+ // set up properties for rendering light onto this entity
+ r_shadow_entitylightcolor[0] = lightcolor[0] * ent->colormod[0] * ent->alpha;
+ r_shadow_entitylightcolor[1] = lightcolor[1] * ent->colormod[1] * ent->alpha;
+ r_shadow_entitylightcolor[2] = lightcolor[2] * ent->colormod[2] * ent->alpha;
+ Matrix4x4_Concat(&r_shadow_entitytolight, &rtlight->matrix_worldtolight, &ent->matrix);
+ Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
+ Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
+ Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, r_shadow_entitylightorigin);
+ Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
+ R_Mesh_Matrix(&ent->matrix);
+ if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
+ {
+ R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_lightcubemap));
+ R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
+ qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);CHECKGLERROR
+ if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
+ {
+ qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);CHECKGLERROR
+ }
+ }
+ if (ent == r_refdef.worldentity)
+ {
+ if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compilelight.integer)
+ {
+ for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
+ R_Shadow_RenderLighting(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, r_shadow_entitylightcolor, vec3_origin, vec3_origin, mesh->map_diffuse, NULL, NULL, mesh->map_normal, mesh->map_specular);
+ }
+ else
+ ent->model->DrawLight(ent, r_shadow_entitylightcolor, numsurfaces, surfacelist);
+ }
+ else
+ ent->model->DrawLight(ent, r_shadow_entitylightcolor, ent->model->nummodelsurfaces, ent->model->surfacelist);
+}
+
+void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
+{
+ int i, usestencil;
+ float f;
+ vec3_t lightcolor;
+ int numleafs, numsurfaces;
+ int *leaflist, *surfacelist;
+ qbyte *leafpvs;
+ int numlightentities;
+ int numshadowentities;
+ entity_render_t *lightentities[MAX_EDICTS];
+ entity_render_t *shadowentities[MAX_EDICTS];
+
+ // skip lights that don't light (corona only lights)
+ if (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale < 0.01)
+ return;
+
+ f = (rtlight->style >= 0 ? d_lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
+ VectorScale(rtlight->color, f, lightcolor);
+ if (VectorLength2(lightcolor) < 0.01)
+ return;
+ /*
+ if (rtlight->selected)
+ {
+ f = 2 + sin(realtime * M_PI * 4.0);
+ VectorScale(lightcolor, f, lightcolor);
+ }
+ */
+
+ // loading is done before visibility checks because loading should happen
+ // all at once at the start of a level, not when it stalls gameplay.
+ // (especially important to benchmarks)
+ // compile light
+ if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
+ R_RTLight_Compile(rtlight);
+ // load cubemap
+ r_shadow_lightcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
+
+ // if the light box is offscreen, skip it
+ if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
+ return;
+
+ if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
+ {
+ // compiled light, world available and can receive realtime lighting
+ // retrieve leaf information
+ numleafs = rtlight->static_numleafs;
+ leaflist = rtlight->static_leaflist;
+ leafpvs = rtlight->static_leafpvs;
+ numsurfaces = rtlight->static_numsurfaces;
+ surfacelist = rtlight->static_surfacelist;
+ }
+ else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
+ {
+ // dynamic light, world available and can receive realtime lighting
+ // calculate lit surfaces and leafs
+ R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
+ r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
+ leaflist = r_shadow_buffer_leaflist;
+ leafpvs = r_shadow_buffer_leafpvs;
+ surfacelist = r_shadow_buffer_surfacelist;
+ // if the reduced leaf bounds are offscreen, skip it
+ if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
+ return;
+ }
+ else
+ {
+ // no world
+ numleafs = 0;
+ leaflist = NULL;
+ leafpvs = NULL;
+ numsurfaces = 0;
+ surfacelist = NULL;
+ }
+ // check if light is illuminating any visible leafs
+ if (numleafs)
+ {
+ for (i = 0;i < numleafs;i++)
+ if (r_worldleafvisible[leaflist[i]])
+ break;
+ if (i == numleafs)
+ return;
+ }
+ // set up a scissor rectangle for this light
+ if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
+ return;
+
+ numlightentities = 0;
+ if (numsurfaces)
+ lightentities[numlightentities++] = r_refdef.worldentity;
+ numshadowentities = 0;
+ if (numsurfaces)
+ shadowentities[numshadowentities++] = r_refdef.worldentity;
+ if (r_drawentities.integer)
+ {
+ for (i = 0;i < r_refdef.numentities;i++)
+ {
+ entity_render_t *ent = r_refdef.entities[i];
+ if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
+ && ent->model
+ && !(ent->flags & RENDER_TRANSPARENT)
+ && (r_refdef.worldmodel == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs)))
+ {
+ // about the VectorDistance2 - light emitting entities should not cast their own shadow
+ if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
+ shadowentities[numshadowentities++] = ent;
+ if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
+ lightentities[numlightentities++] = ent;
+ }
+ }
+ }