add -svg to bsputil

This commit is contained in:
Jonathan 2024-02-14 21:25:39 -05:00
parent b12894de64
commit 0b0dd6759d
2 changed files with 224 additions and 24 deletions

View File

@ -40,6 +40,7 @@
#include <algorithm> // std::sort #include <algorithm> // std::sort
#include <string> #include <string>
#include <fstream> #include <fstream>
#include <fmt/ostream.h>
// TODO // TODO
settings::common_settings bsputil_options; settings::common_settings bsputil_options;
@ -709,7 +710,8 @@ int bsputil_main(int argc, char **argv)
"[--decompile] [--decompile-geomonly] [--decompile-hull n]\n" "[--decompile] [--decompile-geomonly] [--decompile-hull n]\n"
"[--extract-bspx-lump lump_name output_file_name]\n" "[--extract-bspx-lump lump_name output_file_name]\n"
"[--insert-bspx-lump lump_name input_file_name]\n" "[--insert-bspx-lump lump_name input_file_name]\n"
"[--remove-bspx-lump lump_name] bspfile/mapfile\n"); "[--remove-bspx-lump lump_name]\n"
"[--svg] bspfile/mapfile\n");
exit(1); exit(1);
} }
@ -735,7 +737,205 @@ int bsputil_main(int argc, char **argv)
} }
for (int32_t i = 1; i < argc - 1; i++) { for (int32_t i = 1; i < argc - 1; i++) {
if (!strcmp(argv[i], "--scale")) { if (!strcmp(argv[i], "--svg")) {
fs::path svg = fs::path(source).replace_extension(".svg");
std::ofstream f(svg, std::ios_base::out);
f << R"(<?xml version="1.0" encoding="UTF-8"?>)" << std::endl;
f << R"(<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">)" << std::endl;
auto &bsp = std::get<mbsp_t>(bspdata.bsp);
img::load_textures(&bsp, {});
struct rendered_faces_t
{
std::vector<const mface_t *> faces;
qvec3f origin;
aabb3f bounds;
};
std::vector<rendered_faces_t> faces;
aabb3f total_bounds;
size_t total_faces = 0;
auto ents = EntData_Parse(bsp);
auto addSubModel = [&bsp, &faces, &total_bounds, &total_faces](int32_t index, qvec3f origin) {
auto &model = bsp.dmodels[index];
rendered_faces_t f {
{},
origin
};
std::vector<size_t> face_ids;
face_ids.reserve(model.numfaces);
for (size_t i = model.firstface; i < model.firstface + model.numfaces; i++)
{
auto &face = bsp.dfaces[i];
if (face.texinfo == -1)
continue;
auto &texinfo = bsp.texinfo[face.texinfo];
if (texinfo.flags.is_nodraw)
continue;
// TODO
//else if (texinfo.flags.native & Q2_SURF_SKY)
// continue;
else if (!Q_strcasecmp(Face_TextureName(&bsp, &face), "trigger"))
continue;
auto norm = Face_Normal(&bsp, &face);
if (qv::dot(qvec3d(0, 0, 1), norm) <= DEFAULT_ON_EPSILON)
continue;
face_ids.push_back(i);
}
std::sort(face_ids.begin(), face_ids.end(), [&bsp](size_t a, size_t b) {
float za = std::numeric_limits<float>::lowest();
float zb = za;
auto &facea = bsp.dfaces[a];
auto &faceb = bsp.dfaces[b];
for (size_t e = 0; e < facea.numedges; e++)
za = std::max(za, Face_PointAtIndex(&bsp, &facea, e)[2]);
for (size_t e = 0; e < faceb.numedges; e++)
zb = std::max(zb, Face_PointAtIndex(&bsp, &faceb, e)[2]);
return za < zb;
});
for (auto &face_index : face_ids)
{
const auto &face = bsp.dfaces[face_index];
f.faces.push_back(&face);
for (auto pt : Face_Points(&bsp, &face))
f.bounds += f.origin + pt;
}
if (f.faces.empty())
return;
total_bounds += f.bounds;
total_faces += f.faces.size();
faces.emplace_back(std::move(f));
};
addSubModel(0, {});
for (auto &entity : ents)
{
if (!entity.has("model"))
continue;
qvec3f origin {};
int32_t model = atoi(entity.get("model").substr(1).c_str());
if (entity.has("origin"))
entity.get_vector("origin", origin);
addSubModel(model, origin);
}
total_bounds = total_bounds.grow(32);
float xo = total_bounds.mins()[0];
float yo = total_bounds.mins()[1];
float zo = total_bounds.mins()[2];
float xs = total_bounds.maxs()[0] - xo;
float ys = total_bounds.maxs()[1] - yo;
float zs = total_bounds.maxs()[2] - zo;
fmt::print(f, R"(<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{}" height="{}">)", xs, ys);
f << std::endl;
f << R"(<defs><g id="bsp">)" << std::endl;
struct face_id_t
{
size_t model;
size_t face;
};
std::vector<face_id_t> face_ids;
face_ids.reserve(total_faces);
for (size_t i = 0; i < faces.size(); i++)
for (size_t f = 0; f < faces[i].faces.size(); f++)
face_ids.emplace_back(i, f);
std::sort(face_ids.begin(), face_ids.end(), [&bsp, &faces, yo](face_id_t a, face_id_t b) {
float za = yo;
float zb = yo;
auto facea = faces[a.model].faces[a.face];
auto faceb = faces[b.model].faces[b.face];
for (size_t e = 0; e < facea->numedges; e++)
za = std::max(za, Face_PointAtIndex(&bsp, facea, e)[2] + faces[a.model].origin[2]);
for (size_t e = 0; e < faceb->numedges; e++)
zb = std::max(zb, Face_PointAtIndex(&bsp, faceb, e)[2] + faces[b.model].origin[2]);
return za < zb;
});
float low_z = total_bounds.maxs()[2], high_z = total_bounds.mins()[2];
for (auto &face_index : face_ids)
{
auto face = faces[face_index.model].faces[face_index.face];
for (auto &pt : Face_Points(&bsp, face))
{
low_z = std::min(low_z, pt[2] + faces[face_index.model].origin[2]);
high_z = std::max(high_z, pt[2] + faces[face_index.model].origin[2]);
}
}
for (auto &face_index : face_ids)
{
auto face = faces[face_index.model].faces[face_index.face];
auto pts = Face_Points(&bsp, face);
std::string pts_str;
float nz = xo;
for (auto &pt : pts)
{
std::format_to(std::back_inserter(pts_str), "{},{} ", (pt[0] + faces[face_index.model].origin[0]) - xo, ys - ((pt[1] + faces[face_index.model].origin[1]) - yo));
nz = std::max(nz, pt[2] + faces[face_index.model].origin[2]);
}
float z_scale = (nz - low_z) / (high_z - low_z);
float d = (0.5 + (z_scale * 0.5));
qvec3b color { 255, 255, 255 };
const char *tex = Face_TextureName(&bsp, face);
if (tex)
{
if (auto texptr = img::find(tex))
color = texptr->averageColor;
}
fmt::print(f, R"svg(<polygon points="{}" fill="rgb({}, {}, {})" />)svg", pts_str, color[0] * d, color[1] * d, color[2] * d);
f << std::endl;
}
f << R"(</g></defs>)" << std::endl;
f << R"(<use href="#bsp" fill="none" stroke="black" stroke-width="15" stroke-miterlimit="0" />)" << std::endl;
f << R"(<use href="#bsp" fill="white" stroke="black" stroke-width="1" />)" << std::endl;
f << R"(</svg>)" << std::endl;
} else if (!strcmp(argv[i], "--scale")) {
i++; i++;
if (i == argc - 1) { if (i == argc - 1) {

View File

@ -283,39 +283,39 @@ void MainWindow::createPropertiesSidebar()
// setup event handlers // setup event handlers
connect(reload_button, &QAbstractButton::clicked, this, &MainWindow::reload); connect(reload_button, &QAbstractButton::clicked, this, &MainWindow::reload);
connect(lightmap_only, &QAbstractButton::toggled, this, [=](bool checked) { glView->setLighmapOnly(checked); }); connect(lightmap_only, &QAbstractButton::toggled, this, [this](bool checked) { glView->setLighmapOnly(checked); });
connect(fullbright, &QAbstractButton::toggled, this, [=](bool checked) { glView->setFullbright(checked); }); connect(fullbright, &QAbstractButton::toggled, this, [this](bool checked) { glView->setFullbright(checked); });
connect(normals, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawNormals(checked); }); connect(normals, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawNormals(checked); });
connect(showtris, &QAbstractButton::toggled, this, [=](bool checked) { glView->setShowTris(checked); }); connect(showtris, &QAbstractButton::toggled, this, [this](bool checked) { glView->setShowTris(checked); });
connect(showtris_seethrough, &QAbstractButton::toggled, this, connect(showtris_seethrough, &QAbstractButton::toggled, this,
[=](bool checked) { glView->setShowTrisSeeThrough(checked); }); [this](bool checked) { glView->setShowTrisSeeThrough(checked); });
connect(visculling, &QAbstractButton::toggled, this, [=](bool checked) { connect(visculling, &QAbstractButton::toggled, this, [this, keepcullposition, keepcullfrustum](bool checked) {
glView->setVisCulling(checked); glView->setVisCulling(checked);
keepcullposition->setEnabled(checked); keepcullposition->setEnabled(checked);
keepcullfrustum->setEnabled(keepcullposition->isEnabled()); keepcullfrustum->setEnabled(keepcullposition->isEnabled());
}); });
connect(keepcullposition, &QAbstractButton::toggled, this, [=](bool checked) { connect(keepcullposition, &QAbstractButton::toggled, this, [this, keepcullfrustum](bool checked) {
glView->setKeepCullOrigin(checked); glView->setKeepCullOrigin(checked);
keepcullfrustum->setEnabled(checked); keepcullfrustum->setEnabled(checked);
}); });
connect(keepcullfrustum, &QAbstractButton::toggled, this, [=](bool checked) { glView->setKeepCullFrustum(checked); }); connect(keepcullfrustum, &QAbstractButton::toggled, this, [this](bool checked) { glView->setKeepCullFrustum(checked); });
connect(drawflat, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawFlat(checked); }); connect(drawflat, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawFlat(checked); });
connect(hull0, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{0} : std::nullopt); }); connect(hull0, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{0} : std::nullopt); });
connect(hull1, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{1} : std::nullopt); }); connect(hull1, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{1} : std::nullopt); });
connect(hull2, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{2} : std::nullopt); }); connect(hull2, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{2} : std::nullopt); });
connect(hull3, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{3} : std::nullopt); }); connect(hull3, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{3} : std::nullopt); });
connect(hull4, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{4} : std::nullopt); }); connect(hull4, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{4} : std::nullopt); });
connect(hull5, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{5} : std::nullopt); }); connect(hull5, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeafs(checked ? std::optional<int>{5} : std::nullopt); });
connect(drawportals, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawPortals(checked); }); connect(drawportals, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawPortals(checked); });
connect(drawleak, &QAbstractButton::toggled, this, [=](bool checked) { glView->setDrawLeak(checked); }); connect(drawleak, &QAbstractButton::toggled, this, [this](bool checked) { glView->setDrawLeak(checked); });
connect(keepposition, &QAbstractButton::toggled, this, [=](bool checked) { glView->setKeepOrigin(checked); }); connect(keepposition, &QAbstractButton::toggled, this, [this](bool checked) { glView->setKeepOrigin(checked); });
connect(nearest, &QAbstractButton::toggled, this, connect(nearest, &QAbstractButton::toggled, this,
[=](bool checked) { glView->setMagFilter(checked ? QOpenGLTexture::Nearest : QOpenGLTexture::Linear); }); [this](bool checked) { glView->setMagFilter(checked ? QOpenGLTexture::Nearest : QOpenGLTexture::Linear); });
connect(draw_opaque, &QAbstractButton::toggled, this, connect(draw_opaque, &QAbstractButton::toggled, this,
[=](bool checked) { glView->setDrawTranslucencyAsOpaque(checked); }); [this](bool checked) { glView->setDrawTranslucencyAsOpaque(checked); });
connect(glView, &GLView::cameraMoved, this, &MainWindow::displayCameraPositionInfo); connect(glView, &GLView::cameraMoved, this, &MainWindow::displayCameraPositionInfo);
connect(show_bmodels, &QAbstractButton::toggled, this, connect(show_bmodels, &QAbstractButton::toggled, this,
[=](bool checked) { glView->setShowBmodels(checked); }); [this](bool checked) { glView->setShowBmodels(checked); });
// set up load timer // set up load timer
m_fileReloadTimer = std::make_unique<QTimer>(); m_fileReloadTimer = std::make_unique<QTimer>();
@ -476,7 +476,7 @@ void MainWindow::showEvent(QShowEvent *event)
// FIXME: support more command-line options? // FIXME: support more command-line options?
auto args = QCoreApplication::arguments(); auto args = QCoreApplication::arguments();
if (args.size() == 2) { if (args.size() == 2) {
QTimer::singleShot(0, this, [=] { loadFile(args.at(1)); }); QTimer::singleShot(0, this, [this, args] { loadFile(args.at(1)); });
} }
} }