mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-25 05:17:44 +00:00
Add file cache and concurrent item loading to speed up mdlviewer
Instead of extracting item sheets one at a time, it's now done on multiple threads. Loading gear is now faster since reused files are cached, switching between races is still wasteful, but it's a good enough improvement for now.
This commit is contained in:
parent
baf1158e00
commit
47d612eb8f
18 changed files with 322 additions and 265 deletions
|
@ -5,7 +5,7 @@ set(CMAKE_AUTOMOC ON)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Core Widgets CONFIG REQUIRED)
|
find_package(Qt5 COMPONENTS Core Widgets Concurrent CONFIG REQUIRED)
|
||||||
|
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
add_library(NovusCommon STATIC)
|
add_library(NovusCommon STATIC)
|
||||||
target_sources(NovusCommon PRIVATE
|
target_sources(NovusCommon PRIVATE
|
||||||
include/aboutwindow.h
|
include/aboutwindow.h
|
||||||
src/aboutwindow.cpp)
|
include/filecache.h
|
||||||
|
src/aboutwindow.cpp
|
||||||
|
src/filecache.cpp)
|
||||||
target_include_directories(NovusCommon PUBLIC
|
target_include_directories(NovusCommon PUBLIC
|
||||||
include
|
include
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${CMAKE_BINARY_DIR})
|
${CMAKE_BINARY_DIR})
|
||||||
target_link_libraries(NovusCommon PUBLIC Qt5::Core Qt5::Widgets)
|
target_link_libraries(NovusCommon PUBLIC Qt5::Core Qt5::Widgets physis)
|
||||||
|
|
||||||
# meant for including the license text
|
# meant for including the license text
|
||||||
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE LICENSE_TXT)
|
file(READ ${CMAKE_CURRENT_SOURCE_DIR}/../LICENSE LICENSE_TXT)
|
||||||
|
|
18
common/include/filecache.h
Normal file
18
common/include/filecache.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <physis.hpp>
|
||||||
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
struct GameData;
|
||||||
|
|
||||||
|
class FileCache {
|
||||||
|
public:
|
||||||
|
explicit FileCache(GameData& data);
|
||||||
|
|
||||||
|
physis_Buffer& lookupFile(const QString& path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, physis_Buffer> cachedBuffers;
|
||||||
|
GameData& data;
|
||||||
|
};
|
16
common/src/filecache.cpp
Normal file
16
common/src/filecache.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "filecache.h"
|
||||||
|
|
||||||
|
#include <physis.hpp>
|
||||||
|
|
||||||
|
FileCache::FileCache(GameData& data) : data(data) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
physis_Buffer& FileCache::lookupFile(const QString& path) {
|
||||||
|
if (!cachedBuffers.contains(path)) {
|
||||||
|
cachedBuffers[path] = physis_gamedata_extract_file(&data, path.toStdString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedBuffers[path];
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ target_link_libraries(mdlviewer PUBLIC
|
||||||
${LIBRARIES}
|
${LIBRARIES}
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
|
Qt5::Concurrent
|
||||||
renderer
|
renderer
|
||||||
assimp::assimp
|
assimp::assimp
|
||||||
magic_enum
|
magic_enum
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
#include "gearview.h"
|
#include "gearview.h"
|
||||||
|
|
||||||
struct GameData;
|
struct GameData;
|
||||||
|
class FileCache;
|
||||||
|
|
||||||
class FullModelViewer : public QWidget {
|
class FullModelViewer : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit FullModelViewer(GameData* data);
|
explicit FullModelViewer(GameData* data, FileCache& cache);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void gearChanged();
|
void gearChanged();
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gearview.h"
|
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
#include "gearview.h"
|
||||||
|
|
||||||
enum class TreeType {
|
enum class TreeType {
|
||||||
Root,
|
Root,
|
||||||
|
@ -37,6 +39,11 @@ public:
|
||||||
std::optional<GearInfo> getGearFromIndex(const QModelIndex& index);
|
std::optional<GearInfo> getGearFromIndex(const QModelIndex& index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void exdFinished(int index);
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
QFutureWatcher<physis_EXD>* exdFuture;
|
||||||
|
|
||||||
std::vector<GearInfo> gears;
|
std::vector<GearInfo> gears;
|
||||||
QStringList slotNames;
|
QStringList slotNames;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "filecache.h"
|
||||||
#include "mdlpart.h"
|
#include "mdlpart.h"
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
@ -31,7 +32,7 @@ struct GameData;
|
||||||
class GearView : public QWidget {
|
class GearView : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit GearView(GameData* data);
|
explicit GearView(GameData* data, FileCache& cache);
|
||||||
|
|
||||||
/// Returns an inclusive list of races supported by the current gearset.
|
/// Returns an inclusive list of races supported by the current gearset.
|
||||||
std::vector<std::pair<Race, Subrace>> supportedRaces() const;
|
std::vector<std::pair<Race, Subrace>> supportedRaces() const;
|
||||||
|
@ -92,4 +93,5 @@ private:
|
||||||
MDLPart* mdlPart = nullptr;
|
MDLPart* mdlPart = nullptr;
|
||||||
|
|
||||||
GameData* data;
|
GameData* data;
|
||||||
|
FileCache& cache;
|
||||||
};
|
};
|
|
@ -11,6 +11,7 @@
|
||||||
#include "singlegearview.h"
|
#include "singlegearview.h"
|
||||||
|
|
||||||
struct GameData;
|
struct GameData;
|
||||||
|
class FileCache;
|
||||||
|
|
||||||
class MainWindow : public QMainWindow {
|
class MainWindow : public QMainWindow {
|
||||||
public:
|
public:
|
||||||
|
@ -21,4 +22,5 @@ private:
|
||||||
FullModelViewer* fullModelViewer = nullptr;
|
FullModelViewer* fullModelViewer = nullptr;
|
||||||
|
|
||||||
GameData& data;
|
GameData& data;
|
||||||
|
FileCache cache;
|
||||||
};
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "filecache.h"
|
||||||
#include "gearview.h"
|
#include "gearview.h"
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
@ -9,7 +10,7 @@ struct GameData;
|
||||||
class SingleGearView : public QWidget {
|
class SingleGearView : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SingleGearView(GameData* data);
|
explicit SingleGearView(GameData* data, FileCache& cache);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void gearChanged();
|
void gearChanged();
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <QRadioButton>
|
#include <QRadioButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
FullModelViewer::FullModelViewer(GameData* data) : data(data) {
|
FullModelViewer::FullModelViewer(GameData* data, FileCache& cache) : data(data) {
|
||||||
setWindowTitle("Full Model Viewer");
|
setWindowTitle("Full Model Viewer");
|
||||||
setMinimumWidth(640);
|
setMinimumWidth(640);
|
||||||
setMinimumHeight(480);
|
setMinimumHeight(480);
|
||||||
|
@ -44,7 +44,7 @@ FullModelViewer::FullModelViewer(GameData* data) : data(data) {
|
||||||
|
|
||||||
cmp = physis_cmp_parse(physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp"));
|
cmp = physis_cmp_parse(physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp"));
|
||||||
|
|
||||||
gearView = new GearView(data);
|
gearView = new GearView(data, cache);
|
||||||
updateCharacterParameters();
|
updateCharacterParameters();
|
||||||
|
|
||||||
connect(gearView, &GearView::modelReloaded, this, &FullModelViewer::updateCharacterParameters);
|
connect(gearView, &GearView::modelReloaded, this, &FullModelViewer::updateCharacterParameters);
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#include "gearlistmodel.h"
|
#include "gearlistmodel.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QtConcurrent>
|
||||||
#include <magic_enum.hpp>
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
GearListModel::GearListModel(GameData* data) : gameData(data), QAbstractItemModel() {
|
GearListModel::GearListModel(GameData* data) : gameData(data), QAbstractItemModel() {
|
||||||
beginResetModel();
|
|
||||||
// smallclothes body
|
// smallclothes body
|
||||||
{
|
{
|
||||||
GearInfo info = {};
|
GearInfo info = {};
|
||||||
|
@ -25,55 +25,27 @@ GearListModel::GearListModel(GameData* data) : gameData(data), QAbstractItemMode
|
||||||
|
|
||||||
auto exh = physis_gamedata_read_excel_sheet_header(data, "Item");
|
auto exh = physis_gamedata_read_excel_sheet_header(data, "Item");
|
||||||
|
|
||||||
for (int p = 0; p < exh->page_count; p++) {
|
exdFuture = new QFutureWatcher<physis_EXD>(this);
|
||||||
auto exd = physis_gamedata_read_excel_sheet(data, "Item", exh, Language::English, p);
|
connect(exdFuture, &QFutureWatcher<physis_EXD>::resultReadyAt, this, &GearListModel::exdFinished);
|
||||||
|
connect(exdFuture, &QFutureWatcher<physis_EXD>::finished, this, &GearListModel::finished);
|
||||||
|
|
||||||
for (int i = 0; i < exd.row_count; i++) {
|
QVector<int> pages;
|
||||||
const auto row = exd.row_data[i];
|
for(int i = 0; i < exh->page_count; i++) {
|
||||||
auto primaryModel = row.column_data[47].u_int64._0;
|
pages.push_back(i);
|
||||||
auto secondaryModel = row.column_data[48].u_int64._0;
|
|
||||||
|
|
||||||
int16_t parts[4];
|
|
||||||
memcpy(parts, &primaryModel, sizeof(int16_t) * 4);
|
|
||||||
|
|
||||||
GearInfo info = {};
|
|
||||||
info.name = row.column_data[9].string._0;
|
|
||||||
info.slot = physis_slot_from_id(row.column_data[17].u_int8._0);
|
|
||||||
info.modelInfo.primaryID = parts[0];
|
|
||||||
|
|
||||||
gears.push_back(info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::function<physis_EXD(int)> loadEXD = [data, exh](const int page) -> physis_EXD {
|
||||||
|
return physis_gamedata_read_excel_sheet(data, "Item", exh, Language::English, page);
|
||||||
|
};
|
||||||
|
|
||||||
|
exdFuture->setFuture(QtConcurrent::mapped(pages, loadEXD));
|
||||||
|
|
||||||
for (auto slotName : magic_enum::enum_names<Slot>()) {
|
for (auto slotName : magic_enum::enum_names<Slot>()) {
|
||||||
slotNames.push_back(slotName.data());
|
slotNames.push_back(slotName.data());
|
||||||
}
|
}
|
||||||
endResetModel();
|
|
||||||
|
|
||||||
rootItem = new TreeInformation();
|
rootItem = new TreeInformation();
|
||||||
rootItem->type = TreeType::Root;
|
rootItem->type = TreeType::Root;
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (auto slot : magic_enum::enum_values<Slot>()) {
|
|
||||||
TreeInformation* categoryItem = new TreeInformation();
|
|
||||||
categoryItem->type = TreeType::Category;
|
|
||||||
categoryItem->slotType = slot;
|
|
||||||
categoryItem->parent = rootItem;
|
|
||||||
categoryItem->row = i++;
|
|
||||||
rootItem->children.push_back(categoryItem);
|
|
||||||
|
|
||||||
int j = 0;
|
|
||||||
for (auto gear : gears) {
|
|
||||||
if (gear.slot == slot) {
|
|
||||||
TreeInformation* item = new TreeInformation();
|
|
||||||
item->type = TreeType::Item;
|
|
||||||
item->gear = gear;
|
|
||||||
item->parent = categoryItem;
|
|
||||||
item->row = j++;
|
|
||||||
categoryItem->children.push_back(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int GearListModel::rowCount(const QModelIndex& parent) const {
|
int GearListModel::rowCount(const QModelIndex& parent) const {
|
||||||
|
@ -161,4 +133,51 @@ std::optional<GearInfo> GearListModel::getGearFromIndex(const QModelIndex& index
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GearListModel::exdFinished(int index) {
|
||||||
|
auto exd = exdFuture->resultAt(index);
|
||||||
|
|
||||||
|
for (int i = 0; i < exd.row_count; i++) {
|
||||||
|
const auto row = exd.row_data[i];
|
||||||
|
auto primaryModel = row.column_data[47].u_int64._0;
|
||||||
|
auto secondaryModel = row.column_data[48].u_int64._0;
|
||||||
|
|
||||||
|
int16_t parts[4];
|
||||||
|
memcpy(parts, &primaryModel, sizeof(int16_t) * 4);
|
||||||
|
|
||||||
|
GearInfo info = {};
|
||||||
|
info.name = row.column_data[9].string._0;
|
||||||
|
info.slot = physis_slot_from_id(row.column_data[17].u_int8._0);
|
||||||
|
info.modelInfo.primaryID = parts[0];
|
||||||
|
|
||||||
|
gears.push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GearListModel::finished() {
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (auto slot : magic_enum::enum_values<Slot>()) {
|
||||||
|
TreeInformation* categoryItem = new TreeInformation();
|
||||||
|
categoryItem->type = TreeType::Category;
|
||||||
|
categoryItem->slotType = slot;
|
||||||
|
categoryItem->parent = rootItem;
|
||||||
|
categoryItem->row = i++;
|
||||||
|
rootItem->children.push_back(categoryItem);
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (auto gear : gears) {
|
||||||
|
if (gear.slot == slot) {
|
||||||
|
TreeInformation* item = new TreeInformation();
|
||||||
|
item->type = TreeType::Item;
|
||||||
|
item->gear = gear;
|
||||||
|
item->parent = categoryItem;
|
||||||
|
item->row = j++;
|
||||||
|
categoryItem->children.push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_gearlistmodel.cpp"
|
#include "moc_gearlistmodel.cpp"
|
|
@ -1,11 +1,13 @@
|
||||||
#include "gearview.h"
|
#include "gearview.h"
|
||||||
#include "magic_enum.hpp"
|
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
GearView::GearView(GameData* data) : data(data) {
|
#include "magic_enum.hpp"
|
||||||
mdlPart = new MDLPart(data);
|
#include "filecache.h"
|
||||||
|
|
||||||
|
GearView::GearView(GameData* data, FileCache& cache) : data(data), cache(cache) {
|
||||||
|
mdlPart = new MDLPart(data, cache);
|
||||||
|
|
||||||
reloadRaceDeforms();
|
reloadRaceDeforms();
|
||||||
|
|
||||||
|
@ -201,9 +203,7 @@ void GearView::reloadModel() {
|
||||||
maxLod = 0;
|
maxLod = 0;
|
||||||
|
|
||||||
for (const auto& gear : gears) {
|
for (const auto& gear : gears) {
|
||||||
auto mdl_data = physis_gamedata_extract_file(
|
auto mdl_data = cache.lookupFile(physis_build_equipment_path(
|
||||||
data,
|
|
||||||
physis_build_equipment_path(
|
|
||||||
gear.modelInfo.primaryID, currentRace, currentSubrace, currentGender, gear.slot));
|
gear.modelInfo.primaryID, currentRace, currentSubrace, currentGender, gear.slot));
|
||||||
|
|
||||||
// attempt to load the next best race
|
// attempt to load the next best race
|
||||||
|
@ -211,9 +211,7 @@ void GearView::reloadModel() {
|
||||||
Race fallbackRace = currentRace;
|
Race fallbackRace = currentRace;
|
||||||
Subrace fallbackSubrace = currentSubrace;
|
Subrace fallbackSubrace = currentSubrace;
|
||||||
if (mdl_data.size == 0) {
|
if (mdl_data.size == 0) {
|
||||||
mdl_data = physis_gamedata_extract_file(
|
mdl_data = cache.lookupFile(physis_build_equipment_path(
|
||||||
data,
|
|
||||||
physis_build_equipment_path(
|
|
||||||
gear.modelInfo.primaryID, Race::Hyur, Subrace::Midlander, currentGender, gear.slot));
|
gear.modelInfo.primaryID, Race::Hyur, Subrace::Midlander, currentGender, gear.slot));
|
||||||
fallbackRace = Race::Hyur;
|
fallbackRace = Race::Hyur;
|
||||||
fallbackSubrace = Subrace::Midlander;
|
fallbackSubrace = Subrace::Midlander;
|
||||||
|
@ -246,12 +244,12 @@ void GearView::reloadModel() {
|
||||||
fmt::arg("bodyCode", bodyCode));
|
fmt::arg("bodyCode", bodyCode));
|
||||||
|
|
||||||
if (physis_gamedata_exists(data, mtrl_path.c_str())) {
|
if (physis_gamedata_exists(data, mtrl_path.c_str())) {
|
||||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, mtrl_path.c_str()));
|
auto mat = physis_material_parse(cache.lookupFile(mtrl_path.c_str()));
|
||||||
materials.push_back(mat);
|
materials.push_back(mat);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
||||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str()));
|
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
||||||
materials.push_back(mat);
|
materials.push_back(mat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,9 +261,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (face) {
|
if (face) {
|
||||||
auto mdl_data = physis_gamedata_extract_file(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
||||||
data,
|
|
||||||
physis_build_character_path(
|
|
||||||
CharacterCategory::Face, *face, currentRace, currentSubrace, currentGender));
|
CharacterCategory::Face, *face, currentRace, currentSubrace, currentGender));
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
|
@ -285,7 +281,7 @@ void GearView::reloadModel() {
|
||||||
fmt::print("oops: {}", skinmtrl_path);
|
fmt::print("oops: {}", skinmtrl_path);
|
||||||
|
|
||||||
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
||||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str()));
|
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
||||||
materials.push_back(mat);
|
materials.push_back(mat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,9 +291,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hair) {
|
if (hair) {
|
||||||
auto mdl_data = physis_gamedata_extract_file(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
||||||
data,
|
|
||||||
physis_build_character_path(
|
|
||||||
CharacterCategory::Hair, *hair, currentRace, currentSubrace, currentGender));
|
CharacterCategory::Hair, *hair, currentRace, currentSubrace, currentGender));
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
|
@ -317,7 +311,7 @@ void GearView::reloadModel() {
|
||||||
fmt::print("oops: {}", skinmtrl_path);
|
fmt::print("oops: {}", skinmtrl_path);
|
||||||
|
|
||||||
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
||||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str()));
|
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
||||||
materials.push_back(mat);
|
materials.push_back(mat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,9 +321,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ear) {
|
if (ear) {
|
||||||
auto mdl_data = physis_gamedata_extract_file(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
||||||
data,
|
|
||||||
physis_build_character_path(
|
|
||||||
CharacterCategory::Hair, *ear, currentRace, currentSubrace, currentGender));
|
CharacterCategory::Hair, *ear, currentRace, currentSubrace, currentGender));
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
|
@ -349,7 +341,7 @@ void GearView::reloadModel() {
|
||||||
fmt::print("oops: {}", skinmtrl_path);
|
fmt::print("oops: {}", skinmtrl_path);
|
||||||
|
|
||||||
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
||||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str()));
|
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
||||||
materials.push_back(mat);
|
materials.push_back(mat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,9 +351,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tail) {
|
if (tail) {
|
||||||
auto mdl_data = physis_gamedata_extract_file(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
||||||
data,
|
|
||||||
physis_build_character_path(
|
|
||||||
CharacterCategory::Tail, *tail, currentRace, currentSubrace, currentGender));
|
CharacterCategory::Tail, *tail, currentRace, currentSubrace, currentGender));
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
|
@ -377,7 +367,7 @@ void GearView::reloadModel() {
|
||||||
fmt::arg("bodyCode", *tail));
|
fmt::arg("bodyCode", *tail));
|
||||||
|
|
||||||
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
if (physis_gamedata_exists(data, skinmtrl_path.c_str())) {
|
||||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str()));
|
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
||||||
mdlPart->addModel(mdl, {mat}, currentLod);
|
mdlPart->addModel(mdl, {mat}, currentLod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,9 @@
|
||||||
#include "aboutwindow.h"
|
#include "aboutwindow.h"
|
||||||
#include "cmpeditor.h"
|
#include "cmpeditor.h"
|
||||||
#include "gearlistwidget.h"
|
#include "gearlistwidget.h"
|
||||||
|
#include "filecache.h"
|
||||||
|
|
||||||
MainWindow::MainWindow(GameData* in_data) : data(*in_data) {
|
MainWindow::MainWindow(GameData* in_data) : data(*in_data), cache(FileCache{*in_data}) {
|
||||||
setWindowTitle("mdlviewer");
|
setWindowTitle("mdlviewer");
|
||||||
setMinimumSize(QSize(800, 600));
|
setMinimumSize(QSize(800, 600));
|
||||||
|
|
||||||
|
@ -84,12 +85,12 @@ MainWindow::MainWindow(GameData* in_data) : data(*in_data) {
|
||||||
});
|
});
|
||||||
layout->addWidget(gearListWidget);
|
layout->addWidget(gearListWidget);
|
||||||
|
|
||||||
gearView = new SingleGearView(&data);
|
gearView = new SingleGearView(&data, cache);
|
||||||
connect(gearView, &SingleGearView::addToFullModelViewer, this, [=](GearInfo& info) {
|
connect(gearView, &SingleGearView::addToFullModelViewer, this, [=](GearInfo& info) {
|
||||||
fullModelViewer->addGear(info);
|
fullModelViewer->addGear(info);
|
||||||
});
|
});
|
||||||
layout->addWidget(gearView);
|
layout->addWidget(gearView);
|
||||||
|
|
||||||
fullModelViewer = new FullModelViewer(&data);
|
fullModelViewer = new FullModelViewer(&data, cache);
|
||||||
fullModelViewer->show();
|
fullModelViewer->show();
|
||||||
}
|
}
|
|
@ -4,10 +4,11 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include "filecache.h"
|
||||||
#include "magic_enum.hpp"
|
#include "magic_enum.hpp"
|
||||||
|
|
||||||
SingleGearView::SingleGearView(GameData* data) : data(data) {
|
SingleGearView::SingleGearView(GameData* data, FileCache& cache) : data(data) {
|
||||||
gearView = new GearView(data);
|
gearView = new GearView(data, cache);
|
||||||
|
|
||||||
auto layout = new QVBoxLayout();
|
auto layout = new QVBoxLayout();
|
||||||
layout->addWidget(gearView);
|
layout->addWidget(gearView);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
add_library(mdlpart STATIC mdlpart.cpp)
|
add_library(mdlpart STATIC mdlpart.cpp)
|
||||||
target_link_libraries(mdlpart PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets renderer)
|
target_link_libraries(mdlpart PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets renderer NovusCommon)
|
||||||
target_include_directories(mdlpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(mdlpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
@ -17,16 +17,18 @@
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include <glm/gtc/type_ptr.inl>
|
#include <glm/gtc/type_ptr.inl>
|
||||||
|
|
||||||
|
#include "filecache.h"
|
||||||
|
|
||||||
#ifndef USE_STANDALONE_WINDOW
|
#ifndef USE_STANDALONE_WINDOW
|
||||||
class VulkanWindow : public QWindow {
|
class VulkanWindow : public QWindow {
|
||||||
public:
|
public:
|
||||||
VulkanWindow(MDLPart *part, Renderer *renderer, QVulkanInstance *instance)
|
VulkanWindow(MDLPart* part, Renderer* renderer, QVulkanInstance* instance)
|
||||||
: part(part), m_renderer(renderer), m_instance(instance) {
|
: part(part), m_renderer(renderer), m_instance(instance) {
|
||||||
setSurfaceType(VulkanSurface);
|
setSurfaceType(VulkanSurface);
|
||||||
setVulkanInstance(instance);
|
setVulkanInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
void exposeEvent(QExposeEvent *) {
|
void exposeEvent(QExposeEvent*) {
|
||||||
if (isExposed()) {
|
if (isExposed()) {
|
||||||
if (!m_initialized) {
|
if (!m_initialized) {
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
|
@ -40,8 +42,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool event(QEvent *e) {
|
bool event(QEvent* e) {
|
||||||
switch(e->type()) {
|
switch (e->type()) {
|
||||||
case QEvent::UpdateRequest:
|
case QEvent::UpdateRequest:
|
||||||
render();
|
render();
|
||||||
break;
|
break;
|
||||||
|
@ -104,13 +106,11 @@ public:
|
||||||
part->lastY = mouseEvent->y();
|
part->lastY = mouseEvent->y();
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case QEvent::Wheel:
|
case QEvent::Wheel: {
|
||||||
{
|
|
||||||
auto scrollEvent = dynamic_cast<QWheelEvent*>(e);
|
auto scrollEvent = dynamic_cast<QWheelEvent*>(e);
|
||||||
|
|
||||||
part->cameraDistance -= scrollEvent->angleDelta().y() / 120.0f; // FIXME: why 120?
|
part->cameraDistance -= scrollEvent->angleDelta().y() / 120.0f; // FIXME: why 120?
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return QWindow::event(e);
|
return QWindow::event(e);
|
||||||
|
@ -138,12 +138,12 @@ private:
|
||||||
MDLPart* part;
|
MDLPart* part;
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
#include "standalonewindow.h"
|
#include "equipment.h"
|
||||||
#include "equipment.h"
|
#include "standalonewindow.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MDLPart::MDLPart(GameData *data) : data(data) {
|
MDLPart::MDLPart(GameData* data, FileCache& cache) : data(data), cache(cache) {
|
||||||
auto viewportLayout = new QVBoxLayout();
|
auto viewportLayout = new QVBoxLayout();
|
||||||
setLayout(viewportLayout);
|
setLayout(viewportLayout);
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ MDLPart::MDLPart(GameData *data) : data(data) {
|
||||||
connect(this, &MDLPart::skeletonChanged, this, &MDLPart::reloadBoneData);
|
connect(this, &MDLPart::skeletonChanged, this, &MDLPart::reloadBoneData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDLPart::exportModel(const QString &fileName) {
|
void MDLPart::exportModel(const QString& fileName) {
|
||||||
Assimp::Exporter exporter;
|
Assimp::Exporter exporter;
|
||||||
|
|
||||||
aiScene scene;
|
aiScene scene;
|
||||||
|
@ -198,14 +198,14 @@ void MDLPart::exportModel(const QString &fileName) {
|
||||||
|
|
||||||
std::vector<aiNode*> skeletonNodes;
|
std::vector<aiNode*> skeletonNodes;
|
||||||
|
|
||||||
for(int i = 0; i < models[0].model.num_affected_bones; i++) {
|
for (int i = 0; i < models[0].model.num_affected_bones; i++) {
|
||||||
auto& node = skeletonNodes.emplace_back();
|
auto& node = skeletonNodes.emplace_back();
|
||||||
node = new aiNode();
|
node = new aiNode();
|
||||||
node->mName = models[0].model.affected_bone_names[i];
|
node->mName = models[0].model.affected_bone_names[i];
|
||||||
|
|
||||||
int real_bone_id = 0;
|
int real_bone_id = 0;
|
||||||
for(int k = 0; k < skeleton->num_bones; k++) {
|
for (int k = 0; k < skeleton->num_bones; k++) {
|
||||||
if(strcmp(skeleton->bones[k].name, models[0].model.affected_bone_names[i]) == 0) {
|
if (strcmp(skeleton->bones[k].name, models[0].model.affected_bone_names[i]) == 0) {
|
||||||
real_bone_id = k;
|
real_bone_id = k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,18 +217,18 @@ void MDLPart::exportModel(const QString &fileName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup parenting
|
// setup parenting
|
||||||
for(int i = 0; i < models[0].model.num_affected_bones; i++) {
|
for (int i = 0; i < models[0].model.num_affected_bones; i++) {
|
||||||
int real_bone_id = 0;
|
int real_bone_id = 0;
|
||||||
for(int k = 0; k < skeleton->num_bones; k++) {
|
for (int k = 0; k < skeleton->num_bones; k++) {
|
||||||
if(strcmp(skeleton->bones[k].name, models[0].model.affected_bone_names[i]) == 0) {
|
if (strcmp(skeleton->bones[k].name, models[0].model.affected_bone_names[i]) == 0) {
|
||||||
real_bone_id = k;
|
real_bone_id = k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& real_bone = skeleton->bones[real_bone_id];
|
auto& real_bone = skeleton->bones[real_bone_id];
|
||||||
if(real_bone.parent_bone != nullptr) {
|
if (real_bone.parent_bone != nullptr) {
|
||||||
for(int k = 0; k < models[0].model.num_affected_bones; k++) {
|
for (int k = 0; k < models[0].model.num_affected_bones; k++) {
|
||||||
if(strcmp(models[0].model.affected_bone_names[k], real_bone.parent_bone->name) == 0) {
|
if (strcmp(models[0].model.affected_bone_names[k], real_bone.parent_bone->name) == 0) {
|
||||||
skeletonNodes[i]->mParent = skeletonNodes[k];
|
skeletonNodes[i]->mParent = skeletonNodes[k];
|
||||||
skeletonNodes[k]->mChildren[skeletonNodes[k]->mNumChildren++] = skeletonNodes[i];
|
skeletonNodes[k]->mChildren[skeletonNodes[k]->mNumChildren++] = skeletonNodes[i];
|
||||||
}
|
}
|
||||||
|
@ -240,39 +240,39 @@ void MDLPart::exportModel(const QString &fileName) {
|
||||||
skeleton_node->mChildren[0]->mName = "root";
|
skeleton_node->mChildren[0]->mName = "root";
|
||||||
skeleton_node->mChildren[0]->mChildren = new aiNode*[models[0].model.num_affected_bones];
|
skeleton_node->mChildren[0]->mChildren = new aiNode*[models[0].model.num_affected_bones];
|
||||||
|
|
||||||
for(int i = 0; i < skeletonNodes.size(); i++) {
|
for (int i = 0; i < skeletonNodes.size(); i++) {
|
||||||
if(skeletonNodes[i]->mParent == nullptr) {
|
if (skeletonNodes[i]->mParent == nullptr) {
|
||||||
skeleton_node->mChildren[0]->mChildren[skeleton_node->mChildren[0]->mNumChildren++] = skeletonNodes[i];
|
skeleton_node->mChildren[0]->mChildren[skeleton_node->mChildren[0]->mNumChildren++] = skeletonNodes[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < models[0].model.lods[0].num_parts; i++) {
|
for (int i = 0; i < models[0].model.lods[0].num_parts; i++) {
|
||||||
scene.mMeshes[i] = new aiMesh();
|
scene.mMeshes[i] = new aiMesh();
|
||||||
scene.mMeshes[i]->mMaterialIndex = 0;
|
scene.mMeshes[i]->mMaterialIndex = 0;
|
||||||
|
|
||||||
auto& node = scene.mRootNode->mChildren[i];
|
auto& node = scene.mRootNode->mChildren[i];
|
||||||
node = new aiNode();
|
node = new aiNode();
|
||||||
node->mNumMeshes = 1;
|
node->mNumMeshes = 1;
|
||||||
node->mMeshes = new unsigned int [scene.mRootNode->mNumMeshes];
|
node->mMeshes = new unsigned int[scene.mRootNode->mNumMeshes];
|
||||||
node->mMeshes[0] = i;
|
node->mMeshes[0] = i;
|
||||||
|
|
||||||
auto mesh = scene.mMeshes[i];
|
auto mesh = scene.mMeshes[i];
|
||||||
mesh->mNumVertices = models[0].model.lods[0].parts[i].num_vertices;
|
mesh->mNumVertices = models[0].model.lods[0].parts[i].num_vertices;
|
||||||
mesh->mVertices = new aiVector3D [mesh->mNumVertices];
|
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
|
||||||
mesh->mNormals = new aiVector3D [mesh->mNumVertices];
|
mesh->mNormals = new aiVector3D[mesh->mNumVertices];
|
||||||
mesh->mTextureCoords[0] = new aiVector3D [mesh->mNumVertices];
|
mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
|
||||||
mesh->mNumUVComponents[0] = 2;
|
mesh->mNumUVComponents[0] = 2;
|
||||||
|
|
||||||
for(int j = 0; j < mesh->mNumVertices; j++) {
|
for (int j = 0; j < mesh->mNumVertices; j++) {
|
||||||
auto vertex = models[0].model.lods[0].parts[i].vertices[j];
|
auto vertex = models[0].model.lods[0].parts[i].vertices[j];
|
||||||
mesh->mVertices[j] = aiVector3D(vertex.position[0], vertex.position[1], vertex.position[2]);
|
mesh->mVertices[j] = aiVector3D(vertex.position[0], vertex.position[1], vertex.position[2]);
|
||||||
mesh->mNormals[j] = aiVector3D (vertex.normal[0], vertex.normal[1], vertex.normal[2]);
|
mesh->mNormals[j] = aiVector3D(vertex.normal[0], vertex.normal[1], vertex.normal[2]);
|
||||||
mesh->mTextureCoords[0][j] = aiVector3D(vertex.uv[0], vertex.uv[1], 0.0f);
|
mesh->mTextureCoords[0][j] = aiVector3D(vertex.uv[0], vertex.uv[1], 0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh->mNumBones = models[0].model.num_affected_bones;
|
mesh->mNumBones = models[0].model.num_affected_bones;
|
||||||
mesh->mBones = new aiBone*[mesh->mNumBones];
|
mesh->mBones = new aiBone*[mesh->mNumBones];
|
||||||
for(int j = 0; j < mesh->mNumBones; j++) {
|
for (int j = 0; j < mesh->mNumBones; j++) {
|
||||||
int real_bone_id = j;
|
int real_bone_id = j;
|
||||||
// TODO: is this still relevant?5
|
// TODO: is this still relevant?5
|
||||||
/*for(int k = 0; k < skeleton.bones.size(); k++) {
|
/*for(int k = 0; k < skeleton.bones.size(); k++) {
|
||||||
|
@ -287,10 +287,10 @@ void MDLPart::exportModel(const QString &fileName) {
|
||||||
mesh->mBones[j]->mWeights = new aiVertexWeight[mesh->mBones[j]->mNumWeights];
|
mesh->mBones[j]->mWeights = new aiVertexWeight[mesh->mBones[j]->mNumWeights];
|
||||||
// mesh->mBones[j]->mNode = skeleton_node->mChildren[j];
|
// mesh->mBones[j]->mNode = skeleton_node->mChildren[j];
|
||||||
|
|
||||||
for(int k = 0; k < mesh->mNumVertices; k++) {
|
for (int k = 0; k < mesh->mNumVertices; k++) {
|
||||||
for(int z = 0; z < 4; z++) {
|
for (int z = 0; z < 4; z++) {
|
||||||
if (models[0].model.lods[0].parts[i].vertices[k].bone_id[z] == real_bone_id) {
|
if (models[0].model.lods[0].parts[i].vertices[k].bone_id[z] == real_bone_id) {
|
||||||
auto &weight = mesh->mBones[j]->mWeights[k * 4 + z];
|
auto& weight = mesh->mBones[j]->mWeights[k * 4 + z];
|
||||||
weight.mVertexId = k;
|
weight.mVertexId = k;
|
||||||
weight.mWeight = models[0].model.lods[0].parts[i].vertices[k].bone_weight[z];
|
weight.mWeight = models[0].model.lods[0].parts[i].vertices[k].bone_weight[z];
|
||||||
}
|
}
|
||||||
|
@ -302,7 +302,7 @@ void MDLPart::exportModel(const QString &fileName) {
|
||||||
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
||||||
|
|
||||||
int lastFace = 0;
|
int lastFace = 0;
|
||||||
for(int j = 0; j < models[0].model.lods[0].parts[i].num_indices; j += 3) {
|
for (int j = 0; j < models[0].model.lods[0].parts[i].num_indices; j += 3) {
|
||||||
aiFace& face = mesh->mFaces[lastFace++];
|
aiFace& face = mesh->mFaces[lastFace++];
|
||||||
|
|
||||||
face.mNumIndices = 3;
|
face.mNumIndices = 3;
|
||||||
|
@ -332,7 +332,8 @@ void MDLPart::addModel(physis_MDL mdl, std::vector<physis_Material> materials, i
|
||||||
|
|
||||||
auto model = renderer->addModel(mdl, lod);
|
auto model = renderer->addModel(mdl, lod);
|
||||||
|
|
||||||
std::transform(materials.begin(), materials.end(), std::back_inserter(model.materials), [this](const physis_Material& mat) {
|
std::transform(
|
||||||
|
materials.begin(), materials.end(), std::back_inserter(model.materials), [this](const physis_Material& mat) {
|
||||||
return createMaterial(mat);
|
return createMaterial(mat);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -350,8 +351,7 @@ void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDLPart::loadRaceDeformMatrices(physis_Buffer buffer) {
|
void MDLPart::loadRaceDeformMatrices(physis_Buffer buffer) {
|
||||||
QJsonDocument document = QJsonDocument::fromJson(
|
QJsonDocument document = QJsonDocument::fromJson(QByteArray((const char*)buffer.data, buffer.size));
|
||||||
QByteArray((const char *)buffer.data, buffer.size));
|
|
||||||
for (auto boneObj : document.object()["Data"].toArray()) {
|
for (auto boneObj : document.object()["Data"].toArray()) {
|
||||||
QJsonArray matrix = boneObj.toObject()["Matrix"].toArray();
|
QJsonArray matrix = boneObj.toObject()["Matrix"].toArray();
|
||||||
QString boneName = boneObj.toObject()["Name"].toString();
|
QString boneName = boneObj.toObject()["Name"].toString();
|
||||||
|
@ -363,9 +363,8 @@ void MDLPart::loadRaceDeformMatrices(physis_Buffer buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < skeleton->num_bones; i++) {
|
for (int i = 0; i < skeleton->num_bones; i++) {
|
||||||
if (std::string_view{skeleton->bones[i].name} ==
|
if (std::string_view{skeleton->bones[i].name} == boneName.toStdString()) {
|
||||||
boneName.toStdString()) {
|
auto& data = boneData[i];
|
||||||
auto &data = boneData[i];
|
|
||||||
|
|
||||||
data.deformRaceMatrix = actualMatrix;
|
data.deformRaceMatrix = actualMatrix;
|
||||||
}
|
}
|
||||||
|
@ -394,7 +393,7 @@ void MDLPart::reloadRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDLPart::reloadBoneData() {
|
void MDLPart::reloadBoneData() {
|
||||||
if(skeleton) {
|
if (skeleton) {
|
||||||
if (!firstTimeSkeletonDataCalculated) {
|
if (!firstTimeSkeletonDataCalculated) {
|
||||||
if (boneData.empty()) {
|
if (boneData.empty()) {
|
||||||
boneData.resize(skeleton->num_bones);
|
boneData.resize(skeleton->num_bones);
|
||||||
|
@ -402,7 +401,7 @@ void MDLPart::reloadBoneData() {
|
||||||
|
|
||||||
calculateBoneInversePose(*skeleton, *skeleton->root_bone, nullptr);
|
calculateBoneInversePose(*skeleton, *skeleton->root_bone, nullptr);
|
||||||
|
|
||||||
for (auto &bone : boneData) {
|
for (auto& bone : boneData) {
|
||||||
bone.inversePose = glm::inverse(bone.inversePose);
|
bone.inversePose = glm::inverse(bone.inversePose);
|
||||||
}
|
}
|
||||||
firstTimeSkeletonDataCalculated = true;
|
firstTimeSkeletonDataCalculated = true;
|
||||||
|
@ -411,12 +410,13 @@ void MDLPart::reloadBoneData() {
|
||||||
// update data
|
// update data
|
||||||
calculateBone(*skeleton, *skeleton->root_bone, nullptr);
|
calculateBone(*skeleton, *skeleton->root_bone, nullptr);
|
||||||
|
|
||||||
for(auto& model : models) {
|
for (auto& model : models) {
|
||||||
// we want to map the actual affected bones to bone ids
|
// we want to map the actual affected bones to bone ids
|
||||||
std::map<int, int> boneMapping;
|
std::map<int, int> boneMapping;
|
||||||
for (int i = 0; i < model.model.num_affected_bones; i++) {
|
for (int i = 0; i < model.model.num_affected_bones; i++) {
|
||||||
for (int k = 0; k < skeleton->num_bones; k++) {
|
for (int k = 0; k < skeleton->num_bones; k++) {
|
||||||
if (std::string_view{skeleton->bones[k].name} == std::string_view{model.model.affected_bone_names[i]}) {
|
if (std::string_view{skeleton->bones[k].name} ==
|
||||||
|
std::string_view{model.model.affected_bone_names[i]}) {
|
||||||
boneMapping[i] = k;
|
boneMapping[i] = k;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,7 +429,7 @@ void MDLPart::reloadBoneData() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderMaterial MDLPart::createMaterial(const physis_Material &material) {
|
RenderMaterial MDLPart::createMaterial(const physis_Material& material) {
|
||||||
RenderMaterial newMaterial;
|
RenderMaterial newMaterial;
|
||||||
|
|
||||||
for (int i = 0; i < material.num_textures; i++) {
|
for (int i = 0; i < material.num_textures; i++) {
|
||||||
|
@ -441,34 +441,31 @@ RenderMaterial MDLPart::createMaterial(const physis_Material &material) {
|
||||||
|
|
||||||
char type = t[t.length() - 5];
|
char type = t[t.length() - 5];
|
||||||
|
|
||||||
switch(type) {
|
switch (type) {
|
||||||
case 'm': {
|
case 'm': {
|
||||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
auto texture = physis_texture_parse(cache.lookupFile(material.textures[i]));
|
||||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||||
|
|
||||||
newMaterial.multiTexture = new RenderTexture(tex);
|
newMaterial.multiTexture = new RenderTexture(tex);
|
||||||
}
|
}
|
||||||
case 'd': {
|
case 'd': {
|
||||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
auto texture = physis_texture_parse(cache.lookupFile(material.textures[i]));
|
||||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||||
|
|
||||||
newMaterial.diffuseTexture = new RenderTexture(tex);
|
newMaterial.diffuseTexture = new RenderTexture(tex);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
case 'n': {
|
case 'n': {
|
||||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
auto texture = physis_texture_parse(cache.lookupFile(material.textures[i]));
|
||||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||||
|
|
||||||
newMaterial.normalTexture = new RenderTexture(tex);
|
newMaterial.normalTexture = new RenderTexture(tex);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
case 's': {
|
case 's': {
|
||||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
auto texture = physis_texture_parse(cache.lookupFile(material.textures[i]));
|
||||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||||
|
|
||||||
newMaterial.specularTexture = new RenderTexture(tex);
|
newMaterial.specularTexture = new RenderTexture(tex);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
qDebug() << "unhandled type" << type;
|
qDebug() << "unhandled type" << type;
|
||||||
break;
|
break;
|
||||||
|
@ -488,8 +485,9 @@ void MDLPart::calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& b
|
||||||
|
|
||||||
boneData[bone.index].inversePose = parentMatrix * local;
|
boneData[bone.index].inversePose = parentMatrix * local;
|
||||||
|
|
||||||
for(int i = 0; i < skeleton.num_bones; i++) {
|
for (int i = 0; i < skeleton.num_bones; i++) {
|
||||||
if(skeleton.bones[i].parent_bone != nullptr && std::string_view{skeleton.bones[i].parent_bone->name} == std::string_view{bone.name}) {
|
if (skeleton.bones[i].parent_bone != nullptr &&
|
||||||
|
std::string_view{skeleton.bones[i].parent_bone->name} == std::string_view{bone.name}) {
|
||||||
calculateBoneInversePose(skeleton, skeleton.bones[i], &bone);
|
calculateBoneInversePose(skeleton, skeleton.bones[i], &bone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -497,27 +495,20 @@ void MDLPart::calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& b
|
||||||
|
|
||||||
void MDLPart::calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone) {
|
void MDLPart::calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone) {
|
||||||
const glm::mat4 parent_matrix =
|
const glm::mat4 parent_matrix =
|
||||||
parent_bone == nullptr ? glm::mat4(1.0f)
|
parent_bone == nullptr ? glm::mat4(1.0f) : boneData[parent_bone->index].localTransform;
|
||||||
: boneData[parent_bone->index].localTransform;
|
|
||||||
|
|
||||||
glm::mat4 local = glm::mat4(1.0f);
|
glm::mat4 local = glm::mat4(1.0f);
|
||||||
local = glm::translate(
|
local = glm::translate(local, glm::vec3(bone.position[0], bone.position[1], bone.position[2]));
|
||||||
local, glm::vec3(bone.position[0], bone.position[1], bone.position[2]));
|
local *= glm::mat4_cast(glm::quat(bone.rotation[3], bone.rotation[0], bone.rotation[1], bone.rotation[2]));
|
||||||
local *= glm::mat4_cast(glm::quat(bone.rotation[3], bone.rotation[0],
|
local = glm::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||||
bone.rotation[1], bone.rotation[2]));
|
|
||||||
local = glm::scale(local,
|
|
||||||
glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2]));
|
|
||||||
|
|
||||||
boneData[bone.index].localTransform = parent_matrix * local;
|
boneData[bone.index].localTransform = parent_matrix * local;
|
||||||
boneData[bone.index].finalTransform =
|
boneData[bone.index].finalTransform =
|
||||||
boneData[bone.index].localTransform *
|
boneData[bone.index].localTransform * boneData[bone.index].deformRaceMatrix * boneData[bone.index].inversePose;
|
||||||
boneData[bone.index].deformRaceMatrix *
|
|
||||||
boneData[bone.index].inversePose;
|
|
||||||
|
|
||||||
for (int i = 0; i < skeleton.num_bones; i++) {
|
for (int i = 0; i < skeleton.num_bones; i++) {
|
||||||
if (skeleton.bones[i].parent_bone != nullptr &&
|
if (skeleton.bones[i].parent_bone != nullptr &&
|
||||||
std::string_view{skeleton.bones[i].parent_bone->name} ==
|
std::string_view{skeleton.bones[i].parent_bone->name} == std::string_view{bone.name}) {
|
||||||
std::string_view{bone.name}) {
|
|
||||||
calculateBone(skeleton, skeleton.bones[i], &bone);
|
calculateBone(skeleton, skeleton.bones[i], &bone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,19 +10,24 @@ struct GameData;
|
||||||
|
|
||||||
class VulkanWindow;
|
class VulkanWindow;
|
||||||
class StandaloneWindow;
|
class StandaloneWindow;
|
||||||
|
class FileCache;
|
||||||
|
|
||||||
class MDLPart : public QWidget {
|
class MDLPart : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MDLPart(GameData *data);
|
explicit MDLPart(GameData* data, FileCache& cache);
|
||||||
|
|
||||||
void exportModel(const QString &fileName);
|
void exportModel(const QString& fileName);
|
||||||
|
|
||||||
int lastX = -1;
|
int lastX = -1;
|
||||||
int lastY = -1;
|
int lastY = -1;
|
||||||
|
|
||||||
enum class CameraMode { None, Orbit, Move };
|
enum class CameraMode {
|
||||||
|
None,
|
||||||
|
Orbit,
|
||||||
|
Move
|
||||||
|
};
|
||||||
|
|
||||||
CameraMode cameraMode = CameraMode::None;
|
CameraMode cameraMode = CameraMode::None;
|
||||||
float pitch = 0.0f;
|
float pitch = 0.0f;
|
||||||
|
@ -48,8 +53,7 @@ public Q_SLOTS:
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
/// Adds a new MDL with a list of materials used.
|
/// Adds a new MDL with a list of materials used.
|
||||||
void addModel(physis_MDL mdl, std::vector<physis_Material> materials,
|
void addModel(physis_MDL mdl, std::vector<physis_Material> materials, int lod);
|
||||||
int lod);
|
|
||||||
|
|
||||||
/// Sets the skeleton any skinned MDLs should bind to.
|
/// Sets the skeleton any skinned MDLs should bind to.
|
||||||
void setSkeleton(physis_Skeleton skeleton);
|
void setSkeleton(physis_Skeleton skeleton);
|
||||||
|
@ -64,12 +68,13 @@ public Q_SLOTS:
|
||||||
void reloadRenderer();
|
void reloadRenderer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderMaterial createMaterial(const physis_Material &mat);
|
RenderMaterial createMaterial(const physis_Material& mat);
|
||||||
|
|
||||||
void calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& bone, physis_Bone* parent_bone);
|
void calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& bone, physis_Bone* parent_bone);
|
||||||
void calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone);
|
void calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone);
|
||||||
|
|
||||||
GameData* data = nullptr;
|
GameData* data = nullptr;
|
||||||
|
FileCache& cache;
|
||||||
|
|
||||||
std::vector<RenderModel> models;
|
std::vector<RenderModel> models;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue