Merge branch 'type-cleanup' into brushbsp
# Conflicts: # common/bspfile.cc # include/common/bspfile.hh # light/trace.cc
This commit is contained in:
commit
9f734ff976
|
|
@ -1 +1 @@
|
|||
Subproject commit 605a34765aa5d5ecbf476b4598a862ada971b0cc
|
||||
Subproject commit 62fd660583d3ae7a7886930b413c3c570e89786c
|
||||
|
|
@ -98,7 +98,16 @@ public:
|
|||
}
|
||||
|
||||
bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const override
|
||||
int32_t surfflags_from_string(const std::string_view &str) const
|
||||
{
|
||||
if (string_iequals(str, "special")) {
|
||||
return TEX_SPECIAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const override {
|
||||
// anything texname other than "hint" in a hint brush is treated as "hintskip", and discarded
|
||||
return !string_iequals(name, "hint");
|
||||
}
|
||||
|
|
@ -310,8 +319,13 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1, bool transwater, bool transsky) const override
|
||||
int32_t contents_from_string(const std::string_view &str) const override
|
||||
{
|
||||
// Q1 doesn't get contents from files
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1, bool transwater, bool transsky) const override {
|
||||
/* If water is transparent, liquids are like empty space */
|
||||
if (transwater) {
|
||||
if (contents_are_liquid(contents0) && contents_are_empty(contents1))
|
||||
|
|
@ -709,8 +723,21 @@ struct gamedef_q2_t : public gamedef_t
|
|||
return true;
|
||||
}
|
||||
|
||||
bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const override
|
||||
static constexpr const char *surf_bitflag_names[] = {"LIGHT", "SLICK", "SKY", "WARP", "TRANS33", "TRANS66", "FLOWING", "NODRAW",
|
||||
"HINT" };
|
||||
|
||||
int32_t surfflags_from_string(const std::string_view &str) const override
|
||||
{
|
||||
for (size_t i = 0; i < std::size(surf_bitflag_names); i++) {
|
||||
if (string_iequals(str, surf_bitflag_names[i])) {
|
||||
return nth_bit(i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const override {
|
||||
// any face in a hint brush that isn't HINT are treated as "hintskip", and discarded
|
||||
return !(flags.native & Q2_SURF_HINT);
|
||||
}
|
||||
|
|
@ -913,6 +940,22 @@ struct gamedef_q2_t : public gamedef_t
|
|||
return true;
|
||||
}
|
||||
|
||||
static constexpr const char *bitflag_names[] = {"SOLID", "WINDOW", "AUX", "LAVA", "SLIME", "WATER", "MIST", "128",
|
||||
"256", "512", "1024", "2048", "4096", "8192", "16384", "AREAPORTAL", "PLAYERCLIP", "MONSTERCLIP",
|
||||
"CURRENT_0", "CURRENT_90", "CURRENT_180", "CURRENT_270", "CURRENT_UP", "CURRENT_DOWN", "ORIGIN", "MONSTER",
|
||||
"DEADMONSTER", "DETAIL", "TRANSLUCENT", "LADDER", "1073741824", "2147483648"};
|
||||
|
||||
int32_t contents_from_string(const std::string_view &str) const
|
||||
{
|
||||
for (size_t i = 0; i < std::size(bitflag_names); i++) {
|
||||
if (string_iequals(str, bitflag_names[i])) {
|
||||
return nth_bit(i);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the single content bit of the strongest visible content present
|
||||
*/
|
||||
|
|
@ -1002,11 +1045,6 @@ struct gamedef_q2_t : public gamedef_t
|
|||
return "EMPTY";
|
||||
}
|
||||
|
||||
constexpr const char *bitflag_names[] = {"SOLID", "WINDOW", "AUX", "LAVA", "SLIME", "WATER", "MIST", "128",
|
||||
"256", "512", "1024", "2048", "4096", "8192", "16384", "AREAPORTAL", "PLAYERCLIP", "MONSTERCLIP",
|
||||
"CURRENT_0", "CURRENT_90", "CURRENT_180", "CURRENT_270", "CURRENT_UP", "CURRENT_DOWN", "ORIGIN", "MONSTER",
|
||||
"DEADMONSTER", "DETAIL", "TRANSLUCENT", "LADDER", "1073741824", "2147483648"};
|
||||
|
||||
std::string s;
|
||||
|
||||
for (int32_t i = 0; i < std::size(bitflag_names); i++) {
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ string_replaceall(std::string &str, const std::string &from, const std::string &
|
|||
}
|
||||
|
||||
bool // mxd
|
||||
string_iequals(const std::string &a, const std::string &b)
|
||||
string_iequals(const std::string_view &a, const std::string_view &b)
|
||||
{
|
||||
size_t sz = a.size();
|
||||
if (b.size() != sz)
|
||||
|
|
|
|||
151
common/imglib.cc
151
common/imglib.cc
|
|
@ -3,6 +3,7 @@
|
|||
#include <common/fs.hh>
|
||||
#include <common/imglib.hh>
|
||||
#include <common/entdata.h>
|
||||
#include <common/json.hh>
|
||||
|
||||
/*
|
||||
============================================================================
|
||||
|
|
@ -136,8 +137,8 @@ std::optional<texture> load_wal(const std::string_view &name, const fs::data &fi
|
|||
// the .wal is ignored. it's extraneous and well-formed wals
|
||||
// will all match up anyways.
|
||||
tex.meta.name = name;
|
||||
tex.meta.width = mt.width;
|
||||
tex.meta.height = mt.height;
|
||||
tex.meta.width = tex.width = mt.width;
|
||||
tex.meta.height = tex.height = mt.height;
|
||||
tex.meta.contents = {mt.contents};
|
||||
tex.meta.flags = {mt.flags};
|
||||
tex.meta.value = mt.value;
|
||||
|
|
@ -182,17 +183,16 @@ std::optional<texture> load_mip(const std::string_view &name, const fs::data &fi
|
|||
// the mip is ignored. it's extraneous and well-formed mips
|
||||
// will all match up anyways.
|
||||
tex.meta.name = name;
|
||||
tex.meta.width = header.width;
|
||||
tex.meta.height = header.height;
|
||||
tex.meta.width = tex.width = header.width;
|
||||
tex.meta.height = tex.height = header.height;
|
||||
|
||||
if (!meta_only) {
|
||||
// convert the data into RGBA.
|
||||
// miptex only has meta
|
||||
if (header.offsets[0] <= 0) {
|
||||
// this should never happen under normal circumstances
|
||||
logging::funcprint("attempted to load external mip for {}\n", name);
|
||||
return std::nullopt;
|
||||
return tex;
|
||||
}
|
||||
|
||||
|
||||
// convert the data into RGBA.
|
||||
// sanity check
|
||||
if (header.offsets[0] + (header.width * header.height) > file->size()) {
|
||||
logging::funcprint("mip offset0 overrun for {}\n", name);
|
||||
|
|
@ -301,8 +301,8 @@ std::optional<texture> load_tga(const std::string_view &name, const fs::data &fi
|
|||
tex.meta.extension = ext::TGA;
|
||||
|
||||
tex.meta.name = name;
|
||||
tex.meta.width = columns;
|
||||
tex.meta.height = rows;
|
||||
tex.meta.width = tex.width = columns;
|
||||
tex.meta.height = tex.height = rows;
|
||||
|
||||
if (!meta_only) {
|
||||
tex.pixels.resize(numPixels);
|
||||
|
|
@ -450,4 +450,133 @@ std::tuple<std::optional<img::texture>, fs::resolve_result, fs::data> load_textu
|
|||
|
||||
return {std::nullopt, {}, {}};
|
||||
}
|
||||
|
||||
/*
|
||||
JSON meta format, meant to supplant .wal's metadata for external texture use.
|
||||
All of the values are optional.
|
||||
{
|
||||
// valid instances of "contents"; either:
|
||||
// - a case-insensitive string containing the textual representation
|
||||
// of the content type
|
||||
// - a number
|
||||
// - an array of the two above, which will be OR'd together
|
||||
"contents": [ "SOLID", 8 ],
|
||||
"contents": 24,
|
||||
"contents": "SOLID",
|
||||
|
||||
// valid instances of "flags"; either:
|
||||
// - a case-insensitive string containing the textual representation
|
||||
// of the surface flags
|
||||
// - a number
|
||||
// - an array of the two above, which will be OR'd together
|
||||
"flags": [ "SKY", 16 ],
|
||||
"flags": 24,
|
||||
"flags": "SKY",
|
||||
|
||||
// "value" must be an integer
|
||||
"value": 1234,
|
||||
|
||||
// "animation" must be the name of the next texture in
|
||||
// the chain.
|
||||
"animation": "e1u1/comp2",
|
||||
|
||||
// width/height are allowed to be supplied in order to
|
||||
// have the editor treat the surface as if its dimensions
|
||||
// are these rather than the ones pulled in from the image
|
||||
// itself. they must be integers.
|
||||
"width": 64,
|
||||
"height": 64
|
||||
}
|
||||
*/
|
||||
std::optional<texture_meta> load_wal_json_meta(const std::string_view &name, const fs::data &file, const gamedef_t *game)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto json = json::parse(file->begin(), file->end());
|
||||
|
||||
texture_meta meta{};
|
||||
|
||||
if (json.contains("width") && json["width"].is_number_integer()) {
|
||||
meta.width = json["width"].get<int32_t>();
|
||||
}
|
||||
|
||||
if (json.contains("height") && json["height"].is_number_integer()) {
|
||||
meta.height = json["height"].get<int32_t>();
|
||||
}
|
||||
|
||||
if (json.contains("value") && json["value"].is_number_integer()) {
|
||||
meta.value = json["value"].get<int32_t>();
|
||||
}
|
||||
|
||||
if (json.contains("contents")) {
|
||||
auto &contents = json["contents"];
|
||||
|
||||
if (contents.is_number_integer()) {
|
||||
meta.contents.native = contents.get<int32_t>();
|
||||
} else if (contents.is_string()) {
|
||||
meta.contents.native = game->contents_from_string(contents.get<std::string>());
|
||||
} else if (contents.is_array()) {
|
||||
for (auto &content : contents) {
|
||||
if (content.is_number_integer()) {
|
||||
meta.contents.native |= content.get<int32_t>();
|
||||
} else if (content.is_string()) {
|
||||
meta.contents.native |= game->contents_from_string(content.get<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("flags")) {
|
||||
auto &flags = json["flags"];
|
||||
|
||||
if (flags.is_number_integer()) {
|
||||
meta.flags.native = flags.get<int32_t>();
|
||||
} else if (flags.is_string()) {
|
||||
meta.flags.native = game->surfflags_from_string(flags.get<std::string>());
|
||||
} else if (flags.is_array()) {
|
||||
for (auto &flag : flags) {
|
||||
if (flag.is_number_integer()) {
|
||||
meta.flags.native |= flag.get<int32_t>();
|
||||
} else if (flag.is_string()) {
|
||||
meta.flags.native |= game->surfflags_from_string(flag.get<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("animation") && json["animation"].is_string()) {
|
||||
meta.animation = json["animation"].get<std::string>();
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
catch (json::exception e)
|
||||
{
|
||||
logging::funcprint("{}, invalid JSON: {}\n", name, e.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<std::optional<img::texture_meta>, fs::resolve_result, fs::data> load_texture_meta(const std::string_view &name, const gamedef_t *game, const settings::common_settings &options)
|
||||
{
|
||||
fs::path prefix;
|
||||
|
||||
if (game->id == GAME_QUAKE_II) {
|
||||
prefix = "textures";
|
||||
}
|
||||
|
||||
for (auto &ext : img::meta_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)) {
|
||||
if (auto texture = ext.loader(name.data(), data, game)) {
|
||||
return {texture, pos, data};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {std::nullopt, {}, {}};
|
||||
}
|
||||
} // namespace img
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ struct gamedef_t
|
|||
virtual bool surf_is_lightmapped(const surfflags_t &flags) const = 0;
|
||||
virtual bool surf_is_subdivided(const surfflags_t &flags) const = 0;
|
||||
virtual bool surfflags_are_valid(const surfflags_t &flags) 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
|
||||
virtual bool texinfo_is_hintskip(const surfflags_t &flags, const std::string &name) const = 0;
|
||||
virtual contentflags_t cluster_contents(const contentflags_t &contents0, const contentflags_t &contents1) const = 0;
|
||||
|
|
@ -283,6 +284,7 @@ struct gamedef_t
|
|||
virtual bool contents_are_sky(const contentflags_t &contents) const = 0;
|
||||
virtual bool contents_are_liquid(const contentflags_t &contents) const = 0;
|
||||
virtual bool contents_are_valid(const contentflags_t &contents, bool strict = true) const = 0;
|
||||
virtual int32_t contents_from_string(const std::string_view &str) const = 0;
|
||||
virtual bool portal_can_see_through(const contentflags_t &contents0, const contentflags_t &contents1, bool transwater, bool transsky) const = 0;
|
||||
virtual bool contents_seals_map(const contentflags_t &contents) const = 0;
|
||||
virtual contentflags_t contents_remap_for_export(const contentflags_t &contents) const = 0;
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ inline int32_t Q_strcasecmp(const std::string_view &a, const std::string_view &b
|
|||
(a.data(), b.data());
|
||||
}
|
||||
|
||||
bool string_iequals(const std::string &a, const std::string &b); // mxd
|
||||
bool string_iequals(const std::string_view &a, const std::string_view &b); // mxd
|
||||
|
||||
struct case_insensitive_hash
|
||||
{
|
||||
|
|
|
|||
|
|
@ -42,25 +42,35 @@ void init_palette(const gamedef_t *game);
|
|||
struct texture_meta
|
||||
{
|
||||
std::string name;
|
||||
uint32_t width, height;
|
||||
uint32_t width = 0, height = 0;
|
||||
|
||||
ext extension;
|
||||
|
||||
// This member is only set before insertion into the table
|
||||
// and not calculated by individual load functions.
|
||||
qvec3b averageColor;
|
||||
// extension that we pulled the pixels in from.
|
||||
std::optional<ext> extension;
|
||||
|
||||
// Q2/WAL only
|
||||
surfflags_t flags;
|
||||
contentflags_t contents;
|
||||
int32_t value;
|
||||
surfflags_t flags{};
|
||||
contentflags_t contents{};
|
||||
int32_t value = 0;
|
||||
std::string animation;
|
||||
};
|
||||
|
||||
struct texture
|
||||
{
|
||||
texture_meta meta{};
|
||||
|
||||
// in the case of replacement textures, these may not
|
||||
// the width/height of the metadata.
|
||||
uint32_t width = 0, height = 0;
|
||||
|
||||
std::vector<qvec4b> pixels;
|
||||
|
||||
// the scale required to map a pixel from the
|
||||
// meta data onto the real size (16x16 onto 32x32 -> 2)
|
||||
float width_scale = 1, height_scale = 1;
|
||||
|
||||
// This member is only set before insertion into the table
|
||||
// and not calculated by individual load functions.
|
||||
qvec3b averageColor { 0 };
|
||||
};
|
||||
|
||||
extern std::unordered_map<std::string, texture, case_insensitive_hash, case_insensitive_equal> textures;
|
||||
|
|
@ -88,4 +98,31 @@ constexpr struct { const char *suffix; ext id; decltype(load_wal) *loader; } ext
|
|||
|
||||
// Attempt to load a texture from the specified name.
|
||||
std::tuple<std::optional<texture>, fs::resolve_result, fs::data> load_texture(const std::string_view &name, bool meta_only, const gamedef_t *game, const settings::common_settings &options);
|
||||
|
||||
enum class meta_ext
|
||||
{
|
||||
WAL,
|
||||
WAL_JSON
|
||||
};
|
||||
|
||||
// Load wal
|
||||
inline std::optional<texture_meta> load_wal_meta(const std::string_view &name, const fs::data &file, const gamedef_t *game)
|
||||
{
|
||||
if (auto tex = load_wal(name, file, true, game)) {
|
||||
return tex->meta;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<texture_meta> load_wal_json_meta(const std::string_view &name, const fs::data &file, const gamedef_t *game);
|
||||
|
||||
// list of supported meta extensions and their loaders
|
||||
constexpr struct { const char *suffix; meta_ext id; decltype(load_wal_meta) *loader; } meta_extension_list[] = {
|
||||
{ ".wal", meta_ext::WAL, load_wal_meta },
|
||||
{ ".wal_json", meta_ext::WAL_JSON, load_wal_json_meta }
|
||||
};
|
||||
|
||||
// Attempt to load a texture meta from the specified name.
|
||||
std::tuple<std::optional<texture_meta>, fs::resolve_result, fs::data> load_texture_meta(const std::string_view &name, const gamedef_t *game, const settings::common_settings &options);
|
||||
}; // namespace img
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ void to_json(json &j, const qvec<T, N> &p)
|
|||
template<typename T, size_t N>
|
||||
void from_json(const json &j, qvec<T, N> &p)
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
p[i] = j[i].get<T>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ qvec3b Face_LookupTextureColor(const mbsp_t *bsp, const mface_t *face)
|
|||
auto it = img::find(Face_TextureName(bsp, face));
|
||||
|
||||
if (it) {
|
||||
return it->meta.averageColor;
|
||||
return it->averageColor;
|
||||
}
|
||||
|
||||
return {127};
|
||||
|
|
|
|||
106
light/light.cc
106
light/light.cc
|
|
@ -1013,43 +1013,63 @@ static inline void WriteNormals(const mbsp_t &bsp, bspdata_t &bspdata)
|
|||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
Load (Quake 2) / Convert (Quake, Hexen 2) textures from paletted to RGBA (mxd)
|
||||
==============================================================================
|
||||
// Add empty to keep texture index in case of load problems...
|
||||
auto &tex = img::textures.emplace(miptex.name, img::texture{}).first->second;
|
||||
|
||||
// try to load it externally first
|
||||
auto [texture, _0, _1] = img::load_texture(miptex.name, false, bsp->loadversion->game, options);
|
||||
|
||||
if (texture) {
|
||||
tex = std::move(texture.value());
|
||||
} else {
|
||||
if (miptex.data.size() <= sizeof(dmiptex_t)) {
|
||||
logging::funcprint("WARNING: can't find texture {}\n", miptex.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto loaded_tex = img::load_mip(miptex.name, miptex.data, false, bsp->loadversion->game);
|
||||
|
||||
if (!loaded_tex) {
|
||||
logging::funcprint("WARNING: Texture {} is invalid\n", miptex.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
tex = std::move(loaded_tex.value());
|
||||
}
|
||||
|
||||
tex.meta.averageColor = img::calculate_average(tex.pixels);
|
||||
*/
|
||||
static void AddTextureName(const char *textureName, const mbsp_t *bsp)
|
||||
|
||||
// Load the specified texture from the BSP
|
||||
static void AddTextureName(const std::string_view &textureName, const mbsp_t *bsp)
|
||||
{
|
||||
if (img::find(textureName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// always add entry
|
||||
auto &tex = img::textures.emplace(textureName, img::texture{}).first->second;
|
||||
|
||||
// find wal first, since we'll use it for metadata
|
||||
auto wal = fs::load("textures" / fs::path(textureName) += ".wal");
|
||||
// find texture & meta
|
||||
auto [ texture, _0, _1 ] = img::load_texture(textureName, false, bsp->loadversion->game, options);
|
||||
|
||||
if (!wal) {
|
||||
logging::funcprint("WARNING: can't find .wal for {}\n", textureName);
|
||||
if (!texture) {
|
||||
logging::funcprint("WARNING: can't find pixel data for {}\n", textureName);
|
||||
} else {
|
||||
auto walTex = img::load_wal(textureName, wal, false, bsp->loadversion->game);
|
||||
|
||||
if (walTex) {
|
||||
tex = std::move(*walTex);
|
||||
}
|
||||
tex = std::move(texture.value());
|
||||
}
|
||||
|
||||
// now check for replacements
|
||||
auto [replacement_tex, _0, _1] = img::load_texture(textureName, false, bsp->loadversion->game, options);
|
||||
|
||||
// FIXME: I think this is fundamentally wrong; we need the
|
||||
// original texture's size for texcoords
|
||||
if (replacement_tex) {
|
||||
tex.meta.width = replacement_tex->meta.width;
|
||||
tex.meta.height = replacement_tex->meta.height;
|
||||
tex.pixels = std::move(replacement_tex->pixels);
|
||||
auto [ texture_meta, __0, __1 ] = img::load_texture_meta(textureName, bsp->loadversion->game, options);
|
||||
|
||||
if (!texture_meta) {
|
||||
logging::funcprint("WARNING: can't find meta data for {}\n", textureName);
|
||||
} else {
|
||||
tex.meta = std::move(texture_meta.value());
|
||||
}
|
||||
|
||||
tex.meta.averageColor = img::calculate_average(tex.pixels);
|
||||
tex.averageColor = img::calculate_average(tex.pixels);
|
||||
tex.width_scale = (float) tex.width / (float) tex.meta.width;
|
||||
tex.height_scale = (float) tex.height / (float) tex.meta.height;
|
||||
}
|
||||
|
||||
// Load all of the referenced textures from the BSP texinfos into
|
||||
|
|
@ -1088,31 +1108,31 @@ static void ConvertTextures(const mbsp_t *bsp)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Add empty to keep texture index in case of load problems...
|
||||
// always add entry
|
||||
auto &tex = img::textures.emplace(miptex.name, img::texture{}).first->second;
|
||||
|
||||
// try to load it externally first
|
||||
auto [texture, _0, _1] = img::load_texture(miptex.name, false, bsp->loadversion->game, options);
|
||||
|
||||
if (texture) {
|
||||
tex = std::move(texture.value());
|
||||
} else {
|
||||
if (miptex.data.size() <= sizeof(dmiptex_t)) {
|
||||
logging::funcprint("WARNING: can't find texture {}\n", miptex.name);
|
||||
continue;
|
||||
// if the miptex entry isn't a dummy, use it as our base
|
||||
if (miptex.data.size() >= sizeof(dmiptex_t)) {
|
||||
if (auto loaded_tex = img::load_mip(miptex.name, miptex.data, false, bsp->loadversion->game)) {
|
||||
tex = std::move(loaded_tex.value());
|
||||
}
|
||||
|
||||
auto loaded_tex = img::load_mip(miptex.name, miptex.data, false, bsp->loadversion->game);
|
||||
|
||||
if (!loaded_tex) {
|
||||
logging::funcprint("WARNING: Texture {} is invalid\n", miptex.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
tex = std::move(loaded_tex.value());
|
||||
}
|
||||
|
||||
tex.meta.averageColor = img::calculate_average(tex.pixels);
|
||||
// find replacement texture
|
||||
if (auto [ texture, _0, _1 ] = img::load_texture(miptex.name, false, bsp->loadversion->game, options); texture) {
|
||||
tex.width = texture->width;
|
||||
tex.height = texture->height;
|
||||
tex.pixels = std::move(texture->pixels);
|
||||
}
|
||||
|
||||
if (!tex.pixels.size() || !tex.width || !tex.meta.width) {
|
||||
logging::funcprint("WARNING: invalid size data for {}\n", miptex.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
tex.averageColor = img::calculate_average(tex.pixels);
|
||||
tex.width_scale = (float) tex.width / (float) tex.meta.width;
|
||||
tex.height_scale = (float) tex.height / (float) tex.meta.height;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -78,14 +78,14 @@ uint32_t clamp_texcoord(vec_t in, uint32_t width)
|
|||
|
||||
qvec4b SampleTexture(const mface_t *face, const mtexinfo_t *tex, const img::texture *texture, const mbsp_t *bsp, const qvec3d &point)
|
||||
{
|
||||
if (texture == nullptr || !texture->meta.width) {
|
||||
if (texture == nullptr || !texture->width) {
|
||||
return {};
|
||||
}
|
||||
|
||||
qvec2d texcoord = WorldToTexCoord(point, tex);
|
||||
|
||||
const uint32_t x = clamp_texcoord(texcoord[0], texture->meta.width);
|
||||
const uint32_t y = clamp_texcoord(texcoord[1], texture->meta.height);
|
||||
const uint32_t x = clamp_texcoord(texcoord[0], texture->width);
|
||||
const uint32_t y = clamp_texcoord(texcoord[1], texture->width);
|
||||
|
||||
return texture->pixels[(texture->meta.width * y) + x];
|
||||
return texture->pixels[(texture->width * (y * texture->width_scale)) + (x * texture->height_scale)];
|
||||
}
|
||||
|
|
|
|||
23
qbsp/map.cc
23
qbsp/map.cc
|
|
@ -52,9 +52,28 @@ const std::optional<img::texture_meta> &mapdata_t::load_image_meta(const std::st
|
|||
return it->second;
|
||||
}
|
||||
|
||||
auto [texture, _0, _1] = img::load_texture(name, true, options.target_game, options);
|
||||
// try a meta-only texture first; this is all we really need anyways
|
||||
if (auto [texture_meta, _0, _1] = img::load_texture_meta(name, options.target_game, options); texture_meta) {
|
||||
// slight special case: if the meta has no width/height defined,
|
||||
// pull it from the real texture.
|
||||
if (!texture_meta->width || !texture_meta->height) {
|
||||
auto [texture, _0, _1] = img::load_texture(name, true, options.target_game, options);
|
||||
|
||||
if (texture) {
|
||||
texture_meta->width = texture->meta.width;
|
||||
texture_meta->height = texture->meta.height;
|
||||
}
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
if (!texture_meta->width || !texture_meta->height) {
|
||||
logging::print("WARNING: texture {} has empty width/height \n", name);
|
||||
}
|
||||
|
||||
return meta_cache.emplace(name, texture_meta).first->second;
|
||||
}
|
||||
|
||||
// couldn't find a meta texture, so pull it from the pixel image
|
||||
if (auto [texture, _0, _1] = img::load_texture(name, true, options.target_game, options); texture) {
|
||||
return meta_cache.emplace(name, texture->meta).first->second;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue