Kelsidavis-WoWee/include/rendering/camera.hpp
Kelsi 500ad2e711 fix(camera): NaN/range guards on Camera setters
setPosition/setRotation/setAspectRatio/setFov now reject:
- NaN/inf inputs (would produce NaN view/proj matrix → frozen GPU
  on some drivers, garbage frustum culling everywhere)
- aspectRatio <= 0 (degenerate perspective)
- fov <= 0 or >= 180 (degenerate perspective)

Camera is constructed and set from many code paths; pushing the
guards into the setters means none of them need to remember.
2026-05-06 08:57:31 -07:00

81 lines
2.9 KiB
C++

#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <cmath>
namespace wowee {
namespace rendering {
struct Ray {
glm::vec3 origin;
glm::vec3 direction;
};
class Camera {
public:
Camera();
void setPosition(const glm::vec3& pos) {
// Reject NaN/inf — would produce a NaN view matrix and freeze the
// GPU in some drivers, or produce garbage frustum culling.
if (!std::isfinite(pos.x) || !std::isfinite(pos.y) || !std::isfinite(pos.z)) return;
position = pos; updateViewMatrix();
}
void setRotation(float yaw, float pitch) {
if (!std::isfinite(yaw) || !std::isfinite(pitch)) return;
this->yaw = yaw; this->pitch = pitch; updateViewMatrix();
}
void setAspectRatio(float aspect) {
// glm::perspective with aspect <= 0 produces NaN in the projection.
if (!std::isfinite(aspect) || aspect <= 0.0f) return;
aspectRatio = aspect; updateProjectionMatrix();
}
void setFov(float fov) {
// glm::perspective(0) is degenerate; >180 wraps the trig.
if (!std::isfinite(fov) || fov <= 0.0f || fov >= 180.0f) return;
this->fov = fov; updateProjectionMatrix();
}
const glm::vec3& getPosition() const { return position; }
const glm::mat4& getViewMatrix() const { return viewMatrix; }
const glm::mat4& getProjectionMatrix() const { return projectionMatrix; }
const glm::mat4& getUnjitteredProjectionMatrix() const { return unjitteredProjectionMatrix; }
glm::mat4 getViewProjectionMatrix() const { return projectionMatrix * viewMatrix; }
glm::mat4 getUnjitteredViewProjectionMatrix() const { return unjitteredProjectionMatrix * viewMatrix; }
float getAspectRatio() const { return aspectRatio; }
float getFovDegrees() const { return fov; }
float getNearPlane() const { return nearPlane; }
float getFarPlane() const { return farPlane; }
// Sub-pixel jitter for temporal upscaling (FSR 2)
void setJitter(float jx, float jy);
void clearJitter();
glm::vec2 getJitter() const { return jitterOffset; }
glm::vec3 getForward() const;
glm::vec3 getRight() const;
glm::vec3 getUp() const;
Ray screenToWorldRay(float screenX, float screenY, float screenW, float screenH) const;
private:
void updateViewMatrix();
void updateProjectionMatrix();
glm::vec3 position = glm::vec3(0.0f);
float yaw = 0.0f;
float pitch = 0.0f;
float fov = 45.0f;
float aspectRatio = 16.0f / 9.0f;
float nearPlane = 0.5f;
float farPlane = 30000.0f; // Improves depth precision vs extremely large far clip
glm::mat4 viewMatrix = glm::mat4(1.0f);
glm::mat4 projectionMatrix = glm::mat4(1.0f);
glm::mat4 unjitteredProjectionMatrix = glm::mat4(1.0f);
glm::vec2 jitterOffset = glm::vec2(0.0f); // NDC jitter (applied to projection)
};
} // namespace rendering
} // namespace wowee