1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-20 19:57:44 +00:00

Use physis in mdlviewer

This commit is contained in:
Joshua Goins 2022-08-10 14:52:28 -04:00
parent 7a4c5efbf3
commit 2b79c33d1f
7 changed files with 117 additions and 112 deletions

View file

@ -9,13 +9,13 @@ target_include_directories(mdlviewer
PUBLIC PUBLIC
include) include)
target_link_libraries(mdlviewer PUBLIC target_link_libraries(mdlviewer PUBLIC
libxiv
${LIBRARIES} ${LIBRARIES}
Qt5::Core Qt5::Core
Qt5::Widgets Qt5::Widgets
renderer renderer
assimp::assimp assimp::assimp
magic_enum) magic_enum
physis z)
install(TARGETS mdlviewer install(TARGETS mdlviewer
DESTINATION "${INSTALL_BIN_PATH}") DESTINATION "${INSTALL_BIN_PATH}")

View file

@ -3,11 +3,9 @@
#include <QMainWindow> #include <QMainWindow>
#include <unordered_map> #include <unordered_map>
#include <QComboBox> #include <QComboBox>
#include <physis.hpp>
#include "renderer.hpp" #include "renderer.hpp"
#include "types/slot.h"
#include "types/race.h"
#include "havokxmlparser.h"
struct ModelInfo { struct ModelInfo {
int primaryID; int primaryID;
@ -25,31 +23,39 @@ class StandaloneWindow;
class MainWindow : public QMainWindow { class MainWindow : public QMainWindow {
public: public:
MainWindow(GameData& data); MainWindow(GameData* data);
void exportModel(Model& model, Skeleton& skeleton, QString fileName); void exportModel(physis_MDL& model, physis_Skeleton& skeleton, QString fileName);
private: private:
void loadInitialGearInfo(GearInfo& info); void loadInitialGearInfo(GearInfo& info);
void reloadGearModel(); void reloadGearModel();
void reloadGearAppearance(); void reloadGearAppearance();
void calculate_bone_inverse_pose(physis_Skeleton& skeleton, physis_Bone& bone, physis_Bone* parent_bone);
void calculate_bone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone);
std::vector<GearInfo> gears; std::vector<GearInfo> gears;
struct LoadedGear { struct LoadedGear {
GearInfo* gearInfo; GearInfo* gearInfo;
Model model; physis_MDL model;
RenderModel renderModel; RenderModel renderModel;
}; };
struct BoneExtra {
glm::mat4 localTransform, finalTransform, inversePose;
};
LoadedGear loadedGear; LoadedGear loadedGear;
QComboBox* raceCombo, *lodCombo; QComboBox* raceCombo, *lodCombo;
Race currentRace = Race::HyurMidlanderMale; Race currentRace = Race::Hyur;
Subrace currentSubrace = Subrace::Midlander;
Gender currentGender = Gender::Male;
int currentLod = 0; int currentLod = 0;
glm::vec3 currentScale = glm::vec3(1); glm::vec3 currentScale = glm::vec3(1);
Bone* currentEditedBone = nullptr; physis_Bone* currentEditedBone = nullptr;
GameData& data; GameData& data;
@ -57,5 +63,6 @@ private:
VulkanWindow* vkWindow; VulkanWindow* vkWindow;
StandaloneWindow* standaloneWindow; StandaloneWindow* standaloneWindow;
Skeleton skeleton; physis_Skeleton skeleton;
std::vector<BoneExtra> extraBone;
}; };

View file

@ -1,14 +1,12 @@
#include <QApplication> #include <QApplication>
#include <physis.hpp>
#include "mainwindow.h" #include "mainwindow.h"
#include "gamedata.h"
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
QApplication app(argc, argv); QApplication app(argc, argv);
GameData data(argv[1]); MainWindow w(physis_gamedata_initialize(argv[1]));
MainWindow w(data);
w.show(); w.show();
return app.exec(); return app.exec();

View file

@ -22,13 +22,9 @@
#include <QAction> #include <QAction>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <QTreeWidget> #include <QTreeWidget>
#include <physis.hpp>
#include <glm/glm.hpp>
#include "gamedata.h"
#include "exhparser.h"
#include "exdparser.h"
#include "mdlparser.h"
#include "equipment.h"
#include "glm/glm.hpp"
#include "vec3edit.h" #include "vec3edit.h"
#ifndef USE_STANDALONE_WINDOW #ifndef USE_STANDALONE_WINDOW
@ -86,25 +82,26 @@ private:
#endif #endif
void calculate_bone_inverse_pose(Skeleton& skeleton, Bone& bone, Bone* parent_bone) { void MainWindow::calculate_bone_inverse_pose(physis_Skeleton& skeleton, physis_Bone& bone, physis_Bone* parent_bone) {
const glm::mat4 parentMatrix = parent_bone == nullptr ? glm::mat4(1.0f) : parent_bone->inversePose; const glm::mat4 parentMatrix = parent_bone == nullptr ? glm::mat4(1.0f) : extraBone[parent_bone->index].inversePose;
glm::mat4 local(1.0f); glm::mat4 local(1.0f);
local = glm::translate(local, glm::vec3(bone.position[0], bone.position[1], bone.position[2])); local = glm::translate(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], bone.rotation[1], bone.rotation[2]));
local = glm::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2])); local = glm::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2]));
bone.inversePose = parentMatrix * local; extraBone[bone.index].inversePose = parentMatrix * local;
for(auto& b : skeleton.bones) { for(int i = 0; i < skeleton.num_bones; i++) {
if(b.parent != nullptr && b.parent->name == bone.name) if(skeleton.bones[i].parent_bone != nullptr && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0) {
calculate_bone_inverse_pose(skeleton, b, &bone); calculate_bone_inverse_pose(skeleton, skeleton.bones[i], &bone);
}
} }
} }
void addItem(Skeleton& skeleton, Bone& bone, QTreeWidget* widget, QTreeWidgetItem* parent_item = nullptr) { void addItem(physis_Skeleton& skeleton, physis_Bone& bone, QTreeWidget* widget, QTreeWidgetItem* parent_item = nullptr) {
auto item = new QTreeWidgetItem(); auto item = new QTreeWidgetItem();
item->setText(0, bone.name.c_str()); item->setText(0, bone.name);
if(parent_item == nullptr) { if(parent_item == nullptr) {
widget->addTopLevelItem(item); widget->addTopLevelItem(item);
@ -112,13 +109,13 @@ void addItem(Skeleton& skeleton, Bone& bone, QTreeWidget* widget, QTreeWidgetIte
parent_item->addChild(item); parent_item->addChild(item);
} }
for(auto& b : skeleton.bones) { for(int i = 0; i < skeleton.num_bones; i++) {
if(b.parent != nullptr && b.parent->name == bone.name) if(skeleton.bones[i].parent_bone != nullptr && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0)
addItem(skeleton, b, widget, item); addItem(skeleton, skeleton.bones[i], widget, item);
} }
} }
MainWindow::MainWindow(GameData& data) : data(data) { MainWindow::MainWindow(GameData* in_data) : data(*in_data) {
setWindowTitle("mdlviewer"); setWindowTitle("mdlviewer");
setMinimumSize(QSize(640, 480)); setMinimumSize(QSize(640, 480));
@ -131,7 +128,9 @@ MainWindow::MainWindow(GameData& data) : data(data) {
"~", "~",
"FFXIV Model File (*.mdl)"); "FFXIV Model File (*.mdl)");
loadedGear.model = parseMDL(read_file_to_buffer(fileName.toStdString())); auto buffer = physis_read_file(fileName.toStdString().c_str());
loadedGear.model = physis_mdl_parse(buffer.size, buffer.data);
reloadGearAppearance(); reloadGearAppearance();
}); });
@ -160,20 +159,20 @@ MainWindow::MainWindow(GameData& data) : data(data) {
gears.push_back(info); gears.push_back(info);
} }
auto exh = *data.readExcelSheet("Item"); auto exh = physis_gamedata_read_excel_sheet_header(&data, "Item");
auto exd = physis_gamedata_read_excel_sheet(&data, "Item", exh, Language::English, 1);
auto path = getEXDFilename(exh, "item", getLanguageCode(Language::English), exh.pages[1]); for(int i = 0; i < exd.row_count; i++) {
auto exd = readEXD(exh, *data.extractFile("exd/" + path), exh.pages[1]); const auto row = exd.row_data[i];
for(auto row : exd.rows) { auto primaryModel = row.column_data[47].u_int64._0;
auto primaryModel = row.data[47].uint64Data; auto secondaryModel = row.column_data[48].u_int64._0;
auto secondaryModel = row.data[48].uint64Data;
int16_t parts[4]; int16_t parts[4];
memcpy(parts, &primaryModel, sizeof(int16_t) * 4); memcpy(parts, &primaryModel, sizeof(int16_t) * 4);
GearInfo info = {}; GearInfo info = {};
info.name = row.data[9].data; info.name = row.column_data[9].string._0;
info.slot = *get_slot_from_id(row.data[17].uint64Data); info.slot = physis_slot_from_id(row.column_data[17].u_int8._0);
info.modelInfo.primaryID = parts[0]; info.modelInfo.primaryID = parts[0];
gears.push_back(info); gears.push_back(info);
@ -243,7 +242,7 @@ MainWindow::MainWindow(GameData& data) : data(data) {
"model.fbx", "model.fbx",
tr("FBX Files (*.fbx)")); tr("FBX Files (*.fbx)"));
Model model; physis_MDL model;
#ifdef USE_STANDALONE_WINDOW #ifdef USE_STANDALONE_WINDOW
model = standaloneWindow->models[0].model; model = standaloneWindow->models[0].model;
#else #else
@ -263,11 +262,12 @@ MainWindow::MainWindow(GameData& data) : data(data) {
} }
}); });
skeleton = parseHavokXML("test.xml"); skeleton = physis_skeleton_from_skel(physis_read_file("c0101b0001.skel"));
extraBone.resize(skeleton.num_bones);
calculate_bone_inverse_pose(skeleton, *skeleton.root_bone, nullptr); calculate_bone_inverse_pose(skeleton, *skeleton.root_bone, nullptr);
auto boneListWidget = new QTreeWidget(); auto boneListWidget = new QTreeWidget();
for(auto& bone : skeleton.bones) { for(auto& bone : extraBone) {
bone.inversePose = glm::inverse(bone.inversePose); bone.inversePose = glm::inverse(bone.inversePose);
} }
@ -276,10 +276,10 @@ MainWindow::MainWindow(GameData& data) : data(data) {
boneListWidget->setMaximumWidth(200); boneListWidget->setMaximumWidth(200);
connect(boneListWidget, &QTreeWidget::itemClicked, [this](QTreeWidgetItem* item, int column) { connect(boneListWidget, &QTreeWidget::itemClicked, [this](QTreeWidgetItem* item, int column) {
for(auto& bone : skeleton.bones) { for(int i = 0; i < skeleton.num_bones; i++) {
if(bone.name == item->text(column).toStdString()) { if(strcmp(skeleton.bones[i].name, item->text(column).toStdString().c_str()) == 0) {
currentScale = glm::make_vec3(bone.scale.data()); currentScale = glm::make_vec3(skeleton.bones[i].scale);
currentEditedBone = &bone; currentEditedBone = &skeleton.bones[i];
} }
} }
}); });
@ -288,22 +288,22 @@ MainWindow::MainWindow(GameData& data) : data(data) {
Vector3Edit* scaleEdit = new Vector3Edit(currentScale); Vector3Edit* scaleEdit = new Vector3Edit(currentScale);
connect(scaleEdit, &Vector3Edit::onValueChanged, [this] { connect(scaleEdit, &Vector3Edit::onValueChanged, [this] {
memcpy(currentEditedBone->scale.data(), glm::value_ptr(currentScale), sizeof(float) * 3); memcpy(currentEditedBone->scale, glm::value_ptr(currentScale), sizeof(float) * 3);
reloadGearAppearance(); reloadGearAppearance();
}); });
layout->addWidget(scaleEdit); layout->addWidget(scaleEdit);
} }
void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName) { void MainWindow::exportModel(physis_MDL& model, physis_Skeleton& skeleton, QString fileName) {
Assimp::Exporter exporter; Assimp::Exporter exporter;
aiScene scene; aiScene scene;
scene.mRootNode = new aiNode(); scene.mRootNode = new aiNode();
scene.mRootNode->mNumChildren = model.lods[0].parts.size() + 1; // plus one for the skeleton scene.mRootNode->mNumChildren = model.lods[0].num_parts + 1; // plus one for the skeleton
scene.mRootNode->mChildren = new aiNode*[scene.mRootNode->mNumChildren]; scene.mRootNode->mChildren = new aiNode*[scene.mRootNode->mNumChildren];
scene.mNumMeshes = model.lods[0].parts.size(); scene.mNumMeshes = model.lods[0].num_parts;
scene.mMeshes = new aiMesh*[scene.mNumMeshes]; scene.mMeshes = new aiMesh*[scene.mNumMeshes];
auto skeleton_node = new aiNode(); auto skeleton_node = new aiNode();
@ -315,37 +315,37 @@ void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName)
std::vector<aiNode*> skeletonNodes; std::vector<aiNode*> skeletonNodes;
for(int i = 0; i < model.affectedBoneNames.size(); i++) { for(int i = 0; i < model.num_affected_bones; i++) {
auto& node = skeletonNodes.emplace_back(); auto& node = skeletonNodes.emplace_back();
node = new aiNode(); node = new aiNode();
node->mName = model.affectedBoneNames[i]; node->mName = model.affected_bone_names[i];
int real_bone_id = 0; int real_bone_id = 0;
for(int k = 0; k < skeleton.bones.size(); k++) { for(int k = 0; k < skeleton.num_bones; k++) {
if(skeleton.bones[k].name == model.affectedBoneNames[i]) { if(strcmp(skeleton.bones[k].name, model.affected_bone_names[i]) == 0) {
real_bone_id = k; real_bone_id = k;
} }
} }
node->mChildren = new aiNode*[model.affectedBoneNames.size()]; node->mChildren = new aiNode*[model.num_affected_bones];
auto& real_bone = skeleton.bones[real_bone_id]; auto& real_bone = skeleton.bones[real_bone_id];
memcpy(&node->mTransformation, glm::value_ptr(real_bone.finalTransform), sizeof(aiMatrix4x4)); memcpy(&node->mTransformation, glm::value_ptr(extraBone[real_bone.index].finalTransform), sizeof(aiMatrix4x4));
} }
// setup parenting // setup parenting
for(int i = 0; i < model.affectedBoneNames.size(); i++) { for(int i = 0; i < model.num_affected_bones; i++) {
int real_bone_id = 0; int real_bone_id = 0;
for(int k = 0; k < skeleton.bones.size(); k++) { for(int k = 0; k < skeleton.num_bones; k++) {
if(skeleton.bones[k].name == model.affectedBoneNames[i]) { if(strcmp(skeleton.bones[k].name, 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 != nullptr) { if(real_bone.parent_bone != nullptr) {
for(int k = 0; k < model.affectedBoneNames.size(); k++) { for(int k = 0; k < model.num_affected_bones; k++) {
if(model.affectedBoneNames[k] == real_bone.parent->name) { if(strcmp(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];
} }
@ -355,7 +355,7 @@ void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName)
skeleton_node->mChildren[0] = new aiNode(); skeleton_node->mChildren[0] = new aiNode();
skeleton_node->mChildren[0]->mName = "root"; skeleton_node->mChildren[0]->mName = "root";
skeleton_node->mChildren[0]->mChildren = new aiNode*[model.affectedBoneNames.size()]; skeleton_node->mChildren[0]->mChildren = new aiNode*[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) {
@ -363,7 +363,7 @@ void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName)
} }
} }
for(int i = 0; i < model.lods[0].parts.size(); i++) { for(int i = 0; i < 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;
@ -374,7 +374,7 @@ void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName)
node->mMeshes[0] = i; node->mMeshes[0] = i;
auto mesh = scene.mMeshes[i]; auto mesh = scene.mMeshes[i];
mesh->mNumVertices = model.lods[0].parts[i].vertices.size(); mesh->mNumVertices = 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];
@ -386,7 +386,7 @@ void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName)
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 = model.affectedBoneNames.size(); mesh->mNumBones = 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;
@ -397,27 +397,27 @@ void MainWindow::exportModel(Model& model, Skeleton& skeleton, QString fileName)
}*/ }*/
mesh->mBones[j] = new aiBone(); mesh->mBones[j] = new aiBone();
mesh->mBones[j]->mName = model.affectedBoneNames[j]; mesh->mBones[j]->mName = model.affected_bone_names[j];
mesh->mBones[j]->mNumWeights = mesh->mNumVertices * 4; mesh->mBones[j]->mNumWeights = mesh->mNumVertices * 4;
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 (model.lods[0].parts[i].vertices[k].boneIds[z] == real_bone_id) { if (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 = model.lods[0].parts[i].vertices[k].boneWeights[z]; weight.mWeight = model.lods[0].parts[i].vertices[k].bone_weight[z];
} }
} }
} }
} }
mesh->mNumFaces = model.lods[0].parts[i].indices.size() / 3; mesh->mNumFaces = model.lods[0].parts[i].num_indices / 3;
mesh->mFaces = new aiFace[mesh->mNumFaces]; mesh->mFaces = new aiFace[mesh->mNumFaces];
int lastFace = 0; int lastFace = 0;
for(int j = 0; j < model.lods[0].parts[i].indices.size(); j += 3) { for(int j = 0; j < 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;
@ -441,44 +441,45 @@ void MainWindow::loadInitialGearInfo(GearInfo& info) {
raceCombo->clear(); raceCombo->clear();
for(auto [race, race_name] : magic_enum::enum_entries<Race>()) { for(auto [race, race_name] : magic_enum::enum_entries<Race>()) {
if(data.exists(build_equipment_path(loadedGear.gearInfo->modelInfo.primaryID, race, loadedGear.gearInfo->slot))) auto equip_path = physis_build_equipment_path(loadedGear.gearInfo->modelInfo.primaryID, race, currentSubrace, currentGender, loadedGear.gearInfo->slot);
raceCombo->addItem(race_name.data());
if(physis_gamedata_exists(&data, equip_path))
raceCombo->addItem(race_name.data());
} }
currentLod = 0; currentLod = 0;
currentRace = Race::HyurMidlanderMale; currentRace = Race::Hyur;
reloadGearModel(); reloadGearModel();
} }
void MainWindow::reloadGearModel() { void MainWindow::reloadGearModel() {
auto mdl_data = data.extractFile(build_equipment_path(loadedGear.gearInfo->modelInfo.primaryID, currentRace, loadedGear.gearInfo->slot)); auto mdl_data = physis_gamedata_extract_file(&data, physis_build_equipment_path(loadedGear.gearInfo->modelInfo.primaryID, currentRace, currentSubrace, currentGender, loadedGear.gearInfo->slot));
if(mdl_data == std::nullopt)
return;
loadedGear.model = parseMDL(*mdl_data); loadedGear.model = physis_mdl_parse(mdl_data.size, mdl_data.data);
lodCombo->clear(); lodCombo->clear();
for(int i = 0; i < loadedGear.model.lods.size(); i++) for(int i = 0; i < loadedGear.model.num_lod; i++)
lodCombo->addItem(QString::number(i)); lodCombo->addItem(QString::number(i));
reloadGearAppearance(); reloadGearAppearance();
} }
void calculate_bone(Skeleton& skeleton, Bone& bone, const Bone* parent_bone) { void MainWindow::calculate_bone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone) {
const glm::mat4 parent_matrix = parent_bone == nullptr ? glm::mat4(1.0f) : parent_bone->localTransform; const glm::mat4 parent_matrix = parent_bone == nullptr ? glm::mat4(1.0f) : extraBone[parent_bone->index].localTransform;
glm::mat4 local = glm::mat4(1.0f); glm::mat4 local = glm::mat4(1.0f);
local = glm::translate(local, glm::vec3(bone.position[0], bone.position[1], bone.position[2])); local = glm::translate(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], bone.rotation[1], bone.rotation[2]));
local = glm::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2])); local = glm::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2]));
bone.localTransform = parent_matrix * local; extraBone[bone.index].localTransform = parent_matrix * local;
bone.finalTransform = bone.localTransform * bone.inversePose; extraBone[bone.index].finalTransform = extraBone[bone.index].localTransform * extraBone[bone.index].inversePose;
for(auto& b : skeleton.bones) { for(int i = 0; i < skeleton.num_bones; i++) {
if(b.parent != nullptr && b.parent->name == bone.name) if(skeleton.bones[i].parent_bone != nullptr && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0) {
calculate_bone(skeleton, b, &bone); calculate_bone(skeleton, skeleton.bones[i], &bone);
}
} }
} }
@ -489,15 +490,15 @@ void MainWindow::reloadGearAppearance() {
// 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 < loadedGear.model.affectedBoneNames.size(); i++) { for(int i = 0; i < loadedGear.model.num_affected_bones; i++) {
for(int k = 0; k < skeleton.bones.size(); k++) { for(int k = 0; k < skeleton.num_bones; k++) {
if(skeleton.bones[k].name == loadedGear.model.affectedBoneNames[i]) if(strcmp(skeleton.bones[k].name, loadedGear.model.affected_bone_names[i]) == 0)
boneMapping[i] = k; boneMapping[i] = k;
} }
} }
for(int i = 0; i < loadedGear.model.affectedBoneNames.size(); i++) { for(int i = 0; i < loadedGear.model.num_affected_bones; i++) {
loadedGear.renderModel.boneData[i] = skeleton.bones[boneMapping[i]].finalTransform; loadedGear.renderModel.boneData[i] = extraBone[boneMapping[i]].finalTransform;
} }
#ifndef USE_STANDALONE_WINDOW #ifndef USE_STANDALONE_WINDOW

