335 lines
9.7 KiB
C++
335 lines
9.7 KiB
C++
/* Copyright (C) 1996-1997 Id Software, Inc.
|
|
Copyright (C) 2017 Eric Wasylishen
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
See file, 'COPYING', for details.
|
|
*/
|
|
|
|
#include <cstdint>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
|
|
#include <light/light.hh>
|
|
#include <light/bounce.hh>
|
|
#include <light/ltface.hh>
|
|
|
|
#include <common/polylib.hh>
|
|
#include <common/bsputils.hh>
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
#include <set>
|
|
#include <algorithm>
|
|
#include <mutex>
|
|
#include <string>
|
|
|
|
#include <common/qvec.hh>
|
|
|
|
using namespace std;
|
|
using namespace polylib;
|
|
|
|
mutex radlights_lock;
|
|
map<string, qvec3f> texturecolors;
|
|
static std::vector<bouncelight_t> radlights;
|
|
std::map<int, std::vector<int>> radlightsByFacenum;
|
|
|
|
class patch_t {
|
|
public:
|
|
winding_t *w;
|
|
vec3_t center;
|
|
vec3_t samplepoint; // 1 unit above center
|
|
plane_t plane;
|
|
std::map<int, qvec3f> lightByStyle;
|
|
};
|
|
|
|
static unique_ptr<patch_t>
|
|
MakePatch (const globalconfig_t &cfg, winding_t *w)
|
|
{
|
|
unique_ptr<patch_t> p { new patch_t };
|
|
p->w = w;
|
|
|
|
// cache some stuff
|
|
WindingCenter(p->w, p->center);
|
|
WindingPlane(p->w, p->plane.normal, &p->plane.dist);
|
|
|
|
// nudge the cernter point 1 unit off
|
|
VectorMA(p->center, 1.0f, p->plane.normal, p->samplepoint);
|
|
|
|
// calculate direct light
|
|
|
|
raystream_t *rs = MakeRayStream(numDirtVectors);
|
|
p->lightByStyle = GetDirectLighting(cfg, rs, p->samplepoint, p->plane.normal);
|
|
delete rs;
|
|
|
|
return p;
|
|
}
|
|
|
|
struct make_bounce_lights_args_t {
|
|
const mbsp_t *bsp;
|
|
const globalconfig_t *cfg;
|
|
};
|
|
|
|
struct save_winding_args_t {
|
|
vector<unique_ptr<patch_t>> *patches;
|
|
const globalconfig_t *cfg;
|
|
};
|
|
|
|
static void SaveWindingFn(winding_t *w, void *userinfo)
|
|
{
|
|
save_winding_args_t *args = static_cast<save_winding_args_t *>(userinfo);
|
|
args->patches->push_back(MakePatch(*args->cfg, w));
|
|
}
|
|
|
|
static bool
|
|
Face_ShouldBounce(const mbsp_t *bsp, const bsp2_dface_t *face)
|
|
{
|
|
// make bounce light, only if this face is shadow casting
|
|
const modelinfo_t *mi = ModelInfoForFace(bsp, static_cast<int>(face - bsp->dfaces));
|
|
if (!mi || !mi->shadow.boolValue()) {
|
|
return false;
|
|
}
|
|
|
|
if (!Face_IsLightmapped(bsp, face)) {
|
|
return false;
|
|
}
|
|
|
|
const char *texname = Face_TextureName(bsp, face);
|
|
if (!strcmp("skip", texname)) {
|
|
return false;
|
|
}
|
|
|
|
// check for "_bounce" "-1"
|
|
if (extended_texinfo_flags[face->texinfo] & TEX_NOBOUNCE) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Face_LookupTextureColor(const mbsp_t *bsp, const bsp2_dface_t *face, vec3_t color)
|
|
{
|
|
const char *facename = Face_TextureName(bsp, face);
|
|
|
|
if (texturecolors.find(facename) != texturecolors.end()) {
|
|
const qvec3f texcolor = texturecolors.at(facename);
|
|
VectorCopyFromGLM(texcolor, color);
|
|
} else {
|
|
VectorSet(color, 127, 127, 127);
|
|
}
|
|
}
|
|
|
|
static void
|
|
AddBounceLight(const vec3_t pos, const std::map<int, qvec3f> &colorByStyle, const vec3_t surfnormal, vec_t area, const bsp2_dface_t *face, const mbsp_t *bsp);
|
|
|
|
static void *
|
|
MakeBounceLightsThread (void *arg)
|
|
{
|
|
const mbsp_t *bsp = static_cast<make_bounce_lights_args_t *>(arg)->bsp;
|
|
const globalconfig_t &cfg = *static_cast<make_bounce_lights_args_t *>(arg)->cfg;
|
|
|
|
while (1) {
|
|
int i = GetThreadWork();
|
|
if (i == -1)
|
|
break;
|
|
|
|
const bsp2_dface_t *face = BSP_GetFace(bsp, i);
|
|
|
|
if (!Face_ShouldBounce(bsp, face)) {
|
|
continue;
|
|
}
|
|
|
|
vector<unique_ptr<patch_t>> patches;
|
|
|
|
winding_t *winding = WindingFromFace(bsp, face);
|
|
// grab some info about the face winding
|
|
const float facearea = WindingArea(winding);
|
|
|
|
plane_t faceplane;
|
|
WindingPlane(winding, faceplane.normal, &faceplane.dist);
|
|
|
|
vec3_t facemidpoint;
|
|
WindingCenter(winding, facemidpoint);
|
|
VectorMA(facemidpoint, 1, faceplane.normal, facemidpoint); // lift 1 unit
|
|
|
|
save_winding_args_t args;
|
|
args.patches = &patches;
|
|
args.cfg = &cfg;
|
|
|
|
DiceWinding(winding, 64.0f, SaveWindingFn, &args);
|
|
winding = nullptr; // DiceWinding frees winding
|
|
|
|
// average them, area weighted
|
|
map<int, qvec3f> sum;
|
|
float totalarea = 0;
|
|
|
|
for (const auto &patch : patches) {
|
|
const float patcharea = WindingArea(patch->w);
|
|
totalarea += patcharea;
|
|
|
|
for (const auto &styleColor : patch->lightByStyle) {
|
|
sum[styleColor.first] = sum[styleColor.first] + (styleColor.second * patcharea);
|
|
}
|
|
// printf(" %f %f %f\n", patch->directlight[0], patch->directlight[1], patch->directlight[2]);
|
|
}
|
|
|
|
for (auto &styleColor : sum) {
|
|
styleColor.second *= (1.0/totalarea);
|
|
}
|
|
|
|
// avoid small, or zero-area patches ("sum" would be nan)
|
|
if (totalarea < 1) {
|
|
continue;
|
|
}
|
|
|
|
vec3_t texturecolor;
|
|
Face_LookupTextureColor(bsp, face, texturecolor);
|
|
|
|
// lerp between gray and the texture color according to `bouncecolorscale`
|
|
const vec3_t gray = {127, 127, 127};
|
|
vec3_t blendedcolor = {0, 0, 0};
|
|
VectorMA(blendedcolor, cfg.bouncecolorscale.floatValue(), texturecolor, blendedcolor);
|
|
VectorMA(blendedcolor, 1-cfg.bouncecolorscale.floatValue(), gray, blendedcolor);
|
|
|
|
// final colors to emit
|
|
map<int, qvec3f> emitcolors;
|
|
for (const auto &styleColor : sum) {
|
|
qvec3f emitcolor(0);
|
|
for (int k=0; k<3; k++) {
|
|
emitcolor[k] = (styleColor.second[k] / 255.0f) * (blendedcolor[k] / 255.0f);
|
|
}
|
|
emitcolors[styleColor.first] = emitcolor;
|
|
}
|
|
|
|
AddBounceLight(facemidpoint, emitcolors, faceplane.normal, facearea, face, bsp);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
AddBounceLight(const vec3_t pos, const std::map<int, qvec3f> &colorByStyle, const vec3_t surfnormal, vec_t area, const bsp2_dface_t *face, const mbsp_t *bsp)
|
|
{
|
|
for (const auto &styleColor : colorByStyle) {
|
|
Q_assert(styleColor.second[0] >= 0);
|
|
Q_assert(styleColor.second[1] >= 0);
|
|
Q_assert(styleColor.second[2] >= 0);
|
|
}
|
|
Q_assert(area > 0);
|
|
|
|
bouncelight_t l;
|
|
l.poly = GLM_FacePoints(bsp, face);
|
|
l.poly_edgeplanes = GLM_MakeInwardFacingEdgePlanes(l.poly);
|
|
l.pos = vec3_t_to_glm(pos);
|
|
l.colorByStyle = colorByStyle;
|
|
|
|
qvec3f componentwiseMaxColor(0);
|
|
for (const auto &styleColor : colorByStyle) {
|
|
for (int i=0; i<3; i++) {
|
|
if (styleColor.second[i] > componentwiseMaxColor[i]) {
|
|
componentwiseMaxColor[i] = styleColor.second[i];
|
|
}
|
|
}
|
|
}
|
|
l.componentwiseMaxColor = componentwiseMaxColor;
|
|
l.surfnormal = vec3_t_to_glm(surfnormal);
|
|
l.area = area;
|
|
VectorSet(l.mins, 0, 0, 0);
|
|
VectorSet(l.maxs, 0, 0, 0);
|
|
|
|
if (!novisapprox) {
|
|
EstimateVisibleBoundsAtPoint(pos, l.mins, l.maxs);
|
|
}
|
|
|
|
unique_lock<mutex> lck { radlights_lock };
|
|
radlights.push_back(l);
|
|
|
|
const int lastBounceLightIndex = static_cast<int>(radlights.size()) - 1;
|
|
radlightsByFacenum[Face_GetNum(bsp, face)].push_back(lastBounceLightIndex);
|
|
}
|
|
|
|
const std::vector<bouncelight_t> &BounceLights()
|
|
{
|
|
return radlights;
|
|
}
|
|
|
|
const std::vector<int> &BounceLightsForFaceNum(int facenum)
|
|
{
|
|
const auto &vec = radlightsByFacenum.find(facenum);
|
|
if (vec != radlightsByFacenum.end()) {
|
|
return vec->second;
|
|
}
|
|
|
|
static std::vector<int> empty;
|
|
return empty;
|
|
}
|
|
|
|
// Returns color in [0,255]
|
|
static qvec3f
|
|
Texture_AvgColor (const mbsp_t *bsp, const rgba_miptex_t *miptex)
|
|
{
|
|
qvec4f color(0);
|
|
|
|
if (!bsp->rgbatexdatasize)
|
|
return color;
|
|
|
|
//mxd
|
|
const auto miptexsize = miptex->width * miptex->height;
|
|
for(auto i = 0; i < miptexsize; i++)
|
|
{
|
|
auto c = Texture_GetColor(miptex, i);
|
|
if(c[3] < 128) continue; // Skip transparent pixels...
|
|
color += c;
|
|
}
|
|
|
|
return color / miptexsize;
|
|
}
|
|
|
|
void
|
|
MakeTextureColors (const mbsp_t *bsp)
|
|
{
|
|
logprint("--- MakeTextureColors ---\n");
|
|
|
|
if (!bsp->rgbatexdatasize) //mxd. dtexdata -> drgbatexdata
|
|
return;
|
|
|
|
for (int i = 0; i<bsp->drgbatexdata->nummiptex; i++) {
|
|
const int ofs = bsp->drgbatexdata->dataofs[i];
|
|
if (ofs < 0)
|
|
continue;
|
|
|
|
const rgba_miptex_t *miptex = (rgba_miptex_t *)((byte *)bsp->drgbatexdata + ofs);
|
|
const string name { miptex->name };
|
|
const qvec3f color = Texture_AvgColor(bsp, miptex);
|
|
|
|
// printf("%s has color %s\n", name.c_str(), VecStr(color));
|
|
texturecolors[name] = color;
|
|
}
|
|
}
|
|
|
|
void
|
|
MakeBounceLights (const globalconfig_t &cfg, const mbsp_t *bsp)
|
|
{
|
|
logprint("--- MakeBounceLights ---\n");
|
|
|
|
make_bounce_lights_args_t args { bsp, &cfg }; //mxd. https://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.html
|
|
|
|
RunThreadsOn(0, bsp->numfaces, MakeBounceLightsThread, (void *)&args);
|
|
}
|