diff --git a/code/renderer/tr_sun_flare.cpp b/code/renderer/tr_sun_flare.cpp index 1854a9ed..547333e1 100644 --- a/code/renderer/tr_sun_flare.cpp +++ b/code/renderer/tr_sun_flare.cpp @@ -1,6 +1,6 @@ /* =========================================================================== -Copyright (C) 2023 the OpenMoHAA team +Copyright (C) 2023-2024 the OpenMoHAA team This file is part of OpenMoHAA source code. @@ -24,12 +24,706 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "tr_local.h" -void R_InitLensFlare() +#define MAX_FLARES 20 + +class flare_s { - // FIXME: unimplemented +public: + float size; + float where; + shader_t *shader; + float alphascale; +}; + +class lens_flare +{ +public: + static int max_flares; + + float dot_min; + float fullscale; + + // List of flares + int num_flares; + flare_s flares[MAX_FLARES]; + + shader_t *fullscreen; + int fullfade; + qboolean blend_active; + + // Flare location + vec3_t trace_v; + vec3_t v; + + // Color and alpha + vec3_t color; + float alpha; + + // Screen projection + vec2_t screen; + float dot; + + int lasttime; + float lastbright; + + // Whether or not it's inside a portal sky + bool inportalsky; + + qboolean initted; + +public: + lens_flare() + { + color[0] = 1.0; + color[1] = 1.0; + color[2] = 1.0; + alpha = 1.0; + initted = false; + } + + bool CheckRange(); + bool CheckRay(); + bool ScreenCalc(); + virtual void Try(); + virtual void Init(const char *which); + void ScreenBlend(); + void InPortalSky(); + void NotInPortalSky(); + void SetVect(const float *vect); + + void SetColor(const float *c) { VectorCopy(c, color); } + + void SetAlpha(float a) { alpha = a; } +}; + +class dlight_lens_flare : public lens_flare +{ +public: + shader_t *corona_shader; + +public: + dlight_lens_flare() { corona_shader = NULL; } + + void Try() override; + + void Init(const char *which) override + { + lens_flare::Init(which); + corona_shader = R_FindShader("textures/sprites/corona", -1, qfalse, qfalse, qfalse, qfalse); + } +}; + +class sun_flare_class : public lens_flare +{ +public: + bool SunCheckRay(); + void SunScreenCalc(); + void SunTry(); +}; + +lens_flare torches; +dlight_lens_flare dlights; +sun_flare_class sunFlare; +int lens_flare::max_flares; + +void lens_flare::SetVect(const float *vect) +{ + if (inportalsky) { + vec3_t offset; + vec3_t rot_offset; + + VectorSubtract(vect, tr.refdef.sky_origin, offset); + VectorRotate(offset, tr.refdef.sky_axis, rot_offset); + VectorAdd(tr.refdef.vieworg, rot_offset, v); + + VectorNormalize(offset); + VectorMA(tr.refdef.vieworg, 16384, offset, trace_v); + } else { + VectorCopy(vect, v); + VectorCopy(vect, trace_v); + } +} + +void lens_flare::InPortalSky() +{ + inportalsky = true; +} + +void lens_flare::NotInPortalSky() +{ + inportalsky = false; +} + +bool lens_flare::CheckRange() +{ + vec3_t diff; + + VectorSubtract(v, backEnd.viewParms.ori.origin, diff); + VectorNormalizeFast(diff); + dot = DotProduct(backEnd.viewParms.ori.axis[0], diff); + + return dot > dot_min; +} + +bool lens_flare::CheckRay() +{ + trace_t trace; + + ri.CM_BoxTrace(&trace, backEnd.viewParms.ori.origin, trace_v, vec3_origin, vec3_origin, 0, CONTENTS_SOLID, qfalse); + if (inportalsky) { + return (trace.surfaceFlags & 4) != 0; + } + + return trace.fraction == 1.0; +} + +bool lens_flare::ScreenCalc() +{ + vec4_t eye; + vec4_t clip; + vec4_t point; + bool retval; + int i, j; + + retval = true; + + VectorCopy(v, point); + point[3] = 1.0; + + VectorClear4(eye); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + eye[i] += point[j] * tr.ori.modelMatrix[i + j * 4]; + } + } + + VectorClear4(clip); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + clip[i] += eye[j] * tr.viewParms.projectionMatrix[i + j * 4]; + } + } + + for (i = 0; i < 3; i++) { + if (clip[i] >= clip[3] || clip[i] <= -clip[3]) { + retval = false; + } + } + + for (i = 0; i < 2; i++) { + screen[i] = clip[i] / clip[3]; + } + + return retval; +} + +void lens_flare::Try() +{ + vec2_t diff; + vec2_t point; + float alpha; + int i; + + if (!ScreenCalc()) { + return; + } + if (!CheckRange()) { + return; + } + if (!CheckRay()) { + return; + } + + VectorScale2D(screen, -2.0, diff); + alpha = (dot - dot_min) / (1.0 - dot_min); + + for (i = 0; i < num_flares; i++) { + flare_s *flare = &flares[i]; + + VectorMA2D(screen, flare->where, diff, point); + + RB_Color4f(color[0], color[1], color[2], alpha * this->alpha * flare->alphascale); + RB_StreamBegin(flare->shader); + RB_Texcoord2f(0.0, 0.0); + + RB_Vertex2f(point[0] - flare->size, point[1] + flare->size); + RB_Texcoord2f(1.0, 0.0); + + RB_Vertex2f(point[0] + flare->size, point[1] + flare->size); + RB_Texcoord2f(0.0, 1.0); + + RB_Vertex2f(point[0] - flare->size, point[1] - flare->size); + RB_Texcoord2f(1.0, 1.0); + + RB_Vertex2f(point[0] + flare->size, point[1] - flare->size); + RB_StreamEnd(); + } + + lasttime = backEnd.refdef.time; + blend_active = true; + lastbright = Square(alpha) * Square(alpha) * this->fullscale; +} + +void dlight_lens_flare::Try() +{ + float size; + + size = r_lightcoronasize->value; + lens_flare::Try(); + lens_flare::CheckRange(); + + if (dot <= 0) { + return; + } + + if (!CheckRay()) { + return; + } + + RB_Color4f(color[0], color[1], color[2], alpha); + RB_StreamBegin(corona_shader); + RB_Texcoord2f(0.0, 0.0); + + RB_Vertex2f(screen[0] - size, screen[1] + size); + RB_Texcoord2f(1.0, 0.0); + + RB_Vertex2f(screen[0] + size, screen[1] + size); + RB_Texcoord2f(0.0, 1.0); + + RB_Vertex2f(screen[0] - size, screen[1] - size); + RB_Texcoord2f(1.0, 1.0); + + RB_Vertex2f(screen[0] + size, screen[1] - size); + RB_StreamEnd(); +} + +void lens_flare::ScreenBlend() +{ + float alpha; + int timediff; + + if (!fullscreen || !blend_active) { + return; + } + + timediff = backEnd.refdef.time - lasttime; + if (timediff > fullfade) { + blend_active = false; + return; + } + + alpha = (float)(fullfade - timediff) / fullfade * lastbright; + if (alpha <= 0.0001) { + blend_active = false; + return; + } + + RB_Color4f(1.0, 1.0, 1.0, alpha); + + RB_StreamBegin(fullscreen); + RB_Vertex2f(-1.0, -1.0); + RB_Vertex2f(1.0, -1.0); + RB_Vertex2f(-1.0, 1.0); + RB_Vertex2f(1.0, 1.0); + + RB_StreamEnd(); +} + +class tmpRiFile +{ +private: + bool file_exists; + void *buf; + +public: + tmpRiFile() + { + file_exists = false; + buf = NULL; + } + + ~tmpRiFile() { close(); } + + int open(const char *which, void **buffer) + { + close(); + + if (ri.FS_ReadFile(which, buffer) != -1) { + file_exists = true; + buf = *buffer; + } + + return file_exists; + } + + bool exists() const { return file_exists; } + + void close() + { + if (file_exists) { + ri.FS_FreeFile(buf); + file_exists = false; + } + } +}; + +void lens_flare::Init(const char *which) +{ + char *file; + const char *token; + tmpRiFile theFile; + + initted = false; + theFile.open("global/lensflaredefs.txt", (void **)&file); + + if (!theFile.exists()) { + ri.Printf(PRINT_WARNING, "WARNING: Could not open lens flare info file!\n"); + return; + } + + while ((token = COM_ParseExt(&file, qtrue))) { + if (!file) { + ri.Printf(PRINT_WARNING, "WARNING: could not find begin for section '%s' in lensflares\n", which); + return; + } + + if (!Q_stricmp(token, "begin")) { + token = COM_ParseExt(&file, qfalse); + if (!Q_stricmp(token, which)) { + break; + } + } + } + + dot_min = 0.8f; + fullscale = 0.7f; + num_flares = 0; + fullscreen = NULL; + initted = true; + fullfade = 0; + blend_active = false; + + while ((token = COM_ParseExt(&file, qtrue))) { + if (!file) { + ri.Printf(PRINT_WARNING, "WARNING: could not find end for section '%s' in lensflares\n", which); + break; + } + + if (!Q_stricmp(token, "dot_min")) { + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: invalid dot_min in lensflares\n"); + continue; + } + dot_min = atof(token); + } else if (!Q_stricmp(token, "flare")) { + assert(num_flares < MAX_FLARES); + + flares[num_flares].alphascale = 1.0; + + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: no flare arg 1 for flare '%d' in lensflares\n", num_flares); + continue; + } + flares[num_flares].size = atof(token); + + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: no flare arg 2 for flare '%d' in lensflares\n", num_flares); + continue; + } + flares[num_flares].where = atof(token); + + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: no flare arg 3 for flare '%d' in lensflares\n", num_flares); + continue; + } + flares[num_flares].shader = R_FindShader(token, -1, qfalse, qfalse, qfalse, qfalse); + + token = COM_ParseExt(&file, qfalse); + if (token[0]) { + flares[num_flares].alphascale = atof(token); + } + + num_flares++; + } else if (!Q_stricmp(token, "fullscale")) { + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: no arg for fullscale, assuming default (in lensflares)\n"); + continue; + } + fullscale = atof(token); + } else if (!Q_stricmp(token, "fullscreen")) { + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: no arg for fullsreen in lensflares\n"); + continue; + } + fullscreen = R_FindShader(token, -1, qfalse, qfalse, qfalse, qfalse); + } else if (!Q_stricmp(token, "fullfade")) { + token = COM_ParseExt(&file, qfalse); + if (!token[0]) { + ri.Printf(PRINT_WARNING, "WARNING: no arg for fullfade in lensflares\n"); + continue; + } + fullfade = atof(token); + } else if (!Q_stricmp(token, "end")) { + break; + } + } + + if (!num_flares) { + ri.Printf(PRINT_WARNING, "WARNING: no lensflares defined!\n"); + } + lasttime = backEnd.refdef.time - fullfade; +} + +static void R_DrawSunFlare() +{ + if (!s_sun.exists) { + return; + } + if (!sunFlare.initted) { + if (!s_sun.szFlareName[0]) { + return; + } + if (Q_stricmp(s_sun.szFlareName, "none")) { + sunFlare.Init(s_sun.szFlareName); + } + } + + if (!sunFlare.initted) { + s_sun.szFlareName[0] = 0; + return; + } + + if (!sunFlare.num_flares) { + return; + } + + VectorMA(backEnd.viewParms.ori.origin, 16384, s_sun.flaredirection, sunFlare.trace_v); + VectorMA(backEnd.viewParms.ori.origin, 128, s_sun.flaredirection, sunFlare.v); + + sunFlare.SunTry(); +} + +static void R_DrawSunFlareBlend() +{ + if (!s_sun.exists) { + return; + } + if (!sunFlare.initted) { + return; + } + + sunFlare.ScreenBlend(); } void R_DrawLensFlares() { - // FIXME: unimplemented + int i; + + R_RotateForViewer(); + + qglPushMatrix(); + qglLoadIdentity(); + qglMatrixMode(GL_PROJECTION); + qglPushMatrix(); + qglLoadIdentity(); + qglOrtho(-1.0, 1.0, -1.0, 1.0, -99999.0, 99999.0); + + tess.no_global_fog = true; + R_DrawSunFlare(); + + for (i = 0; i < backEnd.refdef.num_entities; i++) { + if ((backEnd.viewParms.isPortalSky && !(backEnd.refdef.entities[i].e.renderfx & RF_SKYENTITY)) + || (!backEnd.viewParms.isPortalSky && (backEnd.refdef.entities[i].e.renderfx & RF_SKYENTITY))) { + continue; + } + + if (backEnd.refdef.entities[i].e.renderfx & RF_LENSFLARE) { + vec3_t rgb; + + if (backEnd.refdef.entities[i].e.renderfx & RF_SKYENTITY) { + dlights.InPortalSky(); + } else { + dlights.NotInPortalSky(); + } + + dlights.SetVect(backEnd.refdef.entities[i].e.origin); + + // + // Set the dlight color from entity + // + + rgb[0] = backEnd.refdef.entities[i].e.shaderRGBA[0] / 255.0; + rgb[1] = backEnd.refdef.entities[i].e.shaderRGBA[1] / 255.0; + rgb[2] = backEnd.refdef.entities[i].e.shaderRGBA[2] / 255.0; + dlights.SetColor(rgb); + + dlights.Try(); + } + + if (backEnd.refdef.entities[i].e.renderfx & RF_VIEWLENSFLARE) { + vec3_t rgb; + + if (backEnd.refdef.entities[i].e.renderfx & RF_SKYENTITY) { + torches.InPortalSky(); + } else { + torches.NotInPortalSky(); + } + + torches.SetVect(backEnd.refdef.entities[i].e.origin); + + rgb[0] = backEnd.refdef.entities[i].e.shaderRGBA[0] / 255.0; + rgb[1] = backEnd.refdef.entities[i].e.shaderRGBA[1] / 255.0; + rgb[2] = backEnd.refdef.entities[i].e.shaderRGBA[2] / 255.0; + torches.SetColor(rgb); + + torches.Try(); + } + } + + for (i = 0; i < backEnd.refdef.num_dlights; i++) { + if ((backEnd.refdef.dlights[i].type & dlighttype_t::lensflare) + || (backEnd.refdef.dlights[i].type & dlighttype_t::additive)) { + dlights.NotInPortalSky(); + dlights.SetVect(backEnd.refdef.dlights[i].origin); + dlights.SetColor(backEnd.refdef.dlights[i].color); + + dlights.Try(); + } + } + + R_DrawSunFlareBlend(); + tess.no_global_fog = qfalse; + + qglPopMatrix(); + qglMatrixMode(GL_MODELVIEW); + qglPopMatrix(); +} + +void R_InitLensFlare() +{ + torches.Init("entity"); + dlights.Init("dlight"); + sunFlare.initted = false; +} + +bool sun_flare_class::SunCheckRay() +{ + mnode_t *pViewLeaf; + trace_t trace; + + pViewLeaf = R_PointInLeaf(tr.refdef.vieworg); + + if (pViewLeaf->area == -1 || !tr.world->vis || tr.sSunLight.leaf != (mnode_s *)-1 + || pViewLeaf->numlights && pViewLeaf->lights[0] == &tr.sSunLight) { + ri.CM_BoxTrace( + &trace, backEnd.viewParms.ori.origin, trace_v, vec3_origin, vec3_origin, 0, CONTENTS_SOLID, qfalse + ); + + if (trace.surfaceFlags & SURF_SKY) { + return true; + } + } + + return false; +} + +void sun_flare_class::SunScreenCalc() +{ + vec4_t eye; + vec4_t clip; + vec4_t point; + int i, j; + + VectorCopy(v, point); + point[3] = 1.0; + + VectorClear4(eye); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + eye[i] += point[j] * tr.ori.modelMatrix[i + j * 4]; + } + } + + VectorClear4(clip); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + clip[i] += eye[j] * tr.viewParms.projectionMatrix[i + j * 4]; + } + } + + for (i = 0; i < 2; i++) { + screen[i] = clip[i] / clip[3]; + } +} + +void sun_flare_class::SunTry() +{ + vec2_t diff; + vec2_t point; + float alpha; + int i; + qboolean bDrawingFade = qfalse; + + if (!CheckRange()) { + return; + } + + if (SunCheckRay()) { + alpha = (dot - dot_min) / (1.0 - dot_min); + } else { + int timediff; + + if (!fullscreen) { + return; + } + if (!blend_active) { + return; + } + + timediff = backEnd.refdef.time - lasttime; + alpha = (float)(fullfade - timediff) / fullfade * lastbright; + + if (alpha <= 0.0001) { + return; + } + + bDrawingFade = qtrue; + } + + SunScreenCalc(); + VectorScale2D(screen, -2.0, diff); + + for (i = 0; i < num_flares; i++) { + flare_s *flare = &flares[i]; + + VectorMA2D(screen, flare->where, diff, point); + + RB_Color4f(color[0], color[1], color[2], alpha * this->alpha * flare->alphascale); + RB_StreamBegin(flare->shader); + RB_Texcoord2f(0.0, 0.0); + + RB_Vertex2f(point[0] - flare->size, point[1] + flare->size); + RB_Texcoord2f(1.0, 0.0); + + RB_Vertex2f(point[0] + flare->size, point[1] + flare->size); + RB_Texcoord2f(0.0, 1.0); + + RB_Vertex2f(point[0] - flare->size, point[1] - flare->size); + RB_Texcoord2f(1.0, 1.0); + + RB_Vertex2f(point[0] + flare->size, point[1] - flare->size); + RB_StreamEnd(); + } + + if (!bDrawingFade) { + lasttime = backEnd.refdef.time; + blend_active = true; + lastbright = Square(alpha) * Square(alpha) * fullscale; + } }