19 return degrees * std::numbers::pi_v<float> / 180.0f;
23 return {left.
y * right.
z - left.
z * right.
y, left.
z * right.
x - left.
x * right.
z,
24 left.
x * right.
y - left.
y * right.
x};
28 const float length = std::sqrt(vector.
x * vector.
x + vector.
y * vector.
y + vector.
z * vector.
z);
29 return length > 0.0f ? vector * (1.0f / length) : vector;
48 const Vector3 forward{std::cos(pitch) * std::sin(yaw), std::sin(pitch), std::cos(pitch) * std::cos(yaw)};
49 const Vector3 worldUp{0.0f, -1.0f, 0.0f};
52 return {.forward = forward, .right = right, .up =
Cross(right, forward)};
58 return {left.
x + right.
x, left.
y + right.
y, left.
z + right.
z};
62 return {left.
x - right.
x, left.
y - right.
y, left.
z - right.
z};
66 return {vector.
x * scale, vector.
y * scale, vector.
z * scale};
73 m_state.yawDegrees -= deltaXPixels * ORBIT_DEGREES_PER_PIXEL;
75 m_state.pitchDegrees = std::clamp(
m_state.pitchDegrees + deltaYPixels * ORBIT_DEGREES_PER_PIXEL,
76 -MAX_PITCH_DEGREES, MAX_PITCH_DEGREES);
80 void OrbitCamera::Pan(
float deltaXPixels,
float deltaYPixels, uint32_t viewportHeight) {
81 if (viewportHeight == 0)
84 const Axes axes = CameraAxes(
m_state);
85 const float cellsPerPixel = 2.0f * HalfHeightAtTarget(
m_state.distance) /
static_cast<float>(viewportHeight);
86 m_state.target =
m_state.target - axes.right * (deltaXPixels * cellsPerPixel) +
87 axes.up * (deltaYPixels * cellsPerPixel);
91 m_state.distance *= std::pow(ZOOM_PER_STEP,
static_cast<float>(wheelSteps));
103 const Axes axes = CameraAxes(
m_state);
104 const float aspect = height > 0 ?
static_cast<float>(width) /
static_cast<float>(height) : 1.0f;
107 const float halfHeight = HalfHeightAtTarget(
m_state.distance);
109 return {.position =
m_state.target - axes.forward * ORTHOGRAPHIC_BACK_OFF,
110 .forward = axes.forward,
111 .right = axes.right * (halfHeight * aspect),
112 .up = axes.up * halfHeight,
113 .orthographic =
true};
118 return {.position =
m_state.target - axes.forward *
m_state.distance,
119 .forward = axes.forward,
120 .right = axes.right * (halfHeight * aspect),
121 .up = axes.up * halfHeight,
122 .orthographic =
false};
126 const float u = width > 0 ? 2.0f * pixelX /
static_cast<float>(width) - 1.0f : 0.0f;
127 const float v = height > 0 ? 1.0f - 2.0f * pixelY /
static_cast<float>(height) : 0.0f;
130 return {.origin = basis.
position + offset, .direction = basis.
forward};
132 return {.origin = basis.
position, .direction = basis.
forward + offset};
137 if (axis > 2 || plane >= gridSize)
141 if (std::abs(along) < PARALLEL_EPSILON)
144 const float distance = (
static_cast<float>(plane) + 0.5f - ray.
origin[axis]) / along;
151 std::array<uint32_t, 3> cell{};
152 for (uint32_t index = 0; index < 3; ++index) {
158 const float value = std::floor(hit[index]);
160 if (!(value >= 0.0f && value <
static_cast<float>(gridSize)))
163 cell[index] =
static_cast<uint32_t
>(value);
171 return {.x = plane, .y = u, .z = v};
174 return {.x = u, .y = plane, .z = v};
176 return {.x = u, .y = v, .z = plane};
CameraBasis Basis(uint32_t width, uint32_t height) const
static constexpr float VERTICAL_FIELD_OF_VIEW_DEGREES
void Focus(Vector3 target, float distance)
void Pan(float deltaXPixels, float deltaYPixels, uint32_t viewportHeight)
static constexpr float MAX_DISTANCE
static constexpr float MIN_DISTANCE
void Orbit(float deltaXPixels, float deltaYPixels)
void Zoom(int32_t wheelSteps)
float Radians(float degrees)
constexpr float ORTHOGRAPHIC_BACK_OFF
constexpr float ZOOM_PER_STEP
constexpr float PARALLEL_EPSILON
Axes CameraAxes(const OrbitCameraState &state)
float HalfHeightAtTarget(float distance)
Vector3 Normalize(Vector3 vector)
Vector3 Cross(Vector3 left, Vector3 right)
constexpr float MAX_PITCH_DEGREES
constexpr float ORBIT_DEGREES_PER_PIXEL
Vector3 operator+(Vector3 left, Vector3 right)
CellCoordinate CellOnSlice(uint32_t axis, uint32_t plane, uint32_t u, uint32_t v)
Vector3 operator-(Vector3 left, Vector3 right)
Vector3 operator*(Vector3 vector, float scale)
CameraRay RayThroughPixel(const CameraBasis &basis, float pixelX, float pixelY, uint32_t width, uint32_t height)
std::optional< CellCoordinate > PickSliceCell(const CameraRay &ray, uint32_t axis, uint32_t plane, uint32_t gridSize)