moving more raw pointers to vectors/smart pointers

support for radiosity _surface lights with `_surface_radiosity` key
allow lightstyles for _surface radiosity lights
bounce enabled by default on Q2
remove ValueForKey, use epairs directly
This commit is contained in:
Jonathan 2022-06-21 13:41:51 -04:00
parent df3e6d3988
commit 72914b4724
8 changed files with 104 additions and 61 deletions

View File

@ -163,9 +163,8 @@ static void Base64EncodeTo(const uint8_t *data, size_t in_len, T p)
static std::string serialize_image(const qvec3b *palette, const uint8_t *image, int32_t width, int32_t height)
{
size_t bufsize = 122 + (width * height * 4);
uint8_t *buf = new uint8_t[bufsize];
omemstream s(buf, bufsize, std::ios_base::out | std::ios_base::binary);
std::vector<uint8_t> buf(bufsize);
omemstream s(buf.data(), bufsize, std::ios_base::out | std::ios_base::binary);
s << endianness<std::endian::little>;
@ -213,9 +212,7 @@ static std::string serialize_image(const qvec3b *palette, const uint8_t *image,
std::string str{"data:image/bmp;base64,"};
Base64EncodeTo(buf, bufsize, std::back_inserter(str));
delete[] buf;
Base64EncodeTo(buf.data(), bufsize, std::back_inserter(str));
return str;
}

View File

@ -126,23 +126,25 @@ struct dmiptex_t
// miptex in memory
struct miptex_t
{
private:
static inline std::unique_ptr<uint8_t[]> copy_bytes(const std::unique_ptr<uint8_t[]> &in, size_t size)
{
std::unique_ptr<uint8_t[]> bytes = std::make_unique<uint8_t[]>(size);
memcpy(bytes.get(), in.get(), size);
return bytes;
}
public:
std::string name;
uint32_t width, height;
std::array<std::unique_ptr<uint8_t[]>, MIPLEVELS> data;
static inline uint8_t *copy_bytes(const uint8_t *in, size_t size)
{
uint8_t *bytes = new uint8_t[size];
memcpy(bytes, in, size);
return bytes;
}
miptex_t() = default;
miptex_t(const miptex_t &copy) : name(copy.name), width(copy.width), height(copy.height)
{
for (int32_t i = 0; i < data.size(); i++) {
if (copy.data[i]) {
data[i] = std::unique_ptr<uint8_t[]>(copy_bytes(copy.data[i].get(), (width >> i) * (height >> i)));
data[i] = copy_bytes(copy.data[i], (width >> i) * (height >> i));
}
}
}
@ -155,7 +157,7 @@ struct miptex_t
for (int32_t i = 0; i < data.size(); i++) {
if (copy.data[i]) {
data[i] = std::unique_ptr<uint8_t[]>(copy_bytes(copy.data[i].get(), (width >> i) * (height >> i)));
data[i] = copy_bytes(copy.data[i], (width >> i) * (height >> i));
}
}

View File

@ -128,6 +128,17 @@ std::string TargetnameForLightStyle(int style);
const std::vector<std::unique_ptr<light_t>> &GetLights();
const std::vector<sun_t> &GetSuns();
const std::vector<std::unique_ptr<light_t>> &GetSurfaceLightTemplates();
enum {
// Q1-style surface light copies
SURFLIGHT_Q1 = 0,
// Q2/Q3-style radiosity
SURFLIGHT_RAD = 1
};
bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const light_t &surflight, int surf_type);
const entdict_t *FindEntDictWithKeyPair(const std::string &key, const std::string &value);
void LoadEntities(const settings::worldspawn_keys &cfg, const mbsp_t *bsp);

View File

@ -41,9 +41,11 @@ struct surfacelight_t
// Estimated visible AABB culling
aabb3d bounds;
int32_t style;
};
const std::vector<surfacelight_t> &SurfaceLights();
int TotalSurfacelightPoints();
const std::vector<int> &SurfaceLightsForFaceNum(int facenum);
void MakeSurfaceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp);
void MakeRadiositySurfaceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp);

