Renders the camera editor UI and writes changes into ECS components.
290 ImGui::SetNextWindowSize(ImVec2(380, 520), ImGuiCond_FirstUseEver);
291
292 if (!ImGui::Begin("Camera ECS", nullptr, ImGuiWindowFlags_NoCollapse)) {
293 ImGui::End();
294 return;
295 }
296
297 if (!gameWorld_) {
298 ImGui::TextDisabled("GameWorld missing.");
299 ImGui::End();
300 return;
301 }
302
303 refreshViewportCameraEntries();
304 if (viewportCameraEntries_.empty()) {
305 ImGui::TextDisabled("No viewport has CameraBindingComponent.");
306 ImGui::End();
307 return;
308 }
309
310 const char* selectedLabel = "<none>";
311 if (selectedViewportCameraIndex_ >= 0 && selectedViewportCameraIndex_ < static_cast<int>(viewportCameraEntries_.size())) {
312 selectedLabel = viewportCameraEntries_[selectedViewportCameraIndex_].label.c_str();
313 }
314
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);
320 }
321 if (isSelected) {
322 ImGui::SetItemDefaultFocus();
323 }
324 }
325 ImGui::EndCombo();
326 }
327
328 auto cameraEntityOptional = gameWorld_->find(cameraHandle_);
329 if (!cameraEntityOptional) {
330 ImGui::TextDisabled("Camera entity not found or stale handle.");
331 ImGui::End();
332 return;
333 }
334
335 auto& cameraEntity = *cameraEntityOptional;
336 if (!syncedFromEntity_) {
337 syncFromEntity(cameraEntity);
338 resetSnapshot_ = captureSnapshot(cameraEntity);
339 syncedFromEntity_ = true;
340 }
341
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;
350
351 ImGui::SeparatorText("LookAt Components");
352
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");
357
358 ImGui::Spacing();
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");
363
364 ImGui::Spacing();
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");
369
370 ImGui::SameLine();
371 if (ImGui::SmallButton("Normalize##Up")) {
372 const float len = tempUp_.length();
373 if (len > 0.0001f) {
374 tempUp_ = tempUp_.normalize();
375 upChanged = true;
376 }
377 }
378
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");
384
385 if (positionChanged) {
386 auto* position = cameraEntity.template get<Position3DComponent>();
387 if (position) {
388 position->setValue(tempPosition_);
389 } else {
390 missingPosition = true;
391 }
392 }
393
394 if (targetChanged) {
395 auto* target = cameraEntity.template get<TargetPosition3DComponent>();
396 if (target) {
397 target->setValue(tempTarget_);
398 } else {
399 missingTarget = true;
400 }
401 }
402
403 if (upChanged) {
404 auto* up = cameraEntity.template get<UpVector3DComponent>();
405 if (up) {
406 up->setValue(tempUp_);
407 } else {
408 missingUp = true;
409 }
410 }
411
412 if (projectionChanged) {
413 missingCamera = !writeProjectionToEntity(cameraEntity);
414 }
415
416 ImGui::Spacing();
417 ImGui::BeginDisabled(!resetSnapshot_.valid);
418 if (ImGui::Button("Reset selected camera")) {
419 if (resetSnapshot_.hasPosition) {
420 auto* position = cameraEntity.template get<Position3DComponent>();
421 if (position) {
422 position->setValue(resetSnapshot_.position);
423 tempPosition_ = resetSnapshot_.position;
424 } else {
425 missingPosition = true;
426 }
427 }
428
429 if (resetSnapshot_.hasTarget) {
430 auto* target = cameraEntity.template get<TargetPosition3DComponent>();
431 if (target) {
432 target->setValue(resetSnapshot_.target);
433 tempTarget_ = resetSnapshot_.target;
434 } else {
435 missingTarget = true;
436 }
437 }
438
439 if (resetSnapshot_.hasUp) {
440 auto* up = cameraEntity.template get<UpVector3DComponent>();
441 if (up) {
442 up->setValue(resetSnapshot_.up);
443 tempUp_ = resetSnapshot_.up;
444 } else {
445 missingUp = true;
446 }
447 }
448
449 if (resetSnapshot_.hasPerspective) {
450 auto* perspective = cameraEntity.template get<PerspectiveCameraComponent>();
451 if (perspective) {
452 perspective->setPerspective(
453 helios::math::radians(resetSnapshot_.fovYDegrees),
454 resetSnapshot_.aspectRatio,
455 resetSnapshot_.zNear,
456 resetSnapshot_.zFar
457 );
458 tempFovDegrees_ = resetSnapshot_.fovYDegrees;
459 tempAspectRatio_ = resetSnapshot_.aspectRatio;
460 tempZNear_ = resetSnapshot_.zNear;
461 tempZFar_ = resetSnapshot_.zFar;
462 } else {
463 missingCamera = true;
464 }
465 }
466 }
467 ImGui::EndDisabled();
468
469 if (missingPosition || missingTarget || missingUp || missingCamera) {
470 ImGui::Separator();
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");
474 }
475 if (missingTarget) {
476 ImGui::TextDisabled("- TargetPosition3DComponent");
477 }
478 if (missingUp) {
479 ImGui::TextDisabled("- UpVector3DComponent");
480 }
481 if (missingCamera) {
482 ImGui::TextDisabled("- PerspectiveCameraComponent");
483 }
484 }
485
486 ImGui::Separator();
487 ImGui::TextDisabled("Handle: entityId=%u versionId=%u", cameraHandle_.entityId, cameraHandle_.versionId);
488
489 ImGui::End();
490 }