Lotta more file stuff. Have to think about fscanf though.
This commit is contained in:
parent
326626f127
commit
49fd62b9f3
|
|
@ -562,11 +562,11 @@ int main(int argc, char **argv)
|
|||
|
||||
f << bsp.dentdata;
|
||||
|
||||
f.close();
|
||||
|
||||
if (!f)
|
||||
Error("{}", strerror(errno));
|
||||
|
||||
f.close();
|
||||
|
||||
printf("done.\n");
|
||||
} else if (!strcmp(argv[i], "--extract-textures")) {
|
||||
source.replace_extension(".wad");
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <tuple>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include "tbb/parallel_for.h"
|
||||
|
||||
|
|
@ -757,7 +758,7 @@ static void DecompileEntity(
|
|||
}
|
||||
|
||||
// First, print the key/values for this entity
|
||||
file << '{' << std::endl;
|
||||
fmt::print(file, "{\n");
|
||||
for (const auto &keyValue : dict) {
|
||||
if (keyValue.first == "model" && !keyValue.second.empty() && keyValue.second[0] == '*') {
|
||||
// strip "model" "*NNN" key/values
|
||||
|
|
@ -769,7 +770,7 @@ static void DecompileEntity(
|
|||
continue;
|
||||
}
|
||||
|
||||
file << "\"" << keyValue.first << "\" \"" << keyValue.second << "\"" << std::endl;
|
||||
fmt::print(file, "\"{}\" \"{}\"\n", keyValue.first, keyValue.second);
|
||||
}
|
||||
|
||||
// Print brushes if any
|
||||
|
|
@ -802,7 +803,7 @@ static void DecompileEntity(
|
|||
}
|
||||
}
|
||||
|
||||
file << "}" << std::endl;
|
||||
fmt::print(file, "}\n");
|
||||
}
|
||||
|
||||
void DecompileBSP(const mbsp_t *bsp, const decomp_options &options, std::ofstream &file)
|
||||
|
|
|
|||
|
|
@ -230,13 +230,13 @@ int Q_strncasecmp(const char *s1, const char *s2, int n)
|
|||
if (c2 >= 'a' && c2 <= 'z')
|
||||
c2 -= ('a' - 'A');
|
||||
if (c1 != c2)
|
||||
return -1; /* strings not equal */
|
||||
return c1 < c2 ? -1 : 1; /* strings not equal */
|
||||
}
|
||||
if (!c1)
|
||||
return 0; /* strings are equal */
|
||||
}
|
||||
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Q_strcasecmp(const char *s1, const char *s2)
|
||||
|
|
@ -296,7 +296,11 @@ qfile_t SafeOpenWrite(const std::filesystem::path &filename)
|
|||
{
|
||||
FILE *f;
|
||||
|
||||
#ifdef _WIN32
|
||||
f = _wfopen(filename.c_str(), L"wb");
|
||||
#else
|
||||
f = fopen(filename.string().c_str(), "wb");
|
||||
#endif
|
||||
|
||||
if (!f)
|
||||
FError("Error opening {}: {}", filename, strerror(errno));
|
||||
|
|
@ -304,28 +308,41 @@ qfile_t SafeOpenWrite(const std::filesystem::path &filename)
|
|||
return { f, fclose };
|
||||
}
|
||||
|
||||
qfile_t SafeOpenRead(const std::filesystem::path &filename)
|
||||
qfile_t SafeOpenRead(const std::filesystem::path &filename, bool must_exist)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
#ifdef _WIN32
|
||||
f = _wfopen(filename.c_str(), L"rb");
|
||||
#else
|
||||
f = fopen(filename.string().c_str(), "rb");
|
||||
#endif
|
||||
|
||||
if (!f)
|
||||
FError("Error opening {}: {}", filename, strerror(errno));
|
||||
{
|
||||
if (must_exist)
|
||||
FError("Error opening {}: {}", filename, strerror(errno));
|
||||
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
|
||||
return { f, fclose };
|
||||
}
|
||||
|
||||
void SafeRead(const qfile_t &f, void *buffer, int count)
|
||||
size_t SafeRead(const qfile_t &f, void *buffer, size_t count)
|
||||
{
|
||||
if (fread(buffer, 1, count, f.get()) != (size_t)count)
|
||||
FError("File read failure");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void SafeWrite(const qfile_t &f, const void *buffer, int count)
|
||||
size_t SafeWrite(const qfile_t &f, const void *buffer, size_t count)
|
||||
{
|
||||
if (fwrite(buffer, 1, count, f.get()) != (size_t)count)
|
||||
FError("File write failure");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void SafeSeek(const qfile_t &f, long offset, int32_t origin)
|
||||
|
|
@ -364,7 +381,7 @@ long LoadFilePak(std::filesystem::path &filename, void *destptr)
|
|||
// check if we have a .pak file in this path
|
||||
for (auto p = filename.parent_path(); !p.empty() && p != p.root_path(); p = p.parent_path()) {
|
||||
if (p.extension() == ".pak") {
|
||||
auto file = SafeOpenRead(p);
|
||||
qfile_t file = SafeOpenRead(p);
|
||||
|
||||
// false positive
|
||||
if (!file)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
#include <light/ltface.hh>
|
||||
#include <common/bsputils.hh>
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
entdict_t::entdict_t(std::initializer_list<keyvalue_t> l) : keyvalues(l) { }
|
||||
|
||||
entdict_t::entdict_t() = default;
|
||||
|
|
@ -170,15 +172,15 @@ std::vector<entdict_t> EntData_Parse(const char *entdata)
|
|||
*/
|
||||
std::string EntData_Write(const std::vector<entdict_t> &ents)
|
||||
{
|
||||
std::stringstream out;
|
||||
std::string out;
|
||||
for (const auto &ent : ents) {
|
||||
out << "{\n";
|
||||
out += "{\n";
|
||||
for (const auto &epair : ent) {
|
||||
out << "\"" << epair.first << "\" \"" << epair.second << "\"\n";
|
||||
fmt::format_to(std::back_inserter(out), "\"{}\" \"{}\"\n", epair.first, epair.second);
|
||||
}
|
||||
out << "}\n";
|
||||
out += "}\n";
|
||||
}
|
||||
return out.str();
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string EntDict_StringForKey(const entdict_t &dict, const std::string key)
|
||||
|
|
|
|||
|
|
@ -56,11 +56,7 @@ bool SetPlanePts(const vec3_t planepts[3], vec3_t &normal, vec_t *dist)
|
|||
*/
|
||||
std::string VecStr(const vec3_t vec)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%i %i %i", (int)vec[0], (int)vec[1], (int)vec[2]);
|
||||
|
||||
return buf;
|
||||
return fmt::format("{} {} {}", (int32_t) vec[0], (int32_t) vec[1], (int32_t) vec[2]);
|
||||
}
|
||||
|
||||
std::string // mxd
|
||||
|
|
@ -73,11 +69,7 @@ VecStr(const qvec3f vec)
|
|||
|
||||
std::string VecStrf(const vec3_t vec)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.2f %.2f %.2f", vec[0], vec[1], vec[2]);
|
||||
|
||||
return buf;
|
||||
return fmt::format("{:.2} {:.2} {:.2}", vec[0], vec[1], vec[2]);
|
||||
}
|
||||
|
||||
std::string // mxd
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include <common/qvec.hh>
|
||||
|
||||
#include <cmath> // for NAN
|
||||
#include <sstream>
|
||||
#include <fmt/format.h>
|
||||
|
||||
/*
|
||||
* SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
|
||||
|
|
@ -143,7 +143,5 @@ qmat2x2f qv::inverse(const qmat2x2f &m)
|
|||
|
||||
std::string qv::to_string(const qvec<3, float> &v1)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << v1[0] << " " << v1[1] << " " << v1[2];
|
||||
return ss.str();
|
||||
return fmt::format("{} {} {}", v1[0], v1[1], v1[2]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,38 @@ extern std::filesystem::path qdir, // c:/Quake/, c:/Hexen II/ etc.
|
|||
|
||||
bool string_iequals(const std::string &a, const std::string &b); // mxd
|
||||
|
||||
struct case_insensitive_hash
|
||||
{
|
||||
std::size_t operator()(const std::string &s) const noexcept
|
||||
{
|
||||
std::size_t hash = 0x811c9dc5;
|
||||
constexpr std::size_t prime = 0x1000193;
|
||||
|
||||
for (auto &c : s) {
|
||||
hash ^= tolower(c);
|
||||
hash *= prime;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct case_insensitive_equal
|
||||
{
|
||||
bool operator()(const std::string &l, const std::string &r) const noexcept
|
||||
{
|
||||
return Q_strcasecmp(l.c_str(), r.c_str()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct case_insensitive_less
|
||||
{
|
||||
bool operator()(const std::string &l, const std::string &r) const noexcept
|
||||
{
|
||||
return Q_strcasecmp(l.c_str(), r.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
void SetQdirFromPath(const std::string &basedirname, std::filesystem::path path);
|
||||
|
||||
// Returns the path itself if it has an extension already, otherwise
|
||||
|
|
@ -106,17 +138,11 @@ int CheckParm(const char *check);
|
|||
using qfile_t = std::unique_ptr<FILE, decltype(&fclose)>;
|
||||
|
||||
qfile_t SafeOpenWrite(const std::filesystem::path &filename);
|
||||
qfile_t SafeOpenRead(const std::filesystem::path &filename);
|
||||
void SafeRead(const qfile_t &f, void *buffer, int count);
|
||||
void SafeWrite(const qfile_t &f, const void *buffer, int count);
|
||||
qfile_t SafeOpenRead(const std::filesystem::path &filename, bool must_exist = false);
|
||||
size_t SafeRead(const qfile_t &f, void *buffer, size_t count);
|
||||
size_t SafeWrite(const qfile_t &f, const void *buffer, size_t count);
|
||||
void SafeSeek(const qfile_t &f, long offset, int32_t origin);
|
||||
long SafeTell(const qfile_t &f);
|
||||
template<typename ...Args>
|
||||
inline void SafePrint(const qfile_t &f, const char *fmt, const Args &...args)
|
||||
{
|
||||
if (fmt::fprintf(f.get(), fmt, std::forward<const Args &>(args)...) < 0)
|
||||
FError("Error writing to file");
|
||||
}
|
||||
|
||||
long LoadFilePak(std::filesystem::path &filename, void *destptr);
|
||||
long LoadFile(const std::filesystem::path &filename, void *destptr);
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ public:
|
|||
|
||||
for (; j < 3; j++)
|
||||
if (p1[j] > bogus_range || p1[j] < -bogus_range)
|
||||
FError("BOGUS_RANGE: %f", p1[j]);
|
||||
FError("BOGUS_RANGE: {}", p1[j]);
|
||||
|
||||
/* check the point is on the face plane */
|
||||
vec_t d = DotProduct(&p1[0], face.normal) - face.dist;
|
||||
|
|
|
|||
|
|
@ -287,9 +287,7 @@ public:
|
|||
|
||||
virtual std::string stringValue() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << _value[0] << " " << _value[1] << " " << _value[2];
|
||||
return ss.str();
|
||||
return fmt::format("{} {} {}", _value[0], _value[1], _value[2]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,17 +22,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <qbsp/parser.hh>
|
||||
#include "common/cmdlib.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
struct epair_t
|
||||
{
|
||||
epair_t *next;
|
||||
char *key;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct mapface_t
|
||||
{
|
||||
qbsp_plane_t plane;
|
||||
|
|
@ -96,7 +90,11 @@ public:
|
|||
// Temporary lists used to build `brushes` in the correct order.
|
||||
brush_t *solid, *sky, *detail, *detail_illusionary, *detail_fence, *liquid;
|
||||
|
||||
epair_t *epairs;
|
||||
// tree of key/value pairs
|
||||
std::map<std::string, std::string, case_insensitive_less> epairs;
|
||||
// order of parse
|
||||
std::vector<std::string> epair_order;
|
||||
|
||||
vec3_t mins, maxs;
|
||||
brush_t *brushes; /* NULL terminated list */
|
||||
int numbrushes;
|
||||
|
|
@ -108,7 +106,7 @@ public:
|
|||
|
||||
mapentity_t()
|
||||
: firstmapbrush(0), nummapbrushes(0), solid(nullptr), sky(nullptr), detail(nullptr),
|
||||
detail_illusionary(nullptr), detail_fence(nullptr), liquid(nullptr), epairs(nullptr), brushes(nullptr),
|
||||
detail_illusionary(nullptr), detail_fence(nullptr), liquid(nullptr), epairs(), brushes(nullptr),
|
||||
numbrushes(0), firstoutputfacenumber(-1), outputmodelnumber(-1)
|
||||
{
|
||||
VectorSet(origin, 0, 0, 0);
|
||||
|
|
@ -252,6 +250,6 @@ void ExportObj_Surfaces(const std::string &filesuffix, const surface_t *surfaces
|
|||
void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes);
|
||||
void ExportObj_Marksurfaces(const std::string &filesuffix, const node_t *nodes);
|
||||
|
||||
void WriteBspBrushMap(const char *name, const std::vector<const brush_t *> &list);
|
||||
void WriteBspBrushMap(const std::filesystem::path &name, const std::vector<const brush_t *> &list);
|
||||
|
||||
bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include "common/cmdlib.hh"
|
||||
|
||||
// Texture data stored for quick searching
|
||||
struct texture_t
|
||||
|
|
@ -51,44 +52,13 @@ struct lumpinfo_t
|
|||
char name[16]; // must be null terminated
|
||||
};
|
||||
|
||||
struct case_insensitive_hash
|
||||
{
|
||||
std::size_t operator()(const std::string &s) const noexcept
|
||||
{
|
||||
std::size_t hash = 0x811c9dc5;
|
||||
constexpr std::size_t prime = 0x1000193;
|
||||
|
||||
for (auto &c : s) {
|
||||
hash ^= tolower(c);
|
||||
hash *= prime;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct case_insensitive_equal
|
||||
{
|
||||
bool operator()(const std::string &l, const std::string &r) const noexcept
|
||||
{
|
||||
return Q_strcasecmp(l.c_str(), r.c_str()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct wad_t
|
||||
{
|
||||
wadinfo_t header;
|
||||
int version;
|
||||
std::unordered_map<std::string, lumpinfo_t, case_insensitive_hash, case_insensitive_equal> lumps;
|
||||
std::unordered_map<std::string, texture_t, case_insensitive_hash, case_insensitive_equal> textures;
|
||||
FILE *file;
|
||||
|
||||
~wad_t()
|
||||
{
|
||||
if (file) {
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
qfile_t file = { nullptr, nullptr };
|
||||
};
|
||||
|
||||
// Q1 miptex format
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <common/cmdlib.hh>
|
||||
|
||||
#include <light/light.hh>
|
||||
|
|
@ -154,25 +154,15 @@ static std::string EntDict_PrettyDescription(const mbsp_t *bsp, const entdict_t
|
|||
{
|
||||
// get the submodel's bbox if it's a brush entity
|
||||
if (bsp != nullptr && EntDict_StringForKey(entity, "origin") == "" && EntDict_StringForKey(entity, "model") != "") {
|
||||
|
||||
const std::string submodel_str = EntDict_StringForKey(entity, "model");
|
||||
const dmodel_t *info = BSP_DModelForModelString(bsp, submodel_str);
|
||||
|
||||
if (info) {
|
||||
std::stringstream s;
|
||||
s << "brush entity with mins (";
|
||||
s << VecStrf(info->mins);
|
||||
s << ") maxs (";
|
||||
s << VecStrf(info->maxs);
|
||||
s << ") (" << EntDict_StringForKey(entity, "classname") << ")";
|
||||
return s.str();
|
||||
return fmt::format("brush entity with mins ({}) maxs ({}) ({})", VecStrf(info->mins), VecStrf(info->maxs), EntDict_StringForKey(entity, "classname"));
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream s;
|
||||
s << "entity at (" << EntDict_StringForKey(entity, "origin") << ") (" << EntDict_StringForKey(entity, "classname")
|
||||
<< ")";
|
||||
return s.str();
|
||||
return fmt::format("entity at ({}) ({})", EntDict_StringForKey(entity, "origin"), EntDict_StringForKey(entity, "classname"));
|
||||
}
|
||||
|
||||
bool EntDict_CheckNoEmptyValues(const mbsp_t *bsp, const entdict_t &entdict)
|
||||
|
|
@ -875,7 +865,9 @@ static rgba_miptex_t *FindProjectionTexture(const mbsp_t *bsp, const char *texna
|
|||
|
||||
static std::string ParseEscapeSequences(const std::string &input)
|
||||
{
|
||||
std::stringstream ss;
|
||||
std::string s;
|
||||
s.reserve(input.size());
|
||||
|
||||
bool bold = false;
|
||||
|
||||
for (size_t i = 0; i < input.length(); i++) {
|
||||
|
|
@ -887,10 +879,10 @@ static std::string ParseEscapeSequences(const std::string &input)
|
|||
if (bold) {
|
||||
c |= 128;
|
||||
}
|
||||
ss.put(static_cast<char>(c));
|
||||
s += static_cast<char>(c);
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1264,10 +1256,10 @@ void WriteEntitiesToString(const globalconfig_t &cfg, mbsp_t *bsp)
|
|||
|
||||
static std::vector<light_t> surfacelight_templates;
|
||||
|
||||
static FILE *surflights_dump_file;
|
||||
static std::ofstream surflights_dump_file;
|
||||
static std::filesystem::path surflights_dump_filename;
|
||||
|
||||
static void SurfLights_WriteEntityToFile(FILE *f, light_t *entity, const vec3_t pos)
|
||||
static void SurfLights_WriteEntityToFile(light_t *entity, const vec3_t pos)
|
||||
{
|
||||
Q_assert(entity->epairs != nullptr);
|
||||
|
||||
|
|
@ -1275,8 +1267,7 @@ static void SurfLights_WriteEntityToFile(FILE *f, light_t *entity, const vec3_t
|
|||
EntDict_RemoveValueForKey(epairs, "_surface");
|
||||
epairs.set("origin", VecStr(pos));
|
||||
|
||||
std::string entstring = EntData_Write({epairs});
|
||||
fwrite(entstring.data(), 1, entstring.size(), f);
|
||||
surflights_dump_file << EntData_Write({epairs});
|
||||
}
|
||||
|
||||
static void CreateSurfaceLight(const vec3_t &origin, const vec3_t &normal, const light_t *surflight_template)
|
||||
|
|
@ -1296,7 +1287,7 @@ static void CreateSurfaceLight(const vec3_t &origin, const vec3_t &normal, const
|
|||
|
||||
/* export it to a map file for debugging */
|
||||
if (surflight_dump) {
|
||||
SurfLights_WriteEntityToFile(surflights_dump_file, &entity, origin);
|
||||
SurfLights_WriteEntityToFile(&entity, origin);
|
||||
}
|
||||
|
||||
all_lights.push_back(entity);
|
||||
|
|
@ -1463,39 +1454,39 @@ static void GL_SubdivideSurface(const bsp2_dface_t *face, const modelinfo_t *fac
|
|||
}
|
||||
|
||||
bool ParseLightsFile(const std::filesystem::path &fname)
|
||||
{ // note: this creates dupes. super bright light! (and super slow, too)
|
||||
light_t l;
|
||||
char buf[1024];
|
||||
char gah[256];
|
||||
const char *t;
|
||||
float r, g, b;
|
||||
FILE *f = fopen(fname.string().c_str(), "r");
|
||||
{
|
||||
// note: this creates dupes. super bright light! (and super slow, too)
|
||||
std::string buf;
|
||||
std::ifstream f(fname);
|
||||
|
||||
if (!f)
|
||||
return false;
|
||||
while (!feof(f)) {
|
||||
fgets(buf, sizeof(buf), f);
|
||||
t = buf;
|
||||
|
||||
t = COM_Parse(buf);
|
||||
while (!f.eof())
|
||||
{
|
||||
std::getline(f, buf);
|
||||
|
||||
const char *t = COM_Parse(buf.c_str());
|
||||
|
||||
if (!t)
|
||||
continue;
|
||||
entdict_t d = {};
|
||||
d.set("_surface", std::string(com_token));
|
||||
|
||||
entdict_t d {};
|
||||
d.set("_surface", com_token);
|
||||
t = COM_Parse(t);
|
||||
r = atof(com_token);
|
||||
float r = atof(com_token);
|
||||
t = COM_Parse(t);
|
||||
g = atof(com_token);
|
||||
float g = atof(com_token);
|
||||
t = COM_Parse(t);
|
||||
b = atof(com_token);
|
||||
snprintf(gah, sizeof(gah), "%f %f %f", r, g, b);
|
||||
d.set("_color", std::string(gah));
|
||||
float b = atof(com_token);
|
||||
d.set("_color", fmt::format("{} {} {}", r, g, b));
|
||||
t = COM_Parse(t);
|
||||
d.set("light", std::string(com_token));
|
||||
d.set("light", com_token);
|
||||
// might be hdr rgbi values here
|
||||
|
||||
radlights.push_back(d);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1531,7 +1522,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp)
|
|||
if (surflight_dump) {
|
||||
surflights_dump_filename = mapfilename;
|
||||
surflights_dump_filename.replace_filename(surflights_dump_filename.filename().string() + "-surflights").replace_extension("map");
|
||||
surflights_dump_file = fopen(surflights_dump_filename.string().c_str(), "w");
|
||||
surflights_dump_file.open(surflights_dump_filename);
|
||||
}
|
||||
|
||||
/* Create the surface lights */
|
||||
|
|
@ -1575,7 +1566,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp)
|
|||
}
|
||||
|
||||
if (surflights_dump_file) {
|
||||
fclose(surflights_dump_file);
|
||||
surflights_dump_file.close();
|
||||
fmt::print("wrote surface lights to '{}'\n", surflights_dump_filename);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,25 +307,25 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width
|
|||
int row, column;
|
||||
TargaHeader targa_header;
|
||||
|
||||
FILE *fin = fopen(filename.string().c_str(), "rb");
|
||||
qfile_t fin = SafeOpenRead(filename);
|
||||
if (!fin) {
|
||||
FLogPrint("Failed to load '{}'. File does not exist.\n", filename);
|
||||
return false; // mxd
|
||||
}
|
||||
|
||||
targa_header.id_length = fgetc(fin);
|
||||
targa_header.colormap_type = fgetc(fin);
|
||||
targa_header.image_type = fgetc(fin);
|
||||
targa_header.id_length = fgetc(fin.get());
|
||||
targa_header.colormap_type = fgetc(fin.get());
|
||||
targa_header.image_type = fgetc(fin.get());
|
||||
|
||||
targa_header.colormap_index = fgetLittleShort(fin);
|
||||
targa_header.colormap_length = fgetLittleShort(fin);
|
||||
targa_header.colormap_size = fgetc(fin);
|
||||
targa_header.x_origin = fgetLittleShort(fin);
|
||||
targa_header.y_origin = fgetLittleShort(fin);
|
||||
targa_header.width = fgetLittleShort(fin);
|
||||
targa_header.height = fgetLittleShort(fin);
|
||||
targa_header.pixel_size = fgetc(fin);
|
||||
targa_header.attributes = fgetc(fin);
|
||||
targa_header.colormap_index = fgetLittleShort(fin.get());
|
||||
targa_header.colormap_length = fgetLittleShort(fin.get());
|
||||
targa_header.colormap_size = fgetc(fin.get());
|
||||
targa_header.x_origin = fgetLittleShort(fin.get());
|
||||
targa_header.y_origin = fgetLittleShort(fin.get());
|
||||
targa_header.width = fgetLittleShort(fin.get());
|
||||
targa_header.height = fgetLittleShort(fin.get());
|
||||
targa_header.pixel_size = fgetc(fin.get());
|
||||
targa_header.attributes = fgetc(fin.get());
|
||||
|
||||
if (targa_header.image_type != 2 && targa_header.image_type != 10) {
|
||||
FLogPrint("Failed to load '{}'. Only type 2 and 10 targa RGB images supported.\n", filename);
|
||||
|
|
@ -350,7 +350,7 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width
|
|||
*pixels = targa_rgba;
|
||||
|
||||
if (targa_header.id_length != 0)
|
||||
fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
|
||||
SafeSeek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment
|
||||
|
||||
if (targa_header.image_type == 2) { // Uncompressed, RGB images
|
||||
for (row = rows - 1; row >= 0; row--) {
|
||||
|
|
@ -359,19 +359,19 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width
|
|||
unsigned char red, green, blue, alphabyte;
|
||||
switch (targa_header.pixel_size) {
|
||||
case 24:
|
||||
blue = getc(fin);
|
||||
green = getc(fin);
|
||||
red = getc(fin);
|
||||
blue = getc(fin.get());
|
||||
green = getc(fin.get());
|
||||
red = getc(fin.get());
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = getc(fin);
|
||||
green = getc(fin);
|
||||
red = getc(fin);
|
||||
alphabyte = getc(fin);
|
||||
blue = getc(fin.get());
|
||||
green = getc(fin.get());
|
||||
red = getc(fin.get());
|
||||
alphabyte = getc(fin.get());
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
|
|
@ -388,21 +388,21 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width
|
|||
for (row = rows - 1; row >= 0; row--) {
|
||||
pixbuf = targa_rgba + row * columns * 4;
|
||||
for (column = 0; column < columns;) {
|
||||
const unsigned char packetHeader = getc(fin);
|
||||
const unsigned char packetHeader = getc(fin.get());
|
||||
const unsigned char packetSize = 1 + (packetHeader & 0x7f);
|
||||
if (packetHeader & 0x80) { // run-length packet
|
||||
switch (targa_header.pixel_size) {
|
||||
case 24:
|
||||
blue = getc(fin);
|
||||
green = getc(fin);
|
||||
red = getc(fin);
|
||||
blue = getc(fin.get());
|
||||
green = getc(fin.get());
|
||||
red = getc(fin.get());
|
||||
alphabyte = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = getc(fin);
|
||||
green = getc(fin);
|
||||
red = getc(fin);
|
||||
alphabyte = getc(fin);
|
||||
blue = getc(fin.get());
|
||||
green = getc(fin.get());
|
||||
red = getc(fin.get());
|
||||
alphabyte = getc(fin.get());
|
||||
break;
|
||||
default:
|
||||
FLogPrint("unsupported pixel size: {}\n", targa_header.pixel_size); // mxd
|
||||
|
|
@ -428,19 +428,19 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width
|
|||
for (j = 0; j < packetSize; j++) {
|
||||
switch (targa_header.pixel_size) {
|
||||
case 24:
|
||||
blue = getc(fin);
|
||||
green = getc(fin);
|
||||
red = getc(fin);
|
||||
blue = getc(fin.get());
|
||||
green = getc(fin.get());
|
||||
red = getc(fin.get());
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
*pixbuf++ = 255;
|
||||
break;
|
||||
case 32:
|
||||
blue = getc(fin);
|
||||
green = getc(fin);
|
||||
red = getc(fin);
|
||||
alphabyte = getc(fin);
|
||||
blue = getc(fin.get());
|
||||
green = getc(fin.get());
|
||||
red = getc(fin.get());
|
||||
alphabyte = getc(fin.get());
|
||||
*pixbuf++ = red;
|
||||
*pixbuf++ = green;
|
||||
*pixbuf++ = blue;
|
||||
|
|
@ -466,8 +466,6 @@ breakOut:;
|
|||
}
|
||||
}
|
||||
|
||||
fclose(fin);
|
||||
|
||||
return true; // mxd
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include <cassert>
|
||||
//#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <light/light.hh>
|
||||
#include <light/phong.hh>
|
||||
|
|
@ -341,9 +343,7 @@ static void FindModelInfo(const mbsp_t *bsp, const char *lmscaleoverride)
|
|||
modelinfo.push_back(info);
|
||||
|
||||
/* Find the entity for the model */
|
||||
std::stringstream ss;
|
||||
ss << "*" << i;
|
||||
std::string modelname = ss.str();
|
||||
std::string modelname = fmt::format("*{}", i);
|
||||
|
||||
const entdict_t *entdict = FindEntDictWithKeyPair("model", modelname);
|
||||
if (entdict == nullptr)
|
||||
|
|
@ -501,7 +501,8 @@ static void LoadExtendedTexinfoFlags(const std::filesystem::path &sourcefilename
|
|||
std::filesystem::path filename(sourcefilename);
|
||||
filename.replace_extension("texinfo");
|
||||
|
||||
FILE *texinfofile = fopen(filename.string().c_str(), "rt");
|
||||
qfile_t texinfofile = SafeOpenRead(filename);
|
||||
|
||||
if (!texinfofile)
|
||||
return;
|
||||
|
||||
|
|
@ -509,58 +510,56 @@ static void LoadExtendedTexinfoFlags(const std::filesystem::path &sourcefilename
|
|||
|
||||
extended_flags_header_t header;
|
||||
|
||||
if (fread(&header, 1, sizeof(extended_flags_header_t), texinfofile) != sizeof(extended_flags_header_t) ||
|
||||
if (SafeRead(texinfofile, &header, sizeof(extended_flags_header_t)) != sizeof(extended_flags_header_t) ||
|
||||
header.num_texinfo != bsp->numtexinfo || header.surfflags_size != sizeof(surfflags_t) ||
|
||||
fread(extended_texinfo_flags, sizeof(surfflags_t), header.num_texinfo, texinfofile) != header.num_texinfo) {
|
||||
SafeRead(texinfofile, extended_texinfo_flags, sizeof(surfflags_t) * header.num_texinfo) != (sizeof(surfflags_t) * header.num_texinfo)) {
|
||||
LogPrint("WARNING: Extended texinfo flags in {} does not match bsp, ignoring\n", filename);
|
||||
fclose(texinfofile);
|
||||
memset(extended_texinfo_flags, 0, bsp->numtexinfo * sizeof(uint32_t));
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(texinfofile);
|
||||
}
|
||||
|
||||
// obj
|
||||
|
||||
static FILE *InitObjFile(const std::filesystem::path &filename)
|
||||
static qfile_t InitObjFile(const std::filesystem::path &filename)
|
||||
{
|
||||
std::filesystem::path objfilename(filename);
|
||||
objfilename.replace_extension("obj");
|
||||
|
||||
FILE *objfile = fopen(objfilename.string().c_str(), "wt");
|
||||
qfile_t objfile = SafeOpenWrite(objfilename);
|
||||
|
||||
if (!objfile)
|
||||
FError("Failed to open {}: {}", objfilename, strerror(errno));
|
||||
|
||||
return objfile;
|
||||
}
|
||||
|
||||
static void ExportObjFace(FILE *f, const mbsp_t *bsp, const bsp2_dface_t *face, int *vertcount)
|
||||
static void ExportObjFace(std::ofstream &f, const mbsp_t *bsp, const bsp2_dface_t *face, int *vertcount)
|
||||
{
|
||||
// export the vertices and uvs
|
||||
for (int i = 0; i < face->numedges; i++) {
|
||||
const int vertnum = Face_VertexAtIndex(bsp, face, i);
|
||||
const qvec3f normal = GetSurfaceVertexNormal(bsp, face, i);
|
||||
const float *pos = bsp->dvertexes[vertnum].point;
|
||||
fprintf(f, "v %.9g %.9g %.9g\n", pos[0], pos[1], pos[2]);
|
||||
fprintf(f, "vn %.9g %.9g %.9g\n", normal[0], normal[1], normal[2]);
|
||||
fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]);
|
||||
fmt::print(f, "vn {:.9} {:.9} {:.9}\n", normal[0], normal[1], normal[2]);
|
||||
}
|
||||
|
||||
fprintf(f, "f");
|
||||
f << "f";
|
||||
for (int i = 0; i < face->numedges; i++) {
|
||||
// .obj vertexes start from 1
|
||||
// .obj faces are CCW, quake is CW, so reverse the order
|
||||
const int vertindex = *vertcount + (face->numedges - 1 - i) + 1;
|
||||
fprintf(f, " %d//%d", vertindex, vertindex);
|
||||
fmt::print(f, " {}//{}", vertindex, vertindex);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
f << '\n';
|
||||
|
||||
*vertcount += face->numedges;
|
||||
}
|
||||
|
||||
static void ExportObj(const std::filesystem::path &filename, const mbsp_t *bsp)
|
||||
{
|
||||
FILE *objfile = InitObjFile(filename);
|
||||
std::ofstream objfile(filename);
|
||||
int vertcount = 0;
|
||||
|
||||
const int start = bsp->dmodels[0].firstface;
|
||||
|
|
@ -570,8 +569,6 @@ static void ExportObj(const std::filesystem::path &filename, const mbsp_t *bsp)
|
|||
ExportObjFace(objfile, bsp, BSP_GetFace(bsp, i), &vertcount);
|
||||
}
|
||||
|
||||
fclose(objfile);
|
||||
|
||||
LogPrint("Wrote {}\n", filename);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
|
@ -547,7 +549,7 @@ position_t CalcPointNormal(const mbsp_t *bsp, const bsp2_dface_t *face, const qv
|
|||
// Dump points to a .map file
|
||||
static void CalcPoints_Debug(const lightsurf_t *surf, const mbsp_t *bsp)
|
||||
{
|
||||
FILE *f = fopen("calcpoints.map", "w");
|
||||
std::ofstream f("calcpoints.map");
|
||||
|
||||
for (int t = 0; t < surf->height; t++) {
|
||||
for (int s = 0; s < surf->width; s++) {
|
||||
|
|
@ -555,20 +557,18 @@ static void CalcPoints_Debug(const lightsurf_t *surf, const mbsp_t *bsp)
|
|||
const vec_t *point = surf->points[i];
|
||||
const qvec3f mangle = mangle_from_vec(surf->normals[i]);
|
||||
|
||||
fprintf(f, "{\n");
|
||||
fprintf(f, "\"classname\" \"light\"\n");
|
||||
fprintf(f, "\"origin\" \"%f %f %f\"\n", point[0], point[1], point[2]);
|
||||
fprintf(f, "\"mangle\" \"%f %f %f\"\n", mangle[0], mangle[1], mangle[2]);
|
||||
fprintf(f, "\"face\" \"%d\"\n", surf->realfacenums[i]);
|
||||
fprintf(f, "\"occluded\" \"%d\"\n", (int)surf->occluded[i]);
|
||||
fprintf(f, "\"s\" \"%d\"\n", s);
|
||||
fprintf(f, "\"t\" \"%d\"\n", t);
|
||||
fprintf(f, "}\n");
|
||||
f << "{\n";
|
||||
f << "\"classname\" \"light\"\n";
|
||||
fmt::print(f, "\"origin\" \"{} {} {}\"\n", point[0], point[1], point[2]);
|
||||
fmt::print(f, "\"mangle\" \"{} {} {}\"\n", mangle[0], mangle[1], mangle[2]);
|
||||
fmt::print(f, "\"face\" \"{}\"\n", surf->realfacenums[i]);
|
||||
fmt::print(f, "\"occluded\" \"{}\"\n", (int)surf->occluded[i]);
|
||||
fmt::print(f, "\"s\" \"{}\"\n", s);
|
||||
fmt::print(f, "\"t\" \"{}\"\n", t);
|
||||
f << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
LogPrint("wrote face {}'s sample points ({}x{}) to calcpoints.map\n", Face_GetNum(bsp, surf->face), surf->width,
|
||||
surf->height);
|
||||
|
||||
|
|
@ -2644,16 +2644,14 @@ static float Lightmap_MaxBrightness(const lightmap_t *lm, const lightsurf_t *lig
|
|||
return maxb;
|
||||
}
|
||||
|
||||
static void WritePPM(std::string fname, int width, int height, const uint8_t *rgbdata)
|
||||
static void WritePPM(const std::filesystem::path &fname, int width, int height, const uint8_t *rgbdata)
|
||||
{
|
||||
FILE *file = fopen(fname.c_str(), "wb");
|
||||
qfile_t file = SafeOpenWrite(fname);
|
||||
|
||||
// see: http://netpbm.sourceforge.net/doc/ppm.html
|
||||
fprintf(file, "P6 %d %d 255 ", width, height);
|
||||
fmt::print(file.get(), "P6 {} {} 255 ", width, height);
|
||||
int bytes = width * height * 3;
|
||||
Q_assert(bytes == fwrite(rgbdata, 1, bytes, file));
|
||||
|
||||
fclose(file);
|
||||
Q_assert(bytes == SafeWrite(file, rgbdata, bytes));
|
||||
}
|
||||
|
||||
static void DumpFullSizeLightmap(const mbsp_t *bsp, const lightsurf_t *lightsurf)
|
||||
|
|
@ -2662,9 +2660,6 @@ static void DumpFullSizeLightmap(const mbsp_t *bsp, const lightsurf_t *lightsurf
|
|||
if (lm != nullptr) {
|
||||
int fnum = Face_GetNum(bsp, lightsurf->face);
|
||||
|
||||
char fname[1024];
|
||||
sprintf(fname, "face%04d.ppm", fnum);
|
||||
|
||||
std::vector<uint8_t> rgbdata;
|
||||
for (int i = 0; i < lightsurf->numpoints; i++) {
|
||||
const vec_t *color = lm->samples[i].color;
|
||||
|
|
@ -2679,7 +2674,7 @@ static void DumpFullSizeLightmap(const mbsp_t *bsp, const lightsurf_t *lightsurf
|
|||
|
||||
Q_assert(lightsurf->numpoints == (oversampled_height * oversampled_width));
|
||||
|
||||
WritePPM(std::string{fname}, oversampled_width, oversampled_height, rgbdata.data());
|
||||
WritePPM(fmt::format("face{:04}.ppm", fnum), oversampled_width, oversampled_height, rgbdata.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2702,8 +2697,6 @@ static void DumpGLMVector(std::string fname, std::vector<qvec3f> vec, int width,
|
|||
static void DumpDownscaledLightmap(const mbsp_t *bsp, const bsp2_dface_t *face, int w, int h, const vec3_t *colors)
|
||||
{
|
||||
int fnum = Face_GetNum(bsp, face);
|
||||
char fname[1024];
|
||||
sprintf(fname, "face-small%04d.ppm", fnum);
|
||||
|
||||
std::vector<uint8_t> rgbdata;
|
||||
for (int i = 0; i < (w * h); i++) {
|
||||
|
|
@ -2713,7 +2706,7 @@ static void DumpDownscaledLightmap(const mbsp_t *bsp, const bsp2_dface_t *face,
|
|||
}
|
||||
}
|
||||
|
||||
WritePPM(std::string{fname}, w, h, rgbdata.data());
|
||||
WritePPM(fmt::format("face-small{:04}.ppm", fnum), w, h, rgbdata.data());
|
||||
}
|
||||
|
||||
static std::vector<qvec4f> LightmapColorsToGLMVector(const lightsurf_t *lightsurf, const lightmap_t *lm)
|
||||
|
|
|
|||
|
|
@ -422,64 +422,6 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args)
|
|||
|
||||
// building faces for skip-textured bmodels
|
||||
|
||||
#if 0
|
||||
|
||||
static FILE *
|
||||
InitObjFile(const char *filename)
|
||||
{
|
||||
FILE *objfile;
|
||||
char objfilename[1024];
|
||||
strcpy(objfilename, filename);
|
||||
StripExtension(objfilename);
|
||||
DefaultExtension(objfilename, ".obj");
|
||||
|
||||
objfile = fopen(objfilename, "wt");
|
||||
if (!objfile)
|
||||
FError("Failed to open {}: {}", objfilename, strerror(errno));
|
||||
|
||||
return objfile;
|
||||
}
|
||||
|
||||
static void
|
||||
ExportObjFace(FILE *f, const winding_t *winding, int *vertcount)
|
||||
{
|
||||
// plane_t plane;
|
||||
// WindingPlane(winding, plane.normal, &plane.dist);
|
||||
|
||||
// export the vertices and uvs
|
||||
for (int i=0; i<winding->numpoints; i++)
|
||||
{
|
||||
fprintf(f, "v %.9g %.9g %.9g\n", winding->p[i][0], winding->p[i][1], winding->p[i][2]);
|
||||
// fprintf(f, "vn %.9g %.9g %.9g\n", plane.normal[0], plane.normal[1], plane.normal[2]);
|
||||
}
|
||||
|
||||
fprintf(f, "f");
|
||||
for (int i=0; i<winding->numpoints; i++) {
|
||||
// .obj vertexes start from 1
|
||||
// .obj faces are CCW, quake is CW, so reverse the order
|
||||
const int vertindex = *vertcount + (winding->numpoints - 1 - i) + 1;
|
||||
fprintf(f, " %d//%d", vertindex, vertindex);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
|
||||
*vertcount += winding->numpoints;
|
||||
}
|
||||
|
||||
static void
|
||||
ExportObj(const char *filename, const vector<winding_t *> &windings)
|
||||
{
|
||||
FILE *objfile = InitObjFile(filename);
|
||||
int vertcount = 0;
|
||||
|
||||
for (const auto &winding : windings) {
|
||||
ExportObjFace(objfile, winding, &vertcount);
|
||||
}
|
||||
|
||||
fclose(objfile);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
plane_t Node_Plane(const mbsp_t *bsp, const bsp2_dnode_t *node, bool side)
|
||||
{
|
||||
const dplane_t *dplane = &bsp->dplanes[node->planenum];
|
||||
|
|
|
|||
|
|
@ -1202,9 +1202,7 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu
|
|||
VectorAdd(brush->mins, brush->maxs, origin);
|
||||
VectorScale(origin, 0.5, origin);
|
||||
|
||||
char value[1024];
|
||||
snprintf(value, sizeof(value), "%.2f %.2f %.2f", origin[0], origin[1], origin[2]);
|
||||
SetKeyValue(dst, "origin", value);
|
||||
SetKeyValue(dst, "origin", VecStrf(origin).c_str());
|
||||
|
||||
VectorCopy(origin, rotate_offset);
|
||||
rottype = rotation_t::origin_brush;
|
||||
|
|
|
|||
|
|
@ -22,29 +22,27 @@
|
|||
#include <qbsp/wad.hh>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
static FILE *InitObjFile(const std::string &filesuffix)
|
||||
static std::ofstream InitObjFile(const std::string &filesuffix)
|
||||
{
|
||||
FILE *objfile;
|
||||
|
||||
std::filesystem::path name = options.szBSPName;
|
||||
name.replace_filename(options.szBSPName.filename().string() + "_" + filesuffix).replace_extension("obj");
|
||||
|
||||
objfile = fopen(name.string().c_str(), "wt");
|
||||
std::ofstream objfile(name);
|
||||
if (!objfile)
|
||||
FError("Failed to open {}: {}", options.szBSPName, strerror(errno));
|
||||
|
||||
return objfile;
|
||||
}
|
||||
|
||||
static FILE *InitMtlFile(const std::string &filesuffix)
|
||||
static std::ofstream InitMtlFile(const std::string &filesuffix)
|
||||
{
|
||||
FILE *file;
|
||||
|
||||
std::filesystem::path name = options.szBSPName;
|
||||
name.replace_filename(options.szBSPName.filename().string() + "_" + filesuffix).replace_extension("mtl");
|
||||
|
||||
file = fopen(name.string().c_str(), "wt");
|
||||
std::ofstream file(name);
|
||||
if (!file)
|
||||
FError("Failed to open {}: {}", options.szBSPName, strerror(errno));
|
||||
|
||||
|
|
@ -61,7 +59,7 @@ static void GetUV(const mtexinfo_t *texinfo, const vec_t *pos, const int width,
|
|||
height;
|
||||
}
|
||||
|
||||
static void ExportObjFace(FILE *f, FILE *mtlF, const face_t *face, int *vertcount)
|
||||
static void ExportObjFace(std::ofstream &f, const face_t *face, int *vertcount)
|
||||
{
|
||||
const mtexinfo_t &texinfo = map.mtexinfos.at(face->texinfo);
|
||||
const char *texname = map.miptexTextureName(texinfo.miptex).c_str();
|
||||
|
|
@ -73,42 +71,41 @@ static void ExportObjFace(FILE *f, FILE *mtlF, const face_t *face, int *vertcoun
|
|||
// export the vertices and uvs
|
||||
for (int i = 0; i < face->w.numpoints; i++) {
|
||||
const vec_t *pos = face->w.points[i];
|
||||
fprintf(f, "v %.9g %.9g %.9g\n", pos[0], pos[1], pos[2]);
|
||||
fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]);
|
||||
|
||||
vec_t u, v;
|
||||
GetUV(&texinfo, pos, width, height, &u, &v);
|
||||
|
||||
// not sure why -v is needed, .obj uses (0, 0) in the top left apparently?
|
||||
fprintf(f, "vt %.9g %.9g\n", u, -v);
|
||||
fmt::print(f, "vt {:.9} {:.9}\n", u, -v);
|
||||
}
|
||||
|
||||
// fprintf(f, "usemtl %s\n", texname);
|
||||
fprintf(f, "usemtl contents%d_%d\n", face->contents[1].native, face->contents->extended);
|
||||
fprintf(f, "f");
|
||||
fmt::print(f, "usemtl contents{}_{}\n", face->contents[0].native, face->contents[0].extended);
|
||||
f << 'f';
|
||||
for (int i = 0; i < face->w.numpoints; i++) {
|
||||
// .obj vertexes start from 1
|
||||
// .obj faces are CCW, quake is CW, so reverse the order
|
||||
const int vertindex = *vertcount + (face->w.numpoints - 1 - i) + 1;
|
||||
fprintf(f, " %d/%d", vertindex, vertindex);
|
||||
fmt::print(f, " {}/{}", vertindex, vertindex);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
f << '\n';
|
||||
|
||||
*vertcount += face->w.numpoints;
|
||||
}
|
||||
|
||||
static void WriteContentsMaterial(FILE *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)
|
||||
{
|
||||
fprintf(mtlf, "newmtl contents%d_%d\n", contents.native, contents.extended);
|
||||
fprintf(mtlf, "Ka 0 0 0\n");
|
||||
fprintf(mtlf, "Kd %f %f %f\n", r, g, b);
|
||||
fprintf(mtlf, "Ks 0 0 0\n");
|
||||
fprintf(mtlf, "illum 0\n");
|
||||
fmt::print(mtlf, "newmtl contents{}_{}\n", contents.native, contents.extended);
|
||||
mtlf << "Ka 0 0 0\n";
|
||||
fmt::print(mtlf, "Kd {} {} {}\n", r, g, b);
|
||||
mtlf << "Ks 0 0 0\n";
|
||||
mtlf << "illum 0\n";
|
||||
}
|
||||
|
||||
void ExportObj_Faces(const std::string &filesuffix, const std::vector<const face_t *> &faces)
|
||||
{
|
||||
FILE *objfile = InitObjFile(filesuffix);
|
||||
FILE *mtlfile = InitMtlFile(filesuffix);
|
||||
std::ofstream objfile = InitObjFile(filesuffix);
|
||||
std::ofstream mtlfile = InitMtlFile(filesuffix);
|
||||
|
||||
WriteContentsMaterial(mtlfile, {}, 0, 0, 0);
|
||||
WriteContentsMaterial(mtlfile, {CONTENTS_EMPTY}, 0, 1, 0);
|
||||
|
|
@ -126,11 +123,8 @@ void ExportObj_Faces(const std::string &filesuffix, const std::vector<const face
|
|||
|
||||
int vertcount = 0;
|
||||
for (const face_t *face : faces) {
|
||||
ExportObjFace(objfile, mtlfile, face, &vertcount);
|
||||
ExportObjFace(objfile, face, &vertcount);
|
||||
}
|
||||
|
||||
fclose(objfile);
|
||||
fclose(mtlfile);
|
||||
}
|
||||
|
||||
void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const brush_t *> &brushes)
|
||||
|
|
|
|||
201
qbsp/map.cc
201
qbsp/map.cc
|
|
@ -25,11 +25,12 @@
|
|||
#include <cstring>
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
#include <qbsp/qbsp.hh>
|
||||
#include <qbsp/parser.hh>
|
||||
|
|
@ -482,35 +483,42 @@ static surfflags_t SurfFlagsForEntity(const mtexinfo_t &texinfo, const mapentity
|
|||
|
||||
static void ParseEpair(parser_t *parser, mapentity_t *entity)
|
||||
{
|
||||
epair_t *epair = new epair_t { };
|
||||
epair->next = entity->epairs;
|
||||
entity->epairs = epair;
|
||||
|
||||
if (strlen(parser->token) >= MAX_KEY - 1)
|
||||
goto parse_error;
|
||||
epair->key = copystring(parser->token);
|
||||
ParseToken(parser, PARSE_SAMELINE);
|
||||
if (strlen(parser->token) >= MAX_VALUE - 1)
|
||||
goto parse_error;
|
||||
epair->value = copystring(parser->token);
|
||||
|
||||
if (!Q_strcasecmp(epair->key, "origin")) {
|
||||
GetVectorForKey(entity, epair->key, entity->origin);
|
||||
} else if (!Q_strcasecmp(epair->key, "classname")) {
|
||||
if (!Q_strcasecmp(epair->value, "info_player_start")) {
|
||||
// Quake II uses multiple starts for level transitions/backtracking.
|
||||
// TODO: instead, this should check targetnames. There should only be
|
||||
// one info_player_start per targetname in Q2.
|
||||
if (options.target_game->id != GAME_QUAKE_II && (rgfStartSpots & info_player_start))
|
||||
LogPrint("WARNING: Multiple info_player_start entities\n");
|
||||
rgfStartSpots |= info_player_start;
|
||||
} else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) {
|
||||
rgfStartSpots |= info_player_deathmatch;
|
||||
} else if (!Q_strcasecmp(epair->value, "info_player_coop")) {
|
||||
rgfStartSpots |= info_player_coop;
|
||||
{
|
||||
std::string key(parser->token);
|
||||
|
||||
ParseToken(parser, PARSE_SAMELINE);
|
||||
|
||||
if (strlen(parser->token) >= MAX_VALUE - 1)
|
||||
goto parse_error;
|
||||
|
||||
{
|
||||
std::string &value = (entity->epairs[key] = parser->token);
|
||||
|
||||
if (!Q_strcasecmp(key.c_str(), "origin")) {
|
||||
GetVectorForKey(entity, key.c_str(), entity->origin);
|
||||
} else if (!Q_strcasecmp(key.c_str(), "classname")) {
|
||||
if (!Q_strcasecmp(value.c_str(), "info_player_start")) {
|
||||
// Quake II uses multiple starts for level transitions/backtracking.
|
||||
// TODO: instead, this should check targetnames. There should only be
|
||||
// one info_player_start per targetname in Q2.
|
||||
if (options.target_game->id != GAME_QUAKE_II && (rgfStartSpots & info_player_start))
|
||||
LogPrint("WARNING: Multiple info_player_start entities\n");
|
||||
rgfStartSpots |= info_player_start;
|
||||
} else if (!Q_strcasecmp(value.c_str(), "info_player_deathmatch")) {
|
||||
rgfStartSpots |= info_player_deathmatch;
|
||||
} else if (!Q_strcasecmp(value.c_str(), "info_player_coop")) {
|
||||
rgfStartSpots |= info_player_coop;
|
||||
}
|
||||
}
|
||||
|
||||
entity->epair_order.push_back(std::move(key));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
parse_error:
|
||||
FError("line {}: Entity key or value too long", parser->linenum);
|
||||
|
|
@ -1988,7 +1996,7 @@ void LoadMapFile(void)
|
|||
}
|
||||
}
|
||||
// Remove dummy entity inserted above
|
||||
assert(map.entities.back().epairs == nullptr);
|
||||
assert(!map.entities.back().epairs.size());
|
||||
assert(map.entities.back().numbrushes == 0);
|
||||
map.entities.pop_back();
|
||||
|
||||
|
|
@ -2046,20 +2054,20 @@ static texdef_valve_t TexDef_BSPToValve(const stvecs &in_vecs)
|
|||
return res;
|
||||
}
|
||||
|
||||
static void fprintDoubleAndSpc(FILE *f, double v)
|
||||
static void fprintDoubleAndSpc(std::ofstream &f, double v)
|
||||
{
|
||||
int rounded = rint(v);
|
||||
if (static_cast<double>(rounded) == v) {
|
||||
fprintf(f, "%d ", rounded);
|
||||
fmt::print(f, "{} ", rounded);
|
||||
} else if (std::isfinite(v)) {
|
||||
fprintf(f, "%0.17g ", v);
|
||||
fmt::print(f, "{:0.17} ", v);
|
||||
} else {
|
||||
printf("WARNING: suppressing nan or infinity\n");
|
||||
fprintf(f, "0 ");
|
||||
f << "0 ";
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t format)
|
||||
static void ConvertMapFace(std::ofstream &f, const mapface_t &mapface, const conversion_t format)
|
||||
{
|
||||
EnsureTexturesLoaded();
|
||||
const texture_t *texture = WADList_GetTexture(mapface.texname.c_str());
|
||||
|
|
@ -2068,11 +2076,11 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t
|
|||
|
||||
// Write plane points
|
||||
for (int i = 0; i < 3; i++) {
|
||||
fprintf(f, " ( ");
|
||||
f << " ( ";
|
||||
for (int j = 0; j < 3; j++) {
|
||||
fprintDoubleAndSpc(f, mapface.planepts[i][j]);
|
||||
}
|
||||
fprintf(f, ") ");
|
||||
f << ") ";
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
|
|
@ -2081,7 +2089,7 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t
|
|||
const texdef_quake_ed_t quakeed =
|
||||
TexDef_BSPToQuakeEd(mapface.plane, texture, texinfo.vecs, mapface.planepts);
|
||||
|
||||
fprintf(f, "%s ", mapface.texname.c_str());
|
||||
fmt::print(f, "{} ", mapface.texname);
|
||||
fprintDoubleAndSpc(f, quakeed.shift[0]);
|
||||
fprintDoubleAndSpc(f, quakeed.shift[1]);
|
||||
fprintDoubleAndSpc(f, quakeed.rotate);
|
||||
|
|
@ -2089,7 +2097,8 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t
|
|||
fprintDoubleAndSpc(f, quakeed.scale[1]);
|
||||
|
||||
if (format == conversion_t::quake2) {
|
||||
fprintf(f, "0 0 0");
|
||||
// TODO??
|
||||
f << "0 0 0";
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -2097,19 +2106,21 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t
|
|||
case conversion_t::valve: {
|
||||
const texdef_valve_t valve = TexDef_BSPToValve(texinfo.vecs);
|
||||
|
||||
fprintf(f, "%s [ ", mapface.texname.c_str());
|
||||
fmt::print(f, "{} [ ", mapface.texname);
|
||||
fprintDoubleAndSpc(f, valve.axis[0][0]);
|
||||
fprintDoubleAndSpc(f, valve.axis[0][1]);
|
||||
fprintDoubleAndSpc(f, valve.axis[0][2]);
|
||||
fprintDoubleAndSpc(f, valve.shift[0]);
|
||||
fprintf(f, "] [ ");
|
||||
f << "] [ ";
|
||||
fprintDoubleAndSpc(f, valve.axis[1][0]);
|
||||
fprintDoubleAndSpc(f, valve.axis[1][1]);
|
||||
fprintDoubleAndSpc(f, valve.axis[1][2]);
|
||||
fprintDoubleAndSpc(f, valve.shift[1]);
|
||||
fprintf(f, "] 0 ");
|
||||
f << "] 0 ";
|
||||
fprintDoubleAndSpc(f, valve.scale[0]);
|
||||
fprintDoubleAndSpc(f, valve.scale[1]);
|
||||
|
||||
// TODO: Q2
|
||||
break;
|
||||
}
|
||||
case conversion_t::bp: {
|
||||
|
|
@ -2118,58 +2129,53 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t
|
|||
texSize[1] = texture ? texture->height : 64;
|
||||
|
||||
const texdef_brush_primitives_t bp = TexDef_BSPToBrushPrimitives(mapface.plane, texSize, texinfo.vecs);
|
||||
fprintf(f, "( ( ");
|
||||
f << "( ( ";
|
||||
fprintDoubleAndSpc(f, bp.texMat[0][0]);
|
||||
fprintDoubleAndSpc(f, bp.texMat[0][1]);
|
||||
fprintDoubleAndSpc(f, bp.texMat[0][2]);
|
||||
fprintf(f, ") ( ");
|
||||
f << ") ( ";
|
||||
fprintDoubleAndSpc(f, bp.texMat[1][0]);
|
||||
fprintDoubleAndSpc(f, bp.texMat[1][1]);
|
||||
fprintDoubleAndSpc(f, bp.texMat[1][2]);
|
||||
|
||||
// N.B.: always print the Q2/Q3 flags
|
||||
fprintf(f, ") ) %s 0 0 0", mapface.texname.c_str());
|
||||
fmt::print(f, ") ) {} 0 0 0", mapface.texname);
|
||||
break;
|
||||
}
|
||||
default: FError("Internal error: unknown texcoord_style_t\n");
|
||||
}
|
||||
|
||||
fprintf(f, "\n");
|
||||
f << '\n';
|
||||
}
|
||||
|
||||
static void ConvertMapBrush(FILE *f, const mapbrush_t &mapbrush, const conversion_t format)
|
||||
static void ConvertMapBrush(std::ofstream &f, const mapbrush_t &mapbrush, const conversion_t format)
|
||||
{
|
||||
fprintf(f, "{\n");
|
||||
f << "{\n";
|
||||
if (format == conversion_t::bp) {
|
||||
fprintf(f, "brushDef\n");
|
||||
fprintf(f, "{\n");
|
||||
f << "brushDef\n";
|
||||
f << "{\n";
|
||||
}
|
||||
for (int i = 0; i < mapbrush.numfaces; i++) {
|
||||
ConvertMapFace(f, mapbrush.face(i), format);
|
||||
}
|
||||
if (format == conversion_t::bp) {
|
||||
fprintf(f, "}\n");
|
||||
f << "}\n";
|
||||
}
|
||||
fprintf(f, "}\n");
|
||||
f << "}\n";
|
||||
}
|
||||
|
||||
static void ConvertEntity(FILE *f, const mapentity_t *entity, const conversion_t format)
|
||||
static void ConvertEntity(std::ofstream &f, const mapentity_t *entity, const conversion_t format)
|
||||
{
|
||||
fprintf(f, "{\n");
|
||||
f << "{\n";
|
||||
|
||||
// put the epairs in a temporary list to reverse the order, so we can print them in the same order as the .MAP file
|
||||
std::list<std::pair<std::string, std::string>> epairs;
|
||||
for (const epair_t *epair = entity->epairs; epair; epair = epair->next) {
|
||||
epairs.push_front(std::make_pair(std::string(epair->key), std::string(epair->value)));
|
||||
}
|
||||
for (const auto &epair : epairs) {
|
||||
fprintf(f, "\"%s\" \"%s\"\n", epair.first.c_str(), epair.second.c_str());
|
||||
for (const auto &key : entity->epair_order) {
|
||||
fmt::print(f, "\"{}\" \"{}\"\n", key, entity->epairs.at(key));
|
||||
}
|
||||
|
||||
for (int i = 0; i < entity->nummapbrushes; i++) {
|
||||
ConvertMapBrush(f, entity->mapbrush(i), format);
|
||||
}
|
||||
fprintf(f, "}\n");
|
||||
f << "}\n";
|
||||
}
|
||||
|
||||
void ConvertMapFile(void)
|
||||
|
|
@ -2189,16 +2195,15 @@ void ConvertMapFile(void)
|
|||
std::filesystem::path filename = options.szBSPName;
|
||||
filename.replace_filename(options.szBSPName.filename().string() + append);
|
||||
|
||||
FILE *f = fopen(filename.string().c_str(), "wb");
|
||||
if (f == nullptr)
|
||||
std::ofstream f(filename);
|
||||
|
||||
if (!f)
|
||||
FError("Couldn't open file\n");
|
||||
|
||||
for (const mapentity_t &entity : map.entities) {
|
||||
ConvertEntity(f, &entity, options.convertMapFormat);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
LogPrint("Conversion saved to {}\n", filename);
|
||||
|
||||
options.fVerbose = false;
|
||||
|
|
@ -2206,39 +2211,23 @@ void ConvertMapFile(void)
|
|||
|
||||
void PrintEntity(const mapentity_t *entity)
|
||||
{
|
||||
epair_t *epair;
|
||||
|
||||
for (epair = entity->epairs; epair; epair = epair->next)
|
||||
LogPrint(LOG_STAT, " {:20} : {}\n", epair->key, epair->value);
|
||||
for (auto &epair : entity->epairs)
|
||||
LogPrint(LOG_STAT, " {:20} : {}\n", epair.first, epair.second);
|
||||
}
|
||||
|
||||
const char *ValueForKey(const mapentity_t *entity, const char *key)
|
||||
{
|
||||
const epair_t *ep;
|
||||
auto it = entity->epairs.find(key);
|
||||
|
||||
for (ep = entity->epairs; ep; ep = ep->next)
|
||||
if (!Q_strcasecmp(ep->key, key))
|
||||
return ep->value;
|
||||
if (it == entity->epairs.end())
|
||||
return "";
|
||||
|
||||
return "";
|
||||
return it->second.c_str();
|
||||
}
|
||||
|
||||
void SetKeyValue(mapentity_t *entity, const char *key, const char *value)
|
||||
{
|
||||
epair_t *ep;
|
||||
|
||||
for (ep = entity->epairs; ep; ep = ep->next)
|
||||
if (!Q_strcasecmp(ep->key, key)) {
|
||||
delete[] ep->value; /* FIXME */
|
||||
ep->value = copystring(value);
|
||||
return;
|
||||
}
|
||||
|
||||
ep = new epair_t { };
|
||||
ep->next = entity->epairs;
|
||||
entity->epairs = ep;
|
||||
ep->key = copystring(key);
|
||||
ep->value = copystring(value);
|
||||
entity->epairs[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2262,32 +2251,31 @@ int GetVectorForKey(const mapentity_t *entity, const char *szKey, vec3_t vec)
|
|||
|
||||
void WriteEntitiesToString(void)
|
||||
{
|
||||
epair_t *ep;
|
||||
int i;
|
||||
mapentity_t *entity;
|
||||
|
||||
std::stringstream ss;
|
||||
std::string ss;
|
||||
|
||||
for (i = 0; i < map.numentities(); i++) {
|
||||
entity = &map.entities.at(i);
|
||||
|
||||
/* Check if entity needs to be removed */
|
||||
if (!entity->epairs || IsWorldBrushEntity(entity)) {
|
||||
if (!entity->epairs.size() || IsWorldBrushEntity(entity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ss << "{\n";
|
||||
ss += "{\n";
|
||||
|
||||
for (ep = entity->epairs; ep; ep = ep->next) {
|
||||
for (auto &ep : entity->epairs) {
|
||||
// Limit on Quake's strings of 128 bytes
|
||||
// TODO: Warn when limit is exceeded
|
||||
ss << "\"" << std::string(ep->key) << "\" \"" << std::string(ep->value) << "\"\n";
|
||||
fmt::format_to(std::back_inserter(ss), "\"{}\" \"{}\"\n", ep.first, ep.second);
|
||||
}
|
||||
|
||||
ss << "}\n";
|
||||
ss += "}\n";
|
||||
}
|
||||
|
||||
map.exported_entities = ss.str();
|
||||
map.exported_entities = std::move(ss);
|
||||
}
|
||||
|
||||
//====================================================================
|
||||
|
|
@ -2299,19 +2287,18 @@ WriteBspBrushMap
|
|||
from q3map
|
||||
==================
|
||||
*/
|
||||
void WriteBspBrushMap(const char *name, const std::vector<const brush_t *> &list)
|
||||
void WriteBspBrushMap(const std::filesystem::path &name, const std::vector<const brush_t *> &list)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
LogPrint("writing {}\n", name);
|
||||
f = fopen(name, "wb");
|
||||
std::ofstream f(name);
|
||||
|
||||
if (!f)
|
||||
FError("Can't write {}", name);
|
||||
|
||||
fprintf(f, "{\n\"classname\" \"worldspawn\"\n");
|
||||
fmt::print(f, "{\n\"classname\" \"worldspawn\"\n");
|
||||
|
||||
for (const brush_t *brush : list) {
|
||||
fprintf(f, "{\n");
|
||||
fmt::print(f, "{\n");
|
||||
for (const face_t *face = brush->faces; face; face = face->next) {
|
||||
// FIXME: Factor out this mess
|
||||
qbsp_plane_t plane = map.planes.at(face->planenum);
|
||||
|
|
@ -2322,19 +2309,19 @@ void WriteBspBrushMap(const char *name, const std::vector<const brush_t *> &list
|
|||
|
||||
winding_t *w = BaseWindingForPlane(&plane);
|
||||
|
||||
fprintf(f, "( %g %g %g ) ", w->points[0][0], w->points[0][1], w->points[0][2]);
|
||||
fprintf(f, "( %g %g %g ) ", w->points[1][0], w->points[1][1], w->points[1][2]);
|
||||
fprintf(f, "( %g %g %g ) ", w->points[2][0], w->points[2][1], w->points[2][2]);
|
||||
fmt::print(f, "( {} {} {} ) ", w->points[0][0], w->points[0][1], w->points[0][2]);
|
||||
fmt::print(f, "( {} {} {} ) ", w->points[1][0], w->points[1][1], w->points[1][2]);
|
||||
fmt::print(f, "( {} {} {} ) ", w->points[2][0], w->points[2][1], w->points[2][2]);
|
||||
|
||||
fprintf(f, "notexture 0 0 0 1 1\n");
|
||||
fmt::print(f, "notexture 0 0 0 1 1\n");
|
||||
|
||||
free(w);
|
||||
}
|
||||
fprintf(f, "}\n");
|
||||
}
|
||||
fprintf(f, "}\n");
|
||||
|
||||
fclose(f);
|
||||
fmt::print(f, "}\n");
|
||||
}
|
||||
|
||||
fmt::print(f, "}\n");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include <set>
|
||||
#include <list>
|
||||
#include <utility>
|
||||
#include <fstream>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
/*
|
||||
===========
|
||||
|
|
@ -45,13 +47,11 @@ node_t *PointInLeaf(node_t *node, const vec3_t point)
|
|||
return node;
|
||||
}
|
||||
|
||||
static FILE *InitPtsFile(void)
|
||||
static std::ofstream InitPtsFile(void)
|
||||
{
|
||||
FILE *ptsfile;
|
||||
|
||||
options.szBSPName.replace_extension("pts");
|
||||
|
||||
ptsfile = fopen(options.szBSPName.string().c_str(), "wt");
|
||||
std::ofstream ptsfile(options.szBSPName);
|
||||
|
||||
if (!ptsfile)
|
||||
FError("Failed to open {}: {}", options.szBSPName, strerror(errno));
|
||||
|
|
@ -191,7 +191,7 @@ static std::pair<std::vector<portal_t *>, node_t *> MakeLeakLine(node_t *outleaf
|
|||
WriteLeakTrail
|
||||
===============
|
||||
*/
|
||||
static void WriteLeakTrail(FILE *leakfile, const vec3_t point1, const vec3_t point2)
|
||||
static void WriteLeakTrail(std::ofstream &leakfile, const vec3_t point1, const vec3_t point2)
|
||||
{
|
||||
vec3_t vector, trail;
|
||||
vec_t dist;
|
||||
|
|
@ -201,7 +201,7 @@ static void WriteLeakTrail(FILE *leakfile, const vec3_t point1, const vec3_t poi
|
|||
|
||||
VectorCopy(point1, trail);
|
||||
while (dist > options.dxLeakDist) {
|
||||
fprintf(leakfile, "%f %f %f\n", trail[0], trail[1], trail[2]);
|
||||
fmt::print(leakfile, "{} {} {}\n", trail[0], trail[1], trail[2]);
|
||||
VectorMA(trail, options.dxLeakDist, vector, trail);
|
||||
dist -= options.dxLeakDist;
|
||||
}
|
||||
|
|
@ -209,7 +209,7 @@ static void WriteLeakTrail(FILE *leakfile, const vec3_t point1, const vec3_t poi
|
|||
|
||||
static void WriteLeakLine(const std::pair<std::vector<portal_t *>, node_t *> &leakline)
|
||||
{
|
||||
FILE *ptsfile = InitPtsFile();
|
||||
std::ofstream ptsfile = InitPtsFile();
|
||||
|
||||
vec3_t prevpt, currpt;
|
||||
VectorCopy(leakline.second->occupant->origin, prevpt);
|
||||
|
|
@ -224,7 +224,6 @@ static void WriteLeakLine(const std::pair<std::vector<portal_t *>, node_t *> &le
|
|||
VectorCopy(currpt, prevpt);
|
||||
}
|
||||
|
||||
fclose(ptsfile);
|
||||
LogPrint("Leak file written to {}\n", options.szBSPName);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@
|
|||
*/
|
||||
// portals.c
|
||||
|
||||
#include <fstream>
|
||||
#include <fmt/ostream.h>
|
||||
#include <qbsp/qbsp.hh>
|
||||
|
||||
node_t outside_node; // portals outside the world face this
|
||||
|
|
@ -44,12 +46,12 @@ PORTAL FILE GENERATION
|
|||
|
||||
static void PlaneFromWinding(const winding_t *w, qbsp_plane_t *plane);
|
||||
|
||||
static void WriteFloat(FILE *portalFile, vec_t v)
|
||||
static void WriteFloat(std::ofstream &portalFile, vec_t v)
|
||||
{
|
||||
if (fabs(v - Q_rint(v)) < ZERO_EPSILON)
|
||||
fprintf(portalFile, "%d ", (int)Q_rint(v));
|
||||
fmt::print(portalFile, "{} ", (int)Q_rint(v));
|
||||
else
|
||||
fprintf(portalFile, "%f ", v);
|
||||
fmt::print(portalFile, "{} ", v);
|
||||
}
|
||||
|
||||
static contentflags_t ClusterContents(const node_t *node)
|
||||
|
|
@ -96,7 +98,7 @@ static bool PortalThru(const portal_t *p)
|
|||
return options.target_game->portal_can_see_through(contents0, contents1);
|
||||
}
|
||||
|
||||
static void WritePortals_r(node_t *node, FILE *portalFile, bool clusters)
|
||||
static void WritePortals_r(node_t *node, std::ofstream &portalFile, bool clusters)
|
||||
{
|
||||
const portal_t *p, *next;
|
||||
const winding_t *w;
|
||||
|
|
@ -131,22 +133,22 @@ static void WritePortals_r(node_t *node, FILE *portalFile, bool clusters)
|
|||
pl = &map.planes[p->planenum];
|
||||
PlaneFromWinding(w, &plane2);
|
||||
if (DotProduct(pl->normal, plane2.normal) < 1.0 - ANGLEEPSILON)
|
||||
fprintf(portalFile, "%d %d %d ", w->numpoints, back, front);
|
||||
fmt::print(portalFile, "{} {} {} ", w->numpoints, back, front);
|
||||
else
|
||||
fprintf(portalFile, "%d %d %d ", w->numpoints, front, back);
|
||||
fmt::print(portalFile, "{} {} {} ", w->numpoints, front, back);
|
||||
|
||||
for (i = 0; i < w->numpoints; i++) {
|
||||
fprintf(portalFile, "(");
|
||||
fmt::print(portalFile, "(");
|
||||
WriteFloat(portalFile, w->points[i][0]);
|
||||
WriteFloat(portalFile, w->points[i][1]);
|
||||
WriteFloat(portalFile, w->points[i][2]);
|
||||
fprintf(portalFile, ") ");
|
||||
fmt::print(portalFile, ") ");
|
||||
}
|
||||
fprintf(portalFile, "\n");
|
||||
fmt::print(portalFile, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int WriteClusters_r(node_t *node, FILE *portalFile, int viscluster)
|
||||
static int WriteClusters_r(node_t *node, std::ofstream &portalFile, int viscluster)
|
||||
{
|
||||
if (node->planenum != PLANENUM_LEAF) {
|
||||
viscluster = WriteClusters_r(node->children[0], portalFile, viscluster);
|
||||
|
|
@ -158,7 +160,7 @@ static int WriteClusters_r(node_t *node, FILE *portalFile, int viscluster)
|
|||
|
||||
/* If we're in the next cluster, start a new line */
|
||||
if (node->viscluster != viscluster) {
|
||||
fprintf(portalFile, "-1\n");
|
||||
fmt::print(portalFile, "-1\n");
|
||||
viscluster++;
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +168,7 @@ static int WriteClusters_r(node_t *node, FILE *portalFile, int viscluster)
|
|||
if (node->viscluster != viscluster)
|
||||
FError("Internal error: Detail cluster mismatch");
|
||||
|
||||
fprintf(portalFile, "%d ", node->visleafnum);
|
||||
fmt::print(portalFile, "%d ", node->visleafnum);
|
||||
|
||||
return viscluster;
|
||||
}
|
||||
|
|
@ -232,7 +234,6 @@ WritePortalfile
|
|||
static void WritePortalfile(node_t *headnode, portal_state_t *state)
|
||||
{
|
||||
int check;
|
||||
FILE *portalFile;
|
||||
|
||||
/*
|
||||
* Set the visleafnum and viscluster field in every leaf and count the
|
||||
|
|
@ -247,38 +248,36 @@ static void WritePortalfile(node_t *headnode, portal_state_t *state)
|
|||
// write the file
|
||||
options.szBSPName.replace_extension("prt");
|
||||
|
||||
portalFile = fopen(options.szBSPName.string().c_str(), "wt");
|
||||
std::ofstream portalFile(options.szBSPName);
|
||||
if (!portalFile)
|
||||
FError("Failed to open {}: {}", options.szBSPName, strerror(errno));
|
||||
|
||||
/* If no detail clusters, just use a normal PRT1 format */
|
||||
if (!state->uses_detail) {
|
||||
fprintf(portalFile, "PRT1\n");
|
||||
fprintf(portalFile, "%d\n", state->num_visleafs);
|
||||
fprintf(portalFile, "%d\n", state->num_visportals);
|
||||
fmt::print(portalFile, "PRT1\n");
|
||||
fmt::print(portalFile, "{}\n", state->num_visleafs);
|
||||
fmt::print(portalFile, "{}\n", state->num_visportals);
|
||||
WritePortals_r(headnode, portalFile, false);
|
||||
} else {
|
||||
if (options.fForcePRT1) {
|
||||
/* Write a PRT1 file for loading in the map editor. Vis will reject it. */
|
||||
fprintf(portalFile, "PRT1\n");
|
||||
fprintf(portalFile, "%d\n", state->num_visclusters);
|
||||
fprintf(portalFile, "%d\n", state->num_visportals);
|
||||
fmt::print(portalFile, "PRT1\n");
|
||||
fmt::print(portalFile, "{}\n", state->num_visclusters);
|
||||
fmt::print(portalFile, "{}\n", state->num_visportals);
|
||||
WritePortals_r(headnode, portalFile, true);
|
||||
} else {
|
||||
/* Write a PRT2 */
|
||||
fprintf(portalFile, "PRT2\n");
|
||||
fprintf(portalFile, "%d\n", state->num_visleafs);
|
||||
fprintf(portalFile, "%d\n", state->num_visclusters);
|
||||
fprintf(portalFile, "%d\n", state->num_visportals);
|
||||
fmt::print(portalFile, "PRT2\n");
|
||||
fmt::print(portalFile, "{}\n", state->num_visleafs);
|
||||
fmt::print(portalFile, "{}\n", state->num_visclusters);
|
||||
fmt::print(portalFile, "{}\n", state->num_visportals);
|
||||
WritePortals_r(headnode, portalFile, true);
|
||||
check = WriteClusters_r(headnode, portalFile, 0);
|
||||
if (check != state->num_visclusters - 1)
|
||||
FError("Internal error: Detail cluster mismatch");
|
||||
fprintf(portalFile, "-1\n");
|
||||
fmt::print(portalFile, "-1\n");
|
||||
}
|
||||
}
|
||||
|
||||
fclose(portalFile);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
|
|||
29
qbsp/wad.cc
29
qbsp/wad.cc
|
|
@ -64,7 +64,7 @@ static bool WAD_LoadInfo(wad_t &wad, bool external)
|
|||
|
||||
external |= options.fNoTextures;
|
||||
|
||||
len = fread(hdr, 1, sizeof(wadinfo_t), wad.file);
|
||||
len = SafeRead(wad.file, hdr, sizeof(wadinfo_t));
|
||||
if (len != sizeof(wadinfo_t))
|
||||
return false;
|
||||
|
||||
|
|
@ -76,21 +76,21 @@ static bool WAD_LoadInfo(wad_t &wad, bool external)
|
|||
if (!wad.version)
|
||||
return false;
|
||||
|
||||
fseek(wad.file, hdr->infotableofs, SEEK_SET);
|
||||
SafeSeek(wad.file, hdr->infotableofs, SEEK_SET);
|
||||
wad.lumps.reserve(wad.header.numlumps);
|
||||
|
||||
/* Get the dimensions and make a texture_t */
|
||||
for (i = 0; i < wad.header.numlumps; i++) {
|
||||
lumpinfo_t lump;
|
||||
|
||||
len = fread(&lump, 1, sizeof(lump), wad.file);
|
||||
len = SafeRead(wad.file, &lump, sizeof(lump));
|
||||
if (len != sizeof(lump))
|
||||
return false;
|
||||
|
||||
auto restore_pos = ftell(wad.file);
|
||||
auto restore_pos = SafeTell(wad.file);
|
||||
|
||||
fseek(wad.file, lump.filepos, SEEK_SET);
|
||||
len = fread(&miptex, 1, sizeof(miptex), wad.file);
|
||||
SafeSeek(wad.file, lump.filepos, SEEK_SET);
|
||||
len = SafeRead(wad.file, &miptex, sizeof(miptex));
|
||||
|
||||
if (len == sizeof(miptex)) {
|
||||
int w = LittleLong(miptex.width);
|
||||
|
|
@ -116,7 +116,7 @@ static bool WAD_LoadInfo(wad_t &wad, bool external)
|
|||
} else
|
||||
lump.size = 0;
|
||||
|
||||
fseek(wad.file, restore_pos, SEEK_SET);
|
||||
SafeSeek(wad.file, restore_pos, SEEK_SET);
|
||||
|
||||
wad.lumps.insert({lump.name, lump});
|
||||
}
|
||||
|
|
@ -128,20 +128,19 @@ static void WADList_OpenWad(const std::filesystem::path &fpath, bool external)
|
|||
{
|
||||
wad_t wad;
|
||||
|
||||
wad.file = fopen(fpath.string().c_str(), "rb");
|
||||
wad.file = SafeOpenRead(fpath);
|
||||
|
||||
if (wad.file) {
|
||||
if (options.fVerbose)
|
||||
LogPrint("Opened WAD: {}\n", fpath);
|
||||
|
||||
if (WAD_LoadInfo(wad, external)) {
|
||||
wadlist.push_front(wad);
|
||||
wad.file = nullptr; // wadlist now owns this file handle
|
||||
wadlist.emplace_front(std::move(wad));
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint("WARNING: {} isn't a wadfile\n", fpath);
|
||||
fclose(wad.file);
|
||||
wad.file.reset();
|
||||
} else {
|
||||
// Message?
|
||||
}
|
||||
|
|
@ -207,10 +206,10 @@ static int WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest)
|
|||
|
||||
auto &lump = it->second;
|
||||
|
||||
fseek(wad.file, lump.filepos, SEEK_SET);
|
||||
SafeSeek(wad.file, lump.filepos, SEEK_SET);
|
||||
|
||||
if (lump.disksize == sizeof(dmiptex_t)) {
|
||||
size = fread(dest, 1, sizeof(dmiptex_t), wad.file);
|
||||
size = SafeRead(wad.file, dest, sizeof(dmiptex_t));
|
||||
if (size != sizeof(dmiptex_t))
|
||||
FError("Failure reading from file");
|
||||
for (i = 0; i < MIPLEVELS; i++)
|
||||
|
|
@ -221,7 +220,7 @@ static int WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest)
|
|||
if (lump.size != lump.disksize) {
|
||||
LogPrint("Texture {} is {} bytes in wad, packed to {} bytes in bsp\n", name, lump.disksize, lump.size);
|
||||
std::vector<uint8_t> data(lump.disksize);
|
||||
size = fread(data.data(), 1, lump.disksize, wad.file);
|
||||
size = SafeRead(wad.file, data.data(), lump.disksize);
|
||||
if (size != lump.disksize)
|
||||
FError("Failure reading from file");
|
||||
auto out = (dmiptex_t *)dest;
|
||||
|
|
@ -249,7 +248,7 @@ static int WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest)
|
|||
memcpy(dest + palofs + 2, thepalette, 3 * 256); // FIXME: quake palette or something.
|
||||
}
|
||||
} else {
|
||||
size = fread(dest, 1, lump.disksize, wad.file);
|
||||
size = SafeRead(wad.file, dest, lump.disksize);
|
||||
if (size != lump.disksize)
|
||||
FError("Failure reading from file");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,10 +345,9 @@ static void WriteExtendedTexinfoFlags(void)
|
|||
std::sort(texinfos_sorted.begin(), texinfos_sorted.end(),
|
||||
[](const mtexinfo_t &a, const mtexinfo_t &b) { return a.outputnum < b.outputnum; });
|
||||
|
||||
FILE *texinfofile;
|
||||
options.szBSPName.replace_extension("texinfo");
|
||||
|
||||
texinfofile = fopen(options.szBSPName.string().c_str(), "wt");
|
||||
qfile_t texinfofile = SafeOpenWrite(options.szBSPName);
|
||||
|
||||
if (!texinfofile)
|
||||
FError("Failed to open {}: {}", options.szBSPName, strerror(errno));
|
||||
|
|
@ -357,7 +356,7 @@ static void WriteExtendedTexinfoFlags(void)
|
|||
header.num_texinfo = map.exported_texinfos.size();
|
||||
header.surfflags_size = sizeof(surfflags_t);
|
||||
|
||||
fwrite(&header, 1, sizeof(header), texinfofile);
|
||||
SafeWrite(texinfofile, &header, sizeof(header));
|
||||
|
||||
int count = 0;
|
||||
for (const auto &tx : texinfos_sorted) {
|
||||
|
|
@ -366,12 +365,10 @@ static void WriteExtendedTexinfoFlags(void)
|
|||
|
||||
Q_assert(count == tx.outputnum.value()); // check we are outputting them in the proper sequence
|
||||
|
||||
fwrite(&tx.flags, 1, sizeof(tx.flags), texinfofile);
|
||||
SafeWrite(texinfofile, &tx.flags, sizeof(tx.flags));
|
||||
count++;
|
||||
}
|
||||
Q_assert(count == static_cast<int>(map.exported_texinfos.size()));
|
||||
|
||||
fclose(texinfofile);
|
||||
}
|
||||
|
||||
template<class C>
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ bool LoadVisState(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
auto infile = SafeOpenRead(statefile.string().c_str());
|
||||
auto infile = SafeOpenRead(statefile, true);
|
||||
|
||||
SafeRead(infile, &state, sizeof(state));
|
||||
state.version = LittleLong(state.version);
|
||||
|
|
|
|||
31
vis/vis.cc
31
vis/vis.cc
|
|
@ -998,32 +998,27 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
portal_t *p;
|
||||
leaf_t *l;
|
||||
char magic[80];
|
||||
FILE *f;
|
||||
qfile_t f { nullptr, nullptr };
|
||||
int numpoints;
|
||||
winding_t *w;
|
||||
int leafnums[2];
|
||||
plane_t plane;
|
||||
|
||||
if (name == "-")
|
||||
f = stdin;
|
||||
f = { stdin, nullptr };
|
||||
else {
|
||||
f = fopen(name.string().c_str(), "r");
|
||||
if (!f) {
|
||||
FLogPrint("couldn't read {}\n", name);
|
||||
LogPrint("No vising performed.\n");
|
||||
exit(1);
|
||||
}
|
||||
f = SafeOpenRead(name, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the portal file header
|
||||
*/
|
||||
count = fscanf(f, "%79s\n", magic);
|
||||
count = fscanf(f.get(), "%79s\n", magic);
|
||||
if (count != 1)
|
||||
FError("unknown header: {}\n", magic);
|
||||
|
||||
if (!strcmp(magic, PORTALFILE)) {
|
||||
count = fscanf(f, "%i\n%i\n", &portalleafs, &numportals);
|
||||
count = fscanf(f.get(), "%i\n%i\n", &portalleafs, &numportals);
|
||||
if (count != 2)
|
||||
FError("unable to parse {} HEADER\n", PORTALFILE);
|
||||
|
||||
|
|
@ -1038,14 +1033,14 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
LogPrint("{:6} portals\n", numportals);
|
||||
}
|
||||
} else if (!strcmp(magic, PORTALFILE2)) {
|
||||
count = fscanf(f, "%i\n%i\n%i\n", &portalleafs_real, &portalleafs, &numportals);
|
||||
count = fscanf(f.get(), "%i\n%i\n%i\n", &portalleafs_real, &portalleafs, &numportals);
|
||||
if (count != 3)
|
||||
FError("unable to parse {} HEADER\n", PORTALFILE);
|
||||
LogPrint("{:6} leafs\n", portalleafs_real);
|
||||
LogPrint("{:6} clusters\n", portalleafs);
|
||||
LogPrint("{:6} portals\n", numportals);
|
||||
} else if (!strcmp(magic, PORTALFILEAM)) {
|
||||
count = fscanf(f, "%i\n%i\n%i\n", &portalleafs, &numportals, &portalleafs_real);
|
||||
count = fscanf(f.get(), "%i\n%i\n%i\n", &portalleafs, &numportals, &portalleafs_real);
|
||||
if (count != 3)
|
||||
FError("unable to parse {} HEADER\n", PORTALFILE);
|
||||
LogPrint("{:6} leafs\n", portalleafs_real);
|
||||
|
|
@ -1080,7 +1075,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
vismap_end = vismap + MAX_MAP_VISIBILITY;
|
||||
|
||||
for (i = 0, p = portals; i < numportals; i++) {
|
||||
if (fscanf(f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)
|
||||
if (fscanf(f.get(), "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3)
|
||||
FError("reading portal {}", i);
|
||||
if (numpoints > MAX_WINDING)
|
||||
FError("portal {} has too many points", i);
|
||||
|
|
@ -1091,10 +1086,10 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
w->numpoints = numpoints;
|
||||
|
||||
for (j = 0; j < numpoints; j++) {
|
||||
if (fscanf(f, "(%lf %lf %lf ) ", &w->points[j][0], &w->points[j][1], &w->points[j][2]) != 3)
|
||||
if (fscanf(f.get(), "(%lf %lf %lf ) ", &w->points[j][0], &w->points[j][1], &w->points[j][2]) != 3)
|
||||
FError("reading portal {}", i);
|
||||
}
|
||||
fscanf(f, "\n");
|
||||
fscanf(f.get(), "\n");
|
||||
|
||||
// calc plane
|
||||
PlaneFromWinding(w, &plane);
|
||||
|
|
@ -1146,7 +1141,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
for (i = 0; i < portalleafs; i++) {
|
||||
while (1) {
|
||||
int leafnum;
|
||||
count = fscanf(f, "%i", &leafnum);
|
||||
count = fscanf(f.get(), "%i", &leafnum);
|
||||
if (!count || count == EOF)
|
||||
break;
|
||||
if (leafnum < 0)
|
||||
|
|
@ -1163,7 +1158,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
} else if (!strcmp(magic, PORTALFILEAM)) {
|
||||
for (i = 0; i < portalleafs_real; i++) {
|
||||
int clusternum;
|
||||
count = fscanf(f, "%i", &clusternum);
|
||||
count = fscanf(f.get(), "%i", &clusternum);
|
||||
if (!count || count == EOF) {
|
||||
Error("Unexpected end of cluster map\n");
|
||||
}
|
||||
|
|
@ -1177,8 +1172,6 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp)
|
|||
FError("Unknown header {}\n", magic);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue