allow Quake-likes to load textures similarly to Q2 (finding best place to load them from)
allow Quake and Q2 to handle other texture formats (mainly just TGA for now)
This commit is contained in:
parent
bf1cb56e5a
commit
c23b7d2ec9
|
|
@ -287,7 +287,7 @@ resolve_result where(const path &p, bool prefer_loose)
|
|||
}
|
||||
|
||||
for (int32_t pass = 0; pass < 2; pass++) {
|
||||
if (prefer_loose == !!pass) {
|
||||
if (prefer_loose != !!pass) {
|
||||
// check absolute + relative
|
||||
if (exists(p)) {
|
||||
return {absrel_dir, p};
|
||||
|
|
@ -296,7 +296,7 @@ resolve_result where(const path &p, bool prefer_loose)
|
|||
for (int32_t archive_pass = 0; archive_pass < 2; archive_pass++) {
|
||||
// check directories & archives, depending on whether
|
||||
// we want loose first or not
|
||||
for (auto &dir : (prefer_loose == !!archive_pass) ? directories : archives) {
|
||||
for (auto &dir : (prefer_loose != !!archive_pass) ? directories : archives) {
|
||||
if (dir->contains(p)) {
|
||||
return {dir, p};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ std::optional<texture> load_wal(const std::string &name, const fs::data &file, b
|
|||
|
||||
texture tex;
|
||||
|
||||
tex.meta.extension = ext::WAL;
|
||||
|
||||
// note: this is a bit of a hack, but the name stored in
|
||||
// the .wal is ignored. it's extraneous and well-formed wals
|
||||
// will all match up anyways.
|
||||
|
|
@ -173,6 +175,8 @@ std::optional<texture> load_mip(const std::string &name, const fs::data &file, b
|
|||
}
|
||||
|
||||
texture tex;
|
||||
|
||||
tex.meta.extension = ext::MIP;
|
||||
|
||||
// note: this is a bit of a hack, but the name stored in
|
||||
// the mip is ignored. it's extraneous and well-formed mips
|
||||
|
|
@ -294,6 +298,8 @@ std::optional<texture> load_tga(const std::string &name, const fs::data &file, b
|
|||
|
||||
texture tex;
|
||||
|
||||
tex.meta.extension = ext::TGA;
|
||||
|
||||
tex.meta.name = name;
|
||||
tex.meta.width = columns;
|
||||
tex.meta.height = rows;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,20 @@ See file, 'COPYING', for details.
|
|||
|
||||
namespace img
|
||||
{
|
||||
enum class ext
|
||||
{
|
||||
TGA,
|
||||
WAL,
|
||||
MIP
|
||||
};
|
||||
|
||||
constexpr struct { const char *suffix; ext id; } extension_list[] = {
|
||||
{ ".tga", ext::TGA },
|
||||
{ ".wal", ext::WAL },
|
||||
{ ".mip", ext::MIP },
|
||||
{ "", ext::MIP }
|
||||
};
|
||||
|
||||
extern std::vector<qvec3b> palette;
|
||||
|
||||
// Palette
|
||||
|
|
@ -37,6 +51,8 @@ struct texture_meta
|
|||
std::string name;
|
||||
uint32_t width, height;
|
||||
|
||||
ext extension;
|
||||
|
||||
// This member is only set before insertion into the table
|
||||
// and not calculated by individual load functions.
|
||||
qvec3b averageColor;
|
||||
|
|
|
|||
|
|
@ -179,8 +179,10 @@ struct mapdata_t
|
|||
uint32_t brush_offset = 0;
|
||||
// Small cache for image meta in the current map
|
||||
std::unordered_map<std::string, std::optional<img::texture_meta>> meta_cache;
|
||||
|
||||
const std::optional<img::texture_meta> &load_image_meta(const char *name);
|
||||
// load or fetch image meta associated with the specified name
|
||||
const std::optional<img::texture_meta> &load_image_meta(const std::string_view &name);
|
||||
// load image data for the specified name
|
||||
std::tuple<std::optional<img::texture>, fs::resolve_result, fs::data> load_image_data(const std::string_view &name, bool meta_only);
|
||||
// whether we had attempted loading texture stuff
|
||||
bool textures_loaded = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -220,8 +220,9 @@ public:
|
|||
"func_detail_fence brushes are omitted from the compile"};
|
||||
setting_bool expand{
|
||||
this, "expand", false, &common_format_group, "write hull 1 expanded brushes to expanded.map for debugging"};
|
||||
setting_wadpathset wadpaths{this, {"wadpath", "xwadpath"}, &debugging_group,
|
||||
setting_wadpathset wadpaths{this, {"wadpath", "xwadpath"}, &map_development_group,
|
||||
"add a path to the wad search paths; wads found in xwadpath's will not be embedded, otherwise they will be embedded (if not -notex)"};
|
||||
setting_set paths{this, "path", "\"/path/to/folder\" <multiple allowed>", &map_development_group, "additional paths or archives to add to the search path, mostly for loose files"};
|
||||
setting_bool notriggermodels{this, "notriggermodels", false, &common_format_group, "for supported game code only: triggers will not write a model\nout, and will instead just write out their mins/maxs."};
|
||||
setting_set aliasdefs{this, "aliasdef", "\"path/to/file.def\" <multiple allowed>", &map_development_group, "path to an alias definition file, which can transform entities in the .map into other entities."};
|
||||
|
||||
|
|
|
|||
75
qbsp/map.cc
75
qbsp/map.cc
|
|
@ -43,38 +43,61 @@
|
|||
|
||||
mapdata_t map;
|
||||
|
||||
const std::optional<img::texture_meta> &mapdata_t::load_image_meta(const char *name)
|
||||
std::tuple<std::optional<img::texture>, fs::resolve_result, fs::data> mapdata_t::load_image_data(const std::string_view &name, bool meta_only)
|
||||
{
|
||||
fs::path prefix;
|
||||
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
prefix = "textures";
|
||||
}
|
||||
|
||||
for (auto &ext : img::extension_list) {
|
||||
fs::path p = (prefix / name) += ext.suffix;
|
||||
|
||||
if (auto pos = fs::where(p, options.filepriority.value() == settings::search_priority_t::LOOSE)) {
|
||||
if (auto data = fs::load(pos)) {
|
||||
std::optional<img::texture> texture;
|
||||
|
||||
switch (ext.id) {
|
||||
case img::ext::TGA:
|
||||
texture = img::load_tga(name.data(), data, meta_only);
|
||||
break;
|
||||
case img::ext::WAL:
|
||||
texture = img::load_wal(name.data(), data, meta_only);
|
||||
break;
|
||||
case img::ext::MIP:
|
||||
texture = img::load_mip(name.data(), data, meta_only, options.target_game);
|
||||
break;
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
return {texture, pos, data};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {std::nullopt, {}, {}};
|
||||
}
|
||||
|
||||
const std::optional<img::texture_meta> &mapdata_t::load_image_meta(const std::string_view &name)
|
||||
{
|
||||
static std::optional<img::texture_meta> nullmeta = std::nullopt;
|
||||
auto it = meta_cache.find(name);
|
||||
auto it = meta_cache.find(name.data());
|
||||
|
||||
if (it != meta_cache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// FIXME: better method
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
fs::path p = fs::path("textures") / name += ".wal";
|
||||
fs::data wal = fs::load(p);
|
||||
auto [texture, _0, _1] = load_image_data(name, true);
|
||||
|
||||
if (!wal) {
|
||||
logging::print("WARNING: Couldn't locate texture for {}\n", name);
|
||||
meta_cache.emplace(name, std::nullopt);
|
||||
return nullmeta;
|
||||
}
|
||||
|
||||
return meta_cache.emplace(name, img::load_wal(name, wal, true)->meta).first->second;
|
||||
} else {
|
||||
fs::data mip = fs::load(name);
|
||||
|
||||
if (!mip) {
|
||||
logging::print("WARNING: Couldn't locate texture for {}\n", name);
|
||||
meta_cache.emplace(name, std::nullopt);
|
||||
return nullmeta;
|
||||
}
|
||||
|
||||
return meta_cache.emplace(name, img::load_mip(name, mip, true, options.target_game)->meta).first->second;
|
||||
if (texture) {
|
||||
return meta_cache.emplace(name, texture->meta).first->second;
|
||||
}
|
||||
|
||||
logging::print("WARNING: Couldn't locate texture for {}\n", name);
|
||||
meta_cache.emplace(name, std::nullopt);
|
||||
return nullmeta;
|
||||
}
|
||||
|
||||
static std::shared_ptr<fs::archive_like> LoadTexturePath(const fs::path &path)
|
||||
|
|
@ -96,6 +119,10 @@ static void EnsureTexturesLoaded()
|
|||
return;
|
||||
|
||||
map.textures_loaded = true;
|
||||
|
||||
for (auto &path : options.paths.values()) {
|
||||
fs::addArchive(path, true);
|
||||
}
|
||||
|
||||
// Q2 doesn't need this
|
||||
if (options.target_game->id == GAME_QUAKE_II) {
|
||||
|
|
@ -133,7 +160,7 @@ static void EnsureTexturesLoaded()
|
|||
defaultwad.replace_extension("wad");
|
||||
|
||||
if (fs::exists(defaultwad)) {
|
||||
logging::print("Using default WAD: {}\n", defaultwad);
|
||||
logging::print("INFO: Using default WAD: {}\n", defaultwad);
|
||||
LoadTexturePath(defaultwad);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
qbsp/qbsp.cc
29
qbsp/qbsp.cc
|
|
@ -1131,18 +1131,14 @@ static void CreateHulls(void)
|
|||
static void LoadTextureData()
|
||||
{
|
||||
for (size_t i = 0; i < map.miptex.size(); i++) {
|
||||
auto pos = fs::where(map.miptex[i].name, options.filepriority.value() == settings::search_priority_t::LOOSE);
|
||||
|
||||
if (!pos) {
|
||||
logging::print("WARNING: Texture {} not found\n", map.miptex[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto file = fs::load(pos);
|
||||
auto tex = img::load_mip(map.miptex[i].name, file, true, options.target_game);
|
||||
auto [tex, pos, file] = map.load_image_data(map.miptex[i].name, true);
|
||||
|
||||
if (!tex) {
|
||||
logging::print("WARNING: unable to load texture {} in archive {}\n", map.miptex[i].name, pos.archive->pathname);
|
||||
if (pos.archive) {
|
||||
logging::print("WARNING: unable to load texture {} in archive {}\n", map.miptex[i].name, pos.archive->pathname);
|
||||
} else {
|
||||
logging::print("WARNING: unable to find texture {}\n", map.miptex[i].name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1151,14 +1147,21 @@ static void LoadTextureData()
|
|||
miptex.width = tex->meta.width;
|
||||
miptex.height = tex->meta.height;
|
||||
|
||||
if (!pos.archive->external) {
|
||||
// only mips can be embedded directly
|
||||
if (!pos.archive->external && tex->meta.extension == img::ext::MIP) {
|
||||
miptex.data = std::move(file.value());
|
||||
} else {
|
||||
// construct fake data that solely contains the header.
|
||||
miptex.data.resize(sizeof(dmiptex_t));
|
||||
|
||||
|
||||
dmiptex_t header {};
|
||||
std::copy(miptex.name.begin(), miptex.name.end(), header.name.begin());
|
||||
if (miptex.name.size() >= 16) {
|
||||
logging::print("WARNING: texture {} name too long for Quake miptex\n", miptex.name);
|
||||
std::copy_n(miptex.name.begin(), 15, header.name.begin());
|
||||
} else {
|
||||
std::copy(miptex.name.begin(), miptex.name.end(), header.name.begin());
|
||||
}
|
||||
|
||||
header.width = miptex.width;
|
||||
header.height = miptex.height;
|
||||
header.offsets = { -1, -1, -1, -1 };
|
||||
|
|
|
|||
Loading…
Reference in New Issue