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
|
||||
{
|
||||
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);
|
||||
setVulkanInstance(instance);
|
||||
}
|
||||
|
@ -38,19 +38,89 @@ public:
|
|||
}
|
||||
|
||||
bool event(QEvent *e) {
|
||||
if (e->type() == QEvent::UpdateRequest)
|
||||
switch(e->type()) {
|
||||
case QEvent::UpdateRequest:
|
||||
render();
|
||||
|
||||
if (e->type() == QEvent::Resize) {
|
||||
break;
|
||||
case QEvent::Resize: {
|
||||
QResizeEvent* resizeEvent = (QResizeEvent*)e;
|
||||
auto surface = m_instance->surfaceForWindow(this);
|
||||
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);
|
||||
}
|
||||
|
||||
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_instance->presentQueued(this);
|
||||
requestUpdate();
|
||||
|
@ -62,6 +132,7 @@ private:
|
|||
bool m_initialized = false;
|
||||
Renderer* m_renderer;
|
||||
QVulkanInstance* m_instance;
|
||||
MDLPart* part;
|
||||
};
|
||||
#else
|
||||
#include "standalonewindow.h"
|
||||
|
@ -81,7 +152,7 @@ MDLPart::MDLPart(GameData *data) : data(data) {
|
|||
inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
|
||||
inst->create();
|
||||
|
||||
vkWindow = new VulkanWindow(renderer, inst);
|
||||
vkWindow = new VulkanWindow(this, renderer, inst);
|
||||
vkWindow->setVulkanInstance(inst);
|
||||
|
||||
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) {
|
||||
skeleton = newSkeleton;
|
||||
skeleton = std::make_unique<physis_Skeleton>(newSkeleton);
|
||||
|
||||
firstTimeSkeletonDataCalculated = false;
|
||||
|
||||
Q_EMIT skeletonChanged();
|
||||
}
|
||||
|
@ -276,12 +349,12 @@ void MDLPart::setSkeleton(physis_Skeleton newSkeleton) {
|
|||
void MDLPart::clearSkeleton() {
|
||||
skeleton.reset();
|
||||
|
||||
firstTimeSkeletonDataCalculated = false;
|
||||
|
||||
Q_EMIT skeletonChanged();
|
||||
}
|
||||
|
||||
void MDLPart::reloadRenderer() {
|
||||
qDebug() << "Reloading render models...";
|
||||
|
||||
reloadBoneData();
|
||||
|
||||
#ifndef USE_STANDALONE_WINDOW
|
||||
|
@ -292,14 +365,17 @@ void MDLPart::reloadRenderer() {
|
|||
}
|
||||
|
||||
void MDLPart::reloadBoneData() {
|
||||
if(skeleton.has_value()) {
|
||||
if(skeleton) {
|
||||
// first-time data, TODO split out
|
||||
if (!firstTimeSkeletonDataCalculated) {
|
||||
boneData.resize(skeleton->num_bones);
|
||||
calculateBoneInversePose(*skeleton, *skeleton->root_bone, nullptr);
|
||||
|
||||
for (auto &bone: boneData) {
|
||||
bone.inversePose = glm::inverse(bone.inversePose);
|
||||
}
|
||||
firstTimeSkeletonDataCalculated = true;
|
||||
}
|
||||
|
||||
// update data
|
||||
calculateBone(*skeleton, *skeleton->root_bone, nullptr);
|
||||
|
@ -309,10 +385,11 @@ void MDLPart::reloadBoneData() {
|
|||
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)
|
||||
if (std::string_view{skeleton->bones[k].name} == std::string_view{model.model.affected_bone_names[i]}) {
|
||||
boneMapping[i] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < model.model.num_affected_bones; i++) {
|
||||
model.boneData[i] = boneData[boneMapping[i]].finalTransform;
|
||||
|
@ -381,7 +458,7 @@ void MDLPart::calculateBoneInversePose(physis_Skeleton& skeleton, physis_Bone& b
|
|||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,29 @@ class StandaloneWindow;
|
|||
|
||||
class MDLPart : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MDLPart(GameData* data);
|
||||
|
||||
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:
|
||||
void modelChanged();
|
||||
void skeletonChanged();
|
||||
|
@ -35,9 +53,8 @@ public Q_SLOTS:
|
|||
/// Clears the current skeleton.
|
||||
void clearSkeleton();
|
||||
|
||||
private Q_SLOTS:
|
||||
void reloadRenderer();
|
||||
void reloadBoneData();
|
||||
void reloadRenderer();
|
||||
|
||||
private:
|
||||
RenderMaterial createMaterial(const physis_Material& mat);
|
||||
|
@ -48,7 +65,6 @@ private:
|
|||
GameData* data = nullptr;
|
||||
|
||||
std::vector<RenderModel> models;
|
||||
std::optional<physis_Skeleton> skeleton;
|
||||
|
||||
struct BoneData {
|
||||
glm::mat4 localTransform, finalTransform, inversePose;
|
||||
|
@ -59,4 +75,5 @@ private:
|
|||
Renderer* renderer;
|
||||
VulkanWindow* vkWindow;
|
||||
StandaloneWindow* standaloneWindow;
|
||||
bool firstTimeSkeletonDataCalculated = false;
|
||||
};
|
Loading…
Add table
Reference in a new issue