feat: move editor to project / add post processing etc

This commit is contained in:
2026-10-23 20:54:12 +02:00
parent f7e7c56392
commit a3d3da4a67
104 changed files with 11750 additions and 2675 deletions
+239
View File
@@ -0,0 +1,239 @@
#include <editor/Editor.h>
#include <algorithm>
#include <filesystem>
#include <vector>
#include <imgui.h>
#include <spdlog/spdlog.h>
#include <destrum/App.h>
#include <destrum/Assets/AssetTypes.h>
#include <destrum/Components/MeshRendererComponent.h>
#include <destrum/Components/Physics/Collider.h>
#include <destrum/Components/Physics/Rigidbody.h>
#include <destrum/FS/AssetFS.h>
#include <destrum/ObjectModel/GameObject.h>
#include <destrum/ObjectModel/Transform.h>
#include <destrum/Scene/ModelSpawner.h>
#include <destrum/Scene/Scene.h>
#include <destrum/Serialization/MaterialSerialization.h>
#include <destrum/Serialization/PrefabSerializer.h>
#include <destrum/Util/ImGuiUtils.h>
#include <destrum/Util/ModelDoc.h>
void Editor::ApplyViewportResize()
{
const glm::ivec2 desired = m_Viewport.desiredSize;
const VkExtent3D current = m_App.getRenderer().getDrawImage().extent;
if (desired.x <= 0 || desired.y <= 0 ||
(static_cast<int>(current.width) == desired.x && static_cast<int>(current.height) == desired.y)) {
return;
}
// While a dock splitter is dragged the old image is stretched, recreating it every frame is wasteful
if (ImGui::GetIO().MouseDown[ImGuiMouseButton_Left]) {
return;
}
m_App.resizeRenderTarget(desired);
}
void Editor::DrawViewport()
{
m_Viewport.mouseClaimed = false;
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{0.0f, 0.0f});
const bool visible = ImGui::Begin("Viewport", nullptr, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
ImGui::PopStyleVar();
if (!visible) {
m_Viewport.hovered = false;
m_Viewport.focused = false;
m_Viewport.hoverPixel.reset();
ImGui::End();
return;
}
const ImVec2 origin = ImGui::GetCursorScreenPos();
const ImVec2 available = ImGui::GetContentRegionAvail();
const ImVec2 framebufferScale = ImGui::GetIO().DisplayFramebufferScale;
m_Viewport.min = {origin.x, origin.y};
m_Viewport.size = {std::max(available.x, 1.0f), std::max(available.y, 1.0f)};
m_Viewport.desiredSize = {
std::max(static_cast<int>(available.x * framebufferScale.x), 1),
std::max(static_cast<int>(available.y * framebufferScale.y), 1),
};
const GPUImage& drawImage = m_App.getRenderer().getDrawImage();
if (const ImTextureID texture = m_App.getImguiPass().getFrameTexture(drawImage.imageView); texture != ImTextureID{}) {
ImGui::Image(texture, ImVec2{m_Viewport.size.x, m_Viewport.size.y});
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(ImGuiUtils::AssetPayload)) {
OnAssetDropped(static_cast<const char*>(payload->Data));
}
ImGui::EndDragDropTarget();
}
}
m_Viewport.hovered = ImGui::IsWindowHovered();
// ImGui only focuses windows on a left click, the camera looks around with the right button
if (m_Viewport.hovered && (ImGui::IsMouseClicked(ImGuiMouseButton_Right) || ImGui::IsMouseClicked(ImGuiMouseButton_Middle))) {
ImGui::SetWindowFocus();
}
m_Viewport.focused = ImGui::IsWindowFocused();
const ImVec2 mouse = ImGui::GetMousePos();
const glm::vec2 uv = (glm::vec2{mouse.x, mouse.y} - m_Viewport.min) / m_Viewport.size;
const bool insideImage = uv.x >= 0.0f && uv.y >= 0.0f && uv.x < 1.0f && uv.y < 1.0f;
m_Viewport.hoverPixel.reset();
if (m_Viewport.hovered && insideImage && !m_Camera.IsLooking()) {
const VkExtent3D extent = drawImage.extent;
m_Viewport.hoverPixel = glm::ivec2{uv * glm::vec2{static_cast<float>(extent.width), static_cast<float>(extent.height)}};
}
ImGui::SetCursorScreenPos(origin);
if (Scene* scene = CurrentScene(); scene != nullptr && m_View.gizmos && UsesEditorCamera()) {
DrawGizmos(*scene);
}
RequestPick();
ImGui::End();
}
void Editor::DrawGizmos(Scene& scene)
{
GameObject* selected = scene.GetSelectedObject();
if (selected == nullptr) {
return;
}
if (RectGizmo::CanManipulate(*selected)) {
const VkExtent3D extent = m_App.getRenderer().getDrawImage().extent;
m_RectGizmo.Manipulate(*selected, glm::vec2{static_cast<float>(extent.width), static_cast<float>(extent.height)},
m_Viewport.min, m_Viewport.size);
m_Viewport.mouseClaimed = m_Viewport.mouseClaimed || m_RectGizmo.IsInteracting();
return;
}
m_TransformGizmo.Manipulate(*selected, m_Camera.GetCamera(), m_Viewport.min, m_Viewport.size);
m_Viewport.mouseClaimed = m_Viewport.mouseClaimed || m_TransformGizmo.IsOver() || m_TransformGizmo.IsUsing();
}
void Editor::RequestPick()
{
const bool picking = m_PlayMode.IsEditing() || m_PlayMode.IsPaused();
if (!picking || !m_Viewport.hoverPixel || m_Viewport.mouseClaimed || !ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
return;
}
m_App.getRenderer().requestSelectionPick(*m_Viewport.hoverPixel);
m_Viewport.pickSelectsParent = ImGui::GetIO().KeyAlt;
}
void Editor::ApplyPick()
{
const auto picked = m_App.getRenderer().takeSelectionPick();
Scene* scene = CurrentScene();
if (!picked || scene == nullptr) {
return;
}
GameObject* object = scene->FindGameObject(*picked);
if (object != nullptr && m_Viewport.pickSelectsParent) {
if (const Transform* parent = object->GetTransform().GetParent(); parent != nullptr && parent->GetOwner() != nullptr) {
object = parent->GetOwner();
}
}
scene->SetSelectedObject(object != nullptr ? object->GetId() : InvalidObjectId);
}
void Editor::OnAssetDropped(std::string_view path)
{
Scene* scene = CurrentScene();
if (scene == nullptr) {
return;
}
if (AssetTypes::IsScene(path)) {
OpenScene(path);
return;
}
const Camera& view = m_Camera.GetCamera();
const glm::vec3 spawnPosition = view.GetPosition() + view.GetForward() * 5.0f;
const std::string name = std::filesystem::path{path}.stem().string();
try {
if (AssetTypes::IsModel(path)) {
ModelDoc::LoadOptions options{};
options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
options.loadMaterials = true;
GameObject* model = ModelSpawner::SpawnStaticModel(
*scene, m_App.getGfxDevice(), m_App.getResources(), path, options, name);
model->GetTransform().SetWorldPosition(spawnPosition);
scene->SetSelectedObject(model->GetId());
} else if (AssetTypes::IsPrefab(path)) {
GameObject* instance = PrefabSerializer::Instantiate(*scene, AssetFS::GetInstance().GetFullPath(path).string());
if (instance == nullptr) {
SetStatus("Could not instantiate " + std::string{path});
return;
}
instance->GetTransform().SetWorldPosition(spawnPosition);
scene->SetSelectedObject(instance->GetId());
} else if (AssetTypes::IsMaterial(path)) {
GameObject* selected = scene->GetSelectedObject();
if (selected == nullptr) {
SetStatus("Select an object to give it " + std::string{path});
return;
}
const MaterialID material = MaterialSerialization::LoadFile(m_App.getResources(), m_App.getGfxDevice(), path);
std::vector<GameObject*> pending{selected};
while (!pending.empty()) {
GameObject* object = pending.back();
pending.pop_back();
if (auto* meshRenderer = object->GetComponent<MeshRendererComponent>()) {
meshRenderer->SetMaterialID(material);
}
for (const Transform* child : object->GetTransform().GetChildren()) {
if (child != nullptr && child->GetOwner() != nullptr) {
pending.push_back(child->GetOwner());
}
}
}
} else {
return;
}
m_EditPending = true;
} catch (const std::exception& exception) {
spdlog::error("Could not use {}: {}", path, exception.what());
SetStatus("Could not use " + std::string{path});
}
}
void Editor::SubmitColliderLines(Scene& scene) const
{
// Triggers orange, then colored by body type: static blue, dynamic green, kinematic purple
for (const auto& object : scene.GetObjects()) {
if (object == nullptr || object->IsBeingDestroyed() || !object->IsActiveInHierarchy()) {
continue;
}
const auto* rigidbody = object->GetComponent<Rigidbody>();
const RigidbodyType type = rigidbody != nullptr ? rigidbody->GetType() : RigidbodyType::Static;
for (const auto& component : object->GetComponents()) {
const auto* collider = dynamic_cast<const Collider*>(component.get());
if (collider == nullptr || collider->IsBeingDestroyed() || !collider->isEnabled()) {
continue;
}
glm::vec4 color{0.1f, 0.8f, 1.0f, 1.0f};
if (collider->IsTrigger()) {
color = {1.0f, 0.75f, 0.1f, 1.0f};
} else if (type == RigidbodyType::Dynamic) {
color = {0.3f, 1.0f, 0.3f, 1.0f};
} else if (type == RigidbodyType::Kinematic) {
color = {0.8f, 0.4f, 1.0f, 1.0f};
}
collider->DrawDebugLines(color);
}
}
}