View File

@ -1071,16 +1071,6 @@ void SetupLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp)
Q_assert(final_lightcount == all_lights.size());
}
const char *ValueForKey(const light_t *ent, const char *key)
{
const auto iter = ent->epairs->find(key);
if (iter != ent->epairs->end()) {
return (*iter).second.c_str();
} else {
return "";
}
}
const entdict_t *FindEntDictWithKeyPair(const std::string &key, const std::string &value)
{
for (const auto &entdict : entdicts) {
@ -1115,6 +1105,11 @@ void WriteEntitiesToString(const settings::worldspawn_keys &cfg, mbsp_t *bsp)
static std::vector<std::unique_ptr<light_t>> surfacelight_templates;
const std::vector<std::unique_ptr<light_t>> &GetSurfaceLightTemplates()
{
return surfacelight_templates;
}
static std::ofstream surflights_dump_file;
static fs::path surflights_dump_filename;
@ -1139,7 +1134,7 @@ static void CreateSurfaceLight(const qvec3d &origin, const qvec3d &normal, const
entity->generated = true;
/* set spotlight vector based on face normal */
if (atoi(ValueForKey(surflight_template, "_surface_spotlight"))) {
if (surflight_template->epairs->get_int("_surface_spotlight")) {
entity->spotlight = true;
entity->spotvec = normal;
}
@ -1161,7 +1156,7 @@ static void CreateSurfaceLightOnFaceSubdivision(const mface_t *face, const model
plane = -plane;
}
vec_t offset = atof(ValueForKey(surflight_template, "_surface_offset"));
vec_t offset = surflight_template->epairs->get_float("_surface_offset");
if (offset == 0)
offset = 2.0;
@ -1184,10 +1179,11 @@ static aabb3d BoundPoly(int numverts, qvec3d *verts)
return bounds;
}
static bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const light_t &surflight)
bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const light_t &surflight, int surf_type)
{
const char *texname = Face_TextureName(bsp, face);
return !Q_strcasecmp(texname, ValueForKey(&surflight, "_surface"));
return !Q_strcasecmp(texname, surflight.epairs->get("_surface")) &&
!!surflight.epairs->get_int("_surface_radiosity") == surf_type;
}
/*
@ -1262,7 +1258,7 @@ static void SubdividePolygon(const mface_t *face, const modelinfo_t *face_modeli
}
for (const auto &surflight : surfacelight_templates) {
if (FaceMatchesSurfaceLightTemplate(bsp, face, *surflight)) {
if (FaceMatchesSurfaceLightTemplate(bsp, face, *surflight, SURFLIGHT_Q1)) {
CreateSurfaceLightOnFaceSubdivision(face, face_modelinfo, surflight.get(), bsp, numverts, verts);
}
}
@ -1339,7 +1335,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp)
}
for (auto &entity : all_lights) {
std::string tex = ValueForKey(entity.get(), "_surface");
std::string tex = entity->epairs->get("_surface");
if (!tex.empty()) {
surfacelight_templates.push_back(DuplicateEntity(*entity)); // makes a copy
@ -1347,7 +1343,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp)
entity->light.setValue(0);
logging::print("Creating surface lights for texture \"{}\" from template at ({})\n", tex,
ValueForKey(entity.get(), "origin"));
entity->epairs->get("origin"));
}
}
@ -1391,7 +1387,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp)
/* Don't bother subdividing if it doesn't match any surface light templates */
if (!std::any_of(surfacelight_templates.begin(), surfacelight_templates.end(),
[&](const auto &surflight) { return FaceMatchesSurfaceLightTemplate(bsp, surf, *surflight); }))
[&](const auto &surflight) { return FaceMatchesSurfaceLightTemplate(bsp, surf, *surflight, SURFLIGHT_Q1); }))
continue;
/* Generate the lights */

View File

