diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 17:54:45 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-04-12 17:54:45 -0500 |
| commit | a0355c9b897281d0bfea48bec9d490724ecabd96 (patch) | |
| tree | 42c56ea4f4829a6115f688956858ada3e59fd9ef /libs/preview_nif/src/PreviewNif.cpp | |
| parent | fa43a71f9d05b3673d42296a80decb2228d68f83 (diff) | |
Add NIF previewer plugin with Linux-specific fixes
Vendored from github.com/Parapets/mo2-preview_nif and adapted for Linux:
- BSA texture resolution via MO2 virtual tree with case-insensitive
fallback walk (Linux FS is case-sensitive; NIFs reference textures
with arbitrary case against files on disk)
- Process-wide cached BSA candidate list, keyed by profile path,
priority-sorted, filtering disabled mods
- Path backslash normalization before loose-file lookup
- Polygon offset on SLSF1_Decal-flagged shapes to break z-ties with
coincident base meshes (road decals, moss overlays)
- Opaque framebuffer via glColorMask alpha lock so alpha-blended shapes
can't bleed the dialog background through the preview area
- QMouseEvent::globalPosition().toPoint() for Qt6 compatibility
Preview dialog behavior (organizercore.cpp):
- Switch previewFile and previewFileWithAlternatives from stack-allocated
exec() to heap + ApplicationModal + show() + WA_DeleteOnClose.
- Parent passed as nullptr so preview lifetime is decoupled from the
stack-allocated ModInfoDialog that spawns it. Nested exec() inside
the enclosing dialog's exec() was softlocking on close.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'libs/preview_nif/src/PreviewNif.cpp')
| -rw-r--r-- | libs/preview_nif/src/PreviewNif.cpp | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/libs/preview_nif/src/PreviewNif.cpp b/libs/preview_nif/src/PreviewNif.cpp new file mode 100644 index 0000000..8e7e03a --- /dev/null +++ b/libs/preview_nif/src/PreviewNif.cpp @@ -0,0 +1,114 @@ +#include <NifFile.hpp> + +#include "PreviewNif.h" +#include "NifExtensions.h" +#include "NifWidget.h" + +#include <QGridLayout> +#include <filesystem> +#include <sstream> +#include <string> + +bool PreviewNif::init(MOBase::IOrganizer* moInfo) +{ + m_MOInfo = moInfo; + return true; +} + +QString PreviewNif::name() const +{ + return "Preview NIF"; +} + +QString PreviewNif::author() const +{ + return "Parapets"; +} + +QString PreviewNif::description() const +{ + return "Supports previewing NIF files"; +} + +MOBase::VersionInfo PreviewNif::version() const +{ + return MOBase::VersionInfo(0, 2, 0, 0, MOBase::VersionInfo::RELEASE_BETA); +} + +QList<MOBase::PluginSetting> PreviewNif::settings() const +{ + return QList<MOBase::PluginSetting>(); +} + +bool PreviewNif::enabledByDefault() const +{ + return true; +} + +std::set<QString> PreviewNif::supportedExtensions() const +{ + return { "bto", "btr", "nif" }; +} + +QWidget* PreviewNif::genFilePreview(const QString& fileName, const QSize&) const +{ + auto path = std::filesystem::path(fileName.toStdString()); + auto nifFile = std::make_shared<nifly::NifFile>(path); + + if (!nifFile->IsValid()) { + qWarning(qUtf8Printable(tr("Failed to load file: %1").arg(fileName))); + return nullptr; + } + + return makeWidget(nifFile); +} + +QWidget* PreviewNif::genDataPreview(const QByteArray& fileData, + const QString& fileName, const QSize&) const +{ + std::string data(fileData.constData(), fileData.size()); + std::istringstream stream(std::move(data), std::ios::binary); + + auto nifFile = std::make_shared<nifly::NifFile>(); + if (nifFile->Load(stream) != 0 || !nifFile->IsValid()) { + qWarning(qUtf8Printable(tr("Failed to load file: %1").arg(fileName))); + return nullptr; + } + + return makeWidget(nifFile); +} + +QWidget* PreviewNif::makeWidget(std::shared_ptr<nifly::NifFile> nifFile) const +{ + auto layout = new QGridLayout(); + layout->setRowStretch(0, 1); + layout->setColumnStretch(0, 1); + + layout->addWidget(makeLabel(nifFile.get()), 1, 0, 1, 1); + + auto nifWidget = new NifWidget(nifFile, m_MOInfo); + layout->addWidget(nifWidget, 0, 0, 1, 1); + + auto widget = new QWidget(); + widget->setLayout(layout); + return widget; +} + +QLabel* PreviewNif::makeLabel(nifly::NifFile* nifFile) const +{ + int shapes = 0; + int faces = 0; + int verts = 0; + + for (auto& shape : nifFile->GetShapes()) { + shapes++; + faces += shape->GetNumTriangles(); + verts += shape->GetNumVertices(); + } + + auto text = tr("Verts: %1 | Faces: %2 | Shapes: %3").arg(verts).arg(faces).arg(shapes); + auto label = new QLabel(text); + label->setWordWrap(true); + label->setTextInteractionFlags(Qt::TextSelectableByMouse); + return label; +} |
