add support for a region brush (code needs to be cleaned up a bit though)

allow _surflight_style on brush models
This commit is contained in:
Jonathan 2023-04-28 13:31:50 -04:00
parent e9539eff82
commit 39a54abfc6
10 changed files with 199 additions and 57 deletions

View File

@ -1803,7 +1803,7 @@ 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.surflight_color, flags.surflight_minlight_scale, flags.phong_angle, flags.phong_angle_concave, flags.phong_group, flags.minlight,
flags.surflight_rescale, flags.surflight_style, flags.surflight_color, flags.surflight_minlight_scale, 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,
flags.world_units_per_luxel, flags.object_channel_mask);
}

View File

@ -192,6 +192,9 @@ struct surfflags_t
// normal if false, use a more natural angle falloff of 0% at 90 degrees
bool surflight_rescale = true;
// override surface lighting style
std::optional<int32_t> surflight_style;
// override the textures' surflight color
std::optional<qvec3b> surflight_color;

View File

@ -305,6 +305,7 @@ public:
setting_scalar surflightskyscale;
// "choplight" - arghrad3 name
setting_scalar surflightsubdivision;
setting_scalar surflight_minlight_scale;
/* sunlight */
/* sun_light, sun_color, sun_angle for http://www.bspquakeeditor.com/arghrad/ compatibility */

View File

@ -250,6 +250,9 @@ struct mapdata_t
// whether we had attempted loading texture stuff
bool textures_loaded = false;
// map compile region
std::optional<mapbrush_t> region = std::nullopt;
// helpers
const std::string &miptexTextureName(int mt) const;
const std::string &texinfoTextureName(int texinfo) const;
@ -284,6 +287,10 @@ struct texture_def_issues_t : logging::stat_tracker_t
stat &num_translucent = register_stat(
"faces have TRANSLUCENT flag swapped to DETAIL; TRANSLUCENT is an internal flag. Use -verbose to display affected faces.",
false, true);
stat &num_repaired = register_stat(
"faces have invalid texture projections and were repaired. Use -verbose to display affected faces."
, false, true);
};
bool ParseEntity(parser_t &parser, mapentity_t &entity, texture_def_issues_t &issues_stats);

View File

@ -112,7 +112,7 @@ light_t::light_t()
suntexture{this, "suntexture", ""},
nostaticlight{this, "nostaticlight", false},
surflight_group{this, "surflight_group", 0},
surflight_minlight_scale{this, "surflight_minlight_scale", 64.f},
surflight_minlight_scale{this, "surflight_minlight_scale", 1.f},
light_channel_mask{this, "light_channel_mask", CHANNEL_MASK_DEFAULT},
shadow_channel_mask{this, "shadow_channel_mask", CHANNEL_MASK_DEFAULT},
nonudge{this, "nonudge", false}
@ -339,9 +339,12 @@ static void CheckEntityFields(const mbsp_t *bsp, const settings::worldspawn_keys
}
if (!entity->surflight_minlight_scale.is_changed()) {
if (bsp->loadversion->game->id != GAME_QUAKE_II) {
// TODO: also use 1.0 for Q2?
entity->surflight_minlight_scale.set_value(1.0f, settings::source::DEFAULT);
if (cfg.surflight_minlight_scale.is_changed()) {
entity->surflight_minlight_scale.set_value(cfg.surflight_minlight_scale.value(), settings::source::DEFAULT);
} else if (bsp->loadversion->game->id == GAME_QUAKE_II) {
// this default value mimicks the fullbright-ish nature of emissive surfaces
// in Q2.
entity->surflight_minlight_scale.set_value(64.0f, settings::source::DEFAULT);
}
}
}

View File