@ -465,16 +465,27 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale)
CalculateVertexNormals(&bsp);
const bool isQuake2map = bsp.loadversion->game->id == GAME_QUAKE_II; // mxd
const bool bouncerequired =
options.bounce.value() && (options.debugmode == debugmodes::none || options.debugmode == debugmodes::bounce ||
options.debugmode == debugmodes::bouncelights); // mxd
const bool isQuake2map = bsp.loadversion->game->id == GAME_QUAKE_II; // mxd
if ((bouncerequired || isQuake2map) && !options.nolighting.value()) {
if (isQuake2map)
MakeSurfaceLights(options, &bsp);
if (bouncerequired)
MakeRadiositySurfaceLights(options, &bsp);
if (bouncerequired && !options.nolighting.value()) {
if (bouncerequired) {
MakeBounceLights(options, &bsp);
}
}
if (SurfaceLights().size()) {
logging::print("{} surface lights ({} light points) in use.\n",
SurfaceLights().size(), TotalSurfacelightPoints());
}
if (BounceLights().size()) { // mxd. Print some extra stats...
logging::print("{} bounce lights in use.\n",
BounceLights().size());
}
#if 0
@ -490,11 +501,6 @@ static void LightWorld(bspdata_t *bspdata, bool forcedscale)
});
#endif
if ((bouncerequired || isQuake2map) && !options.nolighting.value()) { // mxd. Print some extra stats...
logging::print("Indirect lights: {} bounce lights, {} surface lights ({} light points) in use.\n",
BounceLights().size(), SurfaceLights().size(), TotalSurfacelightPoints());
}
logging::print("Lighting Completed.\n\n");
// Transfer greyscale lightmap (or color lightmap for Q2/HL) to the bsp and update lightdatasize
@ -937,6 +943,9 @@ int light_main(int argc, const char **argv)
if (!options.bouncescale.isChanged()) {
options.bouncescale.setValue(1.5f);
}
if (!options.bounce.isChanged()) {
options.bounce.setValue(true);
}
}
// check vis approx type

View File

