Rewritten parts of surface light logic. Should resemble qrad3 looks a bit more now...

Added "surflightscale", "surflightbouncescale" and "surflightsubdivision" cmdline/worldspawn settings.
Fixed: a face should not be skipped when it has both sky and nodraw texinfo flags.
Fixed some non-windows compilation errors.
This commit is contained in:
MaxED 2018-05-25 14:59:22 +03:00
parent 520ad485a4
commit 07447a633e
13 changed files with 272 additions and 227 deletions

View File

@ -423,8 +423,11 @@ void Q2_SwapBSPFile (q2bsp_t *bsp, qboolean todisk)
//
for (i=0 ; i<bsp->numtexinfo ; i++)
{
for (j=0 ; j<8 ; j++)
bsp->texinfo[i].vecs[0][j] = LittleFloat (bsp->texinfo[i].vecs[0][j]);
for (j=0 ; j<4 ; j++)
{
bsp->texinfo[i].vecs[0][j] = LittleFloat(bsp->texinfo[i].vecs[0][j]);
bsp->texinfo[i].vecs[1][j] = LittleFloat(bsp->texinfo[i].vecs[1][j]);
}
bsp->texinfo[i].flags = LittleLong (bsp->texinfo[i].flags);
bsp->texinfo[i].value = LittleLong (bsp->texinfo[i].value);
bsp->texinfo[i].nexttexinfo = LittleLong (bsp->texinfo[i].nexttexinfo);
@ -2096,6 +2099,7 @@ CopyLump(const dheader_t *header, int lumpnum, void *destptr)
break;
default:
Error("Unsupported BSP version: %d", header->version);
throw; //mxd. Fixes "Uninitialized variable" warning
}
length = header->lumps[lumpnum].filelen;
@ -2110,12 +2114,12 @@ CopyLump(const dheader_t *header, int lumpnum, void *destptr)
dmodel_t *out;
int i, j;
if (length % sizeof(dmodelq1_t))
Error("%s: odd %s lump size", __func__, lumpspec->name);
Error("%s: odd %s lump size", __func__, lumpspec->name);
length /= sizeof(dmodelq1_t);
buffer = *bufferptr = static_cast<byte *>(malloc(length * sizeof(dmodel_t)));
if (!buffer)
Error("%s: allocation of %i bytes failed.", __func__, length);
Error("%s: allocation of %i bytes failed.", __func__, length);
out = (dmodel_t*)buffer;
for (i = 0; i < length; i++)
{
@ -2167,6 +2171,7 @@ Q2_CopyLump(const q2_dheader_t *header, int lumpnum, void *destptr)
break;
default:
Error("Unsupported BSP version: %d", header->version);
throw; //mxd. Fixes "Uninitialized variable" warning
}
length = header->lumps[lumpnum].filelen;
@ -2496,8 +2501,8 @@ AddLump(bspfile_t *bspfile, int lumpnum, const void *data, int count)
q2 = true;
break;
default:
Error("Unsupported BSP version: %d",
LittleLong(bspfile->version));
Error("Unsupported BSP version: %d", LittleLong(bspfile->version));
throw; //mxd. Fixes "Uninitialized variable" warning
}
byte pad[4] = {0};

View File

@ -170,7 +170,7 @@ SetQdirFromPath(const char *basedirname, const char *path)
// Expect mod folder to be above "maps" folder
path_s = path_s.substr(0, pos);
strcpy_s(gamedir, (path_s + PATHSEPERATOR).c_str());
strcpy(gamedir, (path_s + PATHSEPERATOR).c_str());
logprint("gamedir: %s\n", gamedir);
// See if it's the main game data folder (ID1 / baseq2 / data1 etc.)
@ -186,7 +186,7 @@ SetQdirFromPath(const char *basedirname, const char *path)
const std::string checkpath_s = path_s + PATHSEPERATOR + basedir_s;
if (dir_exists(checkpath_s.c_str())) {
// Set basedir
strcpy_s(basedir, (checkpath_s + PATHSEPERATOR).c_str());
strcpy(basedir, (checkpath_s + PATHSEPERATOR).c_str());
logprint("basedir: %s\n", basedir);
break;
}
@ -201,7 +201,7 @@ SetQdirFromPath(const char *basedirname, const char *path)
// qdir is already in path_s
} else {
// Set basedir
strcpy_s(basedir, (path_s + PATHSEPERATOR).c_str());
strcpy(basedir, (path_s + PATHSEPERATOR).c_str());
logprint("basedir: %s\n", basedir);
// qdir shound be 1 level above basedir
@ -210,7 +210,7 @@ SetQdirFromPath(const char *basedirname, const char *path)
}
// Store qdir...
strcpy_s(qdir, (path_s + PATHSEPERATOR).c_str());
strcpy(qdir, (path_s + PATHSEPERATOR).c_str());
logprint("qdir: %s\n", qdir);
}

View File

