Loading...
Searching...
No Matches
Camera.hpp
1#pragma once
2#include <halp/controls.hpp>
3#include <halp/meta.hpp>
4
5#include <ossia/dataflow/geometry_port.hpp>
6
7#include <Gfx/Graph/GpuResourceRegistry.hpp>
8
9#include <QMatrix4x4>
10#include <QQuaternion>
11#include <QVector3D>
12
13#include <cmath>
14#include <cstdint>
15#include <memory>
16#include <vector>
17
18class QRhiResourceUpdateBatch;
19
20namespace score::gfx
21{
22class RenderList;
23struct Edge;
24}
25
26namespace Threedim
27{
28
29// Scene-producing camera node. Emits a scene_spec containing:
30// - a scene_node with an id derived from this node's uuid (so the flatten
31// visitor can attribute the camera back to it),
32// - a scene_transform placing the camera at eye looking at target,
33// - a camera_component carrying yfov / znear / zfar.
34//
35// ScenePreprocessor packs every camera it collects into its Camera UBO
36// output — when merged with a scene tree this camera becomes one entry in
37// that array. active_camera_id defaults to this node's id so a single
38// Camera is picked up automatically.
39class Camera
40{
41public:
42 halp_meta(name, "Camera")
43 halp_meta(c_name, "camera_avnd")
44 halp_meta(category, "Visuals/3D/Scene")
45 halp_meta(authors, "ossia team")
46 halp_meta(uuid, "4c91b5e2-8d76-4ab3-9f14-6e0d8b3a2c57")
47
48 struct ins
49 {
50 // Port-driven rebuild: every control carries an `update(Camera&)`
51 // callback that fires only when its value changes, triggering a
52 // `rebuild()` on the Camera. `operator()()` then just republishes
53 // the already-built m_state — no per-frame memcmp, no per-frame
54 // version bump, no merge_scenes / preprocessor thrash.
55 //
56 // halp::range only supports scalar inits (broadcast across x/y/z), so
57 // the non-uniform defaults are applied in the subclass constructor.
58 struct Eye : halp::xyz_spinboxes_f32<"Eye", halp::range{-10000., 10000., 0.}>
59 {
60 Eye() { value = {0.f, 1.f, 3.f}; }
61 void update(Camera& n) { n.rebuild(); }
62 } eye;
63 struct : halp::xyz_spinboxes_f32<"Target", halp::range{-10000., 10000., 0.}>
64 { void update(Camera& n) { n.rebuild(); } } target;
65 struct : halp::hslider_f32<"FOV", halp::range{5., 170., 60.}>
66 { void update(Camera& n) { n.rebuild(); } } fov;
67 struct : halp::hslider_f32<"Near", halp::range{0.001, 10., 0.1}>
68 { void update(Camera& n) { n.rebuild(); } } near_plane;
69 struct : halp::hslider_f32<"Far", halp::range{1., 100000., 1000.}>
70 { void update(Camera& n) { n.rebuild(); } } far_plane;
71 } inputs;
72
73 struct outs
74 {
75 struct
76 {
77 halp_meta(name, "Scene");
78 ossia::scene_spec scene;
79 uint8_t dirty{0};
80 } scene_out;
81 } outputs;
82
83 // Stable scene_node_id for this camera across frames. Set once in the
84 // first call. Used as scene_state::active_camera_id so ScenePreprocessor
85 // picks THIS camera even when other cameras show up in merged scenes.
86 ossia::scene_node_id m_id{};
87 std::shared_ptr<ossia::scene_state> m_state;
88 int64_t m_version{0};
89 // Dirty bits to stamp on the next emission. Accumulated in rebuild()
90 // and cleared after operator()() publishes them. When no control
91 // changed this frame, operator()() republishes the same m_state with
92 // dirty=0 so the preprocessor's pointer+version comparison short-
93 // circuits the rebuild path.
94 uint8_t m_pending_dirty{ossia::scene_port::dirty_transform};
95 // Stable ids for the single scene_transform + camera_component this
96 // node emits (minted on first rebuild).
97 uint64_t m_xform_stable_id{};
98 uint64_t m_camera_stable_id{};
99
100 // Rebuild m_state from current inputs. Called from every port's
101 // `update()` callback (fires only on control changes), and once from
102 // `operator()()` on the first tick to seed m_state.
103 void rebuild()
104 {
105 if(!m_state)
106 {
107 m_state = std::make_shared<ossia::scene_state>();
108 // Deterministic, non-zero id keyed on this node's address. Non-zero
109 // so merge_scenes' active_camera_id resolution treats it as "set".
110 m_id.value = reinterpret_cast<std::uintptr_t>(this) | 0x1u;
111 }
112 if(m_camera_stable_id == 0) m_camera_stable_id = ossia::mint_stable_id();
113 if(m_xform_stable_id == 0) m_xform_stable_id = ossia::mint_stable_id();
114
115 // Rebuild as {scene_transform, camera_component} inside a scene_node.
116 auto cam = std::make_shared<ossia::camera_component>();
117 cam->stable_id = m_camera_stable_id;
118 cam->projection = ossia::camera_projection::perspective;
119 cam->yfov = inputs.fov.value * float(M_PI) / 180.f;
120 cam->znear = inputs.near_plane.value;
121 cam->zfar = inputs.far_plane.value;
122 // Propagate the RawCamera arena slot ref (populated in init()).
123 cam->raw_slot = m_camera_ref;
124
125 // Encode the world transform as TRS for the scene_transform payload.
126 ossia::scene_transform xform;
127 xform.stable_id = m_xform_stable_id;
128 xform.translation[0] = inputs.eye.value.x;
129 xform.translation[1] = inputs.eye.value.y;
130 xform.translation[2] = inputs.eye.value.z;
131 // Build a quaternion for the camera's world orientation. Qt's
132 // QQuaternion::fromDirection(direction, up) maps local +Z (NOT -Z) to
133 // `direction` — see QMatrix4x4::fromAxes in Qt source, which takes
134 // zAxis = direction. We want the camera's local +Z axis (the "back"
135 // axis of a GL camera) to point along (eye − target) so that local -Z
136 // (the GL viewing direction) points from eye toward target. Hence the
137 // -forward. Equivalently: the inverse of the TRS matches
138 // QMatrix4x4::lookAt(eye, target, up).
139 QVector3D forward(
140 inputs.target.value.x - inputs.eye.value.x,
141 inputs.target.value.y - inputs.eye.value.y,
142 inputs.target.value.z - inputs.eye.value.z);
143 if(forward.lengthSquared() > 1e-8f)
144 {
145 forward.normalize();
146 QQuaternion q = QQuaternion::fromDirection(
147 -forward, QVector3D(0.f, 1.f, 0.f));
148 xform.rotation[0] = q.x();
149 xform.rotation[1] = q.y();
150 xform.rotation[2] = q.z();
151 xform.rotation[3] = q.scalar();
152 }
153 else
154 {
155 xform.rotation[0] = 0.f;
156 xform.rotation[1] = 0.f;
157 xform.rotation[2] = 0.f;
158 xform.rotation[3] = 1.f;
159 }
160 xform.scale[0] = 1.f;
161 xform.scale[1] = 1.f;
162 xform.scale[2] = 1.f;
163 // Propagate the RawTransform slot ref (populated in init()).
164 xform.raw_slot = m_xform_ref;
165
166 auto children = std::make_shared<std::vector<ossia::scene_payload>>();
167 children->push_back(xform);
168 children->push_back(ossia::camera_component_ptr(std::move(cam)));
169
170 auto node = std::make_shared<ossia::scene_node>();
171 node->id = m_id;
172 node->children = std::move(children);
173
174 auto roots
175 = std::make_shared<std::vector<ossia::scene_node_ptr>>();
176 roots->push_back(std::move(node));
177
178 m_state->roots = std::move(roots);
179 m_state->active_camera_id = m_id;
180 m_version++;
181 m_state->version = m_version;
182 m_pending_dirty = ossia::scene_port::dirty_transform;
183 }
184
185 void operator()()
186 {
187 if(!m_state)
188 rebuild();
189 outputs.scene_out.scene.state = m_state;
190 outputs.scene_out.dirty = m_pending_dirty;
191 m_pending_dirty = 0;
192 }
193
194 // Render-thread hooks. init claims a RawCamera and a RawTransform slot;
195 // update packs eye / target / up / yfov / znear / zfar into a
196 // RawCameraData and uploads; release returns the slots.
197 void init(score::gfx::RenderList& r, QRhiResourceUpdateBatch& res);
198 void update(
199 score::gfx::RenderList& r, QRhiResourceUpdateBatch& res,
201 void release(score::gfx::RenderList& r);
202
204 score::gfx::GpuResourceRegistry::Slot raw_transform_slot;
205
206 // Ossia-facing snapshots, stamped on the emitted components'
207 // raw_slot fields so the preprocessor can locate this camera's
208 // GPU bytes via isLive() + offset. Written once in init().
209 ossia::gpu_slot_ref m_camera_ref{};
210 ossia::gpu_slot_ref m_xform_ref{};
211};
212
213}
Definition Camera.hpp:40
List of nodes to be rendered to an output.
Definition RenderList.hpp:30
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition Camera.hpp:74
Connection between two score::gfx::Port.
Definition score-plugin-gfx/Gfx/Graph/Utils.hpp:103
Definition GpuResourceRegistry.hpp:84