mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-25 05:17:44 +00:00
Improve bone data calculation in MDLPart, and better camera controls
This commit is contained in:
parent
673a80e781
commit
926853c701
2 changed files with 113 additions and 19 deletions
|
@ -18,7 +18,7 @@
|
||||||
class VulkanWindow : public QWindow
|
class VulkanWindow : public QWindow
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VulkanWindow(Renderer* renderer, QVulkanInstance* instance) : m_renderer(renderer), m_instance(instance) {
|
VulkanWindow(MDLPart* part, Renderer* renderer, QVulkanInstance* instance) : part(part), m_renderer(renderer), m_instance(instance) {
|
||||||
setSurfaceType(VulkanSurface);
|
setSurfaceType(VulkanSurface);
|
||||||
setVulkanInstance(instance);
|
setVulkanInstance(instance);
|
||||||
}
|
}
|
||||||
|
@ -38,19 +38,89 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool event(QEvent *e) {
|
bool event(QEvent *e) {
|
||||||
if (e->type() == QEvent::UpdateRequest)
|
switch(e->type()) {
|
||||||
|
case QEvent::UpdateRequest:
|
||||||
render();
|
render();
|
||||||
|
break;
|
||||||
if (e->type() == QEvent::Resize) {
|
case QEvent::Resize: {
|
||||||
QResizeEvent* resizeEvent = (QResizeEvent*)e;
|
QResizeEvent* resizeEvent = (QResizeEvent*)e;
|
||||||
auto surface = m_instance->surfaceForWindow(this);
|
auto surface = m_instance->surfaceForWindow(this);
|
||||||
m_renderer->resize(surface, resizeEvent->size().width(), resizeEvent->size().height());
|
m_renderer->resize(surface, resizeEvent->size().width(), resizeEvent->size().height());
|
||||||
|
} break;
|
||||||
|
case QEvent::MouseButtonPress: {
|
||||||
|
auto mouseEvent = dynamic_cast<QMouseEvent*>(e);
|
||||||
|
|
||||||
|
if (mouseEvent->button() == Qt::MouseButton::LeftButton) {
|
||||||
|
part->lastX = mouseEvent->x();
|
||||||
|
part->lastY = mouseEvent->y();
|
||||||
|
part->cameraMode = MDLPart::CameraMode::Orbit;
|
||||||
|
|
||||||
|
setKeyboardGrabEnabled(true);
|
||||||
|
setMouseGrabEnabled(true);
|
||||||
|
} else if (mouseEvent->button() == Qt::MouseButton::RightButton) {
|
||||||
|
part->lastX = mouseEvent->x();
|
||||||
|
part->lastY = mouseEvent->y();
|
||||||
|
part->cameraMode = MDLPart::CameraMode::Move;
|
||||||
|
|
||||||
|
setKeyboardGrabEnabled(true);
|
||||||
|
setMouseGrabEnabled(true);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case QEvent::MouseButtonRelease: {
|
||||||
|
part->cameraMode = MDLPart::CameraMode::None;
|
||||||
|
|
||||||
|
setKeyboardGrabEnabled(false);
|
||||||
|
setMouseGrabEnabled(false);
|
||||||
|
} break;
|
||||||
|
case QEvent::MouseMove: {
|
||||||
|
auto mouseEvent = dynamic_cast<QMouseEvent*>(e);
|
||||||
|
if (part->cameraMode != MDLPart::CameraMode::None) {
|
||||||
|
const int deltaX = mouseEvent->x() - part->lastX;
|
||||||
|
const int deltaY = mouseEvent->y() - part->lastY;
|
||||||
|
|
||||||
|
if (part->cameraMode == MDLPart::CameraMode::Orbit) {
|
||||||
|
part->yaw += deltaX * 0.01f; // TODO: remove these magic numbers
|
||||||
|
part->pitch += deltaY * 0.01f;
|
||||||
|
} else {
|
||||||
|
glm::vec3 position(
|
||||||
|
part->cameraDistance * sin(part->yaw),
|
||||||
|
part->cameraDistance * part->pitch,
|
||||||
|
part->cameraDistance * cos(part->yaw));
|
||||||
|
|
||||||
|
glm::quat rot = glm::quatLookAt((part->position + position) - part->position, {0, 1, 0});
|
||||||
|
|
||||||
|
glm::vec3 up, right;
|
||||||
|
up = rot * glm::vec3{0, 1, 0};
|
||||||
|
right = rot * glm::vec3{1, 0, 0};
|
||||||
|
|
||||||
|
part->position += up * (float)deltaY * 0.01f;
|
||||||
|
part->position += right * (float)deltaX * 0.01f;
|
||||||
|
}
|
||||||
|
|
||||||
|
part->lastX = mouseEvent->x();
|
||||||
|
part->lastY = mouseEvent->y();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case QEvent::Wheel:
|
||||||
|
{
|
||||||
|
auto scrollEvent = dynamic_cast<QWheelEvent*>(e);
|
||||||
|
|
||||||
|
part->cameraDistance -= scrollEvent->angleDelta().y() / 120.0f; // FIXME: why 120?
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QWindow::event(e);
|
return QWindow::event(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
|
glm::vec3 position(
|
||||||
|
part->cameraDistance * sin(part->yaw),
|
||||||
|
part->cameraDistance * part->pitch,
|
||||||
|
part->cameraDistance * cos(part->yaw));
|
||||||
|
|
||||||
|
m_renderer->view = glm::lookAt(part->position + position, part->position, glm::vec3(0, -1, 0));
|
||||||
|
|
||||||
m_renderer->render(models);
|
m_renderer->render(models);
|
||||||
m_instance->presentQueued(this);
|
m_instance->presentQueued(this);
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
@ -62,6 +132,7 @@ private:
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
Renderer* m_renderer;
|
Renderer* m_renderer;
|
||||||
QVulkanInstance* m_instance;
|
QVulkanInstance* m_instance;
|
||||||
|
MDLPart* part;
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
#include "standalonewindow.h"
|
#include "standalonewindow.h"
|
||||||
|
@ -81,7 +152,7 @@ MDLPart::MDLPart(GameData *data) : data(data) {
|
||||||
inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
|
inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
|
||||||
inst->create();
|
inst->create();
|
||||||
|
|
||||||
vkWindow = new VulkanWindow(renderer, inst);
|
vkWindow = new VulkanWindow(this, renderer, inst);
|
||||||
vkWindow->setVulkanInstance(inst);
|
vkWindow->setVulkanInstance(inst);
|
||||||
|
|
||||||
auto widget = QWidget::createWindowContainer(vkWindow);
|
auto widget = QWidget::createWindowContainer(vkWindow);
|
||||||
|
@ -268,7 +339,9 @@ void MDLPart::addModel(physis_MDL mdl, std::vector<physis_Material> materials, i
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
||||||
skeleton = newSkeleton;
|
skeleton = std::make_unique<physis_Skeleton>(newSkeleton);
|
||||||
|
|
||||||
|
firstTimeSkeletonDataCalculated = false;
|
||||||
|
|
||||||
Q_EMIT skeletonChanged();
|
Q_EMIT skeletonChanged();
|
||||||
}
|
}
|
||||||
|
@ -276,12 +349,12 @@ void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
||||||
void MDLPart::clearSkeleton() {
|
void MDLPart::clearSkeleton() {
|
||||||
skeleton.reset();
|
skeleton.reset();
|
||||||
|
|
||||||
|
firstTimeSkeletonDataCalculated = false;
|
||||||
|
|
||||||
Q_EMIT skeletonChanged();
|
Q_EMIT skeletonChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDLPart::reloadRenderer() {
|
void MDLPart::reloadRenderer() {
|
||||||
qDebug() << "Reloading render models...";
|
|
||||||
|
|
||||||
reloadBoneData();
|
reloadBoneData();
|
||||||
|
|
||||||
#ifndef USE_STANDALONE_WINDOW
|
#ifndef USE_STANDALONE_WINDOW
|
||||||
|
@ -292,13 +365,16 @@ void MDLPart::reloadRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MDLPart::reloadBoneData() {
|
void MDLPart::reloadBoneData() {
|
||||||
if(skeleton.has_value()) {
|
if(skeleton) {
|
||||||
// first-time data, TODO split out
|
// first-time data, TODO split out
|
||||||
boneData.resize(skeleton->num_bones);
|
if (!firstTimeSkeletonDataCalculated) {
|
||||||
calculateBoneInversePose(*skeleton, *skeleton->root_bone, nullptr);
|
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);
|
bone.inversePose = glm::inverse(bone.inversePose);
|
||||||
|
}
|
||||||
|
firstTimeSkeletonDataCalculated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update data
|
// update data
|
||||||
|
@ -309,8 +385,9 @@ void MDLPart::reloadBoneData() {
|
||||||
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 (strcmp(skeleton->bones[k].name, model.model.affected_bone_names[i]) == 0)
|
if (std::string_view{skeleton->bones[k].name} == std::string_view{model.model.affected_bone_names[i]}) {
|
||||||
boneMapping[i] = k;
|
boneMapping[i] = k;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +458,7 @@ 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 && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0) {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +476,7 @@ void MDLPart::calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const
|
||||||
boneData[bone.index].finalTransform = boneData[bone.index].localTransform * boneData[bone.index].inversePose;
|
boneData[bone.index].finalTransform = boneData[bone.index].localTransform * 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 && strcmp(skeleton.bones[i].parent_bone->name, bone.name) == 0) {
|
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);
|
calculateBone(skeleton, skeleton.bones[i], &bone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,29 @@ class StandaloneWindow;
|
||||||
|
|
||||||
class MDLPart : public QWidget {
|
class MDLPart : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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;
|
||||||
|
|
||||||
|
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};
|
||||||
|
|
||||||
|
std::unique_ptr<physis_Skeleton> skeleton;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void modelChanged();
|
void modelChanged();
|
||||||
void skeletonChanged();
|
void skeletonChanged();
|
||||||
|
@ -35,9 +53,8 @@ public Q_SLOTS:
|
||||||
/// Clears the current skeleton.
|
/// Clears the current skeleton.
|
||||||
void clearSkeleton();
|
void clearSkeleton();
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void reloadRenderer();
|
|
||||||
void reloadBoneData();
|
void reloadBoneData();
|
||||||
|
void reloadRenderer();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderMaterial createMaterial(const physis_Material& mat);
|
RenderMaterial createMaterial(const physis_Material& mat);
|
||||||
|
@ -48,7 +65,6 @@ private:
|
||||||
GameData* data = nullptr;
|
GameData* data = nullptr;
|
||||||
|
|
||||||
std::vector<RenderModel> models;
|
std::vector<RenderModel> models;
|
||||||
std::optional<physis_Skeleton> skeleton;
|
|
||||||
|
|
||||||
struct BoneData {
|
struct BoneData {
|
||||||
glm::mat4 localTransform, finalTransform, inversePose;
|
glm::mat4 localTransform, finalTransform, inversePose;
|
||||||
|
@ -59,4 +75,5 @@ private:
|
||||||
Renderer* renderer;
|
Renderer* renderer;
|
||||||
VulkanWindow* vkWindow;
|
VulkanWindow* vkWindow;
|
||||||
StandaloneWindow* standaloneWindow;
|
StandaloneWindow* standaloneWindow;
|
||||||
|
bool firstTimeSkeletonDataCalculated = false;
|
||||||
};
|
};
|
Loading…
Add table
Reference in a new issue