Skip to main content

CameraWidget Class

Debug widget for real-time camera parameter control and visualization. More...

Declaration

class helios::ext::imgui::widgets::CameraWidget { ... }

Base class

classImGuiWidget

Abstract base class for ImGui widgets rendered in debug overlays. More...

Enumerations Index

enum classLookAtSpace { ... }

Public Constructors Index

CameraWidget ()=default

Public Member Functions Index

voidaddCameraSceneNode (const std::string &name, helios::scene::CameraSceneNode *node)
voidclearCameras () noexcept
voiddraw () override

Renders the widget using ImGui immediate-mode API. More...

Private Member Functions Index

helios::scene::CameraSceneNode *getCurrentCameraNode () noexcept
helios::scene::Camera *getActiveCamera () noexcept
voidapplyTransformToNode (helios::scene::CameraSceneNode *node, bool translationChanged, bool lookAtChanged)
voidsyncTempValuesFromCamera () noexcept
voidcaptureInitialValues (CameraEntry &entry) noexcept
voidresetToInitialValues () noexcept

Private Member Attributes Index

std::vector< CameraEntry >cameras_
intselectedCameraIndex_ = 0
helios::math::vec3ftempTranslation_ {0.0f, 0.0f, 5.0f}
helios::math::vec3ftempLookAtTarget_ {0.0f, 0.0f, 0.0f}
helios::math::vec3ftempUp_ {0.0f, 1.0f, 0.0f}
floattempFovDegrees_ = 90.0f
floattempAspectRatio_ = 16.0f / 9.0f
floattempZNear_ = 0.1f
floattempZFar_ = 1000.0f
LookAtSpacelookAtSpace_ = LookAtSpace::Local
boolapplyTranslationOnChange_ = true
boolapplyLookAtOnChange_ = true
boolapplyBothOnAnyChange_ = false

Description

Debug widget for real-time camera parameter control and visualization.

Definition at line 27 of file CameraWidget.ixx.

Enumerations

LookAtSpace

enum class helios::ext::imgui::widgets::CameraWidget::LookAtSpace
strong
Enumeration values
Local
World

Definition at line 47 of file CameraWidget.ixx.

47 enum class LookAtSpace { Local, World };

Public Constructors

CameraWidget()

helios::ext::imgui::widgets::CameraWidget::CameraWidget ()
default

Definition at line 191 of file CameraWidget.ixx.

Public Member Functions

addCameraSceneNode()

void helios::ext::imgui::widgets::CameraWidget::addCameraSceneNode (const std::string & name, helios::scene::CameraSceneNode * node)
inline

Definition at line 193 of file CameraWidget.ixx.

193 void addCameraSceneNode(const std::string& name, helios::scene::CameraSceneNode* node) {
194 CameraEntry entry{name, node};
195 captureInitialValues(entry);
196
197 // store current temp defaults as initial values for look-at
198 entry.initialLookAtTarget = tempLookAtTarget_;
199 entry.initialUp = tempUp_;
200
201 cameras_.push_back(entry);
202
203 if (cameras_.size() == 1) {
204 syncTempValuesFromCamera();
205 }
206 }

clearCameras()

void helios::ext::imgui::widgets::CameraWidget::clearCameras ()
inline noexcept

Definition at line 208 of file CameraWidget.ixx.

208 void clearCameras() noexcept {
209 cameras_.clear();
210 selectedCameraIndex_ = 0;
211 }

draw()

void helios::ext::imgui::widgets::CameraWidget::draw ()
inline virtual

Renders the widget using ImGui immediate-mode API.

Called once per frame by the owning overlay. Implementers should call ImGui functions (e.g., `ImGui::Begin()`, `ImGui::Text()`) to build the UI.

Definition at line 213 of file CameraWidget.ixx.

213 void draw() override {
214 ImGui::SetNextWindowSize(ImVec2(320, 620), ImGuiCond_FirstUseEver);
215
216 if (!ImGui::Begin("Camera Control", nullptr, ImGuiWindowFlags_NoCollapse)) {
217 ImGui::End();
218 return;
219 }
220
221 if (cameras_.empty()) {
222 ImGui::TextDisabled("No cameras registered.");
223 ImGui::End();
224 return;
225 }
226
227 // Camera selection
228 ImGui::PushItemWidth(-100);
229 if (ImGui::BeginCombo("##Camera", cameras_[selectedCameraIndex_].name.c_str())) {
230 for (int i = 0; i < static_cast<int>(cameras_.size()); ++i) {
231 const bool isSelected = (selectedCameraIndex_ == i);
232 if (ImGui::Selectable(cameras_[i].name.c_str(), isSelected)) {
233 selectedCameraIndex_ = i;
234 syncTempValuesFromCamera();
235 }
236 if (isSelected) {
237 ImGui::SetItemDefaultFocus();
238 }
239 }
240 ImGui::EndCombo();
241 }
242 ImGui::PopItemWidth();
243
244 ImGui::SameLine();
245 if (ImGui::Button("Reset")) {
246 resetToInitialValues();
247 }
248
249 auto* node = getCurrentCameraNode();
250 auto* cameraPtr = getActiveCamera();
251 if (!node || !cameraPtr) {
252 ImGui::End();
253 return;
254 }
255 auto& camera = *cameraPtr;
256
257 ImGui::Separator();
258 ImGui::Spacing();
259
260 bool translationChanged = false;
261 bool lookAtChanged = false;
262
263 // === Transform ===
264 ImGui::Text("Position (Translation)");
265 translationChanged |= ImGui::DragFloat("X##Pos", &tempTranslation_[0], 0.1f, -100.0f, 100.0f, "%.2f");
266 translationChanged |= ImGui::DragFloat("Y##Pos", &tempTranslation_[1], 0.1f, -100.0f, 100.0f, "%.2f");
267 translationChanged |= ImGui::DragFloat("Z##Pos", &tempTranslation_[2], 0.1f, -100.0f, 100.0f, "%.2f");
268
269 ImGui::Spacing();
270
271 // LookAt space toggle
272 ImGui::Text("LookAt Space");
273 const int mode = (lookAtSpace_ == LookAtSpace::Local) ? 0 : 1;
274
275 if (ImGui::RadioButton("Local (parent/model space)", mode == 0)) {
276 lookAtSpace_ = LookAtSpace::Local;
277 lookAtChanged = true;
278 }
279 ImGui::SameLine();
280 if (ImGui::RadioButton("World", mode == 1)) {
281 lookAtSpace_ = LookAtSpace::World;
282 lookAtChanged = true;
283 }
284
285 ImGui::Spacing();
286
287 // Apply behavior toggles (horizontal layout for compactness)
288 ImGui::Separator();
289 ImGui::Text("Apply Behavior");
290 ImGui::Checkbox("Translation", &applyTranslationOnChange_);
291 ImGui::SameLine();
292 ImGui::Checkbox("LookAt", &applyLookAtOnChange_);
293 ImGui::SameLine();
294 ImGui::Checkbox("Both", &applyBothOnAnyChange_);
295 if (ImGui::IsItemHovered()) {
296 ImGui::SetTooltip("If enabled, changing either Position or LookAt re-applies BOTH.");
297 }
298
299 ImGui::Separator();
300 ImGui::Spacing();
301
302 // LookAt target controls
303 ImGui::Text("Look-At Target");
304 lookAtChanged |= ImGui::DragFloat("X##Target", &tempLookAtTarget_[0], 0.1f, -100.0f, 100.0f, "%.2f");
305 lookAtChanged |= ImGui::DragFloat("Y##Target", &tempLookAtTarget_[1], 0.1f, -100.0f, 100.0f, "%.2f");
306 lookAtChanged |= ImGui::DragFloat("Z##Target", &tempLookAtTarget_[2], 0.1f, -100.0f, 100.0f, "%.2f");
307
308 ImGui::Spacing();
309
310 ImGui::Text("Up Vector");
311 lookAtChanged |= ImGui::DragFloat("X##Up", &tempUp_[0], 0.01f, -1.0f, 1.0f, "%.3f");
312 lookAtChanged |= ImGui::DragFloat("Y##Up", &tempUp_[1], 0.01f, -1.0f, 1.0f, "%.3f");
313 lookAtChanged |= ImGui::DragFloat("Z##Up", &tempUp_[2], 0.01f, -1.0f, 1.0f, "%.3f");
314
315 ImGui::SameLine();
316 if (ImGui::SmallButton("N##NormalizeUp")) {
317 float len = tempUp_.length();
318 if (len > 0.0001f) {
319 tempUp_ = tempUp_.normalize();
320 lookAtChanged = true;
321 }
322 }
323 if (ImGui::IsItemHovered()) {
324 ImGui::SetTooltip("Normalize up vector");
325 }
326
327 // Apply changes respecting apply-mode toggles
328 if (translationChanged || lookAtChanged) {
329 applyTransformToNode(node, translationChanged, lookAtChanged);
330 }
331
332 ImGui::Separator();
333 ImGui::Spacing();
334
335 // === Projection ===
336 ImGui::Text("Projection");
337
338 bool projectionChanged = false;
339 projectionChanged |= ImGui::SliderFloat("FOV", &tempFovDegrees_, 30.0f, 120.0f, "%.1f deg");
340 projectionChanged |= ImGui::DragFloat("Near Plane", &tempZNear_, 0.01f, 0.001f, tempZFar_ - 0.01f, "%.3f");
341 projectionChanged |= ImGui::DragFloat("Far Plane", &tempZFar_, 1.0f, tempZNear_ + 0.01f, 100000.0f, "%.1f");
342
343 if (projectionChanged) {
344 camera.setPerspective(helios::math::radians(tempFovDegrees_), tempAspectRatio_, tempZNear_, tempZFar_);
345 }
346
347 ImGui::Spacing();
348
349 ImGui::Text("Aspect Ratio");
350 if (ImGui::DragFloat("##Aspect", &tempAspectRatio_, 0.01f, 0.5f, 4.0f, "%.3f")) {
351 camera.setAspectRatio(tempAspectRatio_);
352 }
353
354 if (ImGui::Button("16:9")) {
355 tempAspectRatio_ = 16.0f / 9.0f;
356 camera.setAspectRatio(tempAspectRatio_);
357 }
358 ImGui::SameLine();
359 if (ImGui::Button("21:9")) {
360 tempAspectRatio_ = 21.0f / 9.0f;
361 camera.setAspectRatio(tempAspectRatio_);
362 }
363 ImGui::SameLine();
364 if (ImGui::Button("32:9")) {
365 tempAspectRatio_ = 32.0f / 9.0f;
366 camera.setAspectRatio(tempAspectRatio_);
367 }
368 ImGui::SameLine();
369 if (ImGui::Button("4:3")) {
370 tempAspectRatio_ = 4.0f / 3.0f;
371 camera.setAspectRatio(tempAspectRatio_);
372 }
373 ImGui::SameLine();
374 if (ImGui::Button("1:1")) {
375 tempAspectRatio_ = 1.0f;
376 camera.setAspectRatio(tempAspectRatio_);
377 }
378
379 ImGui::Separator();
380 ImGui::Spacing();
381
382 // Quick view presets
383 ImGui::Text("Quick View Presets");
384 if (ImGui::Button("Front")) {
385 tempTranslation_ = {0.0f, 0.0f, 5.0f};
386 tempLookAtTarget_ = {0.0f, 0.0f, 0.0f};
387 tempUp_ = {0.0f, 1.0f, 0.0f};
388 applyTransformToNode(node, true, true);
389 }
390 ImGui::SameLine();
391 if (ImGui::Button("Top")) {
392 tempTranslation_ = {0.0f, 5.0f, 0.001f};
393 tempLookAtTarget_ = {0.0f, 0.0f, 0.0f};
394 tempUp_ = {0.0f, 0.0f, -1.0f};
395 applyTransformToNode(node, true, true);
396 }
397 ImGui::SameLine();
398 if (ImGui::Button("Side")) {
399 tempTranslation_ = {5.0f, 0.0f, 0.0f};
400 tempLookAtTarget_ = {0.0f, 0.0f, 0.0f};
401 tempUp_ = {0.0f, 1.0f, 0.0f};
402 applyTransformToNode(node, true, true);
403 }
404 ImGui::SameLine();
405 if (ImGui::Button("Iso")) {
406 tempTranslation_ = {5.0f, 5.0f, 5.0f};
407 tempLookAtTarget_ = {0.0f, 0.0f, 0.0f};
408 tempUp_ = {0.0f, 1.0f, 0.0f};
409 applyTransformToNode(node, true, true);
410 }
411
412 ImGui::Separator();
413
414 helios::math::vec3f diff = tempTranslation_ - tempLookAtTarget_;
415 float distance = diff.length();
416
417 ImGui::TextDisabled("Pos: (%.1f, %.1f, %.1f) | Target: (%.1f, %.1f, %.1f)",
418 tempTranslation_[0], tempTranslation_[1], tempTranslation_[2],
419 tempLookAtTarget_[0], tempLookAtTarget_[1], tempLookAtTarget_[2]);
420 ImGui::TextDisabled("Distance: %.2f | FOV: %.0f° | Z: [%.2f, %.0f]",
421 distance, tempFovDegrees_, tempZNear_, tempZFar_);
422
423 ImGui::End();
424 }