@ -1421,7 +1421,6 @@ std::map<int, qvec3f> GetDirectLighting(
for (const surfacelight_t &vpl : SurfaceLights()) {
// Bounce light falloff. Uses light surface center and intensity based on face area
qvec3d surfpointToLightDir;
// FIXME: this is always 128 because vpl.pos and origin are always equal it seems?
const float surfpointToLightDist =
max(128.0, GetDir(origin, vpl.pos,
surfpointToLightDir)); // Clamp away hotspots, also avoid division by 0...
@ -1445,7 +1444,7 @@ std::map<int, qvec3f> GetDirectLighting(
if (!TestLight(vpl.pos, origin, nullptr).blocked)
continue;
result[0] += color;
result[vpl.style] += color;
}
for (const auto &entity : GetLights()) {
@ -2373,7 +2372,7 @@ LightFace_SurfaceLight(const mbsp_t *bsp, const lightsurf_t *lightsurf, lightmap
total_surflight_rays += rs->numPushedRays();
rs->tracePushedRaysOcclusion(lightsurf->modelinfo);
const int lightmapstyle = 0;
const int lightmapstyle = vpl.style;
lightmap_t *lightmap = Lightmap_ForStyle(lightmaps, lightmapstyle, lightsurf);
bool hit = false;
@ -3459,6 +3458,7 @@ void LightFace(const mbsp_t *bsp, mface_t *face, facesup_t *facesup, const setti
}
/* minlight - Use Q2 surface light, or the greater of global or model minlight. */
// FIXME: _surface 2 support
const mtexinfo_t *texinfo = Face_Texinfo(bsp, face); // mxd. Surface lights...
if (texinfo != nullptr && texinfo->value > 0 && (texinfo->flags.native & Q2_SURF_LIGHT)) {
LightFace_Min(bsp, face, Face_LookupTextureColor(bsp, face), texinfo->value * 2.0f, lightsurf,

View File

@ -46,23 +46,48 @@ std::vector<surfacelight_t> surfacelights;
std::map<int, std::vector<int>> surfacelightsByFacenum;
int total_surflight_points = 0;
// FIXME: support this for Q1 mode too.
static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspawn_keys &cfg, size_t i)
{
const mface_t *face = BSP_GetFace(bsp, i);
// Face casts light?
const mtexinfo_t *info = Face_Texinfo(bsp, face);
if (info == nullptr)
return;
if (!(info->flags.native & Q2_SURF_LIGHT) || info->value == 0) {
if (info->flags.native & Q2_SURF_LIGHT) {
qvec3d wc = winding_t::from_face(bsp, face).center();
logging::print("WARNING: surface light '{}' at [{}] has 0 intensity.\n", Face_TextureName(bsp, face), wc);
int32_t light_value = 0;
bool is_sky = false, is_directional = false;
int32_t style = 0;
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
// first, check if it's a Q2 surface
const mtexinfo_t *info = Face_Texinfo(bsp, face);
if (info == nullptr)
return;
if (!(info->flags.native & Q2_SURF_LIGHT) || info->value == 0) {
if (info->flags.native & Q2_SURF_LIGHT) {
qvec3d wc = winding_t::from_face(bsp, face).center();
logging::print("WARNING: surface light '{}' at [{}] has 0 intensity.\n", Face_TextureName(bsp, face), wc);
}
return;
}
return;
light_value = info->value;
is_sky = (info->flags.native & Q2_SURF_SKY);
}
// check matching templates
if (!light_value) {
for (const auto &surflight : GetSurfaceLightTemplates()) {
if (FaceMatchesSurfaceLightTemplate(bsp, face, *surflight, SURFLIGHT_RAD)) {
light_value = surflight->light.value();
is_sky = surflight->epairs->get_int("_surface_is_sky");
is_directional = !!surflight->epairs->get_int("_surface_spotlight");
style = surflight->epairs->get_int("style");
break;
}
}
}
// Create face points...
auto poly = GLM_FacePoints(bsp, face);
const float facearea = qv::PolyArea(poly.begin(), poly.end());
@ -90,7 +115,7 @@ static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspaw
// Calculate emit color and intensity...
// Handle arghrad sky light settings http://www.bspquakeeditor.com/arghrad/sunlight.html#sky
if (cfg.sky_surface.isChanged() && (info->flags.native & Q2_SURF_SKY)) {
if (cfg.sky_surface.isChanged() && is_sky) {
// FIXME: this only handles the "_sky_surface" "red green blue" format.
// There are other more complex variants we could handle documented in the link above.
// FIXME: we require value to be nonzero, see the check above - not sure if this matches arghrad
@ -99,7 +124,7 @@ static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspaw
texturecolor = qvec3f(Face_LookupTextureColor(bsp, face)) / 255.f;
}
texturecolor *= info->value; // Scale by light value
texturecolor *= light_value; // Scale by light value
// Calculate intensity...
float intensity = qv::max(texturecolor);
@ -117,8 +142,9 @@ static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspaw
// Add surfacelight...
surfacelight_t l;
l.surfnormal = facenormal;
l.omnidirectional = true;//(info->flags.native & Q2_SURF_SKY) ? true : false;
l.omnidirectional = !is_directional;
l.points = points;
l.style = style;
// Init bbox...
l.bounds = EstimateVisibleBoundsAtPoint(facemidpoint);
@ -168,9 +194,9 @@ const std::vector<int> &SurfaceLightsForFaceNum(int facenum)
}
void // Quake 2 surface lights
MakeSurfaceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp)
MakeRadiositySurfaceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp)
{
logging::print("--- MakeSurfaceLights ---\n");
logging::print("--- MakeRadiositySurfaceLights ---\n");
logging::parallel_for(static_cast<size_t>(0), bsp->dfaces.size(), [&](size_t i) { MakeSurfaceLightsThread(bsp, cfg, i); });
}