@ -187,6 +187,7 @@ worldspawn_keys::worldspawn_keys()
surflightscale{this, "surflightscale", 1.0, &worldspawn_group},
surflightskyscale{this, "surflightskyscale", 1.0, &worldspawn_group},
surflightsubdivision{this, {"surflightsubdivision", "choplight"}, 16.0, 1.0, 8192.0, &worldspawn_group},
surflight_minlight_scale{this, "surflight_minlight_scale", 1.0f, 0.f, 510.f, &worldspawn_group },
sunlight{this, {"sunlight", "sun_light"}, 0.0, &worldspawn_group},
sunlight_color{this, {"sunlight_color", "sun_color"}, 255.0, 255.0, 255.0, &worldspawn_group},
sun2{this, "sun2", 0.0, &worldspawn_group},
@ -1119,6 +1120,9 @@ static void LoadExtendedTexinfoFlags(const fs::path &sourcefilename, const mbsp_
if (val.contains("surflight_rescale")) {
flags.surflight_rescale = val.at("surflight_rescale").get<bool>();
}
if (val.contains("surflight_style")) {
flags.surflight_style = val.at("surflight_style").get<int32_t>();
}
if (val.contains("surflight_color")) {
flags.surflight_color = val.at("surflight_color").get<qvec3b>();
}

View File

@ -138,7 +138,11 @@ static void MakeSurfaceLight(const mbsp_t *bsp, const settings::worldspawn_keys
l.surfnormal = facenormal;
l.omnidirectional = !is_directional;
l.points = std::move(points);
l.style = style;
if (extended_flags.surflight_style) {
l.style = extended_flags.surflight_style.value();
} else {
l.style = style;
}
l.rescale = extended_flags.surflight_rescale;
l.minlight_scale = extended_flags.surflight_minlight_scale;

View File

@ -712,6 +712,15 @@ static void Brush_LoadEntity(mapentity_t &dst, mapentity_t &src, hull_index_t hu
for (auto &mapbrush : src.mapbrushes) {
clock();
if (map.region && (map.is_world_entity(src) || IsWorldBrushEntity(src) || IsNonRemoveWorldBrushEntity(src))) {
if (map.region->bounds.disjoint(mapbrush.bounds)) {
//stats.regioned_brushes++;
//it = entity.mapbrushes.erase(it);
//logging::print("removed broosh\n");
continue;
}
}
contentflags_t contents = mapbrush.contents;
if (qbsp_options.nodetail.value()) {

View File

@ -340,6 +340,51 @@ struct texdef_etp_t
bool tx2 = false;
};
/*
================
CalculateBrushBounds
================
*/
inline void CalculateBrushBounds(mapbrush_t &ob)
{
ob.bounds = {};
for (size_t i = 0; i < ob.faces.size(); i++) {
const auto &plane = ob.faces[i].get_plane();
std::optional<winding_t> w = BaseWindingForPlane<winding_t>(plane);
for (size_t j = 0; j < ob.faces.size() && w; j++) {
if (i == j) {
continue;
}
if (ob.faces[j].bevel) {
continue;
}
const auto &plane = map.get_plane(ob.faces[j].planenum ^ 1);
w = w->clip_front(plane, 0); // CLIP_EPSILON);
}
if (w) {
// calc bounds before moving from w
for (auto &p : w.value()) {
ob.bounds += p;
}
ob.faces[i].winding = std::move(w.value());
}
}
for (size_t i = 0; i < 3; i++) {
if (ob.bounds.mins()[0] <= -qbsp_options.worldextent.value() ||
ob.bounds.maxs()[0] >= qbsp_options.worldextent.value()) {
logging::print("WARNING: {}: brush bounds out of range\n", ob.line);
}
if (ob.bounds.mins()[0] >= qbsp_options.worldextent.value() ||
ob.bounds.maxs()[0] <= -qbsp_options.worldextent.value()) {
logging::print("WARNING: {}: no visible sides on brush\n", ob.line);
}
}
}
using texdef_brush_primitives_t = qmat<vec_t, 2, 3>;
static texdef_valve_t TexDef_BSPToValve(const texvecf &in_vecs);
@ -623,11 +668,21 @@ static surfflags_t SurfFlagsForEntity(
qvec3d color;
// FIXME: get_color, to match settings
if (entity.epairs.has("_surflight_color") && entity.epairs.get_vector("_surflight_color", color) == 3) {
flags.surflight_color = qvec3b{ (uint8_t) (color[0] * 255), (uint8_t) (color[1] * 255), (uint8_t) (color[2] * 255) };
if (color[0] <= 1 && color[1] <= 1 && color[2] <= 1) {
flags.surflight_color = qvec3b{ (uint8_t) (color[0] * 255), (uint8_t) (color[1] * 255), (uint8_t) (color[2] * 255) };
} else {
flags.surflight_color = qvec3b{ (uint8_t) (color[0]), (uint8_t) (color[1]), (uint8_t) (color[2]) };
}
}
}
if (entity.epairs.has("_surflight_style") && entity.epairs.get_int("_surflight_style") != 0)
flags.surflight_style = entity.epairs.get_int("_surflight_style");
if (entity.epairs.has("_surflight_minlight_scale"))
flags.surflight_minlight_scale = entity.epairs.get_float("_surflight_minlight_scale");
// Paril: inherit _surflight_minlight_scale from worldspawn if unset
else if (!entity.epairs.has("_surflight_minlight_scale") && map.world_entity().epairs.has("_surflight_minlight_scale"))
flags.surflight_minlight_scale = map.world_entity().epairs.get_float("_surflight_minlight_scale");
// "_minlight_exclude", "_minlight_exclude2", "_minlight_exclude3"...
for (int i = 0; i <= 9; i++) {
@ -1863,11 +1918,15 @@ inline bool IsValidTextureProjection(const mapface_t &mapface, const maptexinfo_
return IsValidTextureProjection(mapface.get_plane().get_normal(), tx->vecs.row(0).xyz(), tx->vecs.row(1).xyz());
}
static void ValidateTextureProjection(mapface_t &mapface, maptexinfo_t *tx)
static void ValidateTextureProjection(mapface_t &mapface, maptexinfo_t *tx, texture_def_issues_t &issue_stats)
{
if (!IsValidTextureProjection(mapface, tx)) {
logging::print("WARNING: {}: repairing invalid texture projection (\"{}\" near {} {} {})\n", mapface.line,
mapface.texname, (int)mapface.planepts[0][0], (int)mapface.planepts[0][1], (int)mapface.planepts[0][2]);
if (qbsp_options.verbose.value()) {
logging::print("WARNING: {}: repairing invalid texture projection (\"{}\" near {} {} {})\n", mapface.line,
mapface.texname, (int)mapface.planepts[0][0], (int)mapface.planepts[0][1], (int)mapface.planepts[0][2]);
} else {
issue_stats.num_repaired++;
}
// Reset texturing to sensible defaults
const std::array<vec_t, 2> shift{0, 0};
@ -1912,7 +1971,7 @@ static std::optional<mapface_t> ParseBrushFace(
}
}
ValidateTextureProjection(face, &tx);
ValidateTextureProjection(face, &tx, issue_stats);
tx.flags = SurfFlagsForEntity(tx, entity, face.contents);
face.texinfo = FindTexinfo(tx);
@ -2459,6 +2518,74 @@ static mapbrush_t ParseBrush(parser_t &parser, mapentity_t &entity, texture_def_
brush.faces.emplace_back(std::move(face.value()));
}
// check for region brush
if (string_iequals(brush.faces[0].texname, "region")) {
if (!map.is_world_entity(entity)) {
FError("Region brush at {} isn't part of the world entity", parser.token);
}
CalculateBrushBounds(brush);
// construct region brushes
for (auto &new_brush_side : brush.faces) {
// copy the brush
mapbrush_t new_brush;
new_brush.contents = brush.contents;
new_brush.line = brush.line;
for (auto &side : brush.faces) {
// if it's the side we're extruding, increase its dist
if (side.planenum == new_brush_side.planenum) {
mapface_t new_side;
new_side.texinfo = side.texinfo;
new_side.contents = side.contents;
new_side.raw_info = side.raw_info;
new_side.texname = side.texname;
new_side.planenum = side.planenum;
new_side.planenum = map.add_or_find_plane({ new_side.get_plane().get_normal(), new_side.get_plane().get_dist() + 16.f });
new_brush.faces.emplace_back(std::move(new_side));
// the inverted side is special
} else if (side.get_plane().get_normal() == -new_brush_side.get_plane().get_normal()) {
// add the other side
mapface_t flipped_side;
flipped_side.texinfo = side.texinfo;
flipped_side.contents = side.contents;
flipped_side.raw_info = side.raw_info;
flipped_side.texname = side.texname;
flipped_side.planenum = map.add_or_find_plane({ -new_brush_side.get_plane().get_normal(), -new_brush_side.get_plane().get_dist() });
new_brush.faces.emplace_back(std::move(flipped_side));
} else {
mapface_t new_side;
new_side.texinfo = side.texinfo;
new_side.contents = side.contents;
new_side.raw_info = side.raw_info;
new_side.texname = side.texname;
new_side.planenum = side.planenum;
new_brush.faces.emplace_back(std::move(new_side));
}
}
// add
new_brush.contents = Brush_GetContents(entity, new_brush);
map.world_entity().mapbrushes.push_back(std::move(new_brush));
}
if (!map.region) {
map.region = std::move(brush);
} else {
FError("Multiple region brushes detected; newest at {}", parser.token);
}
return brush;
}
// mark hintskip faces
if (is_hint) {
int32_t num_hintskip = 0;
@ -2532,7 +2659,11 @@ bool ParseEntity(parser_t &parser, mapentity_t &entity, texture_def_issues_t &is
}
} while (parser.token != "}");
} else {
entity.mapbrushes.emplace_back(ParseBrush(parser, entity, issue_stats));
auto brush = ParseBrush(parser, entity, issue_stats);
if (brush.faces.size()) {
entity.mapbrushes.push_back(std::move(brush));
}
}
} else {
ParseEpair(parser, entity);
@ -2818,51 +2949,6 @@ bool IsNonRemoveWorldBrushEntity(const mapentity_t &entity)
return false;
}
/*
================
CalculateBrushBounds
================
*/
inline void CalculateBrushBounds(mapbrush_t &ob)
{
ob.bounds = {};
for (size_t i = 0; i < ob.faces.size(); i++) {
const auto &plane = ob.faces[i].get_plane();
std::optional<winding_t> w = BaseWindingForPlane<winding_t>(plane);
for (size_t j = 0; j < ob.faces.size() && w; j++) {
if (i == j) {
continue;
}
if (ob.faces[j].bevel) {
continue;
}
const auto &plane = map.get_plane(ob.faces[j].planenum ^ 1);
w = w->clip_front(plane, 0); // CLIP_EPSILON);
}
if (w) {
// calc bounds before moving from w
for (auto &p : w.value()) {
ob.bounds += p;
}
ob.faces[i].winding = std::move(w.value());
}
}
for (size_t i = 0; i < 3; i++) {
if (ob.bounds.mins()[0] <= -qbsp_options.worldextent.value() ||
ob.bounds.maxs()[0] >= qbsp_options.worldextent.value()) {
logging::print("WARNING: {}: brush bounds out of range\n", ob.line);
}
if (ob.bounds.mins()[0] >= qbsp_options.worldextent.value() ||
ob.bounds.maxs()[0] <= -qbsp_options.worldextent.value()) {
logging::print("WARNING: {}: no visible sides on brush\n", ob.line);
}
}
}
inline bool MapBrush_IsHint(const mapbrush_t &brush)
{
for (auto &f : brush.faces) {
@ -2941,6 +3027,11 @@ void ProcessMapBrushes()
map.total_brushes = 0;
if (map.region) {
CalculateBrushBounds(map.region.value());
logging::print("NOTE: map region detected! only compiling map within {}\n", map.region.value().bounds);
}
{
logging::percent_clock clock(map.entities.size());
@ -3082,6 +3173,23 @@ void ProcessMapBrushes()
logging::print(logging::flag::STAT, "\n");
// remove ents in region
if (map.region) {
for (auto it = map.entities.begin(); it != map.entities.end(); ) {
auto &entity = *it;
if (!entity.mapbrushes.size()) {
if (map.region && !map.region->bounds.containsPoint(entity.origin)) {
it = map.entities.erase(it);
continue;
}
}
++it;
}
}
if (qbsp_options.debugexpand.is_changed()) {
aabb3d hull;

View File

@ -384,6 +384,9 @@ static void WriteExtendedTexinfoFlags(void)
if (tx.flags.surflight_rescale == false) {
t["surflight_rescale"] = tx.flags.surflight_rescale;
}
if (tx.flags.surflight_style.has_value()) {
t["surflight_style"] = tx.flags.surflight_style.value();
}
if (tx.flags.surflight_color.has_value()) {
t["surflight_color"] = tx.flags.surflight_color.value();
}