References helios::math::vec3< T >::length and helios::math::radians.

Private Member Functions

applyTransformToNode()

void helios::ext::imgui::widgets::CameraWidget::applyTransformToNode (helios::scene::CameraSceneNode * node, bool translationChanged, bool lookAtChanged)
inline

Applies translation and/or lookAt depending on change flags and apply-mode toggles.

Definition at line 91 of file CameraWidget.ixx.

91 void applyTransformToNode(helios::scene::CameraSceneNode* node,
92 bool translationChanged,
93 bool lookAtChanged)
94 {
95 if (!node) return;
96
97 bool applyTranslation = applyTranslationOnChange_ && translationChanged;
98 bool applyLookAt = applyLookAtOnChange_ && lookAtChanged;
99
100 // Force both together if enabled
101 if (applyBothOnAnyChange_ && (translationChanged || lookAtChanged)) {
102 applyTranslation = true;
103 applyLookAt = true;
104 }
105
106 if (applyTranslation) {
107 node->setTranslation(tempTranslation_);
108 }
109
110 if (!applyLookAt) {
111 return;
112 }
113
114 if (lookAtSpace_ == LookAtSpace::Local) {
115 node->lookAtLocal(tempLookAtTarget_, tempUp_);
116 } else {
117 node->lookAt(tempLookAtTarget_, tempUp_);
118 }
119 }

