X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=r_shadow.c;h=ad071ac82a6d45406a03c45284a74173d2014fdb;hb=832ba0937327c893bfd49f1a738812dae1a995a5;hp=fabed45fb958d763583b821d7bda1b0c5f04a95c;hpb=be9e779df5e4d6c7c1c93254d85d104de4d3b2c8;p=xonotic%2Fdarkplaces.git diff --git a/r_shadow.c b/r_shadow.c index fabed45f..ad071ac8 100644 --- a/r_shadow.c +++ b/r_shadow.c @@ -125,7 +125,6 @@ extern void R_Shadow_EditLights_Init(void); #define SHADOWSTAGE_STENCILTWOSIDE 3 int r_shadowstage = SHADOWSTAGE_NONE; -int r_shadow_reloadlights = false; mempool_t *r_shadow_mempool; @@ -159,49 +158,99 @@ rtexture_t *r_shadow_blankbumptexture; rtexture_t *r_shadow_blankglosstexture; rtexture_t *r_shadow_blankwhitetexture; +// lights are reloaded when this changes +char r_shadow_mapname[MAX_QPATH]; + // used only for light filters (cubemaps) rtexturepool_t *r_shadow_filters_texturepool; -cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"}; +cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"}; +cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"}; +cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"}; +cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"}; +cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1"}; +cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"}; +cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"}; cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"}; cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"}; cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"}; -cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"}; -cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "1"}; -cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"}; -cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"}; -cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"}; -cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"}; -cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"}; -cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"}; -cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"}; -cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"}; -cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"}; -cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"}; cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"}; cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"}; -cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"}; +cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1"}; +cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "0"}; +cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0"}; +cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1"}; +cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0"}; +cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1"}; +cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"}; +cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"}; +cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"}; cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"}; -cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"}; -cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"}; cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"}; -cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"}; +cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"}; +cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"}; cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"}; +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"}; +cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"}; +cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"}; +cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"}; int c_rt_lights, c_rt_clears, c_rt_scissored; int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris; int c_rtcached_shadowmeshes, c_rtcached_shadowtris; +float r_shadow_attenpower, r_shadow_attenscale; + +rtlight_t *r_shadow_compilingrtlight; +dlight_t *r_shadow_worldlightchain; +dlight_t *r_shadow_selectedlight; +dlight_t r_shadow_bufferlight; +vec3_t r_editlights_cursorlocation; + +rtexture_t *lighttextures[5]; + +extern int con_vislines; + +typedef struct cubemapinfo_s +{ + char basename[64]; + rtexture_t *texture; +} +cubemapinfo_t; + +#define MAX_CUBEMAPS 256 +static int numcubemaps; +static cubemapinfo_t cubemaps[MAX_CUBEMAPS]; + +void R_Shadow_UncompileWorldLights(void); void R_Shadow_ClearWorldLights(void); void R_Shadow_SaveWorldLights(void); void R_Shadow_LoadWorldLights(void); void R_Shadow_LoadLightsFile(void); void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void); +void R_Shadow_EditLights_Reload_f(void); +void R_Shadow_ValidateCvars(void); +static void R_Shadow_MakeTextures(void); +void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light); void r_shadow_start(void) { // allocate vertex processing arrays - r_shadow_mempool = Mem_AllocPool("R_Shadow"); + numcubemaps = 0; + r_shadow_normalcubetexture = NULL; + r_shadow_attenuation2dtexture = NULL; + r_shadow_attenuation3dtexture = NULL; + r_shadow_blankbumptexture = NULL; + r_shadow_blankglosstexture = NULL; + r_shadow_blankwhitetexture = NULL; + r_shadow_texturepool = NULL; + r_shadow_filters_texturepool = NULL; + R_Shadow_ValidateCvars(); + R_Shadow_MakeTextures(); maxshadowelements = 0; shadowelements = NULL; maxvertexupdate = 0; @@ -219,22 +268,12 @@ void r_shadow_start(void) r_shadow_buffer_numsurfacepvsbytes = 0; r_shadow_buffer_surfacepvs = NULL; r_shadow_buffer_surfacelist = NULL; - r_shadow_normalcubetexture = NULL; - r_shadow_attenuation2dtexture = NULL; - r_shadow_attenuation3dtexture = NULL; - r_shadow_blankbumptexture = NULL; - r_shadow_blankglosstexture = NULL; - r_shadow_blankwhitetexture = NULL; - r_shadow_texturepool = NULL; - r_shadow_filters_texturepool = NULL; - R_Shadow_ClearWorldLights(); - r_shadow_reloadlights = true; } void r_shadow_shutdown(void) { - R_Shadow_ClearWorldLights(); - r_shadow_reloadlights = true; + R_Shadow_UncompileWorldLights(); + numcubemaps = 0; r_shadow_normalcubetexture = NULL; r_shadow_attenuation2dtexture = NULL; r_shadow_attenuation3dtexture = NULL; @@ -244,29 +283,44 @@ void r_shadow_shutdown(void) R_FreeTexturePool(&r_shadow_texturepool); R_FreeTexturePool(&r_shadow_filters_texturepool); maxshadowelements = 0; + if (shadowelements) + Mem_Free(shadowelements); shadowelements = NULL; maxvertexupdate = 0; + if (vertexupdate) + Mem_Free(vertexupdate); vertexupdate = NULL; + if (vertexremap) + Mem_Free(vertexremap); vertexremap = NULL; vertexupdatenum = 0; maxshadowmark = 0; numshadowmark = 0; + if (shadowmark) + Mem_Free(shadowmark); shadowmark = NULL; + if (shadowmarklist) + Mem_Free(shadowmarklist); shadowmarklist = NULL; shadowmarkcount = 0; r_shadow_buffer_numclusterpvsbytes = 0; + if (r_shadow_buffer_clusterpvs) + Mem_Free(r_shadow_buffer_clusterpvs); r_shadow_buffer_clusterpvs = NULL; + if (r_shadow_buffer_clusterlist) + Mem_Free(r_shadow_buffer_clusterlist); r_shadow_buffer_clusterlist = NULL; r_shadow_buffer_numsurfacepvsbytes = 0; + if (r_shadow_buffer_surfacepvs) + Mem_Free(r_shadow_buffer_surfacepvs); r_shadow_buffer_surfacepvs = NULL; + if (r_shadow_buffer_surfacelist) + Mem_Free(r_shadow_buffer_surfacelist); r_shadow_buffer_surfacelist = NULL; - Mem_FreePool(&r_shadow_mempool); } void r_shadow_newmap(void) { - R_Shadow_ClearWorldLights(); - r_shadow_reloadlights = true; } void R_Shadow_Help_f(void) @@ -274,28 +328,29 @@ void R_Shadow_Help_f(void) Con_Printf( "Documentation on r_shadow system:\n" "Settings:\n" +"r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n" +"r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n" +"r_shadow_debuglight : render only this light number (-1 = all)\n" +"r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n" +"r_shadow_gloss2intensity : brightness of forced gloss\n" +"r_shadow_glossintensity : brightness of textured gloss\n" "r_shadow_lightattenuationpower : used to generate attenuation texture\n" "r_shadow_lightattenuationscale : used to generate attenuation texture\n" "r_shadow_lightintensityscale : scale rendering brightness of all lights\n" -"r_shadow_realtime_world : use realtime world light rendering\n" -"r_shadow_realtime_dlight : use high quality dlight rendering\n" -"r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n" -"r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n" -"r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n" -"r_shadow_glossintensity : brightness of textured gloss\n" -"r_shadow_gloss2intensity : brightness of forced gloss\n" -"r_shadow_debuglight : render only this light number (-1 = all)\n" -"r_shadow_scissor : use scissor optimization\n" -"r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n" -"r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n" -"r_shadow_polygonfactor : nudge shadow volumes closer/further\n" -"r_shadow_polygonoffset : nudge shadow volumes closer/further\n" "r_shadow_portallight : use portal visibility for static light precomputation\n" "r_shadow_projectdistance : shadow volume projection distance\n" -"r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n" +"r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n" +"r_shadow_realtime_dlight_shadows : cast shadows from dlights\n" +"r_shadow_realtime_world : use high quality world lighting mode\n" +"r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n" +"r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n" +"r_shadow_realtime_world_shadows : cast shadows from world lights\n" +"r_shadow_scissor : use scissor optimization\n" +"r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n" +"r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n" "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n" -"r_shadow_worldshadows : enable world shadows\n" -"r_shadow_dlightshadows : enable dlight shadows\n" +"r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n" +"r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n" "Commands:\n" "r_shadow_help : this help\n" ); @@ -303,30 +358,31 @@ void R_Shadow_Help_f(void) void R_Shadow_Init(void) { + Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture); + Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap); + Cvar_RegisterVariable(&r_shadow_cull); + Cvar_RegisterVariable(&r_shadow_debuglight); + Cvar_RegisterVariable(&r_shadow_gloss); + Cvar_RegisterVariable(&r_shadow_gloss2intensity); + Cvar_RegisterVariable(&r_shadow_glossintensity); Cvar_RegisterVariable(&r_shadow_lightattenuationpower); Cvar_RegisterVariable(&r_shadow_lightattenuationscale); Cvar_RegisterVariable(&r_shadow_lightintensityscale); + Cvar_RegisterVariable(&r_shadow_portallight); + Cvar_RegisterVariable(&r_shadow_projectdistance); + Cvar_RegisterVariable(&r_shadow_realtime_dlight); + Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows); Cvar_RegisterVariable(&r_shadow_realtime_world); + Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows); Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps); - Cvar_RegisterVariable(&r_shadow_realtime_dlight); - Cvar_RegisterVariable(&r_shadow_visiblevolumes); - Cvar_RegisterVariable(&r_shadow_gloss); - Cvar_RegisterVariable(&r_shadow_glossintensity); - Cvar_RegisterVariable(&r_shadow_gloss2intensity); - Cvar_RegisterVariable(&r_shadow_debuglight); + Cvar_RegisterVariable(&r_shadow_realtime_world_shadows); Cvar_RegisterVariable(&r_shadow_scissor); - Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap); - Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture); - Cvar_RegisterVariable(&r_shadow_polygonfactor); - Cvar_RegisterVariable(&r_shadow_polygonoffset); - Cvar_RegisterVariable(&r_shadow_portallight); - Cvar_RegisterVariable(&r_shadow_projectdistance); - Cvar_RegisterVariable(&r_shadow_texture3d); + Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor); + Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset); Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration); - Cvar_RegisterVariable(&r_shadow_worldshadows); - Cvar_RegisterVariable(&r_shadow_dlightshadows); Cvar_RegisterVariable(&r_shadow_staticworldlights); - Cvar_RegisterVariable(&r_shadow_cull); + Cvar_RegisterVariable(&r_shadow_texture3d); + Cvar_RegisterVariable(&r_shadow_visiblevolumes); Cvar_RegisterVariable(&gl_ext_stenciltwoside); if (gamemode == GAME_TENEBRAE) { @@ -335,6 +391,25 @@ void R_Shadow_Init(void) } Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f); R_Shadow_EditLights_Init(); + r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL); + r_shadow_worldlightchain = NULL; + maxshadowelements = 0; + shadowelements = NULL; + maxvertexupdate = 0; + vertexupdate = NULL; + vertexremap = NULL; + vertexupdatenum = 0; + maxshadowmark = 0; + numshadowmark = 0; + shadowmark = NULL; + shadowmarklist = NULL; + shadowmarkcount = 0; + r_shadow_buffer_numclusterpvsbytes = 0; + r_shadow_buffer_clusterpvs = NULL; + r_shadow_buffer_clusterlist = NULL; + r_shadow_buffer_numsurfacepvsbytes = 0; + r_shadow_buffer_surfacepvs = NULL; + r_shadow_buffer_surfacelist = NULL; R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap); } @@ -429,7 +504,7 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int * { int i, j, tris = 0, vr[3], t, outvertices = 0; const int *e, *n; - float f, temp[3]; + const float *v; if (maxvertexupdate < innumvertices) { @@ -450,10 +525,12 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int * memset(vertexremap, 0, maxvertexupdate * sizeof(int)); } + for (i = 0;i < numshadowmarktris;i++) + shadowmark[shadowmarktris[i]] = shadowmarkcount; + for (i = 0;i < numshadowmarktris;i++) { t = shadowmarktris[i]; - shadowmark[t] = shadowmarkcount; e = inelement3i + t * 3; // make sure the vertices are created for (j = 0;j < 3;j++) @@ -462,14 +539,34 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int * { vertexupdate[e[j]] = vertexupdatenum; vertexremap[e[j]] = outvertices; - VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp); + v = invertex3f + e[j] * 3; +#if 1 + outvertex3f[0] = v[0]; + outvertex3f[1] = v[1]; + outvertex3f[2] = v[2]; + outvertex3f[3] = v[0] + 1000000 * (v[0] - projectorigin[0]); + outvertex3f[4] = v[1] + 1000000 * (v[1] - projectorigin[1]); + outvertex3f[5] = v[2] + 1000000 * (v[2] - projectorigin[2]); +#else +{ +float f, temp[3]; + VectorSubtract(v, projectorigin, temp); f = projectdistance / VectorLength(temp); - VectorCopy(invertex3f + e[j] * 3, outvertex3f); + VectorCopy(v, outvertex3f); VectorMA(projectorigin, f, temp, (outvertex3f + 3)); +} +#endif outvertex3f += 6; outvertices += 2; } } + } + + for (i = 0;i < numshadowmarktris;i++) + { + t = shadowmarktris[i]; + e = inelement3i + t * 3; + n = inneighbor3i + t * 3; // output the front and back triangles outelement3i[0] = vertexremap[e[0]]; outelement3i[1] = vertexremap[e[1]]; @@ -479,13 +576,6 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int * outelement3i[5] = vertexremap[e[0]] + 1; outelement3i += 6; tris += 2; - } - - for (i = 0;i < numshadowmarktris;i++) - { - t = shadowmarktris[i]; - e = inelement3i + t * 3; - n = inneighbor3i + t * 3; // output the sides (facing outward from this triangle) if (shadowmark[n[0]] != shadowmarkcount) { @@ -532,8 +622,6 @@ int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int * return tris; } -float varray_vertex3f2[65536*3]; - void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris) { int tris, outverts; @@ -551,39 +639,41 @@ void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements); } -void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs) +void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, vec3_t lightmins, vec3_t lightmaxs, vec3_t surfacemins, vec3_t surfacemaxs) { - int i; + int t, tend; + const int *e; const float *v[3]; - - // check which triangles are facing the , and then output - // triangle elements and vertices... by clever use of elements we - // can construct the whole shadow from the unprojected vertices and - // the projected vertices - - // identify lit faces within the bounding box - R_Shadow_PrepareShadowMark(numtris); - for (i = 0;i < numtris;i++) + if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs)) + return; + tend = firsttriangle + numtris; + if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0] + && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1] + && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2]) { - v[0] = invertex3f + elements[i*3+0] * 3; - v[1] = invertex3f + elements[i*3+1] * 3; - v[2] = invertex3f + elements[i*3+2] * 3; - if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) && maxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && mins[0] < max(v[0][0], max(v[1][0], v[2][0])) && maxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && mins[1] < max(v[0][1], max(v[1][1], v[2][1])) && maxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && mins[2] < max(v[0][2], max(v[1][2], v[2][2]))) - shadowmarklist[numshadowmark++] = i; + // surface box entirely inside light box, no box cull + for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3) + if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3)) + shadowmarklist[numshadowmark++] = t; + } + else + { + // surface box not entirely inside light box, cull each triangle + for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3) + { + v[0] = invertex3f + e[0] * 3; + v[1] = invertex3f + e[1] * 3; + v[2] = invertex3f + e[2] * 3; + if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) + && 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]))) + shadowmarklist[numshadowmark++] = t; + } } - R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist); -} - -void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius) -{ - vec3_t mins, maxs; - mins[0] = projectorigin[0] - radius; - mins[1] = projectorigin[1] - radius; - mins[2] = projectorigin[2] - radius; - maxs[0] = projectorigin[0] + radius; - maxs[1] = projectorigin[1] + radius; - maxs[2] = projectorigin[2] + radius; - R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs); } void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i) @@ -617,7 +707,6 @@ void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *verte GL_LockArrays(0, 0); } -float r_shadow_attenpower, r_shadow_attenscale; static void R_Shadow_MakeTextures(void) { int x, y, z, d, side; @@ -746,14 +835,19 @@ static void R_Shadow_MakeTextures(void) Mem_Free(data); } -void R_Shadow_Stage_Begin(void) +void R_Shadow_ValidateCvars(void) { - rmeshstate_t m; - if (r_shadow_texture3d.integer && !gl_texture3d) Cvar_SetValueQuick(&r_shadow_texture3d, 0); if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside) Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0); +} + +void R_Shadow_Stage_Begin(void) +{ + rmeshstate_t m; + + R_Shadow_ValidateCvars(); if (!r_shadow_attenuation2dtexture || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer) @@ -777,22 +871,6 @@ void R_Shadow_Stage_Begin(void) c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0; } -void R_Shadow_LoadWorldLightsIfNeeded(void) -{ - if (r_shadow_reloadlights && cl.worldmodel) - { - R_Shadow_ClearWorldLights(); - r_shadow_reloadlights = false; - R_Shadow_LoadWorldLights(); - if (r_shadow_worldlightchain == NULL) - { - R_Shadow_LoadLightsFile(); - if (r_shadow_worldlightchain == NULL) - R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(); - } - } -} - void R_Shadow_Stage_ShadowVolumes(void) { rmeshstate_t m; @@ -803,10 +881,10 @@ void R_Shadow_Stage_ShadowVolumes(void) GL_BlendFunc(GL_ONE, GL_ZERO); GL_DepthMask(false); GL_DepthTest(true); - qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value); - //if (r_shadow_polygonoffset.value != 0) + qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value); + //if (r_shadow_shadow_polygonoffset.value != 0) //{ - // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value); + // qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value); // qglEnable(GL_POLYGON_OFFSET_FILL); //} //else @@ -846,7 +924,7 @@ void R_Shadow_Stage_ShadowVolumes(void) // optimize for them as noted above } -void R_Shadow_Stage_LightWithoutShadows(void) +void R_Shadow_Stage_Light(int shadowtest) { rmeshstate_t m; memset(&m, 0, sizeof(m)); @@ -857,35 +935,14 @@ void R_Shadow_Stage_LightWithoutShadows(void) qglPolygonOffset(0, 0); //qglDisable(GL_POLYGON_OFFSET_FILL); GL_Color(1, 1, 1, 1); - GL_ColorMask(1, 1, 1, 1); + GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1); qglDepthFunc(GL_EQUAL); qglCullFace(GL_FRONT); // quake is backwards, this culls back faces qglEnable(GL_CULL_FACE); - qglDisable(GL_STENCIL_TEST); - if (gl_support_stenciltwoside) - qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); - qglStencilMask(~0); - qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - qglStencilFunc(GL_EQUAL, 128, ~0); - r_shadowstage = SHADOWSTAGE_LIGHT; - c_rt_lights++; -} - -void R_Shadow_Stage_LightWithShadows(void) -{ - rmeshstate_t m; - memset(&m, 0, sizeof(m)); - R_Mesh_State(&m); - GL_BlendFunc(GL_ONE, GL_ONE); - GL_DepthMask(false); - GL_DepthTest(true); - qglPolygonOffset(0, 0); - //qglDisable(GL_POLYGON_OFFSET_FILL); - GL_Color(1, 1, 1, 1); - GL_ColorMask(1, 1, 1, 1); - qglDepthFunc(GL_EQUAL); - qglCullFace(GL_FRONT); // quake is backwards, this culls back faces - qglEnable(GL_STENCIL_TEST); + if (shadowtest) + qglEnable(GL_STENCIL_TEST); + else + qglDisable(GL_STENCIL_TEST); if (gl_support_stenciltwoside) qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); qglStencilMask(~0); @@ -908,7 +965,7 @@ void R_Shadow_Stage_End(void) qglPolygonOffset(0, 0); //qglDisable(GL_POLYGON_OFFSET_FILL); GL_Color(1, 1, 1, 1); - GL_ColorMask(1, 1, 1, 1); + GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1); GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height); qglDepthFunc(GL_LEQUAL); qglCullFace(GL_FRONT); // quake is backwards, this culls back faces @@ -1105,7 +1162,7 @@ static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float * if ((dot = DotProduct(n, v)) > 0) { dist = sqrt(dist); - intensity = dot / (VectorLength(v) * VectorLength(n)); + intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n)); intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale; VectorScale(lightcolor, intensity, color4f); color4f[3] = 1; @@ -1136,7 +1193,7 @@ static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *ve Matrix4x4_Transform3x3(m, normal3f, n); if ((dot = DotProduct(n, v)) > 0) { - intensity = dot / (VectorLength(v) * VectorLength(n)); + intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n)); intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale; VectorScale(lightcolor, intensity, color4f); color4f[3] = 1; @@ -1165,7 +1222,7 @@ static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const fl Matrix4x4_Transform3x3(m, normal3f, n); if ((dot = DotProduct(n, v)) > 0) { - intensity = dot / (VectorLength(v) * VectorLength(n)); + intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n)); VectorScale(lightcolor, intensity, color4f); color4f[3] = 1; } @@ -1177,10 +1234,12 @@ static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const fl } } -#define USETEXMATRIX 1 +// TODO: use glTexGen instead of feeding vertices to texcoordpointer? +#define USETEXMATRIX + #ifndef USETEXMATRIX -// FIXME: this should be done in a texture matrix or vertex program when possible -// FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE +// this should be done in a texture matrix or vertex program when possible, but here's code to do it manually +// if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix) { do @@ -1248,6 +1307,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements bumptexture = r_shadow_blankbumptexture; if (!glosstexture) glosstexture = r_shadow_blankglosstexture; + // FIXME: support EF_NODEPTHTEST GL_DepthMask(false); GL_DepthTest(true); if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil) @@ -1276,7 +1336,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements m.pointer_texcoord3f[1] = varray_texcoord3f[1]; R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin); m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[2] = vertex3f; m.texmatrix[2] = *matrix_modeltoattenuationxyz; #else @@ -1299,7 +1359,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements if (lightcubemap) { m.texcubemap[1] = R_GetTexture(lightcubemap); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltolight; #else @@ -1314,7 +1374,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements memset(&m, 0, sizeof(m)); m.pointer_vertex = vertex3f; m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[0] = vertex3f; m.texmatrix[0] = *matrix_modeltoattenuationxyz; #else @@ -1354,7 +1414,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements if (lightcubemap) { m.texcubemap[1] = R_GetTexture(lightcubemap); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltolight; #else @@ -1389,7 +1449,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements m.tex[0] = R_GetTexture(basetexture); m.pointer_texcoord[0] = texcoord2f; m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltoattenuationxyz; #else @@ -1410,7 +1470,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements m.pointer_texcoord3f[1] = varray_texcoord3f[1]; R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin); m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[2] = vertex3f; m.texmatrix[2] = *matrix_modeltoattenuationxyz; #else @@ -1418,7 +1478,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz); #endif m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[3] = vertex3f; m.texmatrix[3] = *matrix_modeltoattenuationz; #else @@ -1441,7 +1501,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements if (lightcubemap) { m.texcubemap[1] = R_GetTexture(lightcubemap); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltolight; #else @@ -1456,7 +1516,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements memset(&m, 0, sizeof(m)); m.pointer_vertex = vertex3f; m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[0] = vertex3f; m.texmatrix[0] = *matrix_modeltoattenuationxyz; #else @@ -1464,7 +1524,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz); #endif m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltoattenuationz; #else @@ -1504,7 +1564,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements if (lightcubemap) { m.texcubemap[1] = R_GetTexture(lightcubemap); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltolight; #else @@ -1515,7 +1575,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements } // this final code is shared R_Mesh_State(&m); - GL_ColorMask(1,1,1,0); + GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0); GL_BlendFunc(GL_DST_ALPHA, GL_ONE); VectorScale(lightcolor, colorscale, color2); for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--) @@ -1579,7 +1639,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements memset(&m, 0, sizeof(m)); m.pointer_vertex = vertex3f; m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[0] = vertex3f; m.texmatrix[0] = *matrix_modeltoattenuationxyz; #else @@ -1601,7 +1661,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements if (lightcubemap) { m.texcubemap[1] = R_GetTexture(lightcubemap); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltolight; #else @@ -1654,7 +1714,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements m.tex[0] = R_GetTexture(glosstexture); m.pointer_texcoord[0] = texcoord2f; m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltoattenuationxyz; #else @@ -1704,7 +1764,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements memset(&m, 0, sizeof(m)); m.pointer_vertex = vertex3f; m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[0] = vertex3f; m.texmatrix[0] = *matrix_modeltoattenuationxyz; #else @@ -1712,7 +1772,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz); #endif m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltoattenuationz; #else @@ -1734,7 +1794,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements if (lightcubemap) { m.texcubemap[1] = R_GetTexture(lightcubemap); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltolight; #else @@ -1745,7 +1805,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements } } R_Mesh_State(&m); - GL_ColorMask(1,1,1,0); + GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0); GL_BlendFunc(GL_DST_ALPHA, GL_ONE); VectorScale(lightcolor, colorscale, color2); GL_LockArrays(0, numverts); @@ -1774,7 +1834,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements { // voodoo2 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[1] = vertex3f; m.texmatrix[1] = *matrix_modeltoattenuationxyz; #else @@ -1785,7 +1845,7 @@ void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements { // Geforce3/Radeon class but not using dot3 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture); -#if USETEXMATRIX +#ifdef USETEXMATRIX m.pointer_texcoord3f[2] = vertex3f; m.texmatrix[2] = *matrix_modeltoattenuationz; #else @@ -1859,8 +1919,6 @@ void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int i rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2; } -rtlight_t *r_shadow_compilingrtlight; - // compiles rtlight geometry // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls) void R_RTLight_Compile(rtlight_t *rtlight) @@ -1968,16 +2026,19 @@ void R_RTLight_Uncompile(rtlight_t *rtlight) } } -int shadowframecount = 0; - -void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light); +void R_Shadow_UncompileWorldLights(void) +{ + dlight_t *light; + for (light = r_shadow_worldlightchain;light;light = light->next) + R_RTLight_Uncompile(&light->rtlight); +} void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) { int i, shadow; entity_render_t *ent; float f; - vec3_t relativelightorigin, relativeeyeorigin, lightcolor; + vec3_t relativelightorigin, relativeeyeorigin, lightcolor, lightcolor2; rtexture_t *cubemaptexture; matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz; int numclusters, numsurfaces; @@ -1987,18 +2048,24 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) shadowmesh_t *mesh; rmeshstate_t m; - if (d_lightstylevalue[rtlight->style] <= 0) - return; + // 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) + if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer) + R_RTLight_Compile(rtlight); + if (rtlight->cubemapname[0]) + cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname); + else + cubemaptexture = NULL; + cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius; cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius; cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius; cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius; cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius; cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius; - if (R_CullBox(cullmins, cullmaxs)) + if (d_lightstylevalue[rtlight->style] <= 0) return; - if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer) - R_RTLight_Compile(rtlight); numclusters = 0; clusterlist = NULL; clusterpvs = NULL; @@ -2006,6 +2073,8 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) surfacelist = NULL; if (rtlight->compiled && r_shadow_staticworldlights.integer) { + // compiled light, world available and can receive realtime lighting + // retrieve cluster information numclusters = rtlight->static_numclusters; clusterlist = rtlight->static_clusterlist; clusterpvs = rtlight->static_clusterpvs; @@ -2014,6 +2083,11 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) } else if (cl.worldmodel && cl.worldmodel->GetLightInfo) { + // dynamic light, world available and can receive realtime lighting + // if the light box is offscreen, skip it right away + if (R_CullBox(cullmins, cullmaxs)) + return; + // calculate lit surfaces and clusters R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters); R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->nummodelsurfaces); cl.worldmodel->GetLightInfo(&cl_entities[0].render, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces); @@ -2021,6 +2095,10 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) clusterpvs = r_shadow_buffer_clusterpvs; surfacelist = r_shadow_buffer_surfacelist; } + // if the reduced cluster bounds are offscreen, skip it + if (R_CullBox(cullmins, cullmaxs)) + return; + // check if light is illuminating any visible clusters if (numclusters) { for (i = 0;i < numclusters;i++) @@ -2029,8 +2107,7 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) if (i == numclusters) return; } - if (R_CullBox(cullmins, cullmaxs)) - return; + // set up a scissor rectangle for this light if (R_Shadow_ScissorForBBox(cullmins, cullmaxs)) return; @@ -2044,12 +2121,8 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) } */ - if (rtlight->cubemapname[0]) - cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname); - else - cubemaptexture = NULL; + shadow = rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows); - shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer); if (shadow && (gl_stencil || visiblevolumes)) { if (!visiblevolumes) @@ -2113,13 +2186,10 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) if (!visiblevolumes) { - if (shadow && gl_stencil) - R_Shadow_Stage_LightWithShadows(); - else - R_Shadow_Stage_LightWithoutShadows(); + R_Shadow_Stage_Light(shadow && gl_stencil); ent = &cl_entities[0].render; - if (ent->model && ent->model->DrawLight) + if (ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT)) { Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin); Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin); @@ -2140,14 +2210,17 @@ void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes) for (i = 0;i < r_refdef.numentities;i++) { ent = r_refdef.entities[i]; - if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT)) + // can't draw transparent entity lighting here because + // transparent meshes are deferred for later + if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & (RENDER_LIGHT | RENDER_TRANSPARENT)) == RENDER_LIGHT) { + VectorScale(lightcolor, ent->alpha, lightcolor2); Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin); Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin); Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix); Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix); Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix); - ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->nummodelsurfaces, ent->model->surfacelist); + ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->nummodelsurfaces, ent->model->surfacelist); } } } @@ -2160,6 +2233,9 @@ void R_ShadowVolumeLighting(int visiblevolumes) dlight_t *light; rmeshstate_t m; + if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname))) + R_Shadow_EditLights_Reload_f(); + if (visiblevolumes) { memset(&m, 0, sizeof(m)); @@ -2173,10 +2249,8 @@ void R_ShadowVolumeLighting(int visiblevolumes) } else R_Shadow_Stage_Begin(); - shadowframecount++; - if (r_shadow_realtime_world.integer) + if (r_rtworld) { - R_Shadow_LoadWorldLightsIfNeeded(); if (r_shadow_debuglight.integer >= 0) { for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next) @@ -2187,7 +2261,7 @@ void R_ShadowVolumeLighting(int visiblevolumes) for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next) R_DrawRTLight(&light->rtlight, visiblevolumes); } - if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) + if (r_rtdlight) for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++) R_DrawRTLight(&light->rtlight, visiblevolumes); @@ -2200,38 +2274,23 @@ void R_ShadowVolumeLighting(int visiblevolumes) R_Shadow_Stage_End(); } -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"}; -cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"}; -cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"}; -cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"}; -dlight_t *r_shadow_worldlightchain; -dlight_t *r_shadow_selectedlight; -vec3_t r_editlights_cursorlocation; - -typedef struct cubemapinfo_s -{ - char basename[64]; - rtexture_t *texture; -} -cubemapinfo_t; - -#define MAX_CUBEMAPS 128 -static int numcubemaps; -static cubemapinfo_t cubemaps[MAX_CUBEMAPS]; - //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"}; typedef struct suffixinfo_s { char *suffix; - int flipx, flipy, flipdiagonal; + qboolean flipx, flipy, flipdiagonal; } suffixinfo_t; static suffixinfo_t suffix[3][6] = { + { + {"px", false, false, false}, + {"nx", false, false, false}, + {"py", false, false, false}, + {"ny", false, false, false}, + {"pz", false, false, false}, + {"nz", false, false, false} + }, { {"posx", false, false, false}, {"negx", false, false, false}, @@ -2241,20 +2300,12 @@ static suffixinfo_t suffix[3][6] = {"negz", false, false, false} }, { - {"px", false, false, false}, - {"nx", false, false, false}, - {"py", false, false, false}, - {"ny", false, false, false}, - {"pz", false, false, false}, - {"nz", false, false, false} - }, - { - {"ft", true, false, true}, - {"bk", false, true, true}, - {"lf", true, true, false}, - {"rt", false, false, false}, - {"up", false, false, false}, - {"dn", false, false, false} + {"rt", true, false, true}, + {"lf", false, true, true}, + {"ft", true, true, false}, + {"bk", false, false, false}, + {"up", true, false, true}, + {"dn", true, false, true} } }; @@ -2270,30 +2321,39 @@ rtexture_t *R_Shadow_LoadCubemap(const char *basename) cubemapsize = 0; cubemappixels = NULL; cubemaptexture = NULL; + // keep trying different suffix groups (posx, px, rt) until one loads for (j = 0;j < 3 && !cubemappixels;j++) { + // load the 6 images in the suffix group for (i = 0;i < 6;i++) { + // generate an image name based on the base and and suffix snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix); + // load it if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize))) { + // an image loaded, make sure width and height are equal if (image_width == image_height) { + // if this is the first image to load successfully, allocate the cubemap memory if (!cubemappixels && image_width >= 1) { cubemapsize = image_width; - // note this clears to black, so unavailable sizes are black + // note this clears to black, so unavailable sides are black cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4); } + // copy the image with any flipping needed by the suffix (px and posx types don't need flipping) if (cubemappixels) Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder); } else Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height); + // free the image Mem_Free(image_rgba); } } } + // if a cubemap loaded, upload it if (cubemappixels) { if (!r_shadow_filters_texturepool) @@ -2332,21 +2392,25 @@ void R_Shadow_FreeCubemaps(void) R_FreeTexturePool(&r_shadow_filters_texturepool); } -void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname) +dlight_t *R_Shadow_NewWorldLight(void) { dlight_t *light; - - if (radius < 15 || DotProduct(color, color) < 0.03) - { - Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n"); - return; - } - light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t)); + light->next = r_shadow_worldlightchain; + r_shadow_worldlightchain = light; + return light; +} + +void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname) +{ VectorCopy(origin, light->origin); - VectorCopy(angles, light->angles); - VectorCopy(color, light->color); - light->radius = radius; + light->angles[0] = angles[0] - 360 * floor(angles[0] / 360); + light->angles[1] = angles[1] - 360 * floor(angles[1] / 360); + light->angles[2] = angles[2] - 360 * floor(angles[2] / 360); + light->color[0] = max(color[0], 0); + light->color[1] = max(color[1], 0); + light->color[2] = max(color[2], 0); + light->radius = max(radius, 0); light->style = style; if (light->style < 0 || light->style >= MAX_LIGHTSTYLES) { @@ -2355,25 +2419,22 @@ void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t ra } light->shadow = shadowenable; light->corona = corona; - if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname)) - strcpy(light->cubemapname, cubemapname); + if (!cubemapname) + cubemapname = ""; + strlcpy(light->cubemapname, cubemapname, strlen(light->cubemapname)); Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1); - light->next = r_shadow_worldlightchain; - r_shadow_worldlightchain = light; R_RTLight_UpdateFromDLight(&light->rtlight, light, true); - if (r_shadow_staticworldlights.integer) - R_RTLight_Compile(&light->rtlight); } void R_Shadow_FreeWorldLight(dlight_t *light) { dlight_t **lightpointer; + R_RTLight_Uncompile(&light->rtlight); for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next); if (*lightpointer != light) Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n"); *lightpointer = light->next; - R_RTLight_Uncompile(&light->rtlight); Mem_Free(light); } @@ -2394,8 +2455,6 @@ void R_Shadow_SelectLight(dlight_t *light) r_shadow_selectedlight->selected = true; } -rtexture_t *lighttextures[5]; - void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2) { float scale = r_editlights_cursorgrid.value * 0.5f; @@ -2428,8 +2487,8 @@ void R_Shadow_DrawLightSprites(void) lighttextures[i] = pic->tex; } - for (light = r_shadow_worldlightchain;light;light = light->next) - R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5); + for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next) + R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, i % 5); R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0); } @@ -2519,7 +2578,7 @@ void R_Shadow_LoadWorldLights(void) } VectorScale(color, r_editlights_rtlightscolorscale.value, color); radius *= r_editlights_rtlightssizescale.value; - R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname); + R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname); s++; n++; } @@ -2549,7 +2608,10 @@ void R_Shadow_SaveWorldLights(void) buf = NULL; for (light = r_shadow_worldlightchain;light;light = light->next) { - sprintf(line, "%s%f %f %f %f %f %f %f %d %s %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname[0] ? light->cubemapname : "\"\"", light->corona, light->angles[0], light->angles[1], light->angles[2]); + if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2]) + sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]); + else + sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style); if (bufchars + (int) strlen(line) > bufmaxchars) { bufmaxchars = bufchars + strlen(line) + 2048; @@ -2609,7 +2671,7 @@ void R_Shadow_LoadLightsFile(void) radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f)); radius = bound(15, radius, 4096); VectorScale(color, (2.0f / (8388608.0f)), color); - R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL); + R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL); s++; n++; } @@ -2619,11 +2681,14 @@ void R_Shadow_LoadLightsFile(void) } } +// tyrlite/hmap2 light types in the delay field +typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t; + void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void) { - int entnum, style, islight, skin, pflags, effects; + int entnum, style, islight, skin, pflags, effects, type, n; char key[256], value[1024]; - float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3]; + float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4]; const char *data; if (cl.worldmodel == NULL) @@ -2636,11 +2701,12 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void) return; for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++) { - light = 0; + type = LIGHTTYPE_MINUSX; origin[0] = origin[1] = origin[2] = 0; originhack[0] = originhack[1] = originhack[2] = 0; angles[0] = angles[1] = angles[2] = 0; color[0] = color[1] = color[2] = 1; + light[0] = light[1] = light[2] = 1;light[3] = 300; overridecolor[0] = overridecolor[1] = overridecolor[2] = 1; fadescale = 1; lightscale = 1; @@ -2667,7 +2733,27 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void) // now that we have the key pair worked out... if (!strcmp("light", key)) - light = atof(value); + { + n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]); + if (n == 1) + { + // quake + light[0] = vec[0] * (1.0f / 256.0f); + light[1] = vec[0] * (1.0f / 256.0f); + light[2] = vec[0] * (1.0f / 256.0f); + light[3] = vec[0]; + } + else if (n == 4) + { + // halflife + light[0] = vec[0] * (1.0f / 255.0f); + light[1] = vec[1] * (1.0f / 255.0f); + light[2] = vec[2] * (1.0f / 255.0f); + light[3] = vec[3]; + } + } + else if (!strcmp("delay", key)) + type = atoi(value); else if (!strcmp("origin", key)) sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]); else if (!strcmp("angle", key)) @@ -2764,28 +2850,45 @@ void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void) else if (!strcmp("effects", key)) effects = (int)atof(value); } - if (light <= 0 && islight) - light = 300; + if (!islight) + continue; if (lightscale <= 0) lightscale = 1; if (fadescale <= 0) fadescale = 1; - if (gamemode == GAME_TENEBRAE) + if (color[0] == color[1] && color[0] == color[2]) { - if (effects & EF_NODRAW) - { - pflags |= PFLAGS_FULLDYNAMIC; - effects &= ~EF_NODRAW; - } + color[0] *= overridecolor[0]; + color[1] *= overridecolor[1]; + color[2] *= overridecolor[2]; + } + radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale; + color[0] = color[0] * light[0]; + color[1] = color[1] * light[1]; + color[2] = color[2] * light[2]; + switch (type) + { + case LIGHTTYPE_MINUSX: + break; + case LIGHTTYPE_RECIPX: + radius *= 2; + VectorScale(color, (1.0f / 16.0f), color); + break; + case LIGHTTYPE_RECIPXX: + radius *= 2; + VectorScale(color, (1.0f / 16.0f), color); + break; + default: + case LIGHTTYPE_NONE: + break; + case LIGHTTYPE_SUN: + break; + case LIGHTTYPE_MINUSXX: + break; } - 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); - VectorScale(color, light, color); VectorAdd(origin, originhack, origin); - if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC)) - R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL); + if (radius >= 1) + R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL); } } @@ -2830,13 +2933,24 @@ void R_Shadow_EditLights_Clear_f(void) void R_Shadow_EditLights_Reload_f(void) { - r_shadow_reloadlights = true; + if (!cl.worldmodel) + return; + strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname)); + R_Shadow_ClearWorldLights(); + R_Shadow_LoadWorldLights(); + if (r_shadow_worldlightchain == NULL) + { + R_Shadow_LoadLightsFile(); + if (r_shadow_worldlightchain == NULL) + R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(); + } } void R_Shadow_EditLights_Save_f(void) { - if (cl.worldmodel) - R_Shadow_SaveWorldLights(); + if (!cl.worldmodel) + return; + R_Shadow_SaveWorldLights(); } void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void) @@ -2865,7 +2979,7 @@ void R_Shadow_EditLights_Spawn_f(void) return; } color[0] = color[1] = color[2] = 1; - R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL); + R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL); } void R_Shadow_EditLights_Edit_f(void) @@ -3082,20 +3196,37 @@ void R_Shadow_EditLights_Edit_f(void) Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname); return; } - R_Shadow_FreeWorldLight(r_shadow_selectedlight); - r_shadow_selectedlight = NULL; - R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname); + R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname); +} + +void R_Shadow_EditLights_EditAll_f(void) +{ + dlight_t *light; + + if (!r_editlights.integer) + { + Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n"); + return; + } + + for (light = r_shadow_worldlightchain;light;light = light->next) + { + R_Shadow_SelectLight(light); + R_Shadow_EditLights_Edit_f(); + } } -extern int con_vislines; void R_Shadow_EditLights_DrawSelectedLightProperties(void) { float x, y; char temp[256]; - if (r_shadow_selectedlight == NULL) + if (!r_editlights.integer) return; x = 0; y = con_vislines; + sprintf(temp, "Cursor %f %f %f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8; + if (r_shadow_selectedlight == NULL) + return; sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8; sprintf(temp, "Origin %f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8; sprintf(temp, "Angles %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8; @@ -3119,9 +3250,7 @@ void R_Shadow_EditLights_ToggleShadow_f(void) Con_Print("No selected light.\n"); return; } - R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname); - R_Shadow_FreeWorldLight(r_shadow_selectedlight); - r_shadow_selectedlight = NULL; + R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname); } void R_Shadow_EditLights_ToggleCorona_f(void) @@ -3136,9 +3265,7 @@ void R_Shadow_EditLights_ToggleCorona_f(void) Con_Print("No selected light.\n"); return; } - R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname); - R_Shadow_FreeWorldLight(r_shadow_selectedlight); - r_shadow_selectedlight = NULL; + R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname); } void R_Shadow_EditLights_Remove_f(void) @@ -3204,6 +3331,45 @@ void R_Shadow_EditLights_Help_f(void) ); } +void R_Shadow_EditLights_CopyInfo_f(void) +{ + if (!r_editlights.integer) + { + Con_Print("Cannot copy light info when not in editing mode. Set r_editlights to 1.\n"); + return; + } + if (!r_shadow_selectedlight) + { + Con_Print("No selected light.\n"); + return; + } + VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles); + VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color); + r_shadow_bufferlight.radius = r_shadow_selectedlight->radius; + r_shadow_bufferlight.style = r_shadow_selectedlight->style; + if (r_shadow_selectedlight->cubemapname) + strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname); + else + r_shadow_bufferlight.cubemapname[0] = 0; + r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow; + r_shadow_bufferlight.corona = r_shadow_selectedlight->corona; +} + +void R_Shadow_EditLights_PasteInfo_f(void) +{ + if (!r_editlights.integer) + { + Con_Print("Cannot paste light info when not in editing mode. Set r_editlights to 1.\n"); + return; + } + if (!r_shadow_selectedlight) + { + Con_Print("No selected light.\n"); + return; + } + R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_bufferlight.angles, r_shadow_bufferlight.color, r_shadow_bufferlight.radius, r_shadow_bufferlight.corona, r_shadow_bufferlight.style, r_shadow_bufferlight.shadow, r_shadow_bufferlight.cubemapname); +} + void R_Shadow_EditLights_Init(void) { Cvar_RegisterVariable(&r_editlights); @@ -3220,10 +3386,13 @@ void R_Shadow_EditLights_Init(void) Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f); Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f); Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f); + Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f); Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f); Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f); Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f); Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f); Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f); + Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f); + Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f); }