ImGui widget for editing ECS camera and look-at components. More...
#include <string>
#include <vector>
#include "imgui.h"
#include <helios.math.utils>
#include <helios.math.types>
#include <helios.engine.spatial.components.UpVector3DComponent>
#include <helios.engine.spatial.components.TargetPosition3DComponent>
#include <helios.engine.scene.components.CameraBindingComponent>
#include <helios.engine.scene.components.PerspectiveCameraComponent>
#include <helios.engine.rendering.viewport.types.ViewportHandle>
#include <helios.engine.runtime.world.types.GameObjectHandle>
#include <helios.engine.core.types.ComponentTypeTags>
#include <helios.engine.spatial.components.Position3DComponent>
#include <helios.engine.runtime.world.GameWorld>
#include <helios.engine.core.components.DebugNameComponent>
#include <
helios.imgui.ImGuiWidget>
Namespaces Index
Classes Index
Description
ImGui widget for editing ECS camera and look-at components.
File Listing
The file content with the documentation metadata removed is:
12export module helios.imgui.widgets.CameraWidget;
14import helios.imgui.ImGuiWidget;
16import helios.engine.core.components.DebugNameComponent;
18import helios.engine.runtime.world.GameWorld;
19import helios.engine.runtime.world.types.GameObjectHandle;
20import helios.engine.rendering.viewport.types.ViewportHandle;
22import helios.engine.scene.components.PerspectiveCameraComponent;
23import helios.engine.scene.components.CameraBindingComponent;
24import helios.engine.spatial.components.Position3DComponent;
25import helios.engine.spatial.components.TargetPosition3DComponent;
26import helios.engine.spatial.components.UpVector3DComponent;
28import helios.math.types;
29import helios.math.utils;
30import helios.engine.core.types.ComponentTypeTags;
32using namespace helios::engine::core::types;
33using namespace helios::engine::core::components;
45 using GameWorld = helios::engine::runtime::world::GameWorld;
46 using GameObjectHandle = helios::engine::runtime::world::types::GameObjectHandle;
47 using ViewportHandle = helios::engine::rendering::viewport::types::ViewportHandle;
49 using ViewportCameraBindingComponent = helios::engine::scene::components::CameraBindingComponent<ViewportHandle>;
50 using PerspectiveCameraComponent = helios::engine::scene::components::PerspectiveCameraComponent<GameObjectHandle>;
51 using Position3DComponent = helios::engine::spatial::components::Position3DComponent<GameObjectHandle, Local>;
52 using TargetPosition3DComponent = helios::engine::spatial::components::TargetPosition3DComponent<GameObjectHandle, World>;
53 using UpVector3DComponent = helios::engine::spatial::components::UpVector3DComponent<GameObjectHandle>;
55 struct ViewportCameraEntry {
56 ViewportHandle viewportHandle{};
57 GameObjectHandle cameraHandle{};
61 struct CameraSnapshot {
63 bool hasPosition = false;
64 bool hasTarget = false;
66 bool hasPerspective = false;
68 helios::math::vec3f position{0.0f, 0.0f, 1.0f};
69 helios::math::vec3f target{0.0f, 0.0f, 0.0f};
70 helios::math::vec3f up{0.0f, 1.0f, 0.0f};
72 float fovYDegrees = 90.0f;
73 float aspectRatio = 16.0f / 9.0f;
78 GameWorld* gameWorld_ = nullptr;
79 GameObjectHandle cameraHandle_{};
80 std::vector<ViewportCameraEntry> viewportCameraEntries_{};
81 int selectedViewportCameraIndex_ = -1;
83 helios::math::vec3f tempPosition_{0.0f, 0.0f, 1.0f};
84 helios::math::vec3f tempTarget_{0.0f, 0.0f, 0.0f};
85 helios::math::vec3f tempUp_{0.0f, 1.0f, 0.0f};
87 float tempFovDegrees_ = 90.0f;
88 float tempAspectRatio_ = 16.0f / 9.0f;
89 float tempZNear_ = 0.1f;
90 float tempZFar_ = 1000.0f;
92 bool syncedFromEntity_ = false;
93 CameraSnapshot resetSnapshot_{};
95 [[nodiscard]] std::string makeViewportCameraLabel(
96 const ViewportHandle viewportHandle,
97 const GameObjectHandle cameraHandle
99 auto viewportEntity = gameWorld_->find(viewportHandle);
100 auto cameraEntity = gameWorld_->find(cameraHandle);
102 auto* viewportDebugNameCmp = viewportEntity ? viewportEntity->template get<DebugNameComponent<ViewportHandle>>() : nullptr;
103 auto* cameraDebugNameCmp = cameraEntity ? cameraEntity->template get<DebugNameComponent<GameObjectHandle>>() : nullptr;
105 return (viewportDebugNameCmp ? viewportDebugNameCmp->value : "Viewport " + std::to_string(viewportHandle.entityId))
106 + " -> " + (cameraDebugNameCmp ? cameraDebugNameCmp->value : "Camera " + std::to_string(cameraHandle.entityId));
109 void refreshViewportCameraEntries() {
110 const auto previousCameraHandle = cameraHandle_;
111 ViewportHandle previousViewportHandle{};
112 bool hasPreviousViewport = false;
113 if (selectedViewportCameraIndex_ >= 0
114 && selectedViewportCameraIndex_ < static_cast<int>(viewportCameraEntries_.size())) {
115 previousViewportHandle = viewportCameraEntries_[selectedViewportCameraIndex_].viewportHandle;
116 hasPreviousViewport = true;
119 viewportCameraEntries_.clear();
122 selectedViewportCameraIndex_ = -1;
124 syncedFromEntity_ = false;
128 for (auto [viewportEntity, cameraBinding] : gameWorld_->template view<ViewportHandle, ViewportCameraBindingComponent>()) {
129 const auto viewportHandle = viewportEntity.handle();
130 const auto boundCameraHandle = cameraBinding->targetHandle();
132 viewportCameraEntries_.push_back(ViewportCameraEntry{
135 makeViewportCameraLabel(viewportHandle, boundCameraHandle)
139 if (viewportCameraEntries_.empty()) {
140 selectedViewportCameraIndex_ = -1;
142 syncedFromEntity_ = false;
146 int matchingIndex = -1;
147 if (hasPreviousViewport) {
148 for (int i = 0; i < static_cast<int>(viewportCameraEntries_.size()); ++i) {
149 if (viewportCameraEntries_[i].viewportHandle == previousViewportHandle) {
156 if (matchingIndex < 0) {
157 for (int i = 0; i < static_cast<int>(viewportCameraEntries_.size()); ++i) {
158 if (viewportCameraEntries_[i].cameraHandle == previousCameraHandle) {
165 if (matchingIndex < 0) {
169 if (selectedViewportCameraIndex_ != matchingIndex) {
170 selectedViewportCameraIndex_ = matchingIndex;
171 cameraHandle_ = viewportCameraEntries_[selectedViewportCameraIndex_].cameraHandle;
172 syncedFromEntity_ = false;
176 void selectViewportCameraIndex(const int index) {
177 if (index < 0 || index >= static_cast<int>(viewportCameraEntries_.size())) {
181 if (selectedViewportCameraIndex_ == index) {
185 selectedViewportCameraIndex_ = index;
186 cameraHandle_ = viewportCameraEntries_[selectedViewportCameraIndex_].cameraHandle;
187 syncedFromEntity_ = false;
190 template<typename TEntity>
191 [[nodiscard]] CameraSnapshot captureSnapshot(const TEntity& entity) const noexcept {
192 CameraSnapshot snapshot{};
193 snapshot.valid = true;
195 if (const auto* position = entity.template get<Position3DComponent>()) {
196 snapshot.hasPosition = true;
197 snapshot.position = position->value();
200 if (const auto* target = entity.template get<TargetPosition3DComponent>()) {
201 snapshot.hasTarget = true;
202 snapshot.target = target->value();
205 if (const auto* up = entity.template get<UpVector3DComponent>()) {
206 snapshot.hasUp = true;
207 snapshot.up = up->value();
210 if (const auto* camera = entity.template get<PerspectiveCameraComponent>()) {
211 snapshot.hasPerspective = true;
212 snapshot.fovYDegrees = helios::math::degrees(camera->fovY());
213 snapshot.aspectRatio = camera->aspectRatio();
214 snapshot.zNear = camera->zNear();
215 snapshot.zFar = camera->zFar();
221 template<typename TEntity>
222 void syncFromEntity(TEntity& entity) noexcept {
223 if (auto* position = entity.template get<Position3DComponent>()) {
224 tempPosition_ = position->value();
227 if (auto* target = entity.template get<TargetPosition3DComponent>()) {
228 tempTarget_ = target->value();
231 if (auto* up = entity.template get<UpVector3DComponent>()) {
232 tempUp_ = up->value();
235 if (auto* camera = entity.template get<PerspectiveCameraComponent>()) {
236 tempFovDegrees_ = helios::math::degrees(camera->fovY());
237 tempAspectRatio_ = camera->aspectRatio();
238 tempZNear_ = camera->zNear();
239 tempZFar_ = camera->zFar();
243 template<typename TEntity>
244 bool writeProjectionToEntity(TEntity& entity) {
245 if (tempZNear_ <= 0.0f) {
249 if (tempZFar_ <= tempZNear_) {
250 tempZFar_ = tempZNear_ + 0.01f;
253 if (tempAspectRatio_ <= 0.0f) {
254 tempAspectRatio_ = 1.0f;
257 auto* camera = entity.template get<PerspectiveCameraComponent>();
262 camera->setPerspective(
263 helios::math::radians(tempFovDegrees_),
278 explicit CameraWidget(GameWorld& gameWorld) noexcept : gameWorld_(&gameWorld) {}
290 ImGui::SetNextWindowSize(ImVec2(380, 520), ImGuiCond_FirstUseEver);
292 if (!ImGui::Begin("Camera ECS", nullptr, ImGuiWindowFlags_NoCollapse)) {
298 ImGui::TextDisabled("GameWorld missing.");
303 refreshViewportCameraEntries();
304 if (viewportCameraEntries_.empty()) {
305 ImGui::TextDisabled("No viewport has CameraBindingComponent.");
310 const char* selectedLabel = "<none>";
311 if (selectedViewportCameraIndex_ >= 0 && selectedViewportCameraIndex_ < static_cast<int>(viewportCameraEntries_.size())) {
312 selectedLabel = viewportCameraEntries_[selectedViewportCameraIndex_].label.c_str();
315 if (ImGui::BeginCombo("Viewport / Camera", selectedLabel)) {
316 for (int i = 0; i < static_cast<int>(viewportCameraEntries_.size()); ++i) {
317 const bool isSelected = selectedViewportCameraIndex_ == i;
318 if (ImGui::Selectable(viewportCameraEntries_[i].label.c_str(), isSelected)) {
319 selectViewportCameraIndex(i);
322 ImGui::SetItemDefaultFocus();
328 auto cameraEntityOptional = gameWorld_->find(cameraHandle_);
329 if (!cameraEntityOptional) {
330 ImGui::TextDisabled("Camera entity not found or stale handle.");
335 auto& cameraEntity = *cameraEntityOptional;
336 if (!syncedFromEntity_) {
337 syncFromEntity(cameraEntity);
338 resetSnapshot_ = captureSnapshot(cameraEntity);
339 syncedFromEntity_ = true;
342 bool positionChanged = false;
343 bool targetChanged = false;
344 bool upChanged = false;
345 bool projectionChanged = false;
346 bool missingPosition = false;
347 bool missingTarget = false;
348 bool missingUp = false;
349 bool missingCamera = false;
351 ImGui::SeparatorText("LookAt Components");
353 ImGui::Text("Position3DComponent");
354 positionChanged |= ImGui::DragFloat("X##Pos", &tempPosition_[0], 0.1f, -10000.0f, 10000.0f, "%.3f");
355 positionChanged |= ImGui::DragFloat("Y##Pos", &tempPosition_[1], 0.1f, -10000.0f, 10000.0f, "%.3f");
356 positionChanged |= ImGui::DragFloat("Z##Pos", &tempPosition_[2], 0.1f, -10000.0f, 10000.0f, "%.3f");
359 ImGui::Text("TargetPosition3DComponent");
360 targetChanged |= ImGui::DragFloat("X##Target", &tempTarget_[0], 0.1f, -10000.0f, 10000.0f, "%.3f");
361 targetChanged |= ImGui::DragFloat("Y##Target", &tempTarget_[1], 0.1f, -10000.0f, 10000.0f, "%.3f");
362 targetChanged |= ImGui::DragFloat("Z##Target", &tempTarget_[2], 0.1f, -10000.0f, 10000.0f, "%.3f");
365 ImGui::Text("UpVector3DComponent");
366 upChanged |= ImGui::DragFloat("X##Up", &tempUp_[0], 0.01f, -1.0f, 1.0f, "%.3f");
367 upChanged |= ImGui::DragFloat("Y##Up", &tempUp_[1], 0.01f, -1.0f, 1.0f, "%.3f");
368 upChanged |= ImGui::DragFloat("Z##Up", &tempUp_[2], 0.01f, -1.0f, 1.0f, "%.3f");
371 if (ImGui::SmallButton("Normalize##Up")) {
372 const float len = tempUp_.length();
374 tempUp_ = tempUp_.normalize();
379 ImGui::SeparatorText("PerspectiveCameraComponent");
380 projectionChanged |= ImGui::SliderFloat("FOV Y", &tempFovDegrees_, 10.0f, 170.0f, "%.1f deg");
381 projectionChanged |= ImGui::DragFloat("Aspect", &tempAspectRatio_, 0.01f, 0.1f, 8.0f, "%.3f");
382 projectionChanged |= ImGui::DragFloat("Near", &tempZNear_, 0.001f, 0.001f, 1000.0f, "%.4f");
383 projectionChanged |= ImGui::DragFloat("Far", &tempZFar_, 1.0f, 0.01f, 1000000.0f, "%.2f");
385 if (positionChanged) {
386 auto* position = cameraEntity.template get<Position3DComponent>();
388 position->setValue(tempPosition_);
390 missingPosition = true;
395 auto* target = cameraEntity.template get<TargetPosition3DComponent>();
397 target->setValue(tempTarget_);
399 missingTarget = true;
404 auto* up = cameraEntity.template get<UpVector3DComponent>();
406 up->setValue(tempUp_);
412 if (projectionChanged) {
413 missingCamera = !writeProjectionToEntity(cameraEntity);
417 ImGui::BeginDisabled(!resetSnapshot_.valid);
418 if (ImGui::Button("Reset selected camera")) {
419 if (resetSnapshot_.hasPosition) {
420 auto* position = cameraEntity.template get<Position3DComponent>();
422 position->setValue(resetSnapshot_.position);
423 tempPosition_ = resetSnapshot_.position;
425 missingPosition = true;
429 if (resetSnapshot_.hasTarget) {
430 auto* target = cameraEntity.template get<TargetPosition3DComponent>();
432 target->setValue(resetSnapshot_.target);
433 tempTarget_ = resetSnapshot_.target;
435 missingTarget = true;
439 if (resetSnapshot_.hasUp) {
440 auto* up = cameraEntity.template get<UpVector3DComponent>();
442 up->setValue(resetSnapshot_.up);
443 tempUp_ = resetSnapshot_.up;
449 if (resetSnapshot_.hasPerspective) {
450 auto* perspective = cameraEntity.template get<PerspectiveCameraComponent>();
452 perspective->setPerspective(
453 helios::math::radians(resetSnapshot_.fovYDegrees),
454 resetSnapshot_.aspectRatio,
455 resetSnapshot_.zNear,
458 tempFovDegrees_ = resetSnapshot_.fovYDegrees;
459 tempAspectRatio_ = resetSnapshot_.aspectRatio;
460 tempZNear_ = resetSnapshot_.zNear;
461 tempZFar_ = resetSnapshot_.zFar;
463 missingCamera = true;
467 ImGui::EndDisabled();
469 if (missingPosition || missingTarget || missingUp || missingCamera) {
471 ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.2f, 1.0f), "Missing component(s): values could not be written.");
472 if (missingPosition) {
473 ImGui::TextDisabled("- Position3DComponent");
476 ImGui::TextDisabled("- TargetPosition3DComponent");
479 ImGui::TextDisabled("- UpVector3DComponent");
482 ImGui::TextDisabled("- PerspectiveCameraComponent");
487 ImGui::TextDisabled("Handle: entityId=%u versionId=%u", cameraHandle_.entityId, cameraHandle_.versionId);
Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.9.8.