@ -92,7 +92,9 @@ VecStr(const vec3_t vec)
const char * //mxd
VecStr(const qvec3f vec)
{
return VecStr(vec3_t {vec[0], vec[1], vec[2]});
vec3_t v;
glm_to_vec3_t(vec, v);
return VecStr(v);
}
const char *
@ -112,7 +114,9 @@ VecStrf(const vec3_t vec)
const char * //mxd
VecStrf(const qvec3f vec)
{
return VecStrf(vec3_t{ vec[0], vec[1], vec[2] });
vec3_t v;
glm_to_vec3_t(vec, v);
return VecStrf(v);
}
void ClearBounds(vec3_t mins, vec3_t maxs)
@ -801,7 +805,7 @@ std::vector<qvec3f> GLM_ShrinkPoly(const std::vector<qvec3f> &poly, const float
vector<qvec3f> clipped = poly;
for (const qvec4f &edge : edgeplanes) {
const qvec4f shrunkEdgePlane(edge[0], edge[1], edge[2], edge[3] + 1);
const qvec4f shrunkEdgePlane(edge[0], edge[1], edge[2], edge[3] + amount);
clipped = GLM_ClipPoly(clipped, shrunkEdgePlane).first;
}

View File

@ -37,6 +37,7 @@ bsp2_dface_t *BSP_GetFace(mbsp_t *bsp, int fnum);
int Face_VertexAtIndex(const mbsp_t *bsp, const bsp2_dface_t *f, int v);
void Face_PointAtIndex(const mbsp_t *bsp, const bsp2_dface_t *f, int v, vec3_t point_out);
void Face_Normal(const mbsp_t *bsp, const bsp2_dface_t *f, vec3_t norm); //mxd
plane_t Face_Plane(const mbsp_t *bsp, const bsp2_dface_t *f);
const gtexinfo_t *Face_Texinfo(const mbsp_t *bsp, const bsp2_dface_t *face);
const rgba_miptex_t *Face_Miptex(const mbsp_t *bsp, const bsp2_dface_t *face); //mxd. miptex_t -> rgba_miptex_t

View File

@ -281,8 +281,12 @@ public:
lockable_bool_t bouncestyled;
lockable_vec_t bouncescale, bouncecolorscale;
/* sunlight */
/* Q2 surface lights (mxd) */
lockable_vec_t surflightscale;
lockable_vec_t surflightbouncescale;
lockable_vec_t surflightsubdivision;
/* sunlight */
lockable_vec_t sunlight;
lockable_vec3_t sunlight_color;
lockable_vec_t sun2;
@ -326,20 +330,25 @@ public:
bouncescale {"bouncescale", 1.0f, 0.0f, 100.0f},
bouncecolorscale {"bouncecolorscale", 0.0f, 0.0f, 1.0f},
/* Q2 surface lights (mxd) */
surflightscale { "surflightscale", 0.3f }, // Strange defaults to match arghrad3 look...
surflightbouncescale { "surflightbouncescale", 0.1f },
surflightsubdivision { strings { "surflightsubdivision", "choplight" }, 16.0f, 1.0f, 8192.0f }, // "choplight" - arghrad3 name
/* sun */
sunlight { "sunlight", 0.0f }, /* main sun */
sunlight { "sunlight", 0.0f }, /* main sun */
sunlight_color { "sunlight_color", 255.0f, 255.0f, 255.0f, vec3_transformer_t::NORMALIZE_COLOR_TO_255 },
sun2 { "sun2", 0.0f }, /* second sun */
sun2 { "sun2", 0.0f }, /* second sun */
sun2_color { "sun2_color", 255.0f, 255.0f, 255.0f, vec3_transformer_t::NORMALIZE_COLOR_TO_255 },
sunlight2 { "sunlight2", 0.0f }, /* top sky dome */
sunlight2 { "sunlight2", 0.0f }, /* top sky dome */
sunlight2_color { strings{"sunlight2_color", "sunlight_color2"}, 255.0f, 255.0f, 255.0f, vec3_transformer_t::NORMALIZE_COLOR_TO_255 },
sunlight3 { "sunlight3", 0.0f }, /* bottom sky dome */
sunlight3 { "sunlight3", 0.0f }, /* bottom sky dome */
sunlight3_color { strings{"sunlight3_color", "sunlight_color3"}, 255.0f, 255.0f, 255.0f, vec3_transformer_t::NORMALIZE_COLOR_TO_255 },
sunlight_dirt { "sunlight_dirt", 0.0f },
sunlight2_dirt { "sunlight2_dirt", 0.0f },
sunlight_dirt { "sunlight_dirt", 0.0f },
sunlight2_dirt { "sunlight2_dirt", 0.0f },
sunvec { strings{"sunlight_mangle", "sun_mangle"}, 0.0f, -90.0f, 0.0f, vec3_transformer_t::MANGLE_TO_VEC }, /* defaults to straight down */
sun2vec { "sun2_mangle", 0.0f, -90.0f, 0.0f, vec3_transformer_t::MANGLE_TO_VEC }, /* defaults to straight down */
sun_deviance { "sunlight_penumbra", 0.0f, 0.0f, 180.0f }
sun_deviance { "sunlight_penumbra", 0.0f, 0.0f, 180.0f }
{}
settingsdict_t settings() {
@ -354,6 +363,7 @@ public:
&minlightDirt,
&phongallowed,
&bounce, &bouncestyled, &bouncescale, &bouncecolorscale,
&surflightscale, &surflightbouncescale, &surflightsubdivision, //mxd
&sunlight,
&sunlight_color,
&sun2,
@ -401,7 +411,7 @@ void FixupGlobalSettings(void);
void GetFileSpace(byte **lightdata, byte **colordata, byte **deluxdata, int size);
const modelinfo_t *ModelInfoForModel(const mbsp_t *bsp, int modelnum);
const modelinfo_t *ModelInfoForFace(const mbsp_t *bsp, int facenum);
bool Leaf_HasSky(const mbsp_t *bsp, const mleaf_t *leaf);
//bool Leaf_HasSky(const mbsp_t *bsp, const mleaf_t *leaf); //mxd. Missing definition
int light_main(int argc, const char **argv);
#endif /* __LIGHT_LIGHT_H__ */

View File

@ -179,7 +179,13 @@ public:
}
virtual std::string stringValue() const {
return std::to_string(_value);
//return std::to_string(_value);
//mxd. 1.330000 -> 1.33
std::string str = std::to_string(_value);
const auto lastnonzero = str.find_last_not_of('0');
str.erase(lastnonzero + (lastnonzero == str.find('.') ? 0 : 1), std::string::npos);
return str;
}
lockable_vec_t(std::vector<std::string> names, float v,

View File

@ -24,15 +24,14 @@ See file, 'COPYING', for details.
#include <vector>
typedef struct {
std::vector<qvec3f> poly;
std::vector<qvec4f> poly_edgeplanes;
qvec3f pos;
vec3_t pos;
qvec3f surfnormal;
float areascaler;
std::vector<qvec3f> points;
// Surface light settings...
float value; // Surface light strength
vec3_t color; // Surface color, in [0..1] range
float intensity; // Surface light strength for each point
float totalintensity; // Total surface light strength
vec3_t color; // Surface color
// Estimated visible AABB culling
vec3_t mins;
@ -40,6 +39,7 @@ typedef struct {
} surfacelight_t;
const std::vector<surfacelight_t> &SurfaceLights();
int TotalSurfacelightPoints();
const std::vector<int> &SurfaceLightsForFaceNum(int facenum);
void MakeSurfaceLights (const globalconfig_t &cfg, const mbsp_t *bsp);

View File

@ -295,7 +295,7 @@ static void
SetupSpotlights(const globalconfig_t &cfg)
{
for (light_t &entity : all_lights) {
float targetdist; //mxd
float targetdist = 0.0f; //mxd
if (entity.targetent) {
vec3_t targetOrigin;
EntDict_VectorForKey(*entity.targetent, "origin", targetOrigin);
@ -305,19 +305,17 @@ SetupSpotlights(const globalconfig_t &cfg)
entity.spotlight = true;
}
if (entity.spotlight) {
vec_t angle, angle2;
angle = (entity.spotangle.floatValue() > 0) ? entity.spotangle.floatValue() : 40;
const vec_t angle = (entity.spotangle.floatValue() > 0) ? entity.spotangle.floatValue() : 40;
entity.spotfalloff = -cos(angle / 2 * Q_PI / 180);
angle2 = entity.spotangle2.floatValue();
vec_t angle2 = entity.spotangle2.floatValue();
if (angle2 <= 0 || angle2 > angle)
angle2 = angle;
entity.spotfalloff2 = -cos(angle2 / 2 * Q_PI / 180);
//mxd. Apply autofalloff?
if(entity.falloff.floatValue() == 0 && cfg.spotlightautofalloff.boolValue()) {
float coneradius = targetdist * tan(angle / 2 * Q_PI / 180);
if(targetdist > 0.0f && entity.falloff.floatValue() == 0 && cfg.spotlightautofalloff.boolValue()) {
const float coneradius = targetdist * tan(angle / 2 * Q_PI / 180);
entity.falloff.setFloatValue(targetdist + coneradius);
}
}

View File

@ -51,7 +51,7 @@ LoadPalette(bspdata_t *bspdata)
sprintf(path, "%s%s", gamedir, colormap);
if (FileTime(path) == -1 || !LoadPCX(path, nullptr, &palette, nullptr, nullptr)) {
if (_strcmpi(gamedir, basedir)) {
if (Q_strcasecmp(gamedir, basedir)) {
sprintf(path, "%s%s", basedir, colormap);
if (FileTime(path) == -1 || !LoadPCX(path, nullptr, &palette, nullptr, nullptr)) {
logprint("WARNING: failed to load palette from '%s%s' or '%s%s'.\nUsing built-in palette.\n", gamedir, colormap, basedir, colormap);
@ -303,8 +303,8 @@ LoadTGA(const char *filename, byte **pixels, int *width, int *height)
if (targa_header.image_type == 2) { // Uncompressed, RGB images
for (row = rows - 1; row >= 0; row--) {
pixbuf = targa_rgba + row * columns * 4;
for (column = 0; column<columns; column++) {
unsigned char red, green, blue;
for (column = 0; column < columns; column++) {
unsigned char red, green, blue, alphabyte;
switch (targa_header.pixel_size) {
case 24:
blue = getc(fin);
@ -319,12 +319,15 @@ LoadTGA(const char *filename, byte **pixels, int *width, int *height)
blue = getc(fin);
green = getc(fin);
red = getc(fin);
const unsigned char alphabyte = getc(fin);
alphabyte = getc(fin);
*pixbuf++ = red;
*pixbuf++ = green;
*pixbuf++ = blue;
*pixbuf++ = alphabyte;
break;
default:
logprint("LoadTGA: unsupported pixel size: %i\n", targa_header.pixel_size); //mxd
return false;
}
}
}
@ -349,6 +352,9 @@ LoadTGA(const char *filename, byte **pixels, int *width, int *height)
red = getc(fin);
alphabyte = getc(fin);
break;
default:
logprint("LoadTGA: unsupported pixel size: %i\n", targa_header.pixel_size); //mxd
return false;
}
for (j = 0; j<packetSize; j++) {
@ -388,6 +394,9 @@ LoadTGA(const char *filename, byte **pixels, int *width, int *height)
*pixbuf++ = blue;
*pixbuf++ = alphabyte;
break;
default:
logprint("LoadTGA: unsupported pixel size: %i\n", targa_header.pixel_size); //mxd
return false;
}
column++;
if (column == columns) { // pixel packet run spans across rows
@ -518,7 +527,7 @@ static void AddTextureName(std::map<std::string, std::string> &texturenames, con
return;
char path[4][1024];
static const qboolean is_mod = _strcmpi(gamedir, basedir);
static const qboolean is_mod = Q_strcasecmp(gamedir, basedir);
sprintf(path[0], "%stextures/%s.tga", gamedir, texture); // TGA, in mod dir...
sprintf(path[1], "%stextures/%s.tga", basedir, texture); // TGA, in game dir...

View File

@ -432,32 +432,29 @@ LightWorld(bspdata_t *bspdata, qboolean forcedscale)
info.bsp = bsp;
RunThreadsOn(0, info.all_batches.size(), LightBatchThread, &info);
#else
logprint("--- LightThread ---\n"); //mxd
RunThreadsOn(0, bsp->numfaces, LightThread, bsp);
#endif
if (bouncerequired || isQuake2map) //mxd. Print some extra stats...
logprint("Indirect lights: %d bounce lights, %d surface lights in use.\n", BounceLights().size(), SurfaceLights().size());
logprint("Indirect lights: %i bounce lights, %i surface lights (%i light points) in use.\n", BounceLights().size(), SurfaceLights().size(), TotalSurfacelightPoints());
logprint("Lighting Completed.\n\n");
bsp->lightdatasize = file_p - filebase;
logprint("lightdatasize: %i\n", bsp->lightdatasize);
if (faces_sup)
{
if (faces_sup) {
uint8_t *styles = (uint8_t *)malloc(sizeof(*styles)*4*bsp->numfaces);
int32_t *offsets = (int32_t *)malloc(sizeof(*offsets)*bsp->numfaces);
for (int i = 0; i < bsp->numfaces; i++)
{
for (int i = 0; i < bsp->numfaces; i++) {
offsets[i] = faces_sup[i].lightofs;
for (int j = 0; j < MAXLIGHTMAPS; j++)
styles[i*4+j] = faces_sup[i].styles[j];
}
BSPX_AddLump(bspdata, "LMSTYLE", styles, sizeof(*styles)*4*bsp->numfaces);
BSPX_AddLump(bspdata, "LMOFFSET", offsets, sizeof(*offsets)*bsp->numfaces);
}
else
{ //kill this stuff if its somehow found.
} else {
//kill this stuff if its somehow found.
BSPX_AddLump(bspdata, "LMSTYLE", NULL, 0);
BSPX_AddLump(bspdata, "LMOFFSET", NULL, 0);
}

View File

@ -425,7 +425,7 @@ position_t CalcPointNormal(const mbsp_t *bsp, const bsp2_dface_t *face, const qv
const qvec4f &surfplane = facecache.plane();
const auto &points = facecache.points();
const auto &edgeplanes = facecache.edgePlanes();
const auto &neighbours = facecache.neighbours();
//const auto &neighbours = facecache.neighbours();
// check for degenerate face
if (points.empty() || edgeplanes.empty())
@ -495,7 +495,7 @@ position_t CalcPointNormal(const mbsp_t *bsp, const bsp2_dface_t *face, const qv
if (!edgeplane.first)
continue; // degenerate edge
float planedist = GLM_DistAbovePlane(edgeplane.second, point);
const float planedist = GLM_DistAbovePlane(edgeplane.second, point);
if (planedist < POINT_EQUAL_EPSILON) {
// behind this plane. check whether we're between the endpoints.
@ -1049,33 +1049,6 @@ GetLightContrib(const globalconfig_t &cfg, const light_t *entity, const vec3_t s
*dist_out = dist;
}
static qvec3f LightFace_ClosestOnFace(const surfacelight_t *vpl, const qvec3f pos, const qvec3f normal); //mxd
static float //mxd
SurfaceLight_DistanceScaler(const surfacelight_t *vpl, float dist)
{
dist = qmax(dist, vpl->value / 6.0f); // Clamp away hotspots...
const float scale = vpl->value / (dist*dist);
return scale;
}
void //mxd
GetSurfaceLightContrib(const globalconfig_t &cfg, const surfacelight_t *vpl, const vec3_t surfnorm, const vec3_t surfpoint,
vec3_t color_out, vec3_t surfpointToLightDir_out, float *dist_out)
{
vec3_t lightpos;
glm_to_vec3_t(LightFace_ClosestOnFace(vpl, vec3_t_to_glm(surfpoint), vec3_t_to_glm(surfnorm)), lightpos);
const float dist = GetDir(surfpoint, lightpos, surfpointToLightDir_out);
const float angle = DotProduct(surfpointToLightDir_out, surfnorm);
const float add = (angle < 0.0f ? 0.0f : SurfaceLight_DistanceScaler(vpl, dist) * vpl->areascaler * 8.0f); // Bounce light falloff...
// Write out the final color
VectorScale(vpl->color, add, color_out); // color_out is expected to be in [0..255] range, vpl->color is in [0..1] range.
*dist_out = dist;
}
#define SQR(x) ((x)*(x))
// this is the inverse of GetLightValue
@ -1108,6 +1081,7 @@ GetLightDist(const globalconfig_t &cfg, const light_t *entity, vec_t desiredLigh
break;
default:
Error("Internal error: formula not handled in %s", __func__);
throw; //mxd. Fixes "uninitialized variable" warning
}
}
return fadedist;
@ -1352,23 +1326,29 @@ GetDirectLighting(const globalconfig_t &cfg, raystream_t *rs, const vec3_t origi
//mxd. Surface lights...
for (const surfacelight_t &vpl : SurfaceLights()) {
// Bounce light falloff. Uses light surface center and intensity based on face area
vec3_t surfpointToLightDir;
float surfpointToLightDist;
vec3_t color;
const float surfpointToLightDist = qmax(128.0f, GetDir(surfpointToLightDir, vpl.pos, surfpointToLightDir)); // Clamp away hotspots, also avoid division by 0...
const float angle = DotProduct(surfpointToLightDir, normal);
if (angle <= 0) continue;
GetSurfaceLightContrib(cfg, &vpl, normal, origin, color, surfpointToLightDir, &surfpointToLightDist);
// Exponential falloff
const float add = (vpl.totalintensity / SQR(surfpointToLightDist)) * angle;
if(add <= 0) continue;
// Write out the final color
vec3_t color;
VectorScale(vpl.color, add, color); // color_out is expected to be in [0..255] range, vpl->color is in [0..1] range.
const float dirt = Dirt_GetScaleFactor(cfg, occlusion, nullptr, surfpointToLightDist, /* FIXME: pass */ nullptr);
VectorScale(color, dirt, color);
//VectorScale(color, entity.surflightscale.floatValue(), color); //TODO: entity.surflightscale?
VectorScale(color, cfg.surflightbouncescale.floatValue(), color);
// NOTE: Skip negative lights, which would make no sense to bounce!
if (LightSample_Brightness(color) <= fadegate)
continue;
vec3_t pos;
glm_to_vec3_t(vpl.pos, pos);
if (!TestLight(pos, origin, nullptr))
if (!TestLight(vpl.pos, origin, nullptr))
continue;
result[0] += vec3_t_to_glm(color);
@ -1712,7 +1692,7 @@ LightFace_Min(const mbsp_t *bsp, const bsp2_dface_t *face,
const vec_t *surfpoint = lightsurf->points[i];
if (cfg.addminlight.boolValue() || LightSample_Brightness(sample->color) < entity.light.floatValue()) {
vec3_t surfpointToLightDir;
vec_t surfpointToLightDist = GetDir(surfpoint, *entity.origin.vec3Value(), surfpointToLightDir);
const vec_t surfpointToLightDist = GetDir(surfpoint, *entity.origin.vec3Value(), surfpointToLightDir);
rs->pushRay(i, surfpoint, surfpointToLightDir, surfpointToLightDist, modelinfo);
}
@ -1833,32 +1813,6 @@ LightFace_BounceLightsDebug(const lightsurf_t *lightsurf, lightmapdict_t *lightm
}
}
static qvec3f //mxd
LightFace_ClosestOnFace(const surfacelight_t *vpl, const qvec3f pos, const qvec3f normal)
{
const qvec4f surfplane{ vpl->surfnormal[0], vpl->surfnormal[1], vpl->surfnormal[2], qv::dot(vpl->pos, vpl->surfnormal) };
qvec3f ppos = GLM_ProjectPointOntoPlane(surfplane, pos);
if (!GLM_EdgePlanes_PointInside(vpl->poly_edgeplanes, ppos)) {
ppos = GLM_ClosestPointOnPolyBoundary(vpl->poly, ppos).second;
// Nudge toward the surfacelight center...
vec3_t dir_t, ppos_t, vpl_pos_t;
glm_to_vec3_t(ppos, ppos_t);
glm_to_vec3_t(vpl->pos, vpl_pos_t);
const float dist = GetDir(ppos_t, vpl_pos_t, dir_t);
ppos += vec3_t_to_glm(dir_t) * (dist * 0.1f);
} else {
// Nudge away from target face plane
ppos += normal;
}
// Nudge away from surfacelight plane...
ppos += vpl->surfnormal;
return ppos;
}
// returns color in [0,255]
static inline qvec3f
BounceLight_ColorAtDist(const globalconfig_t &cfg, float area, const qvec3f &bounceLightColor, float dist)
@ -1876,12 +1830,16 @@ BounceLight_ColorAtDist(const globalconfig_t &cfg, float area, const qvec3f &bou
return result;
}
//mxd. Returns color in [0,255]
//mxd. Surface light falloff. Returns color in [0,255]
static qvec3f
SurfaceLight_ColorAtDist(const globalconfig_t &cfg, const surfacelight_t *vpl, float dist)
SurfaceLight_ColorAtDist(const globalconfig_t &cfg, const float intensity, const qvec3f color, const float dist)
{
const float scale = SurfaceLight_DistanceScaler(vpl, dist);
return vec3_t_to_glm(vpl->color) * vpl->areascaler * (76.0f * scale); // Surface light falloff...
// Exponential falloff
const float d = qmax(dist, 16.0f); // Clamp away hotspots, also avoid division by 0...
const float scaledintensity = intensity * cfg.surflightscale.floatValue();
const float scale = (1.0f / (d * d));
return color * scaledintensity * scale;
}
// dir: vpl -> sample point direction
@ -1929,7 +1887,7 @@ GetSurfaceLighting(const globalconfig_t &cfg, const surfacelight_t *vpl, const q
if (dp2 < 0.0f) return result; // vpl behind sample face
// Get light contribution
result = SurfaceLight_ColorAtDist(cfg, vpl , dist);
result = SurfaceLight_ColorAtDist(cfg, vpl->intensity, vec3_t_to_glm(vpl->color), dist);
dp2 = 0.5f + dp2 * 0.5f; // Rescale a bit to brighten the faces nearly-perpendicular to the surface light plane...
// Apply angle scale
@ -1963,11 +1921,11 @@ SurfaceLight_SphereCull(const surfacelight_t *vpl, const lightsurf_t *lightsurf)
return true;
const globalconfig_t &cfg = *lightsurf->cfg;
const qvec3f dir = vec3_t_to_glm(lightsurf->origin) - LightFace_ClosestOnFace(vpl, vec3_t_to_glm(lightsurf->origin), vec3_t_to_glm(lightsurf->snormal)); // vpl -> sample point
const float dist = qv::length(dir) - lightsurf->radius;
const qvec3f dir = vec3_t_to_glm(lightsurf->origin) - vec3_t_to_glm(vpl->pos); // vpl -> sample point
const float dist = qv::length(dir) + lightsurf->radius;
// get light contribution
const qvec3f color = SurfaceLight_ColorAtDist(cfg, vpl, dist);
// Get light contribution
const qvec3f color = SurfaceLight_ColorAtDist(cfg, vpl->totalintensity, vec3_t_to_glm(vpl->color), dist);
return LightSample_Brightness(color) < 0.25f;
}
@ -2054,7 +2012,7 @@ LightFace_Bounce(const mbsp_t *bsp, const bsp2_dface_t *face, const lightsurf_t
VectorAdd(sample->color, indirect, sample->color);
hit = true;
total_bounce_ray_hits++;
++total_bounce_ray_hits;
}
// If this style of this bounce light contributed anything, save.
@ -2178,70 +2136,85 @@ LightFace_SurfaceLight(const lightsurf_t *lightsurf, lightmapdict_t *lightmaps)
continue;
raystream_t *rs = lightsurf->stream;
rs->clearPushedRays();
for (int i = 0; i < lightsurf->numpoints; i++) {
if (lightsurf->occluded[i])
for (int c = 0; c < vpl.points.size(); c++) {
rs->clearPushedRays();
for (int i = 0; i < lightsurf->numpoints; i++) {
if (lightsurf->occluded[i])
continue;
const qvec3f lightsurf_pos = vec3_t_to_glm(lightsurf->points[i]);
const qvec3f lightsurf_normal = vec3_t_to_glm(lightsurf->normals[i]);
// Push 1 unit behind the surflight (fixes darkening near surflight face on neighbouring faces)
qvec3f pos = vpl.points[c] - vpl.surfnormal;
qvec3f dir = lightsurf_pos - pos;
float dist = qv::length(dir);
if (dist == 0.0f)
dir = lightsurf_normal;
else
dir /= dist;
const qvec3f indirect = GetSurfaceLighting(cfg, &vpl, dir, dist, lightsurf_normal);
if (LightSample_Brightness(indirect) < 0.01f) // Each point contributes very little to the final result
continue;
// Push 1 unit in front of the surflight, so embree can properly process it ...
pos = vpl.points[c] + vpl.surfnormal;
dir = lightsurf_pos - pos;
dist = qv::length(dir);
if (dist == 0.0f)
dir = lightsurf_normal;
else
dir /= dist;
vec3_t vplPos, vplDir, vplColor;
glm_to_vec3_t(pos, vplPos);
glm_to_vec3_t(dir, vplDir);
glm_to_vec3_t(indirect, vplColor);
rs->pushRay(i, vplPos, vplDir, dist, lightsurf->modelinfo, vplColor);
}
if (!rs->numPushedRays())
continue;
const qvec3f lightsurf_pos = vec3_t_to_glm(lightsurf->points[i]);
const qvec3f lightsurf_normal = vec3_t_to_glm(lightsurf->normals[i]);
const qvec3f pos = LightFace_ClosestOnFace(&vpl, lightsurf_pos, lightsurf_normal);
qvec3f dir = lightsurf_pos - pos;
const float dist = qv::length(dir);
if (dist == 0.0f)
continue; // FIXME: nudge or something
total_surflight_rays += rs->numPushedRays();
rs->tracePushedRaysOcclusion();
dir /= dist;
const int lightmapstyle = 0;
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, lightmapstyle, lightsurf);
const qvec3f indirect = GetSurfaceLighting(cfg, &vpl, dir, dist, vec3_t_to_glm(lightsurf->normals[i]));
bool hit = false;
const int numrays = rs->numPushedRays();
for (int j = 0; j < numrays; j++) {
if (rs->getPushedRayOccluded(j))
continue;
if (LightSample_Brightness(indirect) < 0.25)
continue;
const int i = rs->getPushedRayPointIndex(j);
vec3_t indirect = { 0 };
rs->getPushedRayColor(j, indirect);
vec3_t vplPos, vplDir, vplColor;
glm_to_vec3_t(pos, vplPos);
glm_to_vec3_t(dir, vplDir);
glm_to_vec3_t(indirect, vplColor);
Q_assert(!std::isnan(indirect[0]));
rs->pushRay(i, vplPos, vplDir, dist, lightsurf->modelinfo, vplColor);
// Use dirt scaling on the surface lighting.
const vec_t dirtscale = Dirt_GetScaleFactor(cfg, lightsurf->occlusion[i], nullptr, 0.0, lightsurf);
VectorScale(indirect, dirtscale, indirect);
lightsample_t *sample = &lightmap->samples[i];
VectorAdd(sample->color, indirect, sample->color);
hit = true;
++total_surflight_ray_hits;
}
// If surface light contributed anything, save.
if (hit)
Lightmap_Save(lightmaps, lightsurf, lightmap, lightmapstyle);
}
if (!rs->numPushedRays())
continue;
total_surflight_rays += rs->numPushedRays();
rs->tracePushedRaysOcclusion();
const int lightmapstyle = 0;
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, lightmapstyle, lightsurf);
bool hit = false;
const int numrays = rs->numPushedRays();
for (int j = 0; j < numrays; j++) {
if (rs->getPushedRayOccluded(j))
continue;
const int i = rs->getPushedRayPointIndex(j);
vec3_t indirect = { 0 };
rs->getPushedRayColor(j, indirect);
Q_assert(!std::isnan(indirect[0]));
// Use dirt scaling on the surface lighting.
const vec_t dirtscale = Dirt_GetScaleFactor(cfg, lightsurf->occlusion[i], nullptr, 0.0, lightsurf);
VectorScale(indirect, dirtscale, indirect);
lightsample_t *sample = &lightmap->samples[i];
VectorAdd(sample->color, indirect, sample->color);
hit = true;
++total_surflight_ray_hits;
}
// If surface light contributed anything, save.
if (hit)
Lightmap_Save(lightmaps, lightsurf, lightmap, lightmapstyle);
}
}
@ -2444,7 +2417,7 @@ GetDirtVector(const globalconfig_t &cfg, int i, vec3_t out)
}
float
DirtAtPoint(const globalconfig_t &cfg, raystream_t *rs, const vec3_t point, const vec3_t normal, const modelinfo_t *modelinfo)
DirtAtPoint(const globalconfig_t &cfg, raystream_t *rs, const vec3_t point, const vec3_t normal, const modelinfo_t *selfshadow)
{
if (!dirt_in_use) {
return 0.0f;
@ -2469,7 +2442,7 @@ DirtAtPoint(const globalconfig_t &cfg, raystream_t *rs, const vec3_t point, cons
vec3_t dir;
TransformToTangentSpace(normal, myUp, myRt, dirtvec, dir);
rs->pushRay(j, point, dir, cfg.dirtDepth.floatValue(), modelinfo);
rs->pushRay(j, point, dir, cfg.dirtDepth.floatValue(), selfshadow);
}
Q_assert(rs->numPushedRays() == numDirtVectors);
@ -2480,7 +2453,7 @@ DirtAtPoint(const globalconfig_t &cfg, raystream_t *rs, const vec3_t point, cons
// accumulate hitdists
for (int j=0; j<numDirtVectors; j++) {
if (rs->getPushedRayHitType(j) == hittype_t::SOLID) {
float dist = rs->getPushedRayHitDist(j);
const float dist = rs->getPushedRayHitDist(j);
occlusion += qmin(cfg.dirtDepth.floatValue(), dist);
} else {
occlusion += cfg.dirtDepth.floatValue();
@ -2488,8 +2461,7 @@ DirtAtPoint(const globalconfig_t &cfg, raystream_t *rs, const vec3_t point, cons
}
// process the results.
vec_t avgHitdist = occlusion / (float)numDirtVectors;
const vec_t avgHitdist = occlusion / numDirtVectors;
occlusion = 1 - (avgHitdist / cfg.dirtDepth.floatValue());
return occlusion;
}
@ -2771,8 +2743,8 @@ IntegerDownsampleImage(const std::vector<qvec4f> &input, int w, int h, int facto
if (factor == 1)
return input;
int outw = w/factor;
int outh = h/factor;
const int outw = w/factor;
const int outh = h/factor;
std::vector<qvec4f> res(static_cast<size_t>(outw * outh));
@ -3100,7 +3072,7 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const
}
const std::vector<qvec4f> output_color = IntegerDownsampleImage(fullres, oversampled_width, oversampled_height, oversample);
const std::vector<qvec4f> output_dir = IntegerDownsampleImage(LightmapNormalsToGLMVector(lightsurf, lm), oversampled_width, oversampled_height, oversample);
const std::vector<qvec4f> output_dir = (lux ? IntegerDownsampleImage(LightmapNormalsToGLMVector(lightsurf, lm), oversampled_width, oversampled_height, oversample) : *new std::vector<qvec4f>); //mxd. Skip when lux isn't needed
// copy from the float buffers to byte buffers in .bsp / .lit / .lux
@ -3108,7 +3080,6 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const
for (int s = 0; s < actual_width; s++) {
const int sampleindex = (t * actual_width) + s;
qvec4f color = output_color.at(sampleindex);
const qvec4f &direction = output_dir.at(sampleindex);
*lit++ = color[0];
*lit++ = color[1];
@ -3135,6 +3106,7 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const
if (lux) {
vec3_t temp;
int v;
const qvec4f &direction = output_dir.at(sampleindex);
temp[0] = qv::dot(qvec3f(direction), vec3_t_to_glm(lightsurf->snormal));
temp[1] = qv::dot(qvec3f(direction), vec3_t_to_glm(lightsurf->tnormal));
temp[2] = qv::dot(qvec3f(direction), vec3_t_to_glm(lightsurf->plane.normal));

View File

@ -43,43 +43,32 @@ using namespace polylib;
mutex surfacelights_lock;
std::vector<surfacelight_t> surfacelights;
std::map<int, std::vector<int>> surfacelightsByFacenum;
int total_surflight_points = 0;
struct make_surface_lights_args_t {
const mbsp_t *bsp;
const globalconfig_t *cfg;
};
static void
AddSurfaceLight(const mbsp_t *bsp, const bsp2_dface_t *face, const float area, const vec3_t pos, const vec3_t surfnormal, const vec3_t color, const float lightvalue)
struct save_winding_points_args_t {
vector<qvec3f> *points;
};
static void
SaveWindingCenterFn(winding_t *w, void *userinfo)
{
surfacelight_t l;
l.poly = GLM_FacePoints(bsp, face);
l.poly_edgeplanes = GLM_MakeInwardFacingEdgePlanes(l.poly);
l.pos = vec3_t_to_glm(pos);
l.areascaler = ((area / 4.0f) / 128.0f);
auto *args = static_cast<save_winding_points_args_t *>(userinfo);
// Store surfacelight settings...
l.value = lightvalue;
VectorCopy(color, l.color);
l.surfnormal = vec3_t_to_glm(surfnormal);
VectorSet(l.mins, 0, 0, 0);
VectorSet(l.maxs, 0, 0, 0);
if (!novisapprox)
EstimateVisibleBoundsAtPoint(pos, l.mins, l.maxs);
unique_lock<mutex> lck{ surfacelights_lock };
surfacelights.push_back(l);
const int index = static_cast<int>(surfacelights.size()) - 1;
surfacelightsByFacenum[Face_GetNum(bsp, face)].push_back(index);
vec3_t center{};
WindingCenter(w, center);
args->points->push_back(vec3_t_to_glm(center));
}
static void *
MakeSurfaceLightsThread(void *arg)
{
const mbsp_t *bsp = static_cast<make_surface_lights_args_t *>(arg)->bsp;
const globalconfig_t &cfg = *static_cast<make_surface_lights_args_t *>(arg)->cfg;
while (true) {
const int i = GetThreadWork();
@ -99,31 +88,80 @@ MakeSurfaceLightsThread(void *arg)
continue;
}
// Grab some info about the face winding
winding_t *winding = WindingFromFace(bsp, face);
const float facearea = WindingArea(winding);
// Create face points...
auto poly = GLM_FacePoints(bsp, face);
const float facearea = GLM_PolyArea(poly);
// Avoid small, or zero-area faces
if (facearea < 1) continue;
if(GLM_PolyArea(poly) < 1) continue;
plane_t faceplane;
WindingPlane(winding, faceplane.normal, &faceplane.dist);
// Create winding...
const int numpoints = poly.size();
winding_t *winding = AllocWinding(numpoints);
for (int c = 0; c < numpoints; c++)
glm_to_vec3_t(poly.at(c), winding->p[c]);
winding->numpoints = numpoints;
RemoveColinearPoints(winding);
vec3_t facemidpoint;
// Get face normal and midpoint...
vec3_t facenormal, facemidpoint;
Face_Normal(bsp, face, facenormal);
WindingCenter(winding, facemidpoint);
VectorMA(facemidpoint, 1, faceplane.normal, facemidpoint); // lift 1 unit
VectorMA(facemidpoint, 1, facenormal, facemidpoint); // Lift 1 unit
// Dice winding...
vector<qvec3f> points;
save_winding_points_args_t args{};
args.points = &points;
DiceWinding(winding, cfg.surflightsubdivision.floatValue(), SaveWindingCenterFn, &args);
winding = nullptr; // DiceWinding frees winding
total_surflight_points += points.size();
// Get texture color
vec3_t blendedcolor = { 0, 0, 0 };
vec3_t texturecolor;
Face_LookupTextureColor(bsp, face, texturecolor);
// Calculate Q2 surface light color and strength
const float scaler = info->value / 256.0f; // Playing by the eye here...
for (int k = 0; k < 3; k++)
blendedcolor[k] = texturecolor[k] * scaler / 255.0f; // Scale by light value, convert to [0..1] range...
// Calculate emit color and intensity...
VectorScale(texturecolor, 1.0f / 255.0f, texturecolor); // Convert to 0..1 range...
VectorScale(texturecolor, info->value, texturecolor); // Scale by light value
AddSurfaceLight(bsp, face, facearea, facemidpoint, faceplane.normal, blendedcolor, info->value);
// Calculate intensity...
float intensity = 0.0f;
for (float c : texturecolor)
if (c > intensity) intensity = c;
if (intensity == 0.0f) continue;
// Normalize color...
if (intensity > 1.0f) VectorScale(texturecolor, 1.0f / intensity, texturecolor);
// Sanity checks...
Q_assert(!points.empty());
// Add surfacelight...
surfacelight_t l;
l.surfnormal = vec3_t_to_glm(facenormal);
l.points = points;
VectorCopy(facemidpoint, l.pos);
// Store surfacelight settings...
l.totalintensity = intensity * facearea;
l.intensity = l.totalintensity / points.size();
VectorCopy(texturecolor, l.color);
// Init bbox...
VectorSet(l.mins, 0, 0, 0);
VectorSet(l.maxs, 0, 0, 0);
if (!novisapprox)
EstimateVisibleBoundsAtPoint(facemidpoint, l.mins, l.maxs);
// Store light...
unique_lock<mutex> lck{ surfacelights_lock };
surfacelights.push_back(l);
const int index = static_cast<int>(surfacelights.size()) - 1;
surfacelightsByFacenum[Face_GetNum(bsp, face)].push_back(index);
}
return nullptr;
@ -134,6 +172,11 @@ const std::vector<surfacelight_t> &SurfaceLights()
return surfacelights;
}
int TotalSurfacelightPoints()
{
return total_surflight_points;
}
// No surflight_debug (yet?), so unused...
const std::vector<int> &SurfaceLightsForFaceNum(int facenum)
{

View File

@ -597,8 +597,8 @@ Embree_TraceInit(const mbsp_t *bsp)
const int contents = Face_Contents(bsp, face); //mxd
//mxd. Skip NODRAW faces
if(bsp->loadversion == Q2_BSPVERSION && (contents & Q2_SURF_NODRAW))
//mxd. Skip NODRAW faces, but not SKY ones (Q2's sky01.wal has both flags set)
if(bsp->loadversion == Q2_BSPVERSION && (contents & Q2_SURF_NODRAW) && !(contents & Q2_SURF_SKY))
continue;
// handle glass