feat: add scene view editor camera controls
This commit is contained in:
@@ -2,10 +2,13 @@
|
||||
|
||||
#include "Core/IEditorContext.h"
|
||||
#include "Core/ISceneManager.h"
|
||||
#include "Core/ISelectionManager.h"
|
||||
#include "IViewportHostService.h"
|
||||
#include "SceneViewportCameraController.h"
|
||||
#include "UI/ImGuiBackendBridge.h"
|
||||
|
||||
#include <XCEngine/Components/CameraComponent.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/RHI/D3D12/D3D12Device.h>
|
||||
#include <XCEngine/RHI/D3D12/D3D12Texture.h>
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
@@ -38,6 +41,7 @@ public:
|
||||
entry = {};
|
||||
}
|
||||
|
||||
m_sceneViewCamera = {};
|
||||
m_device = nullptr;
|
||||
m_backend = nullptr;
|
||||
m_sceneRenderer.reset();
|
||||
@@ -75,6 +79,33 @@ public:
|
||||
return frame;
|
||||
}
|
||||
|
||||
void UpdateSceneViewInput(IEditorContext& context, const SceneViewportInput& input) override {
|
||||
if (!EnsureSceneViewCamera()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.focusSelectionRequested) {
|
||||
FocusSceneView(context);
|
||||
}
|
||||
|
||||
SceneViewportCameraInputState controllerInput = {};
|
||||
controllerInput.viewportHeight = input.viewportSize.y;
|
||||
controllerInput.zoomDelta = input.hovered ? input.mouseWheel : 0.0f;
|
||||
|
||||
if (input.orbiting) {
|
||||
controllerInput.orbitDeltaX = input.mouseDelta.x;
|
||||
controllerInput.orbitDeltaY = input.mouseDelta.y;
|
||||
}
|
||||
|
||||
if (input.panning) {
|
||||
controllerInput.panDeltaX = input.mouseDelta.x;
|
||||
controllerInput.panDeltaY = input.mouseDelta.y;
|
||||
}
|
||||
|
||||
m_sceneViewCamera.controller.ApplyInput(controllerInput);
|
||||
ApplySceneViewCameraController();
|
||||
}
|
||||
|
||||
void RenderRequestedViewports(
|
||||
IEditorContext& context,
|
||||
const Rendering::RenderContext& renderContext) override {
|
||||
@@ -118,6 +149,12 @@ private:
|
||||
std::string statusText;
|
||||
};
|
||||
|
||||
struct SceneViewCameraState {
|
||||
std::unique_ptr<Components::GameObject> gameObject;
|
||||
Components::CameraComponent* camera = nullptr;
|
||||
SceneViewportCameraController controller;
|
||||
};
|
||||
|
||||
ViewportEntry& GetEntry(EditorViewportKind kind) {
|
||||
const size_t index = kind == EditorViewportKind::Scene ? 0u : 1u;
|
||||
m_entries[index].kind = kind;
|
||||
@@ -130,6 +167,72 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool EnsureSceneViewCamera() {
|
||||
if (m_sceneViewCamera.gameObject != nullptr && m_sceneViewCamera.camera != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
m_sceneViewCamera.gameObject = std::make_unique<Components::GameObject>("EditorSceneCamera");
|
||||
m_sceneViewCamera.camera = m_sceneViewCamera.gameObject->AddComponent<Components::CameraComponent>();
|
||||
if (m_sceneViewCamera.camera == nullptr) {
|
||||
m_sceneViewCamera.gameObject.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_sceneViewCamera.camera->SetPrimary(false);
|
||||
m_sceneViewCamera.camera->SetProjectionType(Components::CameraProjectionType::Perspective);
|
||||
m_sceneViewCamera.camera->SetFieldOfView(60.0f);
|
||||
m_sceneViewCamera.camera->SetNearClipPlane(0.03f);
|
||||
m_sceneViewCamera.camera->SetFarClipPlane(2000.0f);
|
||||
m_sceneViewCamera.controller.Reset();
|
||||
ApplySceneViewCameraController();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ApplySceneViewCameraController() {
|
||||
if (m_sceneViewCamera.gameObject == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_sceneViewCamera.controller.ApplyTo(*m_sceneViewCamera.gameObject->GetTransform());
|
||||
}
|
||||
|
||||
void FocusSceneView(IEditorContext& context) {
|
||||
Components::GameObject* target = nullptr;
|
||||
const uint64_t selectedEntity = context.GetSelectionManager().GetSelectedEntity();
|
||||
if (selectedEntity != 0) {
|
||||
target = context.GetSceneManager().GetEntity(selectedEntity);
|
||||
}
|
||||
|
||||
if (target != nullptr) {
|
||||
m_sceneViewCamera.controller.Focus(target->GetTransform()->GetPosition());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& roots = context.GetSceneManager().GetRootEntities();
|
||||
if (roots.empty()) {
|
||||
m_sceneViewCamera.controller.Focus(Math::Vector3::Zero());
|
||||
return;
|
||||
}
|
||||
|
||||
Math::Vector3 center = Math::Vector3::Zero();
|
||||
uint32_t count = 0;
|
||||
for (const Components::GameObject* root : roots) {
|
||||
if (root == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
center += root->GetTransform()->GetPosition();
|
||||
++count;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
center /= static_cast<float>(count);
|
||||
}
|
||||
|
||||
m_sceneViewCamera.controller.Focus(center);
|
||||
}
|
||||
|
||||
bool EnsureViewportResources(ViewportEntry& entry) {
|
||||
if (entry.requestedWidth == 0 || entry.requestedHeight == 0) {
|
||||
return false;
|
||||
@@ -224,6 +327,15 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
Rendering::RenderSurface BuildSurface(const ViewportEntry& entry) const {
|
||||
Rendering::RenderSurface surface(entry.width, entry.height);
|
||||
surface.SetColorAttachment(entry.colorView);
|
||||
surface.SetDepthAttachment(entry.depthView);
|
||||
surface.SetColorStateBefore(entry.colorState);
|
||||
surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
|
||||
return surface;
|
||||
}
|
||||
|
||||
void RenderViewportEntry(
|
||||
ViewportEntry& entry,
|
||||
const Components::Scene* scene,
|
||||
@@ -239,6 +351,27 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
Rendering::RenderSurface surface = BuildSurface(entry);
|
||||
|
||||
if (entry.kind == EditorViewportKind::Scene) {
|
||||
if (!EnsureSceneViewCamera()) {
|
||||
entry.statusText = "Scene view camera is unavailable";
|
||||
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
ApplySceneViewCameraController();
|
||||
if (!m_sceneRenderer->Render(*scene, m_sceneViewCamera.camera, renderContext, surface)) {
|
||||
entry.statusText = "Scene renderer failed";
|
||||
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
entry.colorState = RHI::ResourceStates::PixelShaderResource;
|
||||
entry.statusText.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto cameras = scene->FindObjectsOfType<Components::CameraComponent>();
|
||||
if (cameras.empty()) {
|
||||
entry.statusText = "No camera in scene";
|
||||
@@ -246,12 +379,6 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
Rendering::RenderSurface surface(entry.width, entry.height);
|
||||
surface.SetColorAttachment(entry.colorView);
|
||||
surface.SetDepthAttachment(entry.depthView);
|
||||
surface.SetColorStateBefore(entry.colorState);
|
||||
surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource);
|
||||
|
||||
if (!m_sceneRenderer->Render(*scene, nullptr, renderContext, surface)) {
|
||||
entry.statusText = "Scene renderer failed";
|
||||
ClearViewport(entry, renderContext, 0.18f, 0.07f, 0.07f, 1.0f);
|
||||
@@ -332,6 +459,7 @@ private:
|
||||
RHI::D3D12Device* m_device = nullptr;
|
||||
std::unique_ptr<Rendering::SceneRenderer> m_sceneRenderer;
|
||||
std::array<ViewportEntry, 2> m_entries = {};
|
||||
SceneViewCameraState m_sceneViewCamera;
|
||||
};
|
||||
|
||||
} // namespace Editor
|
||||
|
||||
Reference in New Issue
Block a user