diff --git a/CMakeLists.txt b/CMakeLists.txt index d1f6c18..5e6a4ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,4 +43,5 @@ add_subdirectory(argcracker) add_subdirectory(explorer) add_subdirectory(bonedecomp) add_subdirectory(parts) -add_subdirectory(common) \ No newline at end of file +add_subdirectory(common) +add_subdirectory(mdlviewer) \ No newline at end of file diff --git a/armoury/CMakeLists.txt b/armoury/CMakeLists.txt index 7b3583c..b9fcecd 100644 --- a/armoury/CMakeLists.txt +++ b/armoury/CMakeLists.txt @@ -1,6 +1,3 @@ -find_package(assimp REQUIRED) -set_target_properties(assimp::assimp PROPERTIES MAP_IMPORTED_CONFIG_DEBUG Release) - add_executable(armoury src/main.cpp src/mainwindow.cpp @@ -25,7 +22,6 @@ target_link_libraries(armoury PUBLIC Qt5::Widgets Qt5::Concurrent renderer - assimp::assimp magic_enum physis z mdlpart diff --git a/mdlviewer/CMakeLists.txt b/mdlviewer/CMakeLists.txt new file mode 100644 index 0000000..a8eccfb --- /dev/null +++ b/mdlviewer/CMakeLists.txt @@ -0,0 +1,24 @@ +add_executable(mdlviewer + src/main.cpp + src/mainwindow.cpp) +target_include_directories(mdlviewer + PUBLIC + include) +target_link_libraries(mdlviewer PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets mdlpart NovusCommon) + +install(TARGETS mdlviewer + DESTINATION "${INSTALL_BIN_PATH}") + +if(WIN32) + get_target_property(QMAKE_EXE Qt5::qmake IMPORTED_LOCATION) + get_filename_component(QT_BIN_DIR "${QMAKE_EXE}" DIRECTORY) + + find_program(WINDEPLOYQT_ENV_SETUP qtenv2.bat HINTS "${QT_BIN_DIR}") + find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}") + + # Run windeployqt immediately after build + add_custom_command(TARGET mdlviewer + POST_BUILD + COMMAND "${WINDEPLOYQT_ENV_SETUP}" && "${WINDEPLOYQT_EXECUTABLE}" \"$\" + ) +endif() diff --git a/mdlviewer/include/mainwindow.h b/mdlviewer/include/mainwindow.h new file mode 100644 index 0000000..792d626 --- /dev/null +++ b/mdlviewer/include/mainwindow.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "filecache.h" + +struct GameData; +class MDLPart; + +class MainWindow : public QMainWindow { +public: + MainWindow(GameData* data); + +private: + GameData* data = nullptr; + MDLPart* part = nullptr; + FileCache cache; +}; \ No newline at end of file diff --git a/mdlviewer/src/main.cpp b/mdlviewer/src/main.cpp new file mode 100644 index 0000000..ee450a6 --- /dev/null +++ b/mdlviewer/src/main.cpp @@ -0,0 +1,13 @@ +#include +#include + +#include "mainwindow.h" + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + + MainWindow w(physis_gamedata_initialize(argv[1])); + w.show(); + + return app.exec(); +} \ No newline at end of file diff --git a/mdlviewer/src/mainwindow.cpp b/mdlviewer/src/mainwindow.cpp new file mode 100644 index 0000000..0c3d952 --- /dev/null +++ b/mdlviewer/src/mainwindow.cpp @@ -0,0 +1,69 @@ +#include "mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aboutwindow.h" +#include "mdlpart.h" + +MainWindow::MainWindow(GameData* data) : data(data), cache(FileCache{*data}) { + setWindowTitle("Model Viewer"); + + auto fileMenu = menuBar()->addMenu("File"); + + auto openMDLFile = fileMenu->addAction("Open MDL..."); + connect(openMDLFile, &QAction::triggered, [=] { + auto fileName = QFileDialog::getOpenFileName(nullptr, + "Open MDL File", + "~", + "FFXIV Model File (*.mdl)"); + + auto buffer = physis_read_file(fileName.toStdString().c_str()); + + part->addModel(physis_mdl_parse(buffer.size, buffer.data), {}, 0); + }); + + auto helpMenu = menuBar()->addMenu("Help"); + + auto donateAction = helpMenu->addAction("Donate"); + connect(donateAction, &QAction::triggered, this, [] { + QDesktopServices::openUrl(QUrl("https://redstrate.com/fund")); + }); + donateAction->setIcon(QIcon::fromTheme("help-donate")); + + helpMenu->addSeparator(); + + auto aboutNovusAction = helpMenu->addAction("About Model Viewer"); + aboutNovusAction->setIcon(QIcon::fromTheme("help-about")); + connect(aboutNovusAction, &QAction::triggered, this, [this] { + auto window = new AboutWindow(this); + window->show(); + }); + + auto aboutQtAction = helpMenu->addAction("About Qt"); + aboutQtAction->setIcon(QIcon(":/qt-project.org/qmessagebox/images/qtlogo-64.png")); + connect(aboutQtAction, &QAction::triggered, QApplication::instance(), &QApplication::aboutQt); + + auto dummyWidget = new QWidget(); + setCentralWidget(dummyWidget); + + auto layout = new QHBoxLayout(); + dummyWidget->setLayout(layout); + + part = new MDLPart(data, cache); + + const int raceCode = physis_get_race_code(Race::Hyur, Subrace::Midlander, Gender::Male); + QString skelName = QString{"c%1b0001.skel"}.arg(raceCode, 4, 10, QLatin1Char{'0'}); + part->setSkeleton(physis_skeleton_from_skel(physis_read_file(skelName.toStdString().c_str()))); + + layout->addWidget(part); +} \ No newline at end of file diff --git a/parts/mdl/CMakeLists.txt b/parts/mdl/CMakeLists.txt index beee462..f968459 100644 --- a/parts/mdl/CMakeLists.txt +++ b/parts/mdl/CMakeLists.txt @@ -1,3 +1,5 @@ +find_package(assimp REQUIRED) + add_library(mdlpart STATIC mdlpart.cpp) -target_link_libraries(mdlpart PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets renderer NovusCommon) +target_link_libraries(mdlpart PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets renderer NovusCommon assimp::assimp) target_include_directories(mdlpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/parts/mdl/mdlpart.cpp b/parts/mdl/mdlpart.cpp index 547b33e..8aec835 100644 --- a/parts/mdl/mdlpart.cpp +++ b/parts/mdl/mdlpart.cpp @@ -338,6 +338,10 @@ void MDLPart::addModel(physis_MDL mdl, std::vector materials, i return createMaterial(mat); }); + if (materials.empty()) { + model.materials.push_back(createMaterial(physis_Material{})); + } + models.push_back(model); Q_EMIT modelChanged();