captureInitialValues()

void helios::ext::imgui::widgets::CameraWidget::captureInitialValues (CameraEntry & entry)
inline noexcept

Definition at line 137 of file CameraWidget.ixx.

137 void captureInitialValues(CameraEntry& entry) noexcept {
138 if (!entry.node) {
139 return;
140 }
141
142 const auto& transform = entry.node->localTransform();
143 entry.initialTranslation = transform.translation();
144 entry.initialScale = transform.scaling();
145 entry.initialRotation = transform.rotation();
146
147 const auto& cam = entry.node->camera();
148 entry.initialFovDegrees = helios::math::degrees(cam.fovY());
149 entry.initialAspectRatio = cam.aspectRatio();
150 entry.initialZNear = cam.zNear();
151 entry.initialZFar = cam.zFar();
152 }

getActiveCamera()

helios::scene::Camera * helios::ext::imgui::widgets::CameraWidget::getActiveCamera ()
inline nodiscard noexcept

Definition at line 80 of file CameraWidget.ixx.

80 [[nodiscard]] helios::scene::Camera* getActiveCamera() noexcept {
81 auto* node = getCurrentCameraNode();
82 if (!node) {
83 return nullptr;
84 }
85 return &(node->camera());
86 }

getCurrentCameraNode()

helios::scene::CameraSceneNode * helios::ext::imgui::widgets::CameraWidget::getCurrentCameraNode ()
inline nodiscard noexcept

Definition at line 70 of file CameraWidget.ixx.

70 [[nodiscard]] helios::scene::CameraSceneNode* getCurrentCameraNode() noexcept {
71 if (cameras_.empty()) {
72 return nullptr;
73 }
74 if (selectedCameraIndex_ < 0 || selectedCameraIndex_ >= static_cast<int>(cameras_.size())) {
75 selectedCameraIndex_ = 0;
76 }
77 return cameras_[selectedCameraIndex_].node;
78 }

resetToInitialValues()

void helios::ext::imgui::widgets::CameraWidget::resetToInitialValues ()
inline noexcept

Definition at line 154 of file CameraWidget.ixx.

154 void resetToInitialValues() noexcept {
155 if (cameras_.empty()) {
156 return;
157 }
158
159 auto& entry = cameras_[selectedCameraIndex_];
160 auto* node = entry.node;
161 if (!node) {
162 return;
163 }
164
165 tempTranslation_ = entry.initialTranslation;
166 tempLookAtTarget_ = entry.initialLookAtTarget;
167 tempUp_ = entry.initialUp;
168
169 node->setTranslation(entry.initialTranslation);
170 node->setRotation(entry.initialRotation);
171 node->setScale(entry.initialScale);
172
173 auto& cam = node->camera();
174 cam.setPerspective(helios::math::radians(entry.initialFovDegrees),
175 entry.initialAspectRatio,
176 entry.initialZNear,
177 entry.initialZFar);
178
179 // Apply lookAt immediately after reset if user wants to keep that behavior consistent
180 // (Here we always do it because reset expects the camera to match initial pose.)
181 if (lookAtSpace_ == LookAtSpace::Local) {
182 node->lookAtLocal(tempLookAtTarget_, tempUp_);
183 } else {
184 node->lookAt(tempLookAtTarget_, tempUp_);
185 }
186
187 syncTempValuesFromCamera();
188 }