View file

@ -16,7 +16,7 @@ endif()
add_library(renderer src/renderer.cpp ${EXTRA_SRC}) add_library(renderer src/renderer.cpp ${EXTRA_SRC})
target_include_directories(renderer PUBLIC include) target_include_directories(renderer PUBLIC include)
target_link_libraries(renderer PUBLIC Vulkan::Vulkan fmt::fmt libxiv glm::glm ${EXTRA_LIBRARIES}) target_link_libraries(renderer PUBLIC Vulkan::Vulkan fmt::fmt physis z glm::glm ${EXTRA_LIBRARIES})
target_compile_definitions(renderer PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE) target_compile_definitions(renderer PUBLIC GLM_FORCE_RADIANS GLM_FORCE_DEPTH_ZERO_TO_ONE)
if(USE_STANDALONE_WINDOW) if(USE_STANDALONE_WINDOW)

View file

@ -5,19 +5,17 @@
#include <array> #include <array>
#include <glm/ext/matrix_float4x4.hpp> #include <glm/ext/matrix_float4x4.hpp>
#include "mdlparser.h" #include <physis.hpp>
struct RenderPart { struct RenderPart {
size_t numIndices; size_t numIndices;
VkBuffer vertexBuffer, indexBuffer; VkBuffer vertexBuffer, indexBuffer;
VkDeviceMemory vertexMemory, indexMemory; VkDeviceMemory vertexMemory, indexMemory;
std::vector<PartSubmesh> submeshes;
}; };
struct RenderModel { struct RenderModel {
Model model; physis_MDL model;
std::vector<RenderPart> parts; std::vector<RenderPart> parts;
std::array<glm::mat4, 128> boneData; std::array<glm::mat4, 128> boneData;
}; };
@ -32,7 +30,7 @@ public:
bool initSwapchain(VkSurfaceKHR surface, int width, int height); bool initSwapchain(VkSurfaceKHR surface, int width, int height);
void resize(VkSurfaceKHR surface, int width, int height); void resize(VkSurfaceKHR surface, int width, int height);
RenderModel addModel(const Model& model, int lod); RenderModel addModel(const physis_MDL& model, int lod);
void render(std::vector<RenderModel> models); void render(std::vector<RenderModel> models);

