diff --git a/common/bspfile.cc b/common/bspfile.cc index d5488efe..a2d72f0f 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -1725,13 +1725,15 @@ const bspversion_t bspver_qbism{Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II bool surfflags_t::needs_write() const { return no_dirt || no_shadow || no_bounce || no_minlight || no_expand || no_phong || light_ignore || !surflight_rescale || phong_angle || - phong_angle_concave || phong_group || minlight || !qv::emptyExact(minlight_color) || light_alpha || maxlight || lightcolorscale != 1.0; + phong_angle_concave || phong_group || minlight || !qv::emptyExact(minlight_color) || light_alpha || maxlight || lightcolorscale != 1.0 || + surflight_group; } static auto as_tuple(const surfflags_t &flags) { return std::tie(flags.native, flags.is_nodraw, flags.is_hintskip, flags.is_hint, flags.no_dirt, flags.no_shadow, flags.no_bounce, flags.no_minlight, flags.no_expand, - flags.no_phong, flags.light_ignore, flags.surflight_rescale, flags.phong_angle, flags.phong_angle_concave, flags.phong_group, flags.minlight, flags.minlight_color, flags.light_alpha, flags.maxlight, flags.lightcolorscale); + flags.no_phong, flags.light_ignore, flags.surflight_rescale, flags.phong_angle, flags.phong_angle_concave, flags.phong_group, flags.minlight, flags.minlight_color, flags.light_alpha, flags.maxlight, flags.lightcolorscale, + flags.surflight_group); } bool surfflags_t::operator<(const surfflags_t &other) const diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 5a737a8b..a480d2f6 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -216,6 +216,9 @@ struct surfflags_t // light color scale vec_t lightcolorscale = 1.0; + // surface light group + int32_t surflight_group; + bool needs_write() const; public: diff --git a/include/common/qvec.hh b/include/common/qvec.hh index f522fd1f..13b8b470 100644 --- a/include/common/qvec.hh +++ b/include/common/qvec.hh @@ -534,11 +534,11 @@ template [[nodiscard]] inline bool gate(const qvec &v, T epsilon) { for (size_t i = 0; i < N; i++) { - if (gate(v[i], epsilon)) { - return true; + if (!gate(v[i], epsilon)) { + return false; } } - return false; + return true; } template diff --git a/include/light/entities.hh b/include/light/entities.hh index c5de03f8..6b9cfac2 100644 --- a/include/light/entities.hh +++ b/include/light/entities.hh @@ -98,6 +98,7 @@ public: settings::setting_string project_texture; settings::setting_string suntexture; settings::setting_bool nostaticlight; + settings::setting_int32 surflight_group; light_t(); @@ -130,7 +131,7 @@ std::vector &GetRadLights(); const std::vector> &GetSurfaceLightTemplates(); -bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const light_t &surflight, int surf_type); +bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const modelinfo_t *face_modelinfo, const light_t &surflight, int surf_type); const entdict_t *FindEntDictWithKeyPair(const std::string &key, const std::string &value); diff --git a/include/light/surflight.hh b/include/light/surflight.hh index c04104e6..b09afbd0 100644 --- a/include/light/surflight.hh +++ b/include/light/surflight.hh @@ -51,6 +51,6 @@ struct surfacelight_t void ResetSurflight(); std::vector &GetSurfaceLights(); -std::optional> IsSurfaceLitFace(const mbsp_t *bsp, const mface_t *face); +std::optional> IsSurfaceLitFace(const mbsp_t *bsp, const mface_t *face); const std::vector &SurfaceLightsForFaceNum(int facenum); void MakeRadiositySurfaceLights(const settings::worldspawn_keys &cfg, const mbsp_t *bsp); diff --git a/light/entities.cc b/light/entities.cc index 637680dd..9e790281 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -104,7 +104,8 @@ light_t::light_t() : projangle{this, "project_mangle", 20, 0, 0}, project_texture{this, "project_texture", ""}, suntexture{this, "suntexture", ""}, - nostaticlight{this, "nostaticlight", false} + nostaticlight{this, "nostaticlight", false}, + surflight_group{this, "surflight_group", 0} {} std::string light_t::classname() const @@ -1242,7 +1243,7 @@ static aabb3d BoundPoly(int numverts, qvec3d *verts) return bounds; } -bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const light_t &surflight, int surf_type) +bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, const modelinfo_t *face_modelinfo, const light_t &surflight, int surf_type) { const char *texname = Face_TextureName(bsp, face); @@ -1254,7 +1255,19 @@ bool FaceMatchesSurfaceLightTemplate(const mbsp_t *bsp, const mface_t *face, con radiosity_type = light_options.surflight_radiosity.value(); } - return !Q_strcasecmp(texname, surflight.epairs->get("_surface")) && radiosity_type == surf_type; + if (radiosity_type != surf_type) { + return false; + } + + const surfflags_t &extended_flags = extended_texinfo_flags[face->texinfo]; + + if (extended_flags.surflight_group) { + if (surflight.surflight_group.value() && surflight.surflight_group.value() != extended_flags.surflight_group) { + return false; + } + } + + return !Q_strcasecmp(texname, surflight.epairs->get("_surface")); } /* @@ -1329,7 +1342,7 @@ static void SubdividePolygon(const mface_t *face, const modelinfo_t *face_modeli } for (const auto &surflight : surfacelight_templates) { - if (FaceMatchesSurfaceLightTemplate(bsp, face, *surflight, SURFLIGHT_Q1)) { + if (FaceMatchesSurfaceLightTemplate(bsp, face, face_modelinfo, *surflight, SURFLIGHT_Q1)) { CreateSurfaceLightOnFaceSubdivision(face, face_modelinfo, surflight.get(), bsp, numverts, verts); } } @@ -1470,7 +1483,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, SURFLIGHT_Q1); + return FaceMatchesSurfaceLightTemplate(bsp, surf, face_modelinfo, *surflight, SURFLIGHT_Q1); })) continue; diff --git a/light/light.cc b/light/light.cc index d16f0f75..340ff305 100644 --- a/light/light.cc +++ b/light/light.cc @@ -1113,6 +1113,9 @@ static void LoadExtendedTexinfoFlags(const fs::path &sourcefilename, const mbsp_ if (val.contains("lightcolorscale")) { flags.lightcolorscale = val.at("lightcolorscale").get(); } + if (val.contains("surflight_group")) { + flags.surflight_group = val.at("surflight_group").get(); + } } } @@ -1607,6 +1610,9 @@ int light_main(int argc, const char **argv) if (!light_options.bounce.isChanged()) { light_options.bounce.setValue(true, settings::source::GAME_TARGET); } + if (!light_options.surflight_radiosity.isChanged()) { + light_options.surflight_radiosity.setValue(SURFLIGHT_RAD, settings::source::GAME_TARGET); + } } // check vis approx type diff --git a/light/ltface.cc b/light/ltface.cc index 91aa5734..b959d28d 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -3081,7 +3081,7 @@ void DirectLightFace(const mbsp_t *bsp, lightsurf_t &lightsurf, const settings:: if (auto value = IsSurfaceLitFace(bsp, face)) { minlight = std::get<0>(value.value()) * 64.0f; - minlight_color = Face_LookupTextureColor(bsp, face); + minlight_color = std::get<2>(value.value()); LightFace_Min(bsp, face, minlight_color, minlight, &lightsurf, lightmaps, std::get<1>(value.value())); } diff --git a/light/surflight.cc b/light/surflight.cc index 0436022f..642e317b 100644 --- a/light/surflight.cc +++ b/light/surflight.cc @@ -147,20 +147,20 @@ static void MakeSurfaceLight(const mbsp_t *bsp, const settings::worldspawn_keys surfacelightsByFacenum[Face_GetNum(bsp, face)].push_back(index); } -std::optional> IsSurfaceLitFace(const mbsp_t *bsp, const mface_t *face) +std::optional> IsSurfaceLitFace(const mbsp_t *bsp, const mface_t *face) { 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 && (info->flags.native & Q2_SURF_LIGHT) && info->value > 0) { - return std::make_tuple(info->value, 0); + return std::make_tuple(info->value, 0, Face_LookupTextureColor(bsp, face)); } } for (const auto &surflight : GetSurfaceLightTemplates()) { - if (FaceMatchesSurfaceLightTemplate(bsp, face, *surflight, SURFLIGHT_RAD)) { - return std::make_tuple(surflight->light.value(), surflight->style.value()); + if (FaceMatchesSurfaceLightTemplate(bsp, face, ModelInfoForFace(bsp, face - bsp->dfaces.data()), *surflight, SURFLIGHT_RAD)) { + return std::make_tuple(surflight->light.value(), surflight->style.value(), surflight->color.isChanged() ? qvec3b(surflight->color.value() * 255) : Face_LookupTextureColor(bsp, face)); } } @@ -193,7 +193,7 @@ static void MakeSurfaceLightsThread(const mbsp_t *bsp, const settings::worldspaw // check matching templates for (const auto &surflight : GetSurfaceLightTemplates()) { - if (FaceMatchesSurfaceLightTemplate(bsp, face, *surflight, SURFLIGHT_RAD)) { + if (FaceMatchesSurfaceLightTemplate(bsp, face, ModelInfoForFace(bsp, face - bsp->dfaces.data()), *surflight, SURFLIGHT_RAD)) { std::optional texture_color; if (surflight->color.isChanged()) { diff --git a/qbsp/map.cc b/qbsp/map.cc index fc630d30..8dc59d7b 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -643,6 +643,15 @@ static surfflags_t SurfFlagsForEntity(const maptexinfo_t &texinfo, const mapenti } } + if (entity.epairs.has("_surflight_group")) + { + const int32_t surflight_group = entity.epairs.get_int("_surflight_group"); + + if (surflight_group) { + flags.surflight_group = surflight_group; + } + } + // handle "_mincolor" { qvec3d mincolor{}; diff --git a/qbsp/writebsp.cc b/qbsp/writebsp.cc index f70517ea..91f91b86 100644 --- a/qbsp/writebsp.cc +++ b/qbsp/writebsp.cc @@ -398,6 +398,9 @@ static void WriteExtendedTexinfoFlags(void) if (tx.flags.lightcolorscale != 1.0) { t["lightcolorscale"] = tx.flags.lightcolorscale; } + if (tx.flags.surflight_group) { + t["surflight_group"] = tx.flags.surflight_group; + } texinfofile[std::to_string(*tx.outputnum)].swap(t); }