mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-24 13:07:44 +00:00
Apply racial scaling deforms
This commit is contained in:
parent
fecb015ff2
commit
8daa9f502d
4 changed files with 229 additions and 106 deletions
|
@ -33,7 +33,7 @@ public:
|
|||
explicit GearView(GameData* data);
|
||||
|
||||
/// Returns an inclusive list of races supported by the current gearset.
|
||||
std::vector<Race> supportedRaces() const;
|
||||
std::vector<std::pair<Race, Subrace>> supportedRaces() const;
|
||||
|
||||
/// Returns an inclusive list of genders supported by the current gearset.
|
||||
std::vector<Gender> supportedGenders() const;
|
||||
|
@ -41,31 +41,36 @@ public:
|
|||
/// Returns an inclusive list of LoDs supported by the current gearset.
|
||||
int lodCount() const;
|
||||
|
||||
void exportModel(const QString& fileName);
|
||||
void exportModel(const QString &fileName);
|
||||
|
||||
MDLPart& part() const;
|
||||
MDLPart &part() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
Race currentRace = Race::Hyur;
|
||||
Subrace currentSubrace = Subrace::Midlander;
|
||||
Gender currentGender = Gender::Male;
|
||||
|
||||
Q_SIGNALS:
|
||||
void gearChanged();
|
||||
void modelReloaded();
|
||||
|
||||
void raceChanged();
|
||||
void subraceChanged();
|
||||
void genderChanged();
|
||||
void levelOfDetailChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
public Q_SLOTS:
|
||||
void clear();
|
||||
void addGear(GearInfo& gear);
|
||||
|
||||
void setRace(Race race);
|
||||
void setSubrace(Subrace subrace);
|
||||
void setGender(Gender gender);
|
||||
void setLevelOfDetail(int lod);
|
||||
|
||||
void reloadModel();
|
||||
void reloadRaceDeforms();
|
||||
|
||||
private:
|
||||
Race currentRace = Race::Hyur;
|
||||
Gender currentGender = Gender::Female;
|
||||
private:
|
||||
int currentLod = 0;
|
||||
|
||||
uint32_t maxLod = 0;
|
||||
|
|
|
@ -5,31 +5,40 @@
|
|||
#include <QDebug>
|
||||
|
||||
GearView::GearView(GameData *data) : data(data) {
|
||||
mdlPart = new MDLPart(data);
|
||||
mdlPart = new MDLPart(data);
|
||||
|
||||
mdlPart->setSkeleton(physis_skeleton_from_skel(physis_read_file("c0101b0001.skel")));
|
||||
reloadRaceDeforms();
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(mdlPart);
|
||||
setLayout(layout);
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addWidget(mdlPart);
|
||||
setLayout(layout);
|
||||
|
||||
connect(this, &GearView::gearChanged, this, &GearView::reloadModel);
|
||||
connect(this, &GearView::gearChanged, this, [=] { reloadModel(); });
|
||||
|
||||
connect(this, &GearView::raceChanged, this, &GearView::reloadModel);
|
||||
connect(this, &GearView::genderChanged, this, &GearView::reloadModel);
|
||||
connect(this, &GearView::levelOfDetailChanged, this, &GearView::reloadModel);
|
||||
connect(this, &GearView::raceChanged, this, [=] {
|
||||
reloadRaceDeforms();
|
||||
reloadModel();
|
||||
});
|
||||
connect(this, &GearView::genderChanged, this, [=] {
|
||||
reloadRaceDeforms();
|
||||
reloadModel();
|
||||
});
|
||||
connect(this, &GearView::levelOfDetailChanged, this, &GearView::reloadModel);
|
||||
}
|
||||
|
||||
std::vector<Race> GearView::supportedRaces() const {
|
||||
std::vector<Race> races;
|
||||
for (const auto& gear : gears) {
|
||||
for(auto [race, race_name] : magic_enum::enum_entries<Race>()) {
|
||||
auto equip_path = physis_build_equipment_path(gear.modelInfo.primaryID, race, Subrace::Midlander, currentGender, gear.slot);
|
||||
std::vector<std::pair<Race, Subrace>> GearView::supportedRaces() const {
|
||||
std::vector<std::pair<Race, Subrace>> races;
|
||||
for (const auto &gear : gears) {
|
||||
for (auto [race, race_name] : magic_enum::enum_entries<Race>()) {
|
||||
for (auto subrace : physis_get_supported_subraces(race).subraces) {
|
||||
auto equip_path = physis_build_equipment_path(
|
||||
gear.modelInfo.primaryID, race, subrace, currentGender, gear.slot);
|
||||
|
||||
if(physis_gamedata_exists(data, equip_path))
|
||||
races.push_back(race);
|
||||
}
|
||||
if (physis_gamedata_exists(data, equip_path))
|
||||
races.emplace_back(race, subrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return races;
|
||||
}
|
||||
|
@ -76,9 +85,26 @@ void GearView::setRace(Race race) {
|
|||
}
|
||||
|
||||
currentRace = race;
|
||||
|
||||
auto supportedSubraces = physis_get_supported_subraces(race);
|
||||
if (supportedSubraces.subraces[0] == currentSubrace ||
|
||||
supportedSubraces.subraces[1] == currentSubrace) {
|
||||
} else {
|
||||
setSubrace(supportedSubraces.subraces[0]);
|
||||
}
|
||||
|
||||
Q_EMIT raceChanged();
|
||||
}
|
||||
|
||||
void GearView::setSubrace(Subrace subrace) {
|
||||
if (currentSubrace == subrace) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentSubrace = subrace;
|
||||
Q_EMIT subraceChanged();
|
||||
}
|
||||
|
||||
void GearView::setGender(Gender gender) {
|
||||
if (currentGender == gender) {
|
||||
return;
|
||||
|
@ -103,25 +129,50 @@ void GearView::reloadModel() {
|
|||
maxLod = 0;
|
||||
|
||||
for (const auto& gear : gears) {
|
||||
auto mdl_data = physis_gamedata_extract_file(data, physis_build_equipment_path(gear.modelInfo.primaryID, currentRace, Subrace::Midlander, currentGender, gear.slot));
|
||||
auto mdl_data = physis_gamedata_extract_file(
|
||||
data, physis_build_equipment_path(gear.modelInfo.primaryID,
|
||||
currentRace, currentSubrace,
|
||||
currentGender, gear.slot));
|
||||
|
||||
// attempt to load the next best race
|
||||
// currently hardcoded to hyur midlander
|
||||
Race fallbackRace = currentRace;
|
||||
Subrace fallbackSubrace = currentSubrace;
|
||||
if (mdl_data.size == 0) {
|
||||
mdl_data = physis_gamedata_extract_file(
|
||||
data, physis_build_equipment_path(
|
||||
gear.modelInfo.primaryID, Race::Hyur,
|
||||
Subrace::Midlander, currentGender, gear.slot));
|
||||
fallbackRace = Race::Hyur;
|
||||
fallbackSubrace = Subrace::Midlander;
|
||||
}
|
||||
|
||||
if (mdl_data.size > 0) {
|
||||
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
||||
|
||||
std::vector<physis_Material> materials;
|
||||
for (int i = 0; i < mdl.num_material_names; i++) {
|
||||
const char* material_name = mdl.material_names[i];
|
||||
const char *material_name = mdl.material_names[i];
|
||||
|
||||
//std::string mtrl_path = loadedGear.gearInfo->getMtrlPath(201);
|
||||
std::string mtrl_path = fmt::format("chara/equipment/e{gearId:04d}/material/v{gearVersion:04d}{}", material_name,
|
||||
fmt::arg("gearId", gear.modelInfo.primaryID),
|
||||
// std::string mtrl_path =
|
||||
// loadedGear.gearInfo->getMtrlPath(201);
|
||||
std::string mtrl_path = fmt::format(
|
||||
"chara/equipment/e{gearId:04d}/material/"
|
||||
"v{gearVersion:04d}{}",
|
||||
material_name, fmt::arg("gearId", gear.modelInfo.primaryID),
|
||||
fmt::arg("gearVersion", gear.modelInfo.gearVersion));
|
||||
|
||||
int bodyCode = 1;
|
||||
|
||||
// skin path
|
||||
std::string skinmtrl_path = fmt::format("chara/human/c{raceCode:04d}/obj/body/b{bodyCode:04d}/material/v0001{}", material_name,
|
||||
fmt::arg("raceCode", physis_get_race_code(currentRace, Subrace::Midlander, currentGender)),
|
||||
fmt::arg("bodyCode", bodyCode));
|
||||
std::string skinmtrl_path = fmt::format(
|
||||
"chara/human/c{raceCode:04d}/obj/body/b{bodyCode:04d}/"
|
||||
"material/v0001{}",
|
||||
material_name,
|
||||
fmt::arg("raceCode",
|
||||
physis_get_race_code(fallbackRace, fallbackSubrace,
|
||||
currentGender)),
|
||||
fmt::arg("bodyCode", bodyCode));
|
||||
|
||||
if(physis_gamedata_exists(data, mtrl_path.c_str())) {
|
||||
auto mat = physis_material_parse(physis_gamedata_extract_file(data, mtrl_path.c_str()));
|
||||
|
@ -143,8 +194,33 @@ void GearView::reloadModel() {
|
|||
Q_EMIT modelReloaded();
|
||||
}
|
||||
|
||||
MDLPart &GearView::part() const {
|
||||
return *mdlPart;
|
||||
void GearView::reloadRaceDeforms() {
|
||||
qDebug() << "Loading race deform matrices for "
|
||||
<< magic_enum::enum_name(currentRace).data()
|
||||
<< magic_enum::enum_name(currentSubrace).data()
|
||||
<< magic_enum::enum_name(currentGender).data();
|
||||
const int raceCode =
|
||||
physis_get_race_code(currentRace, currentSubrace, currentGender);
|
||||
qDebug() << "Race code: " << raceCode;
|
||||
|
||||
QString skelName =
|
||||
QString{"c%1b0001.skel"}.arg(raceCode, 4, 10, QLatin1Char{'0'});
|
||||
mdlPart->setSkeleton(physis_skeleton_from_skel(
|
||||
physis_read_file(skelName.toStdString().c_str())));
|
||||
|
||||
// racial deforms don't work on Hyur, not needed? TODO not sure
|
||||
if (currentRace != Race::Hyur) {
|
||||
QString deformName =
|
||||
QString{"c%1_deform.json"}.arg(raceCode, 4, 10, QLatin1Char{'0'});
|
||||
mdlPart->loadRaceDeformMatrices(
|
||||
physis_read_file(deformName.toStdString().c_str()));
|
||||
} else {
|
||||
for (auto &data : mdlPart->boneData) {
|
||||
data.deformRaceMatrix = glm::mat4(1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MDLPart &GearView::part() const { return *mdlPart; }
|
||||
|
||||
#include "moc_gearview.cpp"
|
||||
|
|
|
@ -1,40 +1,43 @@
|
|||
#include "mdlpart.h"
|
||||
#include "glm/gtx/transform.hpp"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QResizeEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <QVulkanInstance>
|
||||
#include <QVulkanWindow>
|
||||
#include <QResizeEvent>
|
||||
#include <QWindow>
|
||||
#include <assimp/Exporter.hpp>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/scene.h>
|
||||
#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
|
||||
{
|
||||
class VulkanWindow : public QWindow {
|
||||
public:
|
||||
VulkanWindow(MDLPart* part, Renderer* renderer, QVulkanInstance* instance) : part(part), m_renderer(renderer), m_instance(instance) {
|
||||
setSurfaceType(VulkanSurface);
|
||||
setVulkanInstance(instance);
|
||||
VulkanWindow(MDLPart *part, Renderer *renderer, QVulkanInstance *instance)
|
||||
: part(part), 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();
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -346,6 +349,32 @@ void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
|||
Q_EMIT skeletonChanged();
|
||||
}
|
||||
|
||||
void MDLPart::loadRaceDeformMatrices(physis_Buffer buffer) {
|
||||
QJsonDocument document = QJsonDocument::fromJson(
|
||||
QByteArray((const char *)buffer.data, buffer.size));
|
||||
for (auto boneObj : document.object()["Data"].toArray()) {
|
||||
QJsonArray matrix = boneObj.toObject()["Matrix"].toArray();
|
||||
QString boneName = boneObj.toObject()["Name"].toString();
|
||||
|
||||
glm::mat4 actualMatrix;
|
||||
int i = 0;
|
||||
for (auto val : matrix) {
|
||||
glm::value_ptr(actualMatrix)[i++] = val.toDouble();
|
||||
}
|
||||
|
||||
for (int i = 0; i < skeleton->num_bones; i++) {
|
||||
if (std::string_view{skeleton->bones[i].name} ==
|
||||
boneName.toStdString()) {
|
||||
auto &data = boneData[i];
|
||||
|
||||
data.deformRaceMatrix = actualMatrix;
|
||||
}
|
||||
}
|
||||
|
||||
firstTimeSkeletonDataCalculated = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MDLPart::clearSkeleton() {
|
||||
skeleton.reset();
|
||||
|
||||
|
@ -366,12 +395,14 @@ void MDLPart::reloadRenderer() {
|
|||
|
||||
void MDLPart::reloadBoneData() {
|
||||
if(skeleton) {
|
||||
// first-time data, TODO split out
|
||||
if (!firstTimeSkeletonDataCalculated) {
|
||||
boneData.resize(skeleton->num_bones);
|
||||
if (boneData.empty()) {
|
||||
boneData.resize(skeleton->num_bones);
|
||||
}
|
||||
|
||||
calculateBoneInversePose(*skeleton, *skeleton->root_bone, nullptr);
|
||||
|
||||
for (auto &bone: boneData) {
|
||||
for (auto &bone : boneData) {
|
||||
bone.inversePose = glm::inverse(bone.inversePose);
|
||||
}
|
||||
firstTimeSkeletonDataCalculated = true;
|
||||
|
@ -450,7 +481,7 @@ RenderMaterial MDLPart::createMaterial(const physis_Material &material) {
|
|||
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);
|
||||
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]));
|
||||
|
@ -465,18 +496,28 @@ void MDLPart::calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& b
|
|||
}
|
||||
|
||||
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;
|
||||
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]));
|
||||
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;
|
||||
boneData[bone.index].finalTransform =
|
||||
boneData[bone.index].localTransform *
|
||||
boneData[bone.index].deformRaceMatrix *
|
||||
boneData[bone.index].inversePose;
|
||||
|
||||
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}) {
|
||||
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}) {
|
||||
calculateBone(skeleton, skeleton.bones[i], &bone);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,66 +12,67 @@ class VulkanWindow;
|
|||
class StandaloneWindow;
|
||||
|
||||
class MDLPart : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MDLPart(GameData* data);
|
||||
explicit MDLPart(GameData *data);
|
||||
|
||||
void exportModel(const QString& fileName);
|
||||
void exportModel(const QString &fileName);
|
||||
|
||||
int lastX = -1;
|
||||
int lastY = -1;
|
||||
int lastX = -1;
|
||||
int lastY = -1;
|
||||
|
||||
enum class CameraMode {
|
||||
None,
|
||||
Orbit,
|
||||
Move
|
||||
};
|
||||
enum class CameraMode { None, Orbit, Move };
|
||||
|
||||
CameraMode cameraMode = CameraMode::None;
|
||||
float pitch = 0.0f;
|
||||
float yaw = 0.0f;
|
||||
float cameraDistance = 5.0f;
|
||||
glm::vec3 position {0, 0, 0};
|
||||
CameraMode cameraMode = CameraMode::None;
|
||||
float pitch = 0.0f;
|
||||
float yaw = 0.0f;
|
||||
float cameraDistance = 5.0f;
|
||||
glm::vec3 position{0, 0, 0};
|
||||
|
||||
std::unique_ptr<physis_Skeleton> skeleton;
|
||||
std::unique_ptr<physis_Skeleton> skeleton;
|
||||
|
||||
struct BoneData {
|
||||
glm::mat4 localTransform, finalTransform, inversePose;
|
||||
glm::mat4 deformRaceMatrix{1.0f};
|
||||
};
|
||||
|
||||
std::vector<BoneData> boneData;
|
||||
|
||||
Q_SIGNALS:
|
||||
void modelChanged();
|
||||
void skeletonChanged();
|
||||
void modelChanged();
|
||||
void skeletonChanged();
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Clears all stored MDLs.
|
||||
void clear();
|
||||
/// 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);
|
||||
/// 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);
|
||||
/// Sets the skeleton any skinned MDLs should bind to.
|
||||
void setSkeleton(physis_Skeleton skeleton);
|
||||
|
||||
/// Clears the current skeleton.
|
||||
void clearSkeleton();
|
||||
/// Sets the race deform matrices
|
||||
void loadRaceDeformMatrices(physis_Buffer buffer);
|
||||
|
||||
void reloadBoneData();
|
||||
void reloadRenderer();
|
||||
/// Clears the current skeleton.
|
||||
void clearSkeleton();
|
||||
|
||||
void reloadBoneData();
|
||||
void reloadRenderer();
|
||||
|
||||
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);
|
||||
|
||||
GameData* data = nullptr;
|
||||
|
||||
std::vector<RenderModel> models;
|
||||
|
||||
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