mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-22 12:07:45 +00:00
Introduce the parts system and EXD and MDL parts
These parts (inspired by the KDE parts system) will allow the tooling to reuse GUI mechanisms. Right now the two supported parts are for Excel and Models, and exdviewer and mdlviewer will be retrofitted to them in future commits.
This commit is contained in:
parent
792da6da6a
commit
97f46bcca1
8 changed files with 595 additions and 1 deletions
|
@ -42,4 +42,5 @@ add_subdirectory(exdviewer)
|
|||
add_subdirectory(mdlviewer)
|
||||
add_subdirectory(argcracker)
|
||||
add_subdirectory(explorer)
|
||||
add_subdirectory(bonedecomp)
|
||||
add_subdirectory(bonedecomp)
|
||||
add_subdirectory(parts)
|
2
parts/CMakeLists.txt
Normal file
2
parts/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
add_subdirectory(exd)
|
||||
add_subdirectory(mdl)
|
3
parts/exd/CMakeLists.txt
Normal file
3
parts/exd/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
add_library(exdpart STATIC exdpart.cpp)
|
||||
target_link_libraries(exdpart PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets)
|
||||
target_include_directories(exdpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
97
parts/exd/exdpart.cpp
Normal file
97
parts/exd/exdpart.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#include "exdpart.h"
|
||||
|
||||
#include <physis.hpp>
|
||||
#include <QTableWidget>
|
||||
#include <QDebug>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
EXDPart::EXDPart(GameData* data) : data(data) {
|
||||
pageTabWidget = new QTabWidget();
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(pageTabWidget);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void EXDPart::loadSheet(const QString& name) {
|
||||
qDebug() << "Loading" << name;
|
||||
|
||||
pageTabWidget->clear();
|
||||
|
||||
auto exh = physis_gamedata_read_excel_sheet_header(data, name.toStdString().c_str());
|
||||
|
||||
for(int i = 0; i < exh.page_count; i++) {
|
||||
QTableWidget* tableWidget = new QTableWidget();
|
||||
|
||||
tableWidget->setColumnCount(exh.column_count);
|
||||
|
||||
auto exd = physis_gamedata_read_excel_sheet(data, name.toStdString().c_str(), &exh, exh.languages[0], i);
|
||||
|
||||
tableWidget->setRowCount(exd.row_count);
|
||||
|
||||
for (int j = 0; j < exd.row_count; j++) {
|
||||
for(int z = 0; z < exd.column_count; z++) {
|
||||
auto data = exd.row_data[j].column_data[z];
|
||||
|
||||
QString columnString;
|
||||
QString columnType;
|
||||
switch (data.tag) {
|
||||
case physis_ColumnData::Tag::String:
|
||||
columnString = QString(data.string._0);
|
||||
columnType = "String";
|
||||
break;
|
||||
case physis_ColumnData::Tag::Bool:
|
||||
columnString = data.bool_._0 ? "True" : "False";
|
||||
columnType = "Bool";
|
||||
break;
|
||||
case physis_ColumnData::Tag::Int8:
|
||||
columnString = QString::number(data.int8._0);
|
||||
columnType = "Int8";
|
||||
break;
|
||||
case physis_ColumnData::Tag::UInt8:
|
||||
columnString = QString::number(data.u_int8._0);
|
||||
columnType = "UInt8";
|
||||
break;
|
||||
case physis_ColumnData::Tag::Int16:
|
||||
columnString = QString::number(data.int16._0);
|
||||
columnType = "Int16";
|
||||
break;
|
||||
case physis_ColumnData::Tag::UInt16:
|
||||
columnString = QString::number(data.u_int16._0);
|
||||
columnType = "UInt16";
|
||||
break;
|
||||
case physis_ColumnData::Tag::Int32:
|
||||
columnString = QString::number(data.int32._0);
|
||||
columnType = "Int32";
|
||||
break;
|
||||
case physis_ColumnData::Tag::UInt32:
|
||||
columnString = QString::number(data.u_int32._0);
|
||||
columnType = "UInt32";
|
||||
break;
|
||||
case physis_ColumnData::Tag::Float32:
|
||||
columnString = QString::number(data.float32._0);
|
||||
columnType = "Float32";
|
||||
break;
|
||||
case physis_ColumnData::Tag::Int64:
|
||||
columnString = QString::number(data.int64._0);
|
||||
columnType = "Int64";
|
||||
break;
|
||||
case physis_ColumnData::Tag::UInt64:
|
||||
columnString = QString::number(data.u_int64._0);
|
||||
columnType = "UInt64";
|
||||
break;
|
||||
}
|
||||
|
||||
auto newItem = new QTableWidgetItem(columnString);
|
||||
|
||||
tableWidget->setItem(i, j, newItem);
|
||||
|
||||
QTableWidgetItem* headerItem = new QTableWidgetItem();
|
||||
headerItem->setText(columnType);
|
||||
tableWidget->setHorizontalHeaderItem(j, headerItem);
|
||||
}
|
||||
}
|
||||
|
||||
pageTabWidget->addTab(tableWidget, QString("Page %1").arg(i));
|
||||
}
|
||||
}
|
18
parts/exd/exdpart.h
Normal file
18
parts/exd/exdpart.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTabWidget>
|
||||
|
||||
struct GameData;
|
||||
|
||||
class EXDPart : public QWidget {
|
||||
public:
|
||||
explicit EXDPart(GameData* data);
|
||||
|
||||
void loadSheet(const QString& name);
|
||||
|
||||
private:
|
||||
GameData* data = nullptr;
|
||||
|
||||
QTabWidget* pageTabWidget = nullptr;
|
||||
};
|
3
parts/mdl/CMakeLists.txt
Normal file
3
parts/mdl/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
add_library(mdlpart STATIC mdlpart.cpp)
|
||||
target_link_libraries(mdlpart PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets renderer)
|
||||
target_include_directories(mdlpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
408
parts/mdl/mdlpart.cpp
Normal file
408
parts/mdl/mdlpart.cpp
Normal file
|
@ -0,0 +1,408 @@
|
|||
#include "mdlpart.h"
|
||||
#include "glm/gtx/transform.hpp"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QVulkanInstance>
|
||||
#include <QVulkanWindow>
|
||||
#include <QResizeEvent>
|
||||
#include <fmt/core.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <assimp/Exporter.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <glm/gtc/type_ptr.inl>
|
||||
|
||||
#ifndef USE_STANDALONE_WINDOW
|
||||
class VulkanWindow : public QWindow
|
||||
{
|
||||
public:
|
||||
VulkanWindow(Renderer* renderer, QVulkanInstance* instance) : m_renderer(renderer), m_instance(instance) {
|
||||
setSurfaceType(VulkanSurface);
|
||||
setVulkanInstance(instance);
|
||||
}
|
||||
|
||||
void exposeEvent(QExposeEvent *) {
|
||||
if (isExposed()) {
|
||||
if (!m_initialized) {
|
||||
m_initialized = true;
|
||||
|
||||
auto surface = m_instance->surfaceForWindow(this);
|
||||
if(!m_renderer->initSwapchain(surface, width(), height()))
|
||||
m_initialized = false;
|
||||
else
|
||||
render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool event(QEvent *e) {
|
||||
if (e->type() == QEvent::UpdateRequest)
|
||||
render();
|
||||
|
||||
if (e->type() == QEvent::Resize) {
|
||||
QResizeEvent* resizeEvent = (QResizeEvent*)e;
|
||||
auto surface = m_instance->surfaceForWindow(this);
|
||||
m_renderer->resize(surface, resizeEvent->size().width(), resizeEvent->size().height());
|
||||
}
|
||||
|
||||
return QWindow::event(e);
|
||||
}
|
||||
|
||||
void render() {
|
||||
m_renderer->render(models);
|
||||
m_instance->presentQueued(this);
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
std::vector<RenderModel> models;
|
||||
|
||||
private:
|
||||
bool m_initialized = false;
|
||||
Renderer* m_renderer;
|
||||
QVulkanInstance* m_instance;
|
||||
};
|
||||
#else
|
||||
#include "standalonewindow.h"
|
||||
#include "equipment.h"
|
||||
|
||||
#endif
|
||||
|
||||
MDLPart::MDLPart(GameData *data) : data(data) {
|
||||
auto viewportLayout = new QVBoxLayout();
|
||||
setLayout(viewportLayout);
|
||||
|
||||
renderer = new Renderer();
|
||||
|
||||
#ifndef USE_STANDALONE_WINDOW
|
||||
auto inst = new QVulkanInstance();
|
||||
inst->setVkInstance(renderer->instance);
|
||||
inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
|
||||
inst->create();
|
||||
|
||||
vkWindow = new VulkanWindow(renderer, inst);
|
||||
vkWindow->setVulkanInstance(inst);
|
||||
|
||||
auto widget = QWidget::createWindowContainer(vkWindow);
|
||||
|
||||
viewportLayout->addWidget(widget);
|
||||
#else
|
||||
standaloneWindow = new StandaloneWindow(renderer);
|
||||
renderer->initSwapchain(standaloneWindow->getSurface(renderer->instance), 640, 480);
|
||||
|
||||
QTimer* timer = new QTimer();
|
||||
connect(timer, &QTimer::timeout, this, [this] {
|
||||
standaloneWindow->render();
|
||||
});
|
||||
timer->start(1000);
|
||||
#endif
|
||||
|
||||
connect(this, &MDLPart::modelChanged, this, &MDLPart::reloadRenderer);
|
||||
connect(this, &MDLPart::skeletonChanged, this, &MDLPart::reloadBoneData);
|
||||
}
|
||||
|
||||
void MDLPart::exportModel(const QString &fileName) {
|
||||
Assimp::Exporter exporter;
|
||||
|
||||
aiScene scene;
|
||||
scene.mRootNode = new aiNode();
|
||||
|
||||
// TODO: hardcoded to the first model for now
|
||||
scene.mRootNode->mNumChildren = models[0].model.lods[0].num_parts + 1; // plus one for the skeleton
|
||||
scene.mRootNode->mChildren = new aiNode*[scene.mRootNode->mNumChildren];
|
||||
|
||||
scene.mNumMeshes = models[0].model.lods[0].num_parts;
|
||||
scene.mMeshes = new aiMesh*[scene.mNumMeshes];
|
||||
|
||||
auto skeleton_node = new aiNode();
|
||||
skeleton_node->mName = "Skeleton";
|
||||
skeleton_node->mNumChildren = 1;
|
||||
skeleton_node->mChildren = new aiNode*[skeleton_node->mNumChildren];
|
||||
|
||||
scene.mRootNode->mChildren[scene.mRootNode->mNumChildren - 1] = skeleton_node;
|
||||
|
||||
std::vector<aiNode*> skeletonNodes;
|
||||
|
||||
for(int i = 0; i < models[0].model.num_affected_bones; i++) {
|
||||
auto& node = skeletonNodes.emplace_back();
|
||||
node = new aiNode();
|
||||
node->mName = models[0].model.affected_bone_names[i];
|
||||
|
||||
int real_bone_id = 0;
|
||||
for(int k = 0; k < skeleton->num_bones; k++) {
|
||||
if(strcmp(skeleton->bones[k].name, models[0].model.affected_bone_names[i]) == 0) {
|
||||
real_bone_id = k;
|
||||
}
|
||||
}
|
||||
|
||||
node->mChildren = new aiNode*[models[0].model.num_affected_bones];
|
||||
|
||||
auto& real_bone = skeleton->bones[real_bone_id];
|
||||
memcpy(&node->mTransformation, glm::value_ptr(boneData[real_bone.index].finalTransform), sizeof(aiMatrix4x4));
|
||||
}
|
||||
|
||||
// setup parenting
|
||||
for(int i = 0; i < models[0].model.num_affected_bones; i++) {
|
||||
int real_bone_id = 0;
|
||||
for(int k = 0; k < skeleton->num_bones; k++) {
|
||||
if(strcmp(skeleton->bones[k].name, models[0].model.affected_bone_names[i]) == 0) {
|
||||
real_bone_id = k;
|
||||
}
|
||||
}
|
||||
|
||||
auto& real_bone = skeleton->bones[real_bone_id];
|
||||
if(real_bone.parent_bone != nullptr) {
|
||||
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) {
|
||||
skeletonNodes[i]->mParent = skeletonNodes[k];
|
||||
skeletonNodes[k]->mChildren[skeletonNodes[k]->mNumChildren++] = skeletonNodes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skeleton_node->mChildren[0] = new aiNode();
|
||||
skeleton_node->mChildren[0]->mName = "root";
|
||||
skeleton_node->mChildren[0]->mChildren = new aiNode*[models[0].model.num_affected_bones];
|
||||
|
||||
for(int i = 0; i < skeletonNodes.size(); i++) {
|
||||
if(skeletonNodes[i]->mParent == nullptr) {
|
||||
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++) {
|
||||
scene.mMeshes[i] = new aiMesh();
|
||||
scene.mMeshes[i]->mMaterialIndex = 0;
|
||||
|
||||
auto& node = scene.mRootNode->mChildren[i];
|
||||
node = new aiNode();
|
||||
node->mNumMeshes = 1;
|
||||
node->mMeshes = new unsigned int [scene.mRootNode->mNumMeshes];
|
||||
node->mMeshes[0] = i;
|
||||
|
||||
auto mesh = scene.mMeshes[i];
|
||||
mesh->mNumVertices = models[0].model.lods[0].parts[i].num_vertices;
|
||||
mesh->mVertices = new aiVector3D [mesh->mNumVertices];
|
||||
mesh->mNormals = new aiVector3D [mesh->mNumVertices];
|
||||
mesh->mTextureCoords[0] = new aiVector3D [mesh->mNumVertices];
|
||||
mesh->mNumUVComponents[0] = 2;
|
||||
|
||||
for(int j = 0; j < mesh->mNumVertices; 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->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->mNumBones = models[0].model.num_affected_bones;
|
||||
mesh->mBones = new aiBone*[mesh->mNumBones];
|
||||
for(int j = 0; j < mesh->mNumBones; j++) {
|
||||
int real_bone_id = j;
|
||||
// TODO: is this still relevant?5
|
||||
/*for(int k = 0; k < skeleton.bones.size(); k++) {
|
||||
if(skeleton.bones[k].name == model.affectedBoneNames[j]) {
|
||||
real_bone_id = k;
|
||||
}
|
||||
}*/
|
||||
|
||||
mesh->mBones[j] = new aiBone();
|
||||
mesh->mBones[j]->mName = models[0].model.affected_bone_names[j];
|
||||
mesh->mBones[j]->mNumWeights = mesh->mNumVertices * 4;
|
||||
mesh->mBones[j]->mWeights = new aiVertexWeight[mesh->mBones[j]->mNumWeights];
|
||||
mesh->mBones[j]->mNode = skeleton_node->mChildren[j];
|
||||
|
||||
for(int k = 0; k < mesh->mNumVertices; k++) {
|
||||
for(int z = 0; z < 4; z++) {
|
||||
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];
|
||||
weight.mVertexId = k;
|
||||
weight.mWeight = models[0].model.lods[0].parts[i].vertices[k].bone_weight[z];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mesh->mNumFaces = models[0].model.lods[0].parts[i].num_indices / 3;
|
||||
mesh->mFaces = new aiFace[mesh->mNumFaces];
|
||||
|
||||
int lastFace = 0;
|
||||
for(int j = 0; j < models[0].model.lods[0].parts[i].num_indices; j += 3) {
|
||||
aiFace& face = mesh->mFaces[lastFace++];
|
||||
|
||||
face.mNumIndices = 3;
|
||||
face.mIndices = new unsigned int[face.mNumIndices];
|
||||
|
||||
face.mIndices[0] = models[0].model.lods[0].parts[i].indices[j];
|
||||
face.mIndices[1] = models[0].model.lods[0].parts[i].indices[j + 1];
|
||||
face.mIndices[2] = models[0].model.lods[0].parts[i].indices[j + 2];
|
||||
}
|
||||
}
|
||||
|
||||
scene.mNumMaterials = 1;
|
||||
scene.mMaterials = new aiMaterial*[1];
|
||||
scene.mMaterials[0] = new aiMaterial();
|
||||
|
||||
exporter.Export(&scene, "fbx", fileName.toStdString());
|
||||
}
|
||||
|
||||
void MDLPart::clear() {
|
||||
models.clear();
|
||||
|
||||
Q_EMIT modelChanged();
|
||||
}
|
||||
|
||||
void MDLPart::addModel(physis_MDL mdl, std::vector<physis_Material> materials, int lod) {
|
||||
qDebug() << "Adding model to MDLPart";
|
||||
|
||||
auto model = renderer->addModel(mdl, lod);
|
||||
|
||||
std::transform(materials.begin(), materials.end(), std::back_inserter(model.materials), [this](const physis_Material& mat) {
|
||||
return createMaterial(mat);
|
||||
});
|
||||
|
||||
models.push_back(model);
|
||||
|
||||
Q_EMIT modelChanged();
|
||||
}
|
||||
|
||||
void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
||||
skeleton = newSkeleton;
|
||||
|
||||
Q_EMIT skeletonChanged();
|
||||
}
|
||||
|
||||
void MDLPart::clearSkeleton() {
|
||||
skeleton.reset();
|
||||
|
||||
Q_EMIT skeletonChanged();
|
||||
}
|
||||
|
||||
void MDLPart::reloadRenderer() {
|
||||
qDebug() << "Reloading render models...";
|
||||
|
||||
reloadBoneData();
|
||||
|
||||
#ifndef USE_STANDALONE_WINDOW
|
||||
vkWindow->models = models;
|
||||
#else
|
||||
standaloneWindow->models = models;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MDLPart::reloadBoneData() {
|
||||
if(skeleton.has_value()) {
|
||||
// first-time data, TODO split out
|
||||
boneData.resize(skeleton->num_bones);
|
||||
calculateBoneInversePose(*skeleton, *skeleton->root_bone, nullptr);
|
||||
|
||||
for(auto& bone : boneData) {
|
||||
bone.inversePose = glm::inverse(bone.inversePose);
|
||||
}
|
||||
|
||||
// update data
|
||||
calculateBone(*skeleton, *skeleton->root_bone, nullptr);
|
||||
|
||||
for(auto& model : models) {
|
||||
// we want to map the actual affected bones to bone ids
|
||||
std::map<int, int> boneMapping;
|
||||
for (int i = 0; i < model.model.num_affected_bones; i++) {
|
||||
for (int k = 0; k < skeleton->num_bones; k++) {
|
||||
if (strcmp(skeleton->bones[k].name, model.model.affected_bone_names[i]) == 0)
|
||||
boneMapping[i] = k;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < model.model.num_affected_bones; i++) {
|
||||
model.boneData[i] = boneData[boneMapping[i]].finalTransform;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderMaterial MDLPart::createMaterial(const physis_Material &material) {
|
||||
RenderMaterial newMaterial;
|
||||
|
||||
for (int i = 0; i < material.num_textures; i++) {
|
||||
std::string t = material.textures[i];
|
||||
|
||||
if (t.find("skin") != std::string::npos) {
|
||||
newMaterial.type = MaterialType::Skin;
|
||||
}
|
||||
|
||||
char type = t[t.length() - 5];
|
||||
|
||||
switch(type) {
|
||||
case 'm': {
|
||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||
|
||||
newMaterial.multiTexture = new RenderTexture(tex);
|
||||
}
|
||||
case 'd': {
|
||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||
|
||||
newMaterial.diffuseTexture = new RenderTexture(tex);
|
||||
}
|
||||
break;
|
||||
case 'n': {
|
||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||
|
||||
newMaterial.normalTexture = new RenderTexture(tex);
|
||||
}
|
||||
break;
|
||||
case 's': {
|
||||
auto texture = physis_texture_parse(physis_gamedata_extract_file(data, material.textures[i]));
|
||||
auto tex = renderer->addTexture(texture.width, texture.height, texture.rgba, texture.rgba_size);
|
||||
|
||||
newMaterial.specularTexture = new RenderTexture(tex);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
qDebug() << "unhandled type" << type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newMaterial;
|
||||
}
|
||||
|
||||
void MDLPart::calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& bone, physis_Bone* parent_bone) {
|
||||
const glm::mat4 parentMatrix = parent_bone == nullptr ? glm::mat4(1.0f) : boneData[parent_bone->index].inversePose;
|
||||
|
||||
glm::mat4 local(1.0f);
|
||||
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::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||
|
||||
boneData[bone.index].inversePose = parentMatrix * local;
|
||||
|
||||
for(int i = 0; i < skeleton.num_bones; i++) {
|
||||
if(skeleton.bones[i].parent_bone != nullptr && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0) {
|
||||
calculateBoneInversePose(skeleton, skeleton.bones[i], &bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MDLPart::calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const physis_Bone* parent_bone) {
|
||||
const glm::mat4 parent_matrix = parent_bone == nullptr ? glm::mat4(1.0f) : boneData[parent_bone->index].localTransform;
|
||||
|
||||
glm::mat4 local = glm::mat4(1.0f);
|
||||
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::scale(local, glm::vec3(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||
|
||||
boneData[bone.index].localTransform = parent_matrix * local;
|
||||
boneData[bone.index].finalTransform = boneData[bone.index].localTransform * boneData[bone.index].inversePose;
|
||||
|
||||
for(int i = 0; i < skeleton.num_bones; i++) {
|
||||
if(skeleton.bones[i].parent_bone != nullptr && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0) {
|
||||
calculateBone(skeleton, skeleton.bones[i], &bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_mdlpart.cpp"
|
62
parts/mdl/mdlpart.h
Normal file
62
parts/mdl/mdlpart.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <optional>
|
||||
#include <physis.hpp>
|
||||
|
||||
#include "renderer.hpp"
|
||||
|
||||
struct GameData;
|
||||
|
||||
class VulkanWindow;
|
||||
class StandaloneWindow;
|
||||
|
||||
class MDLPart : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MDLPart(GameData* data);
|
||||
|
||||
void exportModel(const QString& fileName);
|
||||
|
||||
Q_SIGNALS:
|
||||
void modelChanged();
|
||||
void skeletonChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Clears all stored MDLs.
|
||||
void clear();
|
||||
|
||||
/// Adds a new MDL with a list of materials used.
|
||||
void addModel(physis_MDL mdl, std::vector<physis_Material> materials, int lod);
|
||||
|
||||
/// Sets the skeleton any skinned MDLs should bind to.
|
||||
void setSkeleton(physis_Skeleton skeleton);
|
||||
|
||||
/// Clears the current skeleton.
|
||||
void clearSkeleton();
|
||||
|
||||
private Q_SLOTS:
|
||||
void reloadRenderer();
|
||||
void reloadBoneData();
|
||||
|
||||
private:
|
||||
RenderMaterial createMaterial(const physis_Material& mat);
|
||||
|
||||
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);
|
||||
|
||||
GameData* data = nullptr;
|
||||
|
||||
std::vector<RenderModel> models;
|
||||
std::optional<physis_Skeleton> skeleton;
|
||||
|
||||
struct BoneData {
|
||||
glm::mat4 localTransform, finalTransform, inversePose;
|
||||
};
|
||||
|
||||
std::vector<BoneData> boneData;
|
||||
|
||||
Renderer* renderer;
|
||||
VulkanWindow* vkWindow;
|
||||
StandaloneWindow* standaloneWindow;
|
||||
};
|
Loading…
Add table
Reference in a new issue