diff --git a/src/core/types.h b/src/core/types.h index 29911cc..2618041 100644 --- a/src/core/types.h +++ b/src/core/types.h @@ -24,6 +24,37 @@ #include #include +// Helper utilities for TRS (translation-rotation-scale) transforms using quaternions. +inline glm::mat4 make_trs_matrix(const glm::vec3 &translation, + const glm::quat &rotation, + const glm::vec3 &scale) +{ + glm::mat4 tm = glm::translate(glm::mat4(1.0f), translation); + glm::mat4 rm = glm::mat4_cast(rotation); + glm::mat4 sm = glm::scale(glm::mat4(1.0f), scale); + return tm * rm * sm; +} + +inline void decompose_trs_matrix(const glm::mat4 &m, + glm::vec3 &out_translation, + glm::quat &out_rotation, + glm::vec3 &out_scale) +{ + out_translation = glm::vec3(m[3]); + + glm::vec3 col0 = glm::vec3(m[0]); + glm::vec3 col1 = glm::vec3(m[1]); + glm::vec3 col2 = glm::vec3(m[2]); + + out_scale = glm::vec3(glm::length(col0), glm::length(col1), glm::length(col2)); + if (out_scale.x != 0.0f) col0 /= out_scale.x; + if (out_scale.y != 0.0f) col1 /= out_scale.y; + if (out_scale.z != 0.0f) col2 /= out_scale.z; + + glm::mat3 rot_mat(col0, col1, col2); + out_rotation = glm::quat_cast(rot_mat); +} + #define VK_CHECK(x) \ do { \ @@ -169,10 +200,7 @@ struct Node : public IRenderable { void updateLocalFromTRS() { - glm::mat4 tm = glm::translate(glm::mat4(1.0f), translation); - glm::mat4 rm = glm::mat4_cast(rotation); - glm::mat4 sm = glm::scale(glm::mat4(1.0f), scale); - localTransform = tm * rm * sm; + localTransform = make_trs_matrix(translation, rotation, scale); } void setTRS(const glm::vec3 &t, const glm::quat &r, const glm::vec3 &s) diff --git a/src/scene/camera.cpp b/src/scene/camera.cpp index 04c7b8d..a801b4d 100644 --- a/src/scene/camera.cpp +++ b/src/scene/camera.cpp @@ -38,10 +38,21 @@ void Camera::processSDLEvent(SDL_Event& e) } if (e.type == SDL_MOUSEMOTION && rmbDown) { - // Mouse right (xrel > 0) turns view right with -Z-forward - yaw += (float)e.motion.xrel * lookSensitivity; // axis = +Y - // Mouse up (yrel < 0) looks up with -Z-forward - pitch -= (float)e.motion.yrel * lookSensitivity; + // Convert mouse motion to incremental yaw/pitch angles. + float dx = static_cast(e.motion.xrel) * lookSensitivity; + float dy = static_cast(e.motion.yrel) * lookSensitivity; + + // Mouse right (xrel > 0) turns view right with -Z-forward: yaw around +Y. + glm::quat yawRotation = glm::angleAxis(dx, glm::vec3 { 0.f, 1.f, 0.f }); + + // Mouse up (yrel < 0) looks up with -Z-forward: negative dy. + float pitchDelta = -dy; + // Pitch around the camera's local X (right) axis in world space. + glm::vec3 right = glm::rotate(orientation, glm::vec3 { 1.f, 0.f, 0.f }); + glm::quat pitchRotation = glm::angleAxis(pitchDelta, glm::vec3(right)); + + // Apply yaw, then pitch, to the current orientation. + orientation = glm::normalize(pitchRotation * yawRotation * orientation); } if (e.type == SDL_MOUSEWHEEL) { @@ -72,12 +83,6 @@ glm::mat4 Camera::getViewMatrix() glm::mat4 Camera::getRotationMatrix() { - // fairly typical FPS style camera. we join the pitch and yaw rotations into - // the final rotation matrix - - glm::quat pitchRotation = glm::angleAxis(pitch, glm::vec3 { 1.f, 0.f, 0.f }); - // Yaw around +Y keeps mouse-right -> turn-right with -Z-forward - glm::quat yawRotation = glm::angleAxis(yaw, glm::vec3 { 0.f, 1.f, 0.f }); - - return glm::toMat4(yawRotation) * glm::toMat4(pitchRotation); + // Use the stored quaternion orientation directly. + return glm::toMat4(orientation); } diff --git a/src/scene/camera.h b/src/scene/camera.h index a102572..dabed92 100644 --- a/src/scene/camera.h +++ b/src/scene/camera.h @@ -9,10 +9,8 @@ class Camera { public: glm::vec3 velocity; glm::vec3 position; - // vertical rotation - float pitch { 0.f }; - // horizontal rotation - float yaw { 0.f }; + // Orientation stored as a quaternion (local -> world). + glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f }; // Movement/look tuning float moveSpeed { 0.03f }; diff --git a/src/scene/vk_loader.cpp b/src/scene/vk_loader.cpp index 4cfc15e..abd0634 100644 --- a/src/scene/vk_loader.cpp +++ b/src/scene/vk_loader.cpp @@ -671,18 +671,10 @@ std::optional > loadGltf(VulkanEngine *engine, std:: glm::mat4 m(1.0f); memcpy(&m, matrix.data(), sizeof(matrix)); - glm::vec3 t = glm::vec3(m[3]); - glm::vec3 col0 = glm::vec3(m[0]); - glm::vec3 col1 = glm::vec3(m[1]); - glm::vec3 col2 = glm::vec3(m[2]); - - glm::vec3 s(glm::length(col0), glm::length(col1), glm::length(col2)); - if (s.x != 0.0f) col0 /= s.x; - if (s.y != 0.0f) col1 /= s.y; - if (s.z != 0.0f) col2 /= s.z; - glm::mat3 rotMat(col0, col1, col2); - glm::quat r = glm::quat_cast(rotMat); - + glm::vec3 t; + glm::quat r; + glm::vec3 s; + decompose_trs_matrix(m, t, r, s); newNode->setTRS(t, r, s); }, [&](fastgltf::Node::TRS transform) { diff --git a/src/scene/vk_scene.cpp b/src/scene/vk_scene.cpp index a247b10..4595c79 100644 --- a/src/scene/vk_scene.cpp +++ b/src/scene/vk_scene.cpp @@ -73,8 +73,7 @@ void SceneManager::init(EngineContext *context) mainCamera.velocity = glm::vec3(0.f); mainCamera.position = glm::vec3(30.f, -00.f, 85.f); - mainCamera.pitch = 0; - mainCamera.yaw = 0; + mainCamera.orientation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); sceneData.ambientColor = glm::vec4(0.1f, 0.1f, 0.1f, 1.0f); sceneData.sunlightDirection = glm::vec4(-0.2f, -1.0f, -0.3f, 1.0f); @@ -204,7 +203,8 @@ void SceneManager::update_scene() { mainDrawContext.gltfNodeLocalOverrides = nullptr; } - inst.scene->Draw(inst.transform, mainDrawContext); + glm::mat4 instanceTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); + inst.scene->Draw(instanceTransform, mainDrawContext); mainDrawContext.gltfNodeLocalOverrides = nullptr; tagOwner(RenderObject::OwnerType::GLTFInstance, kv.first, opaqueStart, transpStart); } @@ -216,6 +216,7 @@ void SceneManager::update_scene() { const MeshInstance &inst = kv.second; if (!inst.mesh || inst.mesh->surfaces.empty()) continue; + glm::mat4 instanceTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); uint32_t surfaceIndex = 0; for (const auto &surf: inst.mesh->surfaces) { @@ -231,7 +232,7 @@ void SceneManager::update_scene() { obj.bounds.type = *inst.boundsTypeOverride; } - obj.transform = inst.transform; + obj.transform = instanceTransform; obj.sourceMesh = inst.mesh.get(); obj.surfaceIndex = surfaceIndex++; obj.objectID = mainDrawContext.nextID++; @@ -429,7 +430,7 @@ void SceneManager::addMeshInstance(const std::string &name, std::shared_ptrsecond.transform; + const MeshInstance &inst = it->second; + outTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); return true; } @@ -452,7 +454,8 @@ bool SceneManager::setMeshInstanceTransform(const std::string &name, const glm:: { return false; } - it->second.transform = transform; + MeshInstance &inst = it->second; + decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); return true; } @@ -475,7 +478,7 @@ void SceneManager::addGLTFInstance(const std::string &name, std::shared_ptrdebugName.empty() ? "" : scene->debugName.c_str()); GLTFInstance inst{}; inst.scene = std::move(scene); - inst.transform = transform; + decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); if (inst.scene && !inst.scene->animations.empty()) { inst.animation.activeAnimation = 0; @@ -519,7 +522,8 @@ bool SceneManager::getGLTFInstanceTransform(const std::string &name, glm::mat4 & { return false; } - outTransform = it->second.transform; + const GLTFInstance &inst = it->second; + outTransform = make_trs_matrix(inst.translation, inst.rotation, inst.scale); return true; } @@ -527,7 +531,8 @@ bool SceneManager::setGLTFInstanceTransform(const std::string &name, const glm:: { auto it = dynamicGLTFInstances.find(name); if (it == dynamicGLTFInstances.end()) return false; - it->second.transform = transform; + GLTFInstance &inst = it->second; + decompose_trs_matrix(transform, inst.translation, inst.rotation, inst.scale); return true; } diff --git a/src/scene/vk_scene.h b/src/scene/vk_scene.h index 4b4a325..9dc7fda 100644 --- a/src/scene/vk_scene.h +++ b/src/scene/vk_scene.h @@ -91,7 +91,9 @@ public: struct MeshInstance { std::shared_ptr mesh; - glm::mat4 transform{1.f}; + glm::vec3 translation{0.0f, 0.0f, 0.0f}; + glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f}; + glm::vec3 scale{1.0f, 1.0f, 1.0f}; std::optional boundsTypeOverride; }; @@ -107,7 +109,9 @@ public: struct GLTFInstance { std::shared_ptr scene; - glm::mat4 transform{1.f}; + glm::vec3 translation{0.0f, 0.0f, 0.0f}; + glm::quat rotation{1.0f, 0.0f, 0.0f, 0.0f}; + glm::vec3 scale{1.0f, 1.0f, 1.0f}; LoadedGLTF::AnimationState animation; // Per-instance local-space pose offsets for nodes in this glTF scene. // The offset matrix is post-multiplied onto the node's localTransform.