syncTempValuesFromCamera()

void helios::ext::imgui::widgets::CameraWidget::syncTempValuesFromCamera ()
inline noexcept

Definition at line 121 of file CameraWidget.ixx.

121 void syncTempValuesFromCamera() noexcept {
122 auto* node = getCurrentCameraNode();
123 if (!node) {
124 return;
125 }
126
127 const auto& transform = node->localTransform();
128 tempTranslation_ = transform.translation();
129
130 const auto& cam = node->camera();
131 tempFovDegrees_ = helios::math::degrees(cam.fovY());
132 tempAspectRatio_ = cam.aspectRatio();
133 tempZNear_ = cam.zNear();
134 tempZFar_ = cam.zFar();
135 }

Private Member Attributes

applyBothOnAnyChange_

bool helios::ext::imgui::widgets::CameraWidget::applyBothOnAnyChange_ = false

Definition at line 68 of file CameraWidget.ixx.

68 bool applyBothOnAnyChange_ = false; // if enabled, apply both whenever either changes

applyLookAtOnChange_

bool helios::ext::imgui::widgets::CameraWidget::applyLookAtOnChange_ = true

Definition at line 67 of file CameraWidget.ixx.

67 bool applyLookAtOnChange_ = true;

applyTranslationOnChange_

bool helios::ext::imgui::widgets::CameraWidget::applyTranslationOnChange_ = true

Definition at line 66 of file CameraWidget.ixx.

66 bool applyTranslationOnChange_ = true;

cameras_

std::vector<CameraEntry> helios::ext::imgui::widgets::CameraWidget::cameras_

Definition at line 49 of file CameraWidget.ixx.

49 std::vector<CameraEntry> cameras_;

lookAtSpace_

LookAtSpace helios::ext::imgui::widgets::CameraWidget::lookAtSpace_ = LookAtSpace::Local

Definition at line 63 of file CameraWidget.ixx.

63 LookAtSpace lookAtSpace_ = LookAtSpace::Local;

selectedCameraIndex_

int helios::ext::imgui::widgets::CameraWidget::selectedCameraIndex_ = 0

Definition at line 50 of file CameraWidget.ixx.

50 int selectedCameraIndex_ = 0;

tempAspectRatio_

float helios::ext::imgui::widgets::CameraWidget::tempAspectRatio_ = 16.0f / 9.0f

Definition at line 58 of file CameraWidget.ixx.

58 float tempAspectRatio_ = 16.0f / 9.0f;

tempFovDegrees_

float helios::ext::imgui::widgets::CameraWidget::tempFovDegrees_ = 90.0f

Definition at line 57 of file CameraWidget.ixx.

57 float tempFovDegrees_ = 90.0f;

tempLookAtTarget_

helios::math::vec3f helios::ext::imgui::widgets::CameraWidget::tempLookAtTarget_ {0.0f, 0.0f, 0.0f}

Definition at line 54 of file CameraWidget.ixx.

54 helios::math::vec3f tempLookAtTarget_{0.0f, 0.0f, 0.0f};

tempTranslation_

helios::math::vec3f helios::ext::imgui::widgets::CameraWidget::tempTranslation_ {0.0f, 0.0f, 5.0f}

Definition at line 53 of file CameraWidget.ixx.

53 helios::math::vec3f tempTranslation_{0.0f, 0.0f, 5.0f};

tempUp_

helios::math::vec3f helios::ext::imgui::widgets::CameraWidget::tempUp_ {0.0f, 1.0f, 0.0f}

Definition at line 55 of file CameraWidget.ixx.

55 helios::math::vec3f tempUp_{0.0f, 1.0f, 0.0f};

tempZFar_

float helios::ext::imgui::widgets::CameraWidget::tempZFar_ = 1000.0f

Definition at line 60 of file CameraWidget.ixx.

60 float tempZFar_ = 1000.0f;

tempZNear_

float helios::ext::imgui::widgets::CameraWidget::tempZNear_ = 0.1f

Definition at line 59 of file CameraWidget.ixx.

59 float tempZNear_ = 0.1f;

The documentation for this class was generated from the following file:


Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.