openmohaa/code/renderergl1/tr_sun_flare.cpp

730 lines
17 KiB
C++
Raw Permalink Normal View History

2023-05-09 19:18:16 +02:00
/*
===========================================================================
Copyright (C) 2023-2024 the OpenMoHAA team
2023-05-09 19:18:16 +02:00
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// tr_sun_flare.cpp: Sun flares
#include "tr_local.h"
2023-05-09 21:58:24 +02:00
#define MAX_FLARES 20
class flare_s
{
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)
2023-05-09 21:58:24 +02:00
{
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();
2023-05-09 21:58:24 +02:00
}
2023-05-21 02:40:50 +02:00
void R_DrawLensFlares()
{
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;
}
2023-05-21 02:40:50 +02:00
}