33> ** Fase:** 5 — Transição Dimensional
44> ** Namespace:** ` Caffeine::Render `
55> ** Arquivo:** ` src/render/Camera3D.hpp `
6- > ** Status:** 📅 Planejado
6+ > ** Status:** ✅ Implementado
77> ** RFs:** RF5.5, RF5.6
88
99---
@@ -14,94 +14,66 @@ Camera3D com projeção perspectiva para renderização 3D. Coexiste com `Camera
1414
1515---
1616
17- ## API Planejada
17+ ## API
1818
1919``` cpp
2020namespace Caffeine ::Render {
2121
22- // ============================================================================
23- // @brief Câmera 3D com projeção perspectiva.
24- //
25- // Modos:
26- // - FPS: mouse look, WASD movement
27- // - Orbital: rotação em torno de um ponto (editor, estratégia)
28- // - Follow: segue entidade com offset (terceira pessoa)
29- // ============================================================================
3022class Camera3D {
3123public:
32- Camera3D();
24+ Camera3D() = default ;
3325
3426 // ── Matrizes ───────────────────────────────────────────────
35- Math:: Mat4 viewMatrix() const;
36- Math:: Mat4 projectionMatrix() const;
37- Math:: Mat4 viewProjectionMatrix() const; // cached, dirty-flagged
27+ Mat4 viewMatrix() const;
28+ Mat4 projectionMatrix() const;
29+ Mat4 viewProjectionMatrix() const; // cached, dirty-flagged
3830
39- // Frustum para culling (RF5.6)
31+ // ── Frustum (RF5.6) ────────────────────────────────────────
4032 Spatial::Frustum frustum() const;
4133
4234 // ── Conversão de espaço ────────────────────────────────────
43- Math:: Vec3 worldToScreen(Math:: Vec3 worldPos) const;
44- Math:: Vec3 screenToWorld(Math:: Vec2 screenPos, f32 depth = 0.5f) const;
45- bool isVisible(const Spatial::AABB3D& bounds) const;
35+ Vec3 worldToScreen(Vec3 worldPos) const;
36+ Vec3 screenToWorld(Vec2 screenPos, f32 depth = 0.5f) const;
37+ bool isVisible(const Spatial::AABB3D& bounds) const;
4638
47- // ── Configuração perspectiva ────────────────────────────────
48- void setFOV(f32 fovYDegrees);
49- void setAspect(f32 aspect);
50- void setNearFar(f32 nearPlane, f32 farPlane);
39+ // ── Configuração perspectiva ───────────────────────────────
40+ void setFOV(f32 fovYDegrees); // clamped [1, 179]
41+ void setAspect(f32 aspect); // clamped > 0
42+ void setNearFar(f32 near, f32 far); // near ≥ 0.1, far > near
43+ void setViewport(Rect2D viewport); // necessário para screen <-> world
5144
5245 // ── Transform ──────────────────────────────────────────────
53- void setPosition(Math::Vec3 pos);
54- void setRotation(Math::Quat rot);
55- void lookAt(Math::Vec3 eye, Math::Vec3 target,
56- Math::Vec3 up = {0, 1, 0});
46+ void setPosition(Vec3 pos);
47+ void setRotation(Quat rot);
48+ void lookAt(Vec3 eye, Vec3 target, Vec3 up = {0, 1, 0});
5749
5850 // ── FPS mode ───────────────────────────────────────────────
5951 void rotateFPS(f32 deltaPitch, f32 deltaYaw); // graus
60- void moveFPS(Math:: Vec3 localDelta); // espaço local
52+ void moveFPS(Vec3 localDelta); // espaço local
6153
6254 // ── Orbital mode ───────────────────────────────────────────
6355 void orbit(f32 deltaAzimuth, f32 deltaElevation); // graus
64- void setOrbitTarget(Math:: Vec3 target);
56+ void setOrbitTarget(Vec3 target);
6557 void setOrbitDistance(f32 distance);
6658 void zoom(f32 delta);
6759
6860 // ── Follow mode ────────────────────────────────────────────
69- void follow(ECS::Entity target, Math:: Vec3 offset = {0, 2, -5},
61+ void follow(ECS::Entity target, Vec3 offset = {0, 2, -5},
7062 f32 smoothing = 0.05f);
63+ void stopFollowing();
7164 void update(f64 dt, const ECS::World& world);
7265
7366 // ── Getters ────────────────────────────────────────────────
74- Math::Vec3 position() const { return m_position; }
75- Math::Quat rotation () const { return m_rotation; }
76- Math::Vec3 forward() const;
77- Math::Vec3 right() const;
78- Math::Vec3 up() const;
79- f32 fov() const { return m_fovY; }
80-
81- private:
82- Math::Mat4 calculateViewMatrix() const;
83- Math::Mat4 calculateProjectionMatrix() const;
84-
85- Math::Vec3 m_position = {0, 0, -5};
86- Math::Quat m_rotation = Math::Quat::identity();
87- f32 m_fovY = 60.0f;
88- f32 m_aspect = 16.0f / 9.0f;
89- f32 m_near = 0.1f;
90- f32 m_far = 1000.0f;
91-
92- // Follow
93- ECS::Entity m_followTarget = ECS::Entity::INVALID;
94- Math::Vec3 m_followOffset = {0, 2, -5};
95- f32 m_followSmoothing = 0.05f;
96-
97- // Orbital
98- Math::Vec3 m_orbitTarget = {0, 0, 0};
99- f32 m_orbitDistance = 5.0f;
100- f32 m_azimuth = 0.0f; // graus
101- f32 m_elevation = 30.0f; // graus
102-
103- mutable Math::Mat4 m_cachedVP;
104- mutable bool m_dirty = true;
67+ Vec3 position() const;
68+ Quat rotation() const;
69+ Vec3 forward() const;
70+ Vec3 right() const;
71+ Vec3 up() const;
72+ f32 fov() const;
73+ f32 aspect() const;
74+ f32 nearPlane() const;
75+ f32 farPlane() const;
76+ Rect2D viewport() const;
10577};
10678
10779} // namespace Caffeine::Render
@@ -112,13 +84,18 @@ private:
11284## Matemática da Projeção Perspectiva
11385
11486```
115- Projection Matrix (perspective):
87+ Projection Matrix (Mat4:: perspective):
11688 f = 1 / tan(fovY/2)
117-
118- [f/aspect, 0, 0, 0]
119- [0, f, 0, 0]
120- [0, 0, far/(far-near), 1]
121- [0, 0, -far*near/(far-near), 0]
89+ A = -(far + near) / (far - near) ← row 2, col 2
90+ B = -(2 * far * near) / (far - near) ← row 3, col 2
91+
92+ [f/aspect, 0, 0, 0]
93+ [0, f, 0, 0]
94+ [0, 0, A, -1]
95+ [0, 0, B, 0]
96+
97+ Nota: layout não-padrão — o fator -1 da divisão perspectiva
98+ está em (2,3) ao invés de (3,2), e o termo near/far está em (3,2).
12299
123100View Matrix (lookAt):
124101 forward = normalize(target - eye)
@@ -199,11 +176,13 @@ cmd->endRenderPass();
199176
200177## Critério de Aceitação
201178
202- - [ ] ` lookAt ` produz view matrix correta (comparado com glm::lookAt)
203- - [ ] ` perspective ` produz projection correta (comparado com glm::perspective)
204- - [ ] Frustum culling: objetos fora do frustum não renderizam
205- - [ ] Follow suave sem jitter (igual ao Camera2D)
206- - [ ] FPS mode: pitch clampado a [ -89°, +89°] (sem flip)
179+ - [x] ` lookAt ` produz view matrix correta (verificado via view * proj roundtrip)
180+ - [x] ` perspective ` produz projection correta (comparado com Mat4::perspective direct)
181+ - [x] Frustum culling: objetos fora do frustum não renderizam (teste automatizado)
182+ - [x] Follow suave sem jitter (lerp testado via update)
183+ - [x] FPS mode: pitch clampado a [ -89°, +89°] (teste automatizado)
184+ - [x] screenToWorld roundtrip (world → screen → world produz mesmo ponto)
185+ - [x] worldToScreen projeta origem no centro da viewport
207186
208187---
209188
0 commit comments