qbsp: shrink node_t from 280 to 208 bytes
- contentflags_t from 80 to 8 bytes
This commit is contained in:
parent
3d419853d3
commit
aa3b8479b4
|
|
@ -495,7 +495,7 @@ static void FindLeaf(const mbsp_t *bsp, const qvec3d &pos)
|
||||||
const mleaf_t *leaf = BSP_FindLeafAtPoint(bsp, &bsp->dmodels[0], pos);
|
const mleaf_t *leaf = BSP_FindLeafAtPoint(bsp, &bsp->dmodels[0], pos);
|
||||||
|
|
||||||
fmt::print("leaf {}: contents {} ({})\n", (leaf - bsp->dleafs.data()), leaf->contents,
|
fmt::print("leaf {}: contents {} ({})\n", (leaf - bsp->dleafs.data()), leaf->contents,
|
||||||
contentflags_t{leaf->contents}.to_string(bsp->loadversion->game));
|
bsp->loadversion->game->create_contents_from_native(leaf->contents).to_string(bsp->loadversion->game));
|
||||||
}
|
}
|
||||||
|
|
||||||
// map file stuff
|
// map file stuff
|
||||||
|
|
|
||||||
1113
common/bspfile.cc
1113
common/bspfile.cc
File diff suppressed because it is too large
Load Diff
|
|
@ -186,7 +186,9 @@ struct compiled_brush_t
|
||||||
side.valve.shift[0], side.valve.axis.at(1, 0), side.valve.axis.at(1, 1), side.valve.axis.at(1, 2),
|
side.valve.shift[0], side.valve.axis.at(1, 0), side.valve.axis.at(1, 1), side.valve.axis.at(1, 2),
|
||||||
side.valve.shift[1], 0.0, side.valve.scale[0], side.valve.scale[1]);
|
side.valve.shift[1], 0.0, side.valve.scale[0], side.valve.scale[1]);
|
||||||
|
|
||||||
if (bsp->loadversion->game->id == GAME_QUAKE_II && (contents.native || side.flags.native || side.value)) {
|
int native = bsp->loadversion->game->contents_to_native(contents);
|
||||||
|
|
||||||
|
if (bsp->loadversion->game->id == GAME_QUAKE_II && (native || side.flags.native || side.value)) {
|
||||||
wal_metadata_t *meta = nullptr;
|
wal_metadata_t *meta = nullptr;
|
||||||
|
|
||||||
auto it = wals.find(side.texture_name);
|
auto it = wals.find(side.texture_name);
|
||||||
|
|
@ -207,9 +209,9 @@ struct compiled_brush_t
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!meta || !((meta->contents & ~(Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW)) ==
|
if (!meta || !((meta->contents & ~(Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW)) ==
|
||||||
(contents.native & ~(Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW)) &&
|
(native & ~(Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW)) &&
|
||||||
meta->flags == side.flags.native && meta->value == side.value)) {
|
meta->flags == side.flags.native && meta->value == side.value)) {
|
||||||
ewt::print(stream, " {} {} {}", contents.native, side.flags.native, side.value);
|
ewt::print(stream, " {} {} {}", native, side.flags.native, side.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -549,8 +551,10 @@ static const char *DefaultOriginTexture(const mbsp_t *bsp)
|
||||||
|
|
||||||
static const char *DefaultTextureForContents(const mbsp_t *bsp, const contentflags_t &contents)
|
static const char *DefaultTextureForContents(const mbsp_t *bsp, const contentflags_t &contents)
|
||||||
{
|
{
|
||||||
|
int native = bsp->loadversion->game->contents_to_native(contents);
|
||||||
|
|
||||||
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
||||||
int visible = contents.native & Q2_ALL_VISIBLE_CONTENTS;
|
int visible = native & Q2_ALL_VISIBLE_CONTENTS;
|
||||||
|
|
||||||
if (visible & Q2_CONTENTS_WATER) {
|
if (visible & Q2_CONTENTS_WATER) {
|
||||||
return "e1u1/water4";
|
return "e1u1/water4";
|
||||||
|
|
@ -558,17 +562,17 @@ static const char *DefaultTextureForContents(const mbsp_t *bsp, const contentfla
|
||||||
return "e1u1/sewer1";
|
return "e1u1/sewer1";
|
||||||
} else if (visible & Q2_CONTENTS_LAVA) {
|
} else if (visible & Q2_CONTENTS_LAVA) {
|
||||||
return "e1u1/brlava";
|
return "e1u1/brlava";
|
||||||
} else if (contents.native & Q2_CONTENTS_PLAYERCLIP) {
|
} else if (native & Q2_CONTENTS_PLAYERCLIP) {
|
||||||
return "e1u1/clip";
|
return "e1u1/clip";
|
||||||
} else if (contents.native & Q2_CONTENTS_MONSTERCLIP) {
|
} else if (native & Q2_CONTENTS_MONSTERCLIP) {
|
||||||
return "e1u1/clip_mon";
|
return "e1u1/clip_mon";
|
||||||
} else if (contents.native & Q2_CONTENTS_AREAPORTAL) {
|
} else if (native & Q2_CONTENTS_AREAPORTAL) {
|
||||||
return "e1u1/trigger";
|
return "e1u1/trigger";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "e1u1/skip";
|
return "e1u1/skip";
|
||||||
} else {
|
} else {
|
||||||
switch (contents.native) {
|
switch (native) {
|
||||||
case CONTENTS_WATER: return "*waterskip";
|
case CONTENTS_WATER: return "*waterskip";
|
||||||
case CONTENTS_SLIME: return "*slimeskip";
|
case CONTENTS_SLIME: return "*slimeskip";
|
||||||
case CONTENTS_LAVA: return "*lavaskip";
|
case CONTENTS_LAVA: return "*lavaskip";
|
||||||
|
|
@ -585,9 +589,10 @@ static void OverrideTextureForContents(
|
||||||
compiled_brush_side_t &side, const mbsp_t *bsp, const char *name, const contentflags_t &contents)
|
compiled_brush_side_t &side, const mbsp_t *bsp, const char *name, const contentflags_t &contents)
|
||||||
{
|
{
|
||||||
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
if (bsp->loadversion->game->id == GAME_QUAKE_II) {
|
||||||
|
int native = bsp->loadversion->game->contents_to_native(contents);
|
||||||
|
|
||||||
if (contents.native & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP)) {
|
if (native & (Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP)) {
|
||||||
if (!(contents.native & Q2_CONTENTS_PLAYERCLIP)) {
|
if (!(native & Q2_CONTENTS_PLAYERCLIP)) {
|
||||||
side.texture_name = "e1u1/clip_mon";
|
side.texture_name = "e1u1/clip_mon";
|
||||||
} else {
|
} else {
|
||||||
side.texture_name = "e1u1/clip";
|
side.texture_name = "e1u1/clip";
|
||||||
|
|
@ -808,7 +813,9 @@ static std::vector<compiled_brush_t> DecompileLeafTaskGeometryOnly(
|
||||||
compiled_brush_t brush;
|
compiled_brush_t brush;
|
||||||
brush.source = task.brush;
|
brush.source = task.brush;
|
||||||
brush.brush_offset = brush_offset;
|
brush.brush_offset = brush_offset;
|
||||||
brush.contents = {task.brush ? task.brush->contents : task.leaf ? task.leaf->contents : task.contents.value()};
|
brush.contents = bsp->loadversion->game->create_contents_from_native(
|
||||||
|
task.brush ? task.brush->contents : task.leaf ? task.leaf->contents : task.contents.value()
|
||||||
|
);
|
||||||
|
|
||||||
brush.sides.reserve(task.allPlanes.size());
|
brush.sides.reserve(task.allPlanes.size());
|
||||||
|
|
||||||
|
|
@ -868,7 +875,7 @@ static std::vector<compiled_brush_t> DecompileLeafTask(
|
||||||
compiled_brush_t brush;
|
compiled_brush_t brush;
|
||||||
brush.source = task.brush;
|
brush.source = task.brush;
|
||||||
brush.brush_offset = brush_offset;
|
brush.brush_offset = brush_offset;
|
||||||
brush.contents = {task.brush ? task.brush->contents : task.leaf ? task.leaf->contents : task.contents.value()};
|
brush.contents = bsp->loadversion->game->create_contents_from_native(task.brush ? task.brush->contents : task.leaf ? task.leaf->contents : task.contents.value());
|
||||||
|
|
||||||
for (auto &finalSide : finalBrush.sides) {
|
for (auto &finalSide : finalBrush.sides) {
|
||||||
compiled_brush_side_t &side = brush.sides.emplace_back();
|
compiled_brush_side_t &side = brush.sides.emplace_back();
|
||||||
|
|
@ -876,7 +883,7 @@ static std::vector<compiled_brush_t> DecompileLeafTask(
|
||||||
side.winding = std::move(finalSide.winding);
|
side.winding = std::move(finalSide.winding);
|
||||||
side.source = finalSide.plane.source;
|
side.source = finalSide.plane.source;
|
||||||
|
|
||||||
if (brush.contents.native == 0) {
|
if (bsp->loadversion->game->contents_to_native(brush.contents) == 0) {
|
||||||
// hint brush
|
// hint brush
|
||||||
side.texture_name = "e1u1/hint";
|
side.texture_name = "e1u1/hint";
|
||||||
|
|
||||||
|
|
@ -959,7 +966,9 @@ static std::vector<compiled_brush_t> DecompileLeafTaskLeafVisualization(
|
||||||
compiled_brush_t brush;
|
compiled_brush_t brush;
|
||||||
brush.source = task.brush;
|
brush.source = task.brush;
|
||||||
brush.brush_offset = brush_offset;
|
brush.brush_offset = brush_offset;
|
||||||
brush.contents = task.leaf ? contentflags_t{task.leaf->contents} : contentflags_t{task.contents.value()};
|
brush.contents = bsp->loadversion->game->create_contents_from_native(
|
||||||
|
task.leaf ? task.leaf->contents : task.contents.value()
|
||||||
|
);
|
||||||
|
|
||||||
for (auto &finalSide : finalBrush.sides) {
|
for (auto &finalSide : finalBrush.sides) {
|
||||||
compiled_brush_side_t &side = brush.sides.emplace_back();
|
compiled_brush_side_t &side = brush.sides.emplace_back();
|
||||||
|
|
@ -1324,7 +1333,7 @@ static void DecompileEntity(
|
||||||
std::vector<compiled_brush_t> &brushlist = compiledBrushes.emplace_back();
|
std::vector<compiled_brush_t> &brushlist = compiledBrushes.emplace_back();
|
||||||
compiled_brush_t &brush = brushlist.emplace_back();
|
compiled_brush_t &brush = brushlist.emplace_back();
|
||||||
brush.brush_offset = brush_offset;
|
brush.brush_offset = brush_offset;
|
||||||
brush.contents = {Q2_CONTENTS_ORIGIN};
|
brush.contents = contentflags_t::make(EWT_INVISCONTENTS_ORIGIN);
|
||||||
|
|
||||||
constexpr qplane3d planes[] = {
|
constexpr qplane3d planes[] = {
|
||||||
{{-1, 0, 0}, 8},
|
{{-1, 0, 0}, 8},
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ std::optional<texture> load_wal(
|
||||||
tex.meta.name = name;
|
tex.meta.name = name;
|
||||||
tex.meta.width = tex.width = mt.width;
|
tex.meta.width = tex.width = mt.width;
|
||||||
tex.meta.height = tex.height = mt.height;
|
tex.meta.height = tex.height = mt.height;
|
||||||
tex.meta.contents = {mt.contents};
|
tex.meta.contents_native = mt.contents;
|
||||||
tex.meta.flags = {mt.flags};
|
tex.meta.flags = {mt.flags};
|
||||||
tex.meta.value = mt.value;
|
tex.meta.value = mt.value;
|
||||||
tex.meta.animation = mt.animname.data();
|
tex.meta.animation = mt.animname.data();
|
||||||
|
|
@ -402,17 +402,20 @@ std::optional<texture_meta> load_wal_json_meta(
|
||||||
auto &contents = json["contents"];
|
auto &contents = json["contents"];
|
||||||
|
|
||||||
if (contents.is_number_integer()) {
|
if (contents.is_number_integer()) {
|
||||||
meta.contents.native = contents.get<int32_t>();
|
meta.contents_native = contents.get<int32_t>();
|
||||||
} else if (contents.is_string()) {
|
} else if (contents.is_string()) {
|
||||||
meta.contents.native = game->contents_from_string(contents.get<std::string>());
|
meta.contents_native =
|
||||||
|
game->contents_from_string(contents.get<std::string>());
|
||||||
} else if (contents.is_array()) {
|
} else if (contents.is_array()) {
|
||||||
|
int native = 0;
|
||||||
for (auto &content : contents) {
|
for (auto &content : contents) {
|
||||||
if (content.is_number_integer()) {
|
if (content.is_number_integer()) {
|
||||||
meta.contents.native |= content.get<int32_t>();
|
native |= content.get<int32_t>();
|
||||||
} else if (content.is_string()) {
|
} else if (content.is_string()) {
|
||||||
meta.contents.native |= game->contents_from_string(content.get<std::string>());
|
native |= game->contents_from_string(content.get<std::string>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
meta.contents_native = native;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ void brush_side_t::parse_extended_texinfo(parser_t &parser)
|
||||||
if (parser.parse_token(PARSE_OPTIONAL)) {
|
if (parser.parse_token(PARSE_OPTIONAL)) {
|
||||||
texinfo_quake2_t q2_info;
|
texinfo_quake2_t q2_info;
|
||||||
|
|
||||||
q2_info.contents = {std::stoi(parser.token)};
|
q2_info.contents = std::stoi(parser.token);
|
||||||
|
|
||||||
if (parser.parse_token(PARSE_OPTIONAL)) {
|
if (parser.parse_token(PARSE_OPTIONAL)) {
|
||||||
q2_info.flags.native = std::stoi(parser.token);
|
q2_info.flags.native = std::stoi(parser.token);
|
||||||
|
|
@ -482,7 +482,7 @@ parse_error:
|
||||||
void brush_side_t::write_extended_info(std::ostream &stream)
|
void brush_side_t::write_extended_info(std::ostream &stream)
|
||||||
{
|
{
|
||||||
if (extended_info) {
|
if (extended_info) {
|
||||||
ewt::print(stream, " {} {} {}", extended_info->contents.native, extended_info->flags.native, extended_info->value);
|
ewt::print(stream, " {} {} {}", extended_info->contents, extended_info->flags.native, extended_info->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
#include <common/bitflags.hh>
|
||||||
#include <common/fs.hh>
|
#include <common/fs.hh>
|
||||||
#include <common/qvec.hh>
|
#include <common/qvec.hh>
|
||||||
#include <common/aabb.hh>
|
#include <common/aabb.hh>
|
||||||
|
|
@ -47,26 +48,94 @@ struct lump_t
|
||||||
void stream_read(std::istream &s);
|
void stream_read(std::istream &s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using contents_int_t = uint64_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Superset of Q1- and Q2- features, plus EWT extensions.
|
||||||
|
*/
|
||||||
|
enum contents_t : contents_int_t {
|
||||||
|
EWT_VISCONTENTS_EMPTY = 0,
|
||||||
|
EWT_VISCONTENTS_SOLID = nth_bit<uint64_t>(0), // an eye is never valid in a solid
|
||||||
|
EWT_VISCONTENTS_SKY = nth_bit<uint64_t>(1),
|
||||||
|
EWT_VISCONTENTS_DETAIL_WALL = nth_bit<uint64_t>(2),
|
||||||
|
EWT_VISCONTENTS_WINDOW = nth_bit<uint64_t>(3), // translucent, but not watery (detail fence)
|
||||||
|
EWT_VISCONTENTS_AUX = nth_bit<uint64_t>(4),
|
||||||
|
EWT_VISCONTENTS_LAVA = nth_bit<uint64_t>(5),
|
||||||
|
EWT_VISCONTENTS_SLIME = nth_bit<uint64_t>(6),
|
||||||
|
EWT_VISCONTENTS_WATER = nth_bit<uint64_t>(7),
|
||||||
|
EWT_VISCONTENTS_MIST = nth_bit<uint64_t>(8),
|
||||||
|
|
||||||
|
EWT_LAST_VISIBLE_CONTENTS_INDEX = 8,
|
||||||
|
EWT_LAST_VISIBLE_CONTENTS = EWT_VISCONTENTS_MIST,
|
||||||
|
|
||||||
|
EWT_ALL_LIQUIDS =
|
||||||
|
EWT_VISCONTENTS_LAVA |
|
||||||
|
EWT_VISCONTENTS_SLIME |
|
||||||
|
EWT_VISCONTENTS_WATER,
|
||||||
|
|
||||||
|
EWT_ALL_VISIBLE_CONTENTS =
|
||||||
|
EWT_VISCONTENTS_SOLID |
|
||||||
|
EWT_VISCONTENTS_SKY |
|
||||||
|
EWT_VISCONTENTS_DETAIL_WALL |
|
||||||
|
EWT_VISCONTENTS_WINDOW |
|
||||||
|
EWT_VISCONTENTS_AUX |
|
||||||
|
EWT_VISCONTENTS_LAVA |
|
||||||
|
EWT_VISCONTENTS_SLIME |
|
||||||
|
EWT_VISCONTENTS_WATER |
|
||||||
|
EWT_VISCONTENTS_MIST,
|
||||||
|
|
||||||
|
EWT_INVISCONTENTS_ORIGIN = nth_bit<uint64_t>(9), // removed before bsping an entity
|
||||||
|
// Q1 clip
|
||||||
|
EWT_INVISCONTENTS_PLAYERCLIP = nth_bit<uint64_t>(10),
|
||||||
|
EWT_INVISCONTENTS_MONSTERCLIP = nth_bit<uint64_t>(11),
|
||||||
|
EWT_INVISCONTENTS_AREAPORTAL = nth_bit<uint64_t>(12),
|
||||||
|
EWT_INVISCONTENTS_ILLUSIONARY_VISBLOCKER = nth_bit<uint64_t>(13),
|
||||||
|
|
||||||
|
EWT_ALL_INVISCONTENTS =
|
||||||
|
EWT_INVISCONTENTS_ORIGIN |
|
||||||
|
EWT_INVISCONTENTS_PLAYERCLIP |
|
||||||
|
EWT_INVISCONTENTS_MONSTERCLIP |
|
||||||
|
EWT_INVISCONTENTS_AREAPORTAL |
|
||||||
|
EWT_INVISCONTENTS_ILLUSIONARY_VISBLOCKER,
|
||||||
|
|
||||||
|
EWT_CFLAG_DETAIL = nth_bit<uint64_t>(14), // brushes to be added after vis leafs
|
||||||
|
EWT_CFLAG_MIRROR_INSIDE = nth_bit<uint64_t>(15),
|
||||||
|
EWT_CFLAG_MIRROR_INSIDE_SET = nth_bit<uint64_t>(16),
|
||||||
|
EWT_CFLAG_SUPPRESS_CLIPPING_SAME_TYPE = nth_bit<uint64_t>(17),
|
||||||
|
|
||||||
|
EWT_CFLAG_CURRENT_0 = nth_bit<uint64_t>(18),
|
||||||
|
EWT_CFLAG_CURRENT_90 = nth_bit<uint64_t>(19),
|
||||||
|
EWT_CFLAG_CURRENT_180 = nth_bit<uint64_t>(20),
|
||||||
|
EWT_CFLAG_CURRENT_270 = nth_bit<uint64_t>(21),
|
||||||
|
EWT_CFLAG_CURRENT_UP = nth_bit<uint64_t>(22),
|
||||||
|
EWT_CFLAG_CURRENT_DOWN = nth_bit<uint64_t>(23),
|
||||||
|
EWT_CFLAG_TRANSLUCENT = nth_bit<uint64_t>(24), // auto set if any surface has trans,
|
||||||
|
EWT_CFLAG_LADDER = nth_bit<uint64_t>(25),
|
||||||
|
EWT_CFLAG_MONSTER = nth_bit<uint64_t>(26), // disallowed in maps, only for gamecode use
|
||||||
|
EWT_CFLAG_DEADMONSTER = nth_bit<uint64_t>(27), // disallowed in maps, only for gamecode use
|
||||||
|
|
||||||
|
// unused Q2 contents bits - just present here so we can roundtrip all 32-bit Q2 contents
|
||||||
|
EWT_CFLAG_Q2_UNUSED_7 = nth_bit<uint64_t>(28),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_8 = nth_bit<uint64_t>(29),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_9 = nth_bit<uint64_t>(30),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_10 = nth_bit<uint64_t>(31),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_11 = nth_bit<uint64_t>(32),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_12 = nth_bit<uint64_t>(33),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_13 = nth_bit<uint64_t>(34),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_14 = nth_bit<uint64_t>(35),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_30 = nth_bit<uint64_t>(36),
|
||||||
|
EWT_CFLAG_Q2_UNUSED_31 = nth_bit<uint64_t>(37)
|
||||||
|
};
|
||||||
|
|
||||||
struct gamedef_t;
|
struct gamedef_t;
|
||||||
|
|
||||||
struct contentflags_t
|
struct contentflags_t
|
||||||
{
|
{
|
||||||
// native flags value; what's written to the BSP basically
|
contents_t flags;
|
||||||
int32_t native = 0;
|
|
||||||
|
|
||||||
// extra data supplied by the game
|
static contentflags_t make(contents_int_t f) {
|
||||||
std::any game_data;
|
return contentflags_t{.flags = static_cast<contents_t>(f)};
|
||||||
|
}
|
||||||
// the value set directly from `_mirrorinside` on the brush, if available.
|
|
||||||
// don't check this directly, use `is_mirror_inside` to allow the game to decide.
|
|
||||||
std::optional<bool> mirror_inside = std::nullopt;
|
|
||||||
|
|
||||||
// don't clip the same content type. mostly intended for CONTENTS_DETAIL_ILLUSIONARY.
|
|
||||||
// don't check this directly, use `will_clip_same_type` to allow the game to decide.
|
|
||||||
std::optional<bool> clips_same_type = std::nullopt;
|
|
||||||
|
|
||||||
// always blocks vis, even if it normally wouldn't
|
|
||||||
bool illusionary_visblocker = false;
|
|
||||||
|
|
||||||
bool equals(const gamedef_t *game, const contentflags_t &other) const;
|
bool equals(const gamedef_t *game, const contentflags_t &other) const;
|
||||||
|
|
||||||
|
|
@ -77,10 +146,22 @@ struct contentflags_t
|
||||||
bool is_detail_fence(const gamedef_t *game) const;
|
bool is_detail_fence(const gamedef_t *game) const;
|
||||||
bool is_detail_illusionary(const gamedef_t *game) const;
|
bool is_detail_illusionary(const gamedef_t *game) const;
|
||||||
|
|
||||||
|
std::optional<bool> mirror_inside() const {
|
||||||
|
if (flags & EWT_CFLAG_MIRROR_INSIDE_SET) {
|
||||||
|
return {(flags & EWT_CFLAG_MIRROR_INSIDE) != 0};
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
contentflags_t &set_mirrored(const std::optional<bool> &mirror_inside_value);
|
contentflags_t &set_mirrored(const std::optional<bool> &mirror_inside_value);
|
||||||
|
|
||||||
inline bool will_clip_same_type(const gamedef_t *game) const { return will_clip_same_type(game, *this); }
|
inline bool will_clip_same_type(const gamedef_t *game) const { return will_clip_same_type(game, *this); }
|
||||||
bool will_clip_same_type(const gamedef_t *game, const contentflags_t &other) const;
|
bool will_clip_same_type(const gamedef_t *game, const contentflags_t &other) const;
|
||||||
|
std::optional<bool> clips_same_type() const {
|
||||||
|
if (flags & EWT_CFLAG_SUPPRESS_CLIPPING_SAME_TYPE) {
|
||||||
|
return {false};
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
contentflags_t &set_clips_same_type(const std::optional<bool> &clips_same_type_value);
|
contentflags_t &set_clips_same_type(const std::optional<bool> &clips_same_type_value);
|
||||||
|
|
||||||
bool is_empty(const gamedef_t *game) const;
|
bool is_empty(const gamedef_t *game) const;
|
||||||
|
|
@ -112,6 +193,26 @@ struct contentflags_t
|
||||||
bool chops(const gamedef_t *game) const;
|
bool chops(const gamedef_t *game) const;
|
||||||
|
|
||||||
std::string to_string(const gamedef_t *game) const;
|
std::string to_string(const gamedef_t *game) const;
|
||||||
|
|
||||||
|
// returns the bit index (starting from 0) of the strongest visible content type
|
||||||
|
// set, or -1 if no visible content bits are set (i.e. EWT_VISCONTENTS_EMPTY)
|
||||||
|
int visible_contents_index() const {
|
||||||
|
for (uint32_t index = 0; nth_bit(index) <= EWT_LAST_VISIBLE_CONTENTS; ++index) {
|
||||||
|
if (flags & nth_bit(index)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the strongest EWT_VISCONTENTS_ bit, discarding all other flags
|
||||||
|
contentflags_t visible_contents() const {
|
||||||
|
int index = visible_contents_index();
|
||||||
|
if (index >= 0) {
|
||||||
|
return contentflags_t::make(static_cast<contents_t>(nth_bit(index)));
|
||||||
|
}
|
||||||
|
return contentflags_t::make(EWT_VISCONTENTS_EMPTY);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct surfflags_t
|
struct surfflags_t
|
||||||
|
|
@ -270,6 +371,8 @@ struct gamedef_t
|
||||||
virtual int32_t surfflags_from_string(const std::string_view &str) const = 0;
|
virtual int32_t surfflags_from_string(const std::string_view &str) const = 0;
|
||||||
// FIXME: fix so that we don't have to pass a name here
|
// FIXME: fix so that we don't have to pass a name here
|
||||||
virtual bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const = 0;
|
virtual bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const = 0;
|
||||||
|
virtual contentflags_t create_contents_from_native(int32_t native) const = 0;
|
||||||
|
virtual int32_t contents_to_native(const contentflags_t &contents) const = 0;
|
||||||
virtual contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const = 0;
|
virtual contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const = 0;
|
||||||
virtual contentflags_t create_empty_contents() const = 0;
|
virtual contentflags_t create_empty_contents() const = 0;
|
||||||
virtual contentflags_t create_solid_contents() const = 0;
|
virtual contentflags_t create_solid_contents() const = 0;
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,16 @@ enum q2_contents_t : int32_t
|
||||||
Q2_CONTENTS_SLIME = nth_bit(4),
|
Q2_CONTENTS_SLIME = nth_bit(4),
|
||||||
Q2_CONTENTS_WATER = nth_bit(5),
|
Q2_CONTENTS_WATER = nth_bit(5),
|
||||||
Q2_CONTENTS_MIST = nth_bit(6),
|
Q2_CONTENTS_MIST = nth_bit(6),
|
||||||
|
|
||||||
|
Q2_CONTENTS_UNUSED_7 = nth_bit(7),
|
||||||
|
Q2_CONTENTS_UNUSED_8 = nth_bit(8),
|
||||||
|
Q2_CONTENTS_UNUSED_9 = nth_bit(9),
|
||||||
|
Q2_CONTENTS_UNUSED_10 = nth_bit(10),
|
||||||
|
Q2_CONTENTS_UNUSED_11 = nth_bit(11),
|
||||||
|
Q2_CONTENTS_UNUSED_12 = nth_bit(12),
|
||||||
|
Q2_CONTENTS_UNUSED_13 = nth_bit(13),
|
||||||
|
Q2_CONTENTS_UNUSED_14 = nth_bit(14),
|
||||||
|
|
||||||
Q2_LAST_VISIBLE_CONTENTS = Q2_CONTENTS_MIST,
|
Q2_LAST_VISIBLE_CONTENTS = Q2_CONTENTS_MIST,
|
||||||
Q2_ALL_VISIBLE_CONTENTS = Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW | Q2_CONTENTS_AUX | Q2_CONTENTS_LAVA |
|
Q2_ALL_VISIBLE_CONTENTS = Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW | Q2_CONTENTS_AUX | Q2_CONTENTS_LAVA |
|
||||||
Q2_CONTENTS_SLIME | Q2_CONTENTS_WATER | Q2_CONTENTS_MIST,
|
Q2_CONTENTS_SLIME | Q2_CONTENTS_WATER | Q2_CONTENTS_MIST,
|
||||||
|
|
@ -137,8 +147,8 @@ enum q2_contents_t : int32_t
|
||||||
Q2_CONTENTS_TRANSLUCENT = nth_bit(28), // auto set if any surface has trans
|
Q2_CONTENTS_TRANSLUCENT = nth_bit(28), // auto set if any surface has trans
|
||||||
Q2_CONTENTS_LADDER = nth_bit(29),
|
Q2_CONTENTS_LADDER = nth_bit(29),
|
||||||
|
|
||||||
// HACK: using Q2_CONTENTS_MONSTER for func_detail_wall
|
Q2_CONTENTS_UNUSED_30 = nth_bit(30),
|
||||||
Q2_ALL_VISIBLE_CONTENTS_PLUS_MONSTER = Q2_ALL_VISIBLE_CONTENTS | Q2_CONTENTS_MONSTER,
|
Q2_CONTENTS_UNUSED_31 = nth_bit(31)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct q2_dnode_t
|
struct q2_dnode_t
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ struct texture_meta
|
||||||
|
|
||||||
// Q2/WAL only
|
// Q2/WAL only
|
||||||
surfflags_t flags{};
|
surfflags_t flags{};
|
||||||
contentflags_t contents{};
|
uint32_t contents_native = 0;
|
||||||
int32_t value = 0;
|
int32_t value = 0;
|
||||||
std::string animation;
|
std::string animation;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ struct texdef_etp_t : texdef_quake_ed_t
|
||||||
// extra Q2 info
|
// extra Q2 info
|
||||||
struct texinfo_quake2_t
|
struct texinfo_quake2_t
|
||||||
{
|
{
|
||||||
contentflags_t contents;
|
int contents;
|
||||||
surfflags_t flags;
|
surfflags_t flags;
|
||||||
int value;
|
int value;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ enum class conversion_t
|
||||||
// used by Q2 format; used by various systems.
|
// used by Q2 format; used by various systems.
|
||||||
struct extended_texinfo_t
|
struct extended_texinfo_t
|
||||||
{
|
{
|
||||||
contentflags_t contents = {0};
|
uint32_t contents_native = 0;
|
||||||
surfflags_t flags = {0};
|
surfflags_t flags = {0};
|
||||||
int value = 0;
|
int value = 0;
|
||||||
std::string animation;
|
std::string animation;
|
||||||
|
|
@ -410,7 +410,6 @@ struct nodedata_t {
|
||||||
|
|
||||||
struct leafdata_t {
|
struct leafdata_t {
|
||||||
// information for leafs
|
// information for leafs
|
||||||
contentflags_t contents; // leaf nodes (0 for decision nodes)
|
|
||||||
std::vector<face_t *> markfaces; // leaf nodes only, point to node faces
|
std::vector<face_t *> markfaces; // leaf nodes only, point to node faces
|
||||||
int outside_distance; // -1 = can't reach outside, 0 = first void node, >0 = distance from void, in number of
|
int outside_distance; // -1 = can't reach outside, 0 = first void node, >0 = distance from void, in number of
|
||||||
// portals used to write leak lines that take the shortest path to the void
|
// portals used to write leak lines that take the shortest path to the void
|
||||||
|
|
@ -419,6 +418,7 @@ struct leafdata_t {
|
||||||
uint32_t firstleafbrush; // Q2
|
uint32_t firstleafbrush; // Q2
|
||||||
uint32_t numleafbrushes;
|
uint32_t numleafbrushes;
|
||||||
int32_t area;
|
int32_t area;
|
||||||
|
contentflags_t contents; // leaf nodes (0 for decision nodes)
|
||||||
std::vector<bspbrush_t *> original_brushes;
|
std::vector<bspbrush_t *> original_brushes;
|
||||||
bspbrush_t::container bsp_brushes;
|
bspbrush_t::container bsp_brushes;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -490,7 +490,8 @@ static bool Mod_LeafPvs(const mbsp_t *bsp, const mleaf_t *leaf, uint8_t *out)
|
||||||
|
|
||||||
static const std::vector<uint8_t> *Mod_LeafPvs(const mbsp_t *bsp, const mleaf_t *leaf)
|
static const std::vector<uint8_t> *Mod_LeafPvs(const mbsp_t *bsp, const mleaf_t *leaf)
|
||||||
{
|
{
|
||||||
if (bsp->loadversion->game->contents_are_liquid({leaf->contents})) {
|
if (bsp->loadversion->game->contents_are_liquid(
|
||||||
|
bsp->loadversion->game->create_contents_from_native(leaf->contents))) {
|
||||||
// the liquid case is because leaf->contents might be in an opaque liquid,
|
// the liquid case is because leaf->contents might be in an opaque liquid,
|
||||||
// which we typically want light to pass through, but visdata would report that
|
// which we typically want light to pass through, but visdata would report that
|
||||||
// there's no visibility across the opaque liquid. so, skip culling and do the raytracing.
|
// there's no visibility across the opaque liquid. so, skip culling and do the raytracing.
|
||||||
|
|
@ -540,7 +541,8 @@ static void CalcPvs(const mbsp_t *bsp, lightsurf_t *lightsurf)
|
||||||
/* copy the pvs for this leaf into pointpvs */
|
/* copy the pvs for this leaf into pointpvs */
|
||||||
Mod_LeafPvs(bsp, leaf, pointpvs);
|
Mod_LeafPvs(bsp, leaf, pointpvs);
|
||||||
|
|
||||||
if (bsp->loadversion->game->contents_are_liquid({leaf->contents})) {
|
if (bsp->loadversion->game->contents_are_liquid(
|
||||||
|
bsp->loadversion->game->create_contents_from_native(leaf->contents))) {
|
||||||
// hack for when the sample point might be in an opaque liquid, blocking vis,
|
// hack for when the sample point might be in an opaque liquid, blocking vis,
|
||||||
// but we typically want light to pass through these.
|
// but we typically want light to pass through these.
|
||||||
// see also VisCullEntity() which handles the case when the light emitter is in liquid.
|
// see also VisCullEntity() which handles the case when the light emitter is in liquid.
|
||||||
|
|
@ -1154,9 +1156,11 @@ static bool VisCullEntity(const mbsp_t *bsp, const std::vector<uint8_t> &pvs, co
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bsp->loadversion->game->contents_are_solid({entleaf->contents}) ||
|
auto contents = bsp->loadversion->game->create_contents_from_native(entleaf->contents);
|
||||||
bsp->loadversion->game->contents_are_sky({entleaf->contents}) ||
|
|
||||||
bsp->loadversion->game->contents_are_liquid({entleaf->contents})) {
|
if (bsp->loadversion->game->contents_are_solid(contents) ||
|
||||||
|
bsp->loadversion->game->contents_are_sky(contents) ||
|
||||||
|
bsp->loadversion->game->contents_are_liquid(contents)) {
|
||||||
// the liquid case is because entleaf->contents might be in an opaque liquid,
|
// the liquid case is because entleaf->contents might be in an opaque liquid,
|
||||||
// which we typically want light to pass through, but visdata would report that
|
// which we typically want light to pass through, but visdata would report that
|
||||||
// there's no visibility across the opaque liquid. so, skip culling and do the raytracing.
|
// there's no visibility across the opaque liquid. so, skip culling and do the raytracing.
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ static void maputil_make_brush_side(lua_State *state, const brush_side_t &side)
|
||||||
if (side.extended_info) {
|
if (side.extended_info) {
|
||||||
// set info
|
// set info
|
||||||
lua_createtable(state, 0, 3);
|
lua_createtable(state, 0, 3);
|
||||||
lua_pushnumber(state, side.extended_info->contents.native);
|
lua_pushnumber(state, side.extended_info->contents);
|
||||||
lua_setfield(state, -2, "contents");
|
lua_setfield(state, -2, "contents");
|
||||||
lua_pushnumber(state, side.extended_info->value);
|
lua_pushnumber(state, side.extended_info->value);
|
||||||
lua_setfield(state, -2, "value");
|
lua_setfield(state, -2, "value");
|
||||||
|
|
@ -538,7 +538,7 @@ static void maputil_copy_side(lua_State *state, brush_side_t &side)
|
||||||
texinfo_quake2_t q2;
|
texinfo_quake2_t q2;
|
||||||
|
|
||||||
lua_getfield(state, -1, "contents");
|
lua_getfield(state, -1, "contents");
|
||||||
q2.contents.native = lua_tonumber(state, -1);
|
q2.contents = lua_tonumber(state, -1);
|
||||||
lua_pop(state, 1);
|
lua_pop(state, 1);
|
||||||
|
|
||||||
lua_getfield(state, -1, "value");
|
lua_getfield(state, -1, "value");
|
||||||
|
|
@ -716,7 +716,7 @@ static int l_load_texture_meta(lua_State *state)
|
||||||
|
|
||||||
lua_createtable(state, 0, 5);
|
lua_createtable(state, 0, 5);
|
||||||
|
|
||||||
lua_pushnumber(state, result.contents.native);
|
lua_pushnumber(state, result.contents_native);
|
||||||
lua_setfield(state, -2, "contents");
|
lua_setfield(state, -2, "contents");
|
||||||
lua_pushnumber(state, result.flags.native);
|
lua_pushnumber(state, result.flags.native);
|
||||||
lua_setfield(state, -2, "flags");
|
lua_setfield(state, -2, "flags");
|
||||||
|
|
|
||||||
|
|
@ -840,8 +840,8 @@ static void Brush_LoadEntity(mapentity_t &dst, mapentity_t &src, hull_index_t hu
|
||||||
|
|
||||||
// fixme-brushbsp: function calls above can override the values below
|
// fixme-brushbsp: function calls above can override the values below
|
||||||
// so we have to re-set them to be sure they stay what the mapper intended..
|
// so we have to re-set them to be sure they stay what the mapper intended..
|
||||||
contents.set_mirrored(mapbrush.contents.mirror_inside);
|
contents.set_mirrored(mapbrush.contents.mirror_inside());
|
||||||
contents.set_clips_same_type(mapbrush.contents.clips_same_type);
|
contents.set_clips_same_type(mapbrush.contents.clips_same_type());
|
||||||
|
|
||||||
auto brush = LoadBrush(src, mapbrush, contents, hullnum, num_clipped);
|
auto brush = LoadBrush(src, mapbrush, contents, hullnum, num_clipped);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ static void ExportObjFace(
|
||||||
static void WriteContentsMaterial(std::ofstream &mtlf, contentflags_t contents, float r, float g, float b)
|
static void WriteContentsMaterial(std::ofstream &mtlf, contentflags_t contents, float r, float g, float b)
|
||||||
{
|
{
|
||||||
// fixme-brushbsp
|
// fixme-brushbsp
|
||||||
ewt::print(mtlf, "newmtl contents{}\n", contents.native);
|
ewt::print(mtlf, "newmtl contents{}\n", qbsp_options.target_game->contents_to_native(contents));
|
||||||
mtlf << "Ka 0 0 0\n";
|
mtlf << "Ka 0 0 0\n";
|
||||||
ewt::print(mtlf, "Kd {} {} {}\n", r, g, b);
|
ewt::print(mtlf, "Kd {} {} {}\n", r, g, b);
|
||||||
mtlf << "Ks 0 0 0\n";
|
mtlf << "Ks 0 0 0\n";
|
||||||
|
|
@ -105,14 +105,15 @@ void ExportObj_Faces(const std::string &filesuffix, const std::vector<const face
|
||||||
std::ofstream mtlfile = InitMtlFile(filesuffix);
|
std::ofstream mtlfile = InitMtlFile(filesuffix);
|
||||||
|
|
||||||
WriteContentsMaterial(mtlfile, {}, 0, 0, 0);
|
WriteContentsMaterial(mtlfile, {}, 0, 0, 0);
|
||||||
WriteContentsMaterial(mtlfile, {CONTENTS_EMPTY}, 0, 1, 0);
|
// fixme-brushbsp
|
||||||
WriteContentsMaterial(mtlfile, {CONTENTS_SOLID}, 0.2, 0.2, 0.2);
|
// WriteContentsMaterial(mtlfile, {CONTENTS_EMPTY}, 0, 1, 0);
|
||||||
|
// WriteContentsMaterial(mtlfile, {CONTENTS_SOLID}, 0.2, 0.2, 0.2);
|
||||||
WriteContentsMaterial(mtlfile, {CONTENTS_WATER}, 0.0, 0.0, 0.2);
|
//
|
||||||
WriteContentsMaterial(mtlfile, {CONTENTS_SLIME}, 0.0, 0.2, 0.0);
|
// WriteContentsMaterial(mtlfile, {CONTENTS_WATER}, 0.0, 0.0, 0.2);
|
||||||
WriteContentsMaterial(mtlfile, {CONTENTS_LAVA}, 0.2, 0.0, 0.0);
|
// WriteContentsMaterial(mtlfile, {CONTENTS_SLIME}, 0.0, 0.2, 0.0);
|
||||||
|
// WriteContentsMaterial(mtlfile, {CONTENTS_LAVA}, 0.2, 0.0, 0.0);
|
||||||
WriteContentsMaterial(mtlfile, {CONTENTS_SKY}, 0.8, 0.8, 1.0);
|
//
|
||||||
|
// WriteContentsMaterial(mtlfile, {CONTENTS_SKY}, 0.8, 0.8, 1.0);
|
||||||
// fixme-brushbsp
|
// fixme-brushbsp
|
||||||
// WriteContentsMaterial(mtlfile, {CONTENTS_SOLID, CFLAGS_CLIP}, 1, 0.8, 0.8);
|
// WriteContentsMaterial(mtlfile, {CONTENTS_SOLID, CFLAGS_CLIP}, 1, 0.8, 0.8);
|
||||||
// WriteContentsMaterial(mtlfile, {CONTENTS_EMPTY, CFLAGS_HINT}, 1, 1, 1);
|
// WriteContentsMaterial(mtlfile, {CONTENTS_EMPTY, CFLAGS_HINT}, 1, 1, 1);
|
||||||
|
|
@ -121,7 +122,7 @@ void ExportObj_Faces(const std::string &filesuffix, const std::vector<const face
|
||||||
|
|
||||||
int vertcount = 0;
|
int vertcount = 0;
|
||||||
for (const face_t *face : faces) {
|
for (const face_t *face : faces) {
|
||||||
std::string mtlname = fmt::format("contents{}\n", face->contents.back.native);
|
std::string mtlname = fmt::format("contents{}\n", qbsp_options.target_game->contents_to_native(face->contents.back));
|
||||||
|
|
||||||
ExportObjFace(objfile, mtlname, face->w, face->get_texinfo(), &vertcount);
|
ExportObjFace(objfile, mtlname, face->w, face->get_texinfo(), &vertcount);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
47
qbsp/map.cc
47
qbsp/map.cc
|
|
@ -462,7 +462,7 @@ int FindMiptex(const char *name, std::optional<extended_texinfo_t> &extended_inf
|
||||||
auto wal = map.load_image_meta(name);
|
auto wal = map.load_image_meta(name);
|
||||||
|
|
||||||
if (wal && !internal && !extended_info.has_value()) {
|
if (wal && !internal && !extended_info.has_value()) {
|
||||||
extended_info = extended_texinfo_t{wal->contents, wal->flags, wal->value, wal->animation};
|
extended_info = extended_texinfo_t{wal->contents_native, wal->flags, wal->value, wal->animation};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!extended_info.has_value()) {
|
if (!extended_info.has_value()) {
|
||||||
|
|
@ -906,7 +906,8 @@ static quark_tx_info_t ParseExtendedTX(parser_t &parser)
|
||||||
} else {
|
} else {
|
||||||
// Parse extra Quake 2 surface info
|
// Parse extra Quake 2 surface info
|
||||||
if (parser.parse_token(PARSE_OPTIONAL)) {
|
if (parser.parse_token(PARSE_OPTIONAL)) {
|
||||||
result.info = extended_texinfo_t{{std::stoi(parser.token)}};
|
uint32_t native = std::stoul(parser.token);
|
||||||
|
result.info = extended_texinfo_t{.contents_native = native};
|
||||||
|
|
||||||
if (parser.parse_token(PARSE_OPTIONAL)) {
|
if (parser.parse_token(PARSE_OPTIONAL)) {
|
||||||
result.info->flags.native = std::stoi(parser.token);
|
result.info->flags.native = std::stoi(parser.token);
|
||||||
|
|
@ -1781,20 +1782,20 @@ static void ParseTextureDef(const mapentity_t &entity, parser_t &parser, mapface
|
||||||
// first one first
|
// first one first
|
||||||
if (auto &wal = map.load_image_meta(mapface.texname.c_str())) {
|
if (auto &wal = map.load_image_meta(mapface.texname.c_str())) {
|
||||||
if (!extinfo.info) {
|
if (!extinfo.info) {
|
||||||
extinfo.info = extended_texinfo_t{wal->contents, wal->flags, wal->value};
|
extinfo.info = extended_texinfo_t{wal->contents_native, wal->flags, wal->value};
|
||||||
}
|
}
|
||||||
extinfo.info->animation = wal->animation;
|
extinfo.info->animation = wal->animation;
|
||||||
} else if (!extinfo.info) {
|
} else if (!extinfo.info) {
|
||||||
extinfo.info = extended_texinfo_t{};
|
extinfo.info = extended_texinfo_t{};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extinfo.info->contents.native & Q2_CONTENTS_TRANSLUCENT) {
|
if (extinfo.info->contents_native & Q2_CONTENTS_TRANSLUCENT) {
|
||||||
// remove TRANSLUCENT; it's only meant to be set by the compiler
|
// remove TRANSLUCENT; it's only meant to be set by the compiler
|
||||||
extinfo.info->contents.native &= ~Q2_CONTENTS_TRANSLUCENT;
|
extinfo.info->contents_native &= ~Q2_CONTENTS_TRANSLUCENT;
|
||||||
|
|
||||||
// but give us detail if we lack trans. this is likely what they intended
|
// but give us detail if we lack trans. this is likely what they intended
|
||||||
if (!(extinfo.info->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
|
if (!(extinfo.info->flags.native & (Q2_SURF_TRANS33 | Q2_SURF_TRANS66))) {
|
||||||
extinfo.info->contents.native |= Q2_CONTENTS_DETAIL;
|
extinfo.info->contents_native |= Q2_CONTENTS_DETAIL;
|
||||||
|
|
||||||
if (qbsp_options.verbose.value()) {
|
if (qbsp_options.verbose.value()) {
|
||||||
logging::print("WARNING: {}: swapped TRANSLUCENT for DETAIL\n", mapface.line);
|
logging::print("WARNING: {}: swapped TRANSLUCENT for DETAIL\n", mapface.line);
|
||||||
|
|
@ -1817,23 +1818,25 @@ static void ParseTextureDef(const mapentity_t &entity, parser_t &parser, mapface
|
||||||
|
|
||||||
// Mixing visible contents on the input brush is illegal
|
// Mixing visible contents on the input brush is illegal
|
||||||
{
|
{
|
||||||
const int32_t visible_contents = extinfo.info->contents.native & Q2_ALL_VISIBLE_CONTENTS;
|
const int32_t visible_contents = extinfo.info->contents_native & Q2_ALL_VISIBLE_CONTENTS;
|
||||||
|
|
||||||
// TODO: Move to bspfile.hh API
|
// TODO: Move to bspfile.hh API
|
||||||
for (int32_t i = Q2_CONTENTS_SOLID; i <= Q2_LAST_VISIBLE_CONTENTS; i <<= 1) {
|
for (int32_t i = Q2_CONTENTS_SOLID; i <= Q2_LAST_VISIBLE_CONTENTS; i <<= 1) {
|
||||||
if (visible_contents & i) {
|
if (visible_contents & i) {
|
||||||
if (visible_contents != i) {
|
if (visible_contents != i) {
|
||||||
FError("{}: Mixed visible contents: {}", mapface.line,
|
FError("{}: Mixed visible contents: {}", mapface.line,
|
||||||
extinfo.info->contents.to_string(qbsp_options.target_game));
|
qbsp_options.target_game->create_contents_from_native(extinfo.info->contents_native)
|
||||||
|
.to_string(qbsp_options.target_game));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Other Q2 hard errors
|
// Other Q2 hard errors
|
||||||
if (extinfo.info->contents.native & (Q2_CONTENTS_MONSTER | Q2_CONTENTS_DEADMONSTER)) {
|
if (extinfo.info->contents_native & (Q2_CONTENTS_MONSTER | Q2_CONTENTS_DEADMONSTER)) {
|
||||||
FError(
|
FError(
|
||||||
"{}: Illegal contents: {}", mapface.line, extinfo.info->contents.to_string(qbsp_options.target_game));
|
"{}: Illegal contents: {}", mapface.line, qbsp_options.target_game->create_contents_from_native(
|
||||||
|
extinfo.info->contents_native).to_string(qbsp_options.target_game));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Q2 style phong is enabled on a mirrored face, `light` will erroneously try to blend normals between
|
// If Q2 style phong is enabled on a mirrored face, `light` will erroneously try to blend normals between
|
||||||
|
|
@ -1841,8 +1844,8 @@ static void ParseTextureDef(const mapentity_t &entity, parser_t &parser, mapface
|
||||||
const bool wants_phong = !(extinfo.info->flags.native & Q2_SURF_LIGHT) && (extinfo.info->value != 0);
|
const bool wants_phong = !(extinfo.info->flags.native & Q2_SURF_LIGHT) && (extinfo.info->value != 0);
|
||||||
// Technically this is not the 100% correct check for mirrored, but we don't have the full brush
|
// Technically this is not the 100% correct check for mirrored, but we don't have the full brush
|
||||||
// contents set up at this point. Correct would be to call `portal_generates_face()`.
|
// contents set up at this point. Correct would be to call `portal_generates_face()`.
|
||||||
bool mirrored = (extinfo.info->contents.native != 0) &&
|
bool mirrored = (extinfo.info->contents_native != 0) &&
|
||||||
!(extinfo.info->contents.native &
|
!(extinfo.info->contents_native &
|
||||||
(Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW | Q2_CONTENTS_AUX));
|
(Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID | Q2_CONTENTS_WINDOW | Q2_CONTENTS_AUX));
|
||||||
|
|
||||||
if (entity.epairs.has("_mirrorinside") && !entity.epairs.get_int("_mirrorinside")) {
|
if (entity.epairs.has("_mirrorinside") && !entity.epairs.get_int("_mirrorinside")) {
|
||||||
|
|
@ -1855,7 +1858,10 @@ static void ParseTextureDef(const mapentity_t &entity, parser_t &parser, mapface
|
||||||
}
|
}
|
||||||
|
|
||||||
tx->miptex = FindMiptex(mapface.texname.c_str(), extinfo.info);
|
tx->miptex = FindMiptex(mapface.texname.c_str(), extinfo.info);
|
||||||
mapface.contents = {extinfo.info->contents};
|
if (extinfo.info->contents_native != 0)
|
||||||
|
mapface.contents = qbsp_options.target_game->create_contents_from_native(extinfo.info->contents_native);
|
||||||
|
else
|
||||||
|
mapface.contents = contentflags_t::make(EWT_VISCONTENTS_EMPTY);
|
||||||
tx->flags = {extinfo.info->flags};
|
tx->flags = {extinfo.info->flags};
|
||||||
tx->value = extinfo.info->value;
|
tx->value = extinfo.info->value;
|
||||||
|
|
||||||
|
|
@ -2485,8 +2491,9 @@ static contentflags_t Brush_GetContents(const mapentity_t &entity, const mapbrus
|
||||||
base_contents.set_clips_same_type(std::nullopt);
|
base_contents.set_clips_same_type(std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
base_contents.illusionary_visblocker =
|
if (string_iequals(entity.epairs.get("classname"), "func_illusionary_visblocker")) {
|
||||||
string_iequals(entity.epairs.get("classname"), "func_illusionary_visblocker");
|
base_contents = contentflags_t::make(base_contents.flags | EWT_INVISCONTENTS_ILLUSIONARY_VISBLOCKER);
|
||||||
|
}
|
||||||
|
|
||||||
// non-Q2: -transwater implies liquids are detail
|
// non-Q2: -transwater implies liquids are detail
|
||||||
if (qbsp_options.target_game->id != GAME_QUAKE_II && qbsp_options.transwater.value()) {
|
if (qbsp_options.target_game->id != GAME_QUAKE_II && qbsp_options.transwater.value()) {
|
||||||
|
|
@ -2996,10 +3003,10 @@ void ProcessAreaPortal(mapentity_t &entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &brush : entity.mapbrushes) {
|
for (auto &brush : entity.mapbrushes) {
|
||||||
brush.contents.native = Q2_CONTENTS_AREAPORTAL;
|
brush.contents = contentflags_t::make(EWT_INVISCONTENTS_AREAPORTAL);
|
||||||
|
|
||||||
for (auto &face : brush.faces) {
|
for (auto &face : brush.faces) {
|
||||||
face.contents.native = brush.contents.native;
|
face.contents = brush.contents;
|
||||||
face.texinfo = map.skip_texinfo;
|
face.texinfo = map.skip_texinfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3530,7 +3537,7 @@ static void ConvertMapFace(std::ofstream &f, const mapface_t &mapface, const con
|
||||||
fprintDoubleAndSpc(f, quakeed.scale[1]);
|
fprintDoubleAndSpc(f, quakeed.scale[1]);
|
||||||
|
|
||||||
if (mapface.raw_info.has_value()) {
|
if (mapface.raw_info.has_value()) {
|
||||||
f << mapface.raw_info->contents.native << " " << mapface.raw_info->flags.native << " "
|
f << mapface.raw_info->contents_native << " " << mapface.raw_info->flags.native << " "
|
||||||
<< mapface.raw_info->value;
|
<< mapface.raw_info->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3554,7 +3561,7 @@ static void ConvertMapFace(std::ofstream &f, const mapface_t &mapface, const con
|
||||||
fprintDoubleAndSpc(f, valve.scale[1]);
|
fprintDoubleAndSpc(f, valve.scale[1]);
|
||||||
|
|
||||||
if (mapface.raw_info.has_value()) {
|
if (mapface.raw_info.has_value()) {
|
||||||
f << mapface.raw_info->contents.native << " " << mapface.raw_info->flags.native << " "
|
f << mapface.raw_info->contents_native << " " << mapface.raw_info->flags.native << " "
|
||||||
<< mapface.raw_info->value;
|
<< mapface.raw_info->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3580,7 +3587,7 @@ static void ConvertMapFace(std::ofstream &f, const mapface_t &mapface, const con
|
||||||
ewt::print(f, ") ) {} ", mapface.texname);
|
ewt::print(f, ") ) {} ", mapface.texname);
|
||||||
|
|
||||||
if (mapface.raw_info.has_value()) {
|
if (mapface.raw_info.has_value()) {
|
||||||
f << mapface.raw_info->contents.native << " " << mapface.raw_info->flags.native << " "
|
f << mapface.raw_info->contents_native << " " << mapface.raw_info->flags.native << " "
|
||||||
<< mapface.raw_info->value;
|
<< mapface.raw_info->value;
|
||||||
} else {
|
} else {
|
||||||
f << "0 0 0";
|
f << "0 0 0";
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,8 @@ bool Portal_VisFlood(const portal_t *p)
|
||||||
contentflags_t contents1 = ClusterContents(p->nodes[1]);
|
contentflags_t contents1 = ClusterContents(p->nodes[1]);
|
||||||
|
|
||||||
/* Can't see through func_illusionary_visblocker */
|
/* Can't see through func_illusionary_visblocker */
|
||||||
if (contents0.illusionary_visblocker || contents1.illusionary_visblocker)
|
if ((contents0.flags & EWT_INVISCONTENTS_ILLUSIONARY_VISBLOCKER)
|
||||||
|
|| (contents1.flags & EWT_INVISCONTENTS_ILLUSIONARY_VISBLOCKER))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check per-game visibility
|
// Check per-game visibility
|
||||||
|
|
@ -613,7 +614,7 @@ static void FloodAreas_r(node_t *node)
|
||||||
auto *leafdata = node->get_leafdata();
|
auto *leafdata = node->get_leafdata();
|
||||||
Q_assert(leafdata);
|
Q_assert(leafdata);
|
||||||
|
|
||||||
if (leafdata->contents.native & Q2_CONTENTS_AREAPORTAL) {
|
if (leafdata->contents.flags & EWT_INVISCONTENTS_AREAPORTAL) {
|
||||||
// grab the func_areanode entity
|
// grab the func_areanode entity
|
||||||
mapentity_t *entity = AreanodeEntityForLeaf(node);
|
mapentity_t *entity = AreanodeEntityForLeaf(node);
|
||||||
|
|
||||||
|
|
@ -701,7 +702,7 @@ static void FindAreas_r(node_t *node)
|
||||||
|
|
||||||
// area portals are always only flooded into, never
|
// area portals are always only flooded into, never
|
||||||
// out of
|
// out of
|
||||||
if (leafdata->contents.native & Q2_CONTENTS_AREAPORTAL)
|
if (leafdata->contents.flags & EWT_INVISCONTENTS_AREAPORTAL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
map.c_areas++;
|
map.c_areas++;
|
||||||
|
|
@ -781,7 +782,7 @@ static void FindAreaPortalExits_R(node_t *n, std::unordered_set<node_t *> &visit
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// is this an exit?
|
// is this an exit?
|
||||||
if (!(neighbour->get_leafdata()->contents.native & Q2_CONTENTS_AREAPORTAL) &&
|
if (!(neighbour->get_leafdata()->contents.flags & EWT_INVISCONTENTS_AREAPORTAL) &&
|
||||||
!neighbour->get_leafdata()->contents.is_solid(qbsp_options.target_game)) {
|
!neighbour->get_leafdata()->contents.is_solid(qbsp_options.target_game)) {
|
||||||
exits.emplace_back(p, neighbour);
|
exits.emplace_back(p, neighbour);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -789,7 +790,7 @@ static void FindAreaPortalExits_R(node_t *n, std::unordered_set<node_t *> &visit
|
||||||
|
|
||||||
// valid edge to explore?
|
// valid edge to explore?
|
||||||
// if this isn't an exit, don't leave AREAPORTAL
|
// if this isn't an exit, don't leave AREAPORTAL
|
||||||
if (!(neighbour->get_leafdata()->contents.native & Q2_CONTENTS_AREAPORTAL))
|
if (!(neighbour->get_leafdata()->contents.flags & EWT_INVISCONTENTS_AREAPORTAL))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// continue exploding
|
// continue exploding
|
||||||
|
|
@ -839,8 +840,8 @@ static void DebugAreaPortalBothSidesLeak(node_t *node)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// don't go back into an areaportal
|
// don't go back into an areaportal
|
||||||
if ((p->nodes[0]->get_leafdata()->contents.native & Q2_CONTENTS_AREAPORTAL) ||
|
if ((p->nodes[0]->get_leafdata()->contents.flags & EWT_INVISCONTENTS_AREAPORTAL) ||
|
||||||
(p->nodes[1]->get_leafdata()->contents.native & Q2_CONTENTS_AREAPORTAL))
|
(p->nodes[1]->get_leafdata()->contents.flags & EWT_INVISCONTENTS_AREAPORTAL))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -898,7 +899,7 @@ static void SetAreaPortalAreas_r(node_t *node)
|
||||||
|
|
||||||
auto *leafdata = node->get_leafdata();
|
auto *leafdata = node->get_leafdata();
|
||||||
|
|
||||||
if (leafdata->contents.native != Q2_CONTENTS_AREAPORTAL)
|
if (leafdata->contents.flags != EWT_INVISCONTENTS_AREAPORTAL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (leafdata->area)
|
if (leafdata->area)
|
||||||
|
|
|
||||||
20
qbsp/qbsp.cc
20
qbsp/qbsp.cc
|
|
@ -632,7 +632,9 @@ void qbsp_settings::load_texture_def(const std::string &pathname)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parser.parse_token(PARSE_SAMELINE | PARSE_OPTIONAL)) {
|
if (parser.parse_token(PARSE_SAMELINE | PARSE_OPTIONAL)) {
|
||||||
texinfo = extended_texinfo_t{{std::stoi(parser.token)}};
|
uint32_t native = std::stoul(parser.token);
|
||||||
|
|
||||||
|
texinfo = extended_texinfo_t{.contents_native = native};
|
||||||
|
|
||||||
if (parser.parse_token(PARSE_SAMELINE | PARSE_OPTIONAL)) {
|
if (parser.parse_token(PARSE_SAMELINE | PARSE_OPTIONAL)) {
|
||||||
texinfo->flags.native = std::stoi(parser.token);
|
texinfo->flags.native = std::stoi(parser.token);
|
||||||
|
|
@ -803,7 +805,9 @@ static void ExportBrushList_r(const mapentity_t &entity, node_t *node, brush_lis
|
||||||
{
|
{
|
||||||
if (node->is_leaf()) {
|
if (node->is_leaf()) {
|
||||||
auto *leafdata = node->get_leafdata();
|
auto *leafdata = node->get_leafdata();
|
||||||
if (leafdata->contents.native) {
|
int native = qbsp_options.target_game->contents_to_native(
|
||||||
|
qbsp_options.target_game->contents_remap_for_export(leafdata->contents, gamedef_t::remap_type_t::leaf));
|
||||||
|
if (native) {
|
||||||
if (leafdata->original_brushes.size()) {
|
if (leafdata->original_brushes.size()) {
|
||||||
leafdata->numleafbrushes = leafdata->original_brushes.size();
|
leafdata->numleafbrushes = leafdata->original_brushes.size();
|
||||||
stats.total_leaf_brushes += leafdata->numleafbrushes;
|
stats.total_leaf_brushes += leafdata->numleafbrushes;
|
||||||
|
|
@ -813,12 +817,13 @@ static void ExportBrushList_r(const mapentity_t &entity, node_t *node, brush_lis
|
||||||
if (!b->mapbrush->outputnumber.has_value()) {
|
if (!b->mapbrush->outputnumber.has_value()) {
|
||||||
b->mapbrush->outputnumber = {static_cast<uint32_t>(map.bsp.dbrushes.size())};
|
b->mapbrush->outputnumber = {static_cast<uint32_t>(map.bsp.dbrushes.size())};
|
||||||
|
|
||||||
|
int brushcontents = qbsp_options.target_game->contents_to_native(qbsp_options.target_game
|
||||||
|
->contents_remap_for_export(b->contents, gamedef_t::remap_type_t::brush));
|
||||||
|
|
||||||
dbrush_t &brush = map.bsp.dbrushes.emplace_back(
|
dbrush_t &brush = map.bsp.dbrushes.emplace_back(
|
||||||
dbrush_t{.firstside = static_cast<int32_t>(map.bsp.dbrushsides.size()),
|
dbrush_t{.firstside = static_cast<int32_t>(map.bsp.dbrushsides.size()),
|
||||||
.numsides = 0,
|
.numsides = 0,
|
||||||
.contents = qbsp_options.target_game
|
.contents = brushcontents});
|
||||||
->contents_remap_for_export(b->contents, gamedef_t::remap_type_t::brush)
|
|
||||||
.native});
|
|
||||||
|
|
||||||
for (auto &side : b->mapbrush->faces) {
|
for (auto &side : b->mapbrush->faces) {
|
||||||
|
|
||||||
|
|
@ -1348,8 +1353,9 @@ static bspxbrushes_permodel BSPX_Brushes_AddModel(int modelnum, const std::vecto
|
||||||
perbrush.bounds = b->bounds;
|
perbrush.bounds = b->bounds;
|
||||||
|
|
||||||
const auto &contents = b->contents;
|
const auto &contents = b->contents;
|
||||||
|
const int native = qbsp_options.target_game->contents_to_native(contents);
|
||||||
|
|
||||||
switch (contents.native) {
|
switch (native) {
|
||||||
// contents should match the engine.
|
// contents should match the engine.
|
||||||
case CONTENTS_EMPTY: // really an error, but whatever
|
case CONTENTS_EMPTY: // really an error, but whatever
|
||||||
case CONTENTS_SOLID: // these are okay
|
case CONTENTS_SOLID: // these are okay
|
||||||
|
|
@ -1360,7 +1366,7 @@ static bspxbrushes_permodel BSPX_Brushes_AddModel(int modelnum, const std::vecto
|
||||||
if (contents.is_clip(qbsp_options.target_game)) {
|
if (contents.is_clip(qbsp_options.target_game)) {
|
||||||
perbrush.contents = BSPXBRUSHES_CONTENTS_CLIP;
|
perbrush.contents = BSPXBRUSHES_CONTENTS_CLIP;
|
||||||
} else {
|
} else {
|
||||||
perbrush.contents = contents.native;
|
perbrush.contents = native;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// case CONTENTS_LADDER:
|
// case CONTENTS_LADDER:
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ ExportClipNodes
|
||||||
static size_t ExportClipNodes(node_t *node)
|
static size_t ExportClipNodes(node_t *node)
|
||||||
{
|
{
|
||||||
if (auto *leafdata = node->get_leafdata()) {
|
if (auto *leafdata = node->get_leafdata()) {
|
||||||
return leafdata->contents.native;
|
return qbsp_options.target_game->contents_to_native(leafdata->contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *nodedata = node->get_nodedata();
|
auto *nodedata = node->get_nodedata();
|
||||||
|
|
@ -173,7 +173,7 @@ static void ExportLeaf(node_t *node)
|
||||||
remapped.to_string(qbsp_options.target_game));
|
remapped.to_string(qbsp_options.target_game));
|
||||||
}
|
}
|
||||||
|
|
||||||
dleaf.contents = remapped.native;
|
dleaf.contents = qbsp_options.target_game->contents_to_native(remapped);
|
||||||
|
|
||||||
if (node->bounds.maxs()[0] < node->bounds.mins()[0]) {
|
if (node->bounds.maxs()[0] < node->bounds.mins()[0]) {
|
||||||
throw std::runtime_error("leaf bounds was unassigned");
|
throw std::runtime_error("leaf bounds was unassigned");
|
||||||
|
|
@ -330,7 +330,7 @@ void BeginBSPFile()
|
||||||
|
|
||||||
// Leave room for leaf 0 (must be solid)
|
// Leave room for leaf 0 (must be solid)
|
||||||
auto &solid_leaf = map.bsp.dleafs.emplace_back();
|
auto &solid_leaf = map.bsp.dleafs.emplace_back();
|
||||||
solid_leaf.contents = qbsp_options.target_game->create_solid_contents().native;
|
solid_leaf.contents = qbsp_options.target_game->contents_to_native(qbsp_options.target_game->create_solid_contents());
|
||||||
solid_leaf.cluster = CLUSTER_INVALID;
|
solid_leaf.cluster = CLUSTER_INVALID;
|
||||||
Q_assert(map.bsp.dleafs.size() == 1);
|
Q_assert(map.bsp.dleafs.size() == 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,21 +27,22 @@ TEST_SUITE("common")
|
||||||
const auto detail_fence = game_q1->create_detail_fence_contents(solid);
|
const auto detail_fence = game_q1->create_detail_fence_contents(solid);
|
||||||
const auto detail_illusionary = game_q1->create_detail_illusionary_contents(solid);
|
const auto detail_illusionary = game_q1->create_detail_illusionary_contents(solid);
|
||||||
|
|
||||||
const std::array test_contents{contentflags_t{CONTENTS_EMPTY}, contentflags_t{CONTENTS_SOLID},
|
const std::array test_contents{game_q1->create_contents_from_native(CONTENTS_EMPTY),
|
||||||
contentflags_t{CONTENTS_WATER}, contentflags_t{CONTENTS_SLIME}, contentflags_t{CONTENTS_LAVA},
|
game_q1->create_contents_from_native(CONTENTS_SOLID),
|
||||||
contentflags_t{CONTENTS_SKY},
|
game_q1->create_contents_from_native(CONTENTS_WATER),
|
||||||
|
game_q1->create_contents_from_native(CONTENTS_SLIME),
|
||||||
|
game_q1->create_contents_from_native(CONTENTS_LAVA),
|
||||||
|
game_q1->create_contents_from_native(CONTENTS_SKY),
|
||||||
detail_solid, detail_wall, detail_fence, detail_illusionary};
|
detail_solid, detail_wall, detail_fence, detail_illusionary};
|
||||||
|
|
||||||
SUBCASE("solid combined with others")
|
SUBCASE("solid combined with others")
|
||||||
{
|
{
|
||||||
CHECK(solid.native == CONTENTS_SOLID);
|
CHECK(game_q1->contents_to_native(solid) == CONTENTS_SOLID);
|
||||||
CHECK(!solid.game_data.has_value());
|
|
||||||
|
|
||||||
for (const auto &c : test_contents) {
|
for (const auto &c : test_contents) {
|
||||||
auto combined = game_q1->combine_contents(solid, c);
|
auto combined = game_q1->combine_contents(solid, c);
|
||||||
|
|
||||||
CHECK(combined.native == CONTENTS_SOLID);
|
CHECK(game_q1->contents_to_native(combined) == CONTENTS_SOLID);
|
||||||
CHECK(combined.is_solid(game_q1));
|
CHECK(combined.is_solid(game_q1));
|
||||||
|
|
||||||
CHECK(!combined.is_any_detail(game_q1));
|
CHECK(!combined.is_any_detail(game_q1));
|
||||||
|
|
@ -50,15 +51,15 @@ TEST_SUITE("common")
|
||||||
|
|
||||||
SUBCASE("detail_illusionary plus water")
|
SUBCASE("detail_illusionary plus water")
|
||||||
{
|
{
|
||||||
auto combined = game_q1->combine_contents(detail_illusionary, contentflags_t{CONTENTS_WATER});
|
auto combined = game_q1->combine_contents(detail_illusionary, game_q1->create_contents_from_native(CONTENTS_WATER));
|
||||||
|
|
||||||
CHECK(combined.native == CONTENTS_WATER);
|
CHECK(game_q1->contents_to_native(combined) == CONTENTS_WATER);
|
||||||
CHECK(combined.is_detail_illusionary(game_q1));
|
CHECK(combined.is_detail_illusionary(game_q1));
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("detail_solid plus water")
|
SUBCASE("detail_solid plus water")
|
||||||
{
|
{
|
||||||
auto combined = game_q1->combine_contents(detail_solid, contentflags_t{CONTENTS_WATER});
|
auto combined = game_q1->combine_contents(detail_solid, game_q1->create_contents_from_native(CONTENTS_WATER));
|
||||||
|
|
||||||
CHECK(combined.is_any_solid(game_q1));
|
CHECK(combined.is_any_solid(game_q1));
|
||||||
CHECK(combined.is_detail_solid(game_q1));
|
CHECK(combined.is_detail_solid(game_q1));
|
||||||
|
|
@ -68,7 +69,7 @@ TEST_SUITE("common")
|
||||||
|
|
||||||
SUBCASE("detail_solid plus sky")
|
SUBCASE("detail_solid plus sky")
|
||||||
{
|
{
|
||||||
auto combined = game_q1->combine_contents(detail_solid, contentflags_t{CONTENTS_SKY});
|
auto combined = game_q1->combine_contents(detail_solid, game_q1->create_contents_from_native(CONTENTS_SKY));
|
||||||
|
|
||||||
CHECK(!combined.is_detail_solid(game_q1));
|
CHECK(!combined.is_detail_solid(game_q1));
|
||||||
CHECK(combined.is_sky(game_q1));
|
CHECK(combined.is_sky(game_q1));
|
||||||
|
|
@ -123,7 +124,7 @@ TEST_SUITE("common")
|
||||||
{
|
{
|
||||||
auto *game = bspver_q2.game;
|
auto *game = bspver_q2.game;
|
||||||
|
|
||||||
auto origin = game->face_get_contents("", {}, {Q2_CONTENTS_ORIGIN});
|
auto origin = game->face_get_contents("", {}, game->create_contents_from_native(Q2_CONTENTS_ORIGIN));
|
||||||
|
|
||||||
CHECK(origin.is_origin(game));
|
CHECK(origin.is_origin(game));
|
||||||
CHECK(!origin.is_empty(game));
|
CHECK(!origin.is_empty(game));
|
||||||
|
|
@ -219,24 +220,30 @@ TEST_SUITE("common")
|
||||||
|
|
||||||
TEST_CASE("q2 contents")
|
TEST_CASE("q2 contents")
|
||||||
{
|
{
|
||||||
const std::array test_contents{contentflags_t{Q2_CONTENTS_EMPTY}, contentflags_t{Q2_CONTENTS_SOLID},
|
|
||||||
contentflags_t{Q2_CONTENTS_WINDOW}, contentflags_t{Q2_CONTENTS_AUX}, contentflags_t{Q2_CONTENTS_LAVA},
|
|
||||||
contentflags_t{Q2_CONTENTS_SLIME}, contentflags_t{Q2_CONTENTS_WATER}, contentflags_t{Q2_CONTENTS_MIST},
|
|
||||||
|
|
||||||
contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID},
|
|
||||||
contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW},
|
|
||||||
contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX}, contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA},
|
|
||||||
contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME},
|
|
||||||
contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_WATER},
|
|
||||||
contentflags_t{Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST}};
|
|
||||||
|
|
||||||
auto *game_q2 = bspver_q2.game;
|
auto *game_q2 = bspver_q2.game;
|
||||||
|
|
||||||
|
const std::array test_contents{
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_EMPTY),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_SOLID),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_WINDOW),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_AUX),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_LAVA),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_SLIME),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_WATER),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_MIST),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_SOLID),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_WINDOW),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_AUX),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_LAVA),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_SLIME),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_WATER),
|
||||||
|
game_q2->create_contents_from_native(Q2_CONTENTS_DETAIL | Q2_CONTENTS_MIST)
|
||||||
|
};
|
||||||
|
|
||||||
SUBCASE("solid combined with others")
|
SUBCASE("solid combined with others")
|
||||||
{
|
{
|
||||||
auto solid = game_q2->create_solid_contents();
|
auto solid = game_q2->create_solid_contents();
|
||||||
CHECK(solid.native == Q2_CONTENTS_SOLID);
|
CHECK(game_q2->contents_to_native(solid) == Q2_CONTENTS_SOLID);
|
||||||
CHECK(!solid.game_data.has_value());
|
|
||||||
|
|
||||||
for (const auto &c : test_contents) {
|
for (const auto &c : test_contents) {
|
||||||
// solid is treated specially in Q2 and wipes out any other content
|
// solid is treated specially in Q2 and wipes out any other content
|
||||||
|
|
@ -244,30 +251,60 @@ TEST_SUITE("common")
|
||||||
auto combined = game_q2->contents_remap_for_export(
|
auto combined = game_q2->contents_remap_for_export(
|
||||||
game_q2->combine_contents(solid, c), gamedef_t::remap_type_t::leaf);
|
game_q2->combine_contents(solid, c), gamedef_t::remap_type_t::leaf);
|
||||||
|
|
||||||
CHECK(combined.native == Q2_CONTENTS_SOLID);
|
CHECK(game_q2->contents_to_native(combined) == Q2_CONTENTS_SOLID);
|
||||||
CHECK(!combined.game_data.has_value());
|
|
||||||
CHECK(combined.is_solid(game_q2));
|
CHECK(combined.is_solid(game_q2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SUBCASE("water combined with others")
|
SUBCASE("water combined with others")
|
||||||
{
|
{
|
||||||
contentflags_t water{Q2_CONTENTS_WATER};
|
contentflags_t water = game_q2->create_contents_from_native(Q2_CONTENTS_WATER);
|
||||||
|
|
||||||
for (const auto &c : test_contents) {
|
for (const auto &c : test_contents) {
|
||||||
auto combined = game_q2->combine_contents(water, c);
|
auto combined = game_q2->combine_contents(water, c);
|
||||||
CHECK(!combined.game_data.has_value());
|
|
||||||
|
|
||||||
SUBCASE(fmt::format("water combined with {}", c.to_string(game_q2)).c_str())
|
SUBCASE(fmt::format("water combined with {}", c.to_string(game_q2)).c_str())
|
||||||
{
|
{
|
||||||
if (!(c.native & Q2_CONTENTS_SOLID)) {
|
if (!(game_q2->contents_to_native(c) & Q2_CONTENTS_SOLID)) {
|
||||||
CHECK(combined.native == (Q2_CONTENTS_WATER | c.native));
|
CHECK(game_q2->contents_to_native(combined) == (Q2_CONTENTS_WATER | game_q2->contents_to_native(c)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("q1 contents roundtrip")
|
||||||
|
{
|
||||||
|
auto *game_q1 = bspver_q1.game;
|
||||||
|
|
||||||
|
for (int i = CONTENTS_EMPTY; i >= CONTENTS_MIN; --i) {
|
||||||
|
contentflags_t test_internal = game_q1->create_contents_from_native(i);
|
||||||
|
|
||||||
|
uint32_t test_out = game_q1->contents_to_native(test_internal);
|
||||||
|
|
||||||
|
INFO("contents " << i);
|
||||||
|
CHECK(test_out == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("q2 contents roundtrip")
|
||||||
|
{
|
||||||
|
auto *game_q2 = bspver_q2.game;
|
||||||
|
|
||||||
|
CHECK(game_q2->contents_to_native(game_q2->create_contents_from_native(0)) == 0);
|
||||||
|
|
||||||
|
for (int i = 0; i <= 31; ++i) {
|
||||||
|
uint32_t test_in = nth_bit<uint32_t>(i);
|
||||||
|
|
||||||
|
contentflags_t test_internal = game_q2->create_contents_from_native(test_in);
|
||||||
|
|
||||||
|
uint32_t test_out = game_q2->contents_to_native(test_internal);
|
||||||
|
|
||||||
|
INFO("contents bit " << i);
|
||||||
|
CHECK(test_out == test_in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("imglib png loader")
|
TEST_CASE("imglib png loader")
|
||||||
{
|
{
|
||||||
auto *game = bspver_q2.game;
|
auto *game = bspver_q2.game;
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ static testresults_t QbspVisLight_Common(const std::filesystem::path &name, std:
|
||||||
args.push_back("-noverbose");
|
args.push_back("-noverbose");
|
||||||
} else {
|
} else {
|
||||||
args.push_back("-nopercent");
|
args.push_back("-nopercent");
|
||||||
|
args.push_back("-loghulls");
|
||||||
}
|
}
|
||||||
for (auto &extra : extra_qbsp_args) {
|
for (auto &extra : extra_qbsp_args) {
|
||||||
args.push_back(extra);
|
args.push_back(extra);
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,16 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
// parse "-threads 1"
|
// parse "-threads 1"
|
||||||
if (!strcmp("-threads", argv[i]) && (i + 1) < argc) {
|
if (!strcmp("-threads", argv[i]) || !strcmp("--threads", argv[i])) {
|
||||||
|
if (!(i + 1 < argc)) {
|
||||||
|
logging::print("--threads requires an argument\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
configureTBB(atoi(argv[i + 1]), false);
|
configureTBB(atoi(argv[i + 1]), false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// parse "-verbose"
|
// parse "-verbose"
|
||||||
if (!strcmp("-verbose", argv[i])) {
|
if (!strcmp("-verbose", argv[i]) || !strcmp("--verbose", argv[i])) {
|
||||||
tests_verbose = true;
|
tests_verbose = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ std::tuple<mbsp_t, bspxentries_t, std::optional<prtfile_t>> LoadTestmap(
|
||||||
args.push_back("-noverbose");
|
args.push_back("-noverbose");
|
||||||
} else {
|
} else {
|
||||||
args.push_back("-nopercent");
|
args.push_back("-nopercent");
|
||||||
|
args.push_back("-loghulls");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &arg : extra_args) {
|
for (auto &arg : extra_args) {
|
||||||
|
|
@ -351,7 +352,9 @@ TEST_CASE("duplicatePlanes" * doctest::test_suite("qbsp"))
|
||||||
REQUIRE(1 == worldspawn.mapbrushes.size());
|
REQUIRE(1 == worldspawn.mapbrushes.size());
|
||||||
CHECK(6 == worldspawn.mapbrushes.front().faces.size());
|
CHECK(6 == worldspawn.mapbrushes.front().faces.size());
|
||||||
|
|
||||||
auto brush = LoadBrush(worldspawn, worldspawn.mapbrushes.front(), {CONTENTS_SOLID}, 0, std::nullopt);
|
auto *game = bspver_q1.game;
|
||||||
|
|
||||||
|
auto brush = LoadBrush(worldspawn, worldspawn.mapbrushes.front(), game->create_contents_from_native(CONTENTS_SOLID), 0, std::nullopt);
|
||||||
CHECK(6 == brush->sides.size());
|
CHECK(6 == brush->sides.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1413,13 +1416,15 @@ TEST_CASE("q1_sealing" * doctest::test_suite("testmaps_q1"))
|
||||||
|
|
||||||
TEST_CASE("q1_csg" * doctest::test_suite("testmaps_q1"))
|
TEST_CASE("q1_csg" * doctest::test_suite("testmaps_q1"))
|
||||||
{
|
{
|
||||||
|
auto *game = bspver_q1.game;
|
||||||
|
|
||||||
auto &entity = LoadMapPath("q1_csg.map");
|
auto &entity = LoadMapPath("q1_csg.map");
|
||||||
|
|
||||||
REQUIRE(entity.mapbrushes.size() == 2);
|
REQUIRE(entity.mapbrushes.size() == 2);
|
||||||
|
|
||||||
bspbrush_t::container bspbrushes;
|
bspbrush_t::container bspbrushes;
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
auto b = LoadBrush(entity, entity.mapbrushes[i], {CONTENTS_SOLID}, 0, std::nullopt);
|
auto b = LoadBrush(entity, entity.mapbrushes[i], game->create_contents_from_native(CONTENTS_SOLID), 0, std::nullopt);
|
||||||
|
|
||||||
CHECK(6 == b->sides.size());
|
CHECK(6 == b->sides.size());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -773,13 +773,13 @@ TEST_CASE("q2_detail_wall" * doctest::test_suite("testmaps_q2"))
|
||||||
{
|
{
|
||||||
INFO("check leaf / brush contents");
|
INFO("check leaf / brush contents");
|
||||||
|
|
||||||
CAPTURE(contentflags_t{detail_wall_leaf->contents}.to_string(game));
|
CAPTURE(game->create_contents_from_native(detail_wall_leaf->contents).to_string(game));
|
||||||
CHECK(Q2_CONTENTS_SOLID == detail_wall_leaf->contents);
|
CHECK(Q2_CONTENTS_SOLID == detail_wall_leaf->contents);
|
||||||
|
|
||||||
REQUIRE(1 == Leaf_Brushes(&bsp, detail_wall_leaf).size());
|
REQUIRE(1 == Leaf_Brushes(&bsp, detail_wall_leaf).size());
|
||||||
auto *brush = Leaf_Brushes(&bsp, detail_wall_leaf).at(0);
|
auto *brush = Leaf_Brushes(&bsp, detail_wall_leaf).at(0);
|
||||||
|
|
||||||
CAPTURE(contentflags_t{brush->contents}.to_string(game));
|
CAPTURE(game->create_contents_from_native(brush->contents).to_string(game));
|
||||||
CHECK(Q2_CONTENTS_SOLID == brush->contents);
|
CHECK(Q2_CONTENTS_SOLID == brush->contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -817,7 +817,7 @@ TEST_CASE("q2_detail_fence" * doctest::test_suite("testmaps_q2"))
|
||||||
|
|
||||||
{
|
{
|
||||||
INFO("check leaf / brush contents");
|
INFO("check leaf / brush contents");
|
||||||
CAPTURE(contentflags_t{detail_wall_leaf->contents}.to_string(game));
|
CAPTURE(game->create_contents_from_native(detail_wall_leaf->contents).to_string(game));
|
||||||
|
|
||||||
CHECK(
|
CHECK(
|
||||||
(Q2_CONTENTS_WINDOW | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT) == detail_wall_leaf->contents);
|
(Q2_CONTENTS_WINDOW | Q2_CONTENTS_DETAIL | Q2_CONTENTS_TRANSLUCENT) == detail_wall_leaf->contents);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue