Repair an externally-sourced CameraState (a deserialized layout, or a state adopted across a model switch) so it can never drive a degenerate projection: non-finite fields fall back to the default, then ranges are clamped (radius/ ortho_scale > 0, fov_y off 0/π, elevation off the poles). One guard for every untrusted entry point — cameraStateFromJson and each adoptState().
| 75 | // ortho_scale > 0, fov_y off 0/π, elevation off the poles). One guard for every |
| 76 | // untrusted entry point — cameraStateFromJson and each adoptState(). |
| 77 | CameraState sanitizeCameraState(CameraState state) { |
| 78 | const CameraState defaults; |
| 79 | const auto finite_or = [](float value, float fallback) { return std::isfinite(value) ? value : fallback; }; |
| 80 | state.focal = glm::vec3{ |
| 81 | finite_or(state.focal.x, defaults.focal.x), finite_or(state.focal.y, defaults.focal.y), |
| 82 | finite_or(state.focal.z, defaults.focal.z)}; |
| 83 | state.azimuth = finite_or(state.azimuth, defaults.azimuth); |
| 84 | state.elevation = std::clamp(finite_or(state.elevation, defaults.elevation), -kPolarLimit, kPolarLimit); |
| 85 | state.radius = std::max(finite_or(state.radius, defaults.radius), 1e-3f); |
| 86 | state.ortho_scale = std::max(finite_or(state.ortho_scale, defaults.ortho_scale), 1e-3f); |
| 87 | state.fov_y = std::clamp(finite_or(state.fov_y, defaults.fov_y), glm::radians(1.0f), glm::radians(179.0f)); |
| 88 | return state; |
| 89 | } |
| 90 | } // namespace |
| 91 | |
| 92 | glm::mat4 OrbitCamera::viewMatrix() const { |
no outgoing calls
no test coverage detected