View file

@ -442,7 +442,7 @@ void Renderer::render(std::vector<RenderModel> models) {
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, 0, nullptr); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, 0, nullptr);
for(auto part : model.parts) { for(const auto& part : model.parts) {
VkDeviceSize offsets[] = {0}; VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &part.vertexBuffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16); vkCmdBindIndexBuffer(commandBuffer, part.indexBuffer, 0, VK_INDEX_TYPE_UINT16);
@ -554,21 +554,22 @@ uint32_t Renderer::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags pro
return -1; return -1;
} }
RenderModel Renderer::addModel(const Model& model, int lod) { RenderModel Renderer::addModel(const physis_MDL& model, int lod) {
RenderModel renderModel; RenderModel renderModel;
renderModel.model = model; renderModel.model = model;
if(lod < 0 || lod > model.lods.size()) if(lod < 0 || lod > model.num_lod)
return {}; return {};
for(auto part : model.lods[lod].parts) { for(int i = 0; i < model.lods[0].num_parts; i++) {
RenderPart renderPart; RenderPart renderPart;
renderPart.submeshes = part.submeshes;
size_t vertexSize = part.vertices.size() * sizeof(Vertex); const physis_Part part = model.lods[0].parts[i];
size_t vertexSize = part.num_vertices * sizeof(Vertex);
auto[vertexBuffer, vertexMemory] = createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); auto[vertexBuffer, vertexMemory] = createBuffer(vertexSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
size_t indexSize = part.indices.size() * sizeof(uint16_t); size_t indexSize = part.num_indices * sizeof(uint16_t);
auto[indexBuffer, indexMemory] = createBuffer(indexSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); auto[indexBuffer, indexMemory] = createBuffer(indexSize, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
// copy vertex data // copy vertex data
@ -576,7 +577,7 @@ RenderModel Renderer::addModel(const Model& model, int lod) {
void* mapped_data = nullptr; void* mapped_data = nullptr;
vkMapMemory(device, vertexMemory, 0, vertexSize, 0, &mapped_data); vkMapMemory(device, vertexMemory, 0, vertexSize, 0, &mapped_data);
memcpy(mapped_data, part.vertices.data(), vertexSize); memcpy(mapped_data, part.vertices, vertexSize);
VkMappedMemoryRange range = {}; VkMappedMemoryRange range = {};
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
@ -592,7 +593,7 @@ RenderModel Renderer::addModel(const Model& model, int lod) {
void* mapped_data = nullptr; void* mapped_data = nullptr;
vkMapMemory(device, indexMemory, 0, indexSize, 0, &mapped_data); vkMapMemory(device, indexMemory, 0, indexSize, 0, &mapped_data);
memcpy(mapped_data, part.indices.data(), indexSize); memcpy(mapped_data, part.indices, indexSize);
VkMappedMemoryRange range = {}; VkMappedMemoryRange range = {};
range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
@ -603,7 +604,7 @@ RenderModel Renderer::addModel(const Model& model, int lod) {
vkUnmapMemory(device, indexMemory); vkUnmapMemory(device, indexMemory);
} }
renderPart.numIndices = part.indices.size(); renderPart.numIndices = part.num_indices;
renderPart.vertexBuffer = vertexBuffer; renderPart.vertexBuffer = vertexBuffer;
renderPart.vertexMemory = vertexMemory; renderPart.vertexMemory = vertexMemory;
@ -648,12 +649,12 @@ void Renderer::initPipeline() {
VkVertexInputAttributeDescription boneWeightAttribute = {}; VkVertexInputAttributeDescription boneWeightAttribute = {};
boneWeightAttribute.format = VK_FORMAT_R32G32B32_SFLOAT; boneWeightAttribute.format = VK_FORMAT_R32G32B32_SFLOAT;
boneWeightAttribute.location = 2; boneWeightAttribute.location = 2;
boneWeightAttribute.offset = offsetof(Vertex, boneWeights); boneWeightAttribute.offset = offsetof(Vertex, bone_weight);
VkVertexInputAttributeDescription boneIdAttribute = {}; VkVertexInputAttributeDescription boneIdAttribute = {};
boneIdAttribute.format = VK_FORMAT_R8G8B8A8_UINT; boneIdAttribute.format = VK_FORMAT_R8G8B8A8_UINT;
boneIdAttribute.location = 3; boneIdAttribute.location = 3;
boneIdAttribute.offset = offsetof(Vertex, boneIds); boneIdAttribute.offset = offsetof(Vertex, bone_id);
std::array<VkVertexInputAttributeDescription, 4> attributes = {positionAttribute, normalAttribute, boneWeightAttribute, boneIdAttribute}; std::array<VkVertexInputAttributeDescription, 4> attributes = {positionAttribute, normalAttribute, boneWeightAttribute, boneIdAttribute};