Summary
Camera::setFront() cannot change the camera's orientation. The vector it writes to front is unconditionally overwritten by updateView() on the very next statement, so the function is input-independent — every call leaves the camera exactly as it was.
Details
src/limitless/camera.cpp:
void Camera::setFront(const glm::vec3& _front) noexcept {
front = _front;
updateView();
}
updateView() recomputes front from yaw/pitch without reading the previous value:
void Camera::updateView() noexcept {
// euler angles
front.x = glm::cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = glm::sin(glm::radians(pitch));
front.z = glm::sin(glm::radians(yaw)) * cos(glm::radians(pitch));
front = glm::normalize(front);
...
yaw and pitch are only ever written by the constructor and by mouseMove() (which applies relative deltas), so there is currently no way to set an absolute camera orientation from a direction vector.
Origin
This looks like a regression rather than intent. setFront() worked when it was introduced — the original updateView() was quaternion-based and used front as an input:
front = glm::normalize(pitch_quaternion * yaw_quaternion * front);
Commit 7f7229a ("cube demo") replaced that with the Euler formulation above, moving front from the right-hand side to the left. The assignment in setFront() became a dead store at that point, silently and without a compiler warning.
Impact
front feeds the view matrix (view = glm::lookAt(position, position + front, up)), which propagates to:
- the scene UBO (
view, view_inverse, VP) in src/limitless/renderer/scene_data.cpp
- frustum culling via
Frustum::fromCamera()
- cascade shadow split frustums, which also read
camera.getFront() directly
CameraMovement::Forward / Backward translation
The in-tree caller is samples/gltf_viewer/gltf_viewer.cpp:110, which calls camera.setFront({1.f, 0.f, 0.f}) to look down +X. It silently keeps the constructor defaults (pitch = -60, yaw = 270) instead.
Reproduction
Headless — Camera's constructor and updateView() are pure glm, so no GL context is required:
#include <limitless/camera.hpp>
#include <cstdio>
int main() {
Limitless::Camera camera{{1920, 1080}};
const auto before = camera.getFront();
camera.setFront({1.0f, 0.0f, 0.0f});
const auto after = camera.getFront();
printf("before: (%.4f, %.4f, %.4f)\n", before.x, before.y, before.z);
printf("after: (%.4f, %.4f, %.4f)\n", after.x, after.y, after.z);
}
Output:
before: (0.0000, -0.8660, -0.5000)
after: (0.0000, -0.8660, -0.5000)
after should be (1.0000, 0.0000, 0.0000). Every input produces this same constructor-default result, including {0, 1, 0} and {0, -1, 0}.
Suggested fix
Set yaw/pitch from the direction so that updateView() reproduces it, inverting the same Euler convention updateView() already uses:
void Camera::setFront(const glm::vec3& _front) noexcept {
const auto direction = glm::normalize(_front);
pitch = glm::degrees(glm::asin(direction.y));
yaw = glm::degrees(glm::atan(direction.z, direction.x));
updateView();
}
This round-trips exactly for arbitrary directions, and the resulting basis stays finite, orthogonal and unit-length in the straight-up/straight-down cases (glm::cos(glm::radians(90.0f)) is not exactly zero in float, so cross(front, world_up) does not degenerate).
I have this fix working locally and am happy to open a PR.
Summary
Camera::setFront()cannot change the camera's orientation. The vector it writes tofrontis unconditionally overwritten byupdateView()on the very next statement, so the function is input-independent — every call leaves the camera exactly as it was.Details
src/limitless/camera.cpp:updateView()recomputesfrontfromyaw/pitchwithout reading the previous value:yawandpitchare only ever written by the constructor and bymouseMove()(which applies relative deltas), so there is currently no way to set an absolute camera orientation from a direction vector.Origin
This looks like a regression rather than intent.
setFront()worked when it was introduced — the originalupdateView()was quaternion-based and usedfrontas an input:Commit 7f7229a ("cube demo") replaced that with the Euler formulation above, moving
frontfrom the right-hand side to the left. The assignment insetFront()became a dead store at that point, silently and without a compiler warning.Impact
frontfeeds the view matrix (view = glm::lookAt(position, position + front, up)), which propagates to:view,view_inverse,VP) insrc/limitless/renderer/scene_data.cppFrustum::fromCamera()camera.getFront()directlyCameraMovement::Forward/BackwardtranslationThe in-tree caller is
samples/gltf_viewer/gltf_viewer.cpp:110, which callscamera.setFront({1.f, 0.f, 0.f})to look down +X. It silently keeps the constructor defaults (pitch = -60,yaw = 270) instead.Reproduction
Headless —
Camera's constructor andupdateView()are pure glm, so no GL context is required:Output:
aftershould be(1.0000, 0.0000, 0.0000). Every input produces this same constructor-default result, including{0, 1, 0}and{0, -1, 0}.Suggested fix
Set
yaw/pitchfrom the direction so thatupdateView()reproduces it, inverting the same Euler conventionupdateView()already uses:This round-trips exactly for arbitrary directions, and the resulting basis stays finite, orthogonal and unit-length in the straight-up/straight-down cases (
glm::cos(glm::radians(90.0f))is not exactly zero in float, socross(front, world_up)does not degenerate).I have this fix working locally and am happy to open a PR.