Files
HelloVulkan/App/main.cpp
T
atdunbg df4871a49b feat: 透明窗口支持 + 退出确认流程 + ImGui 总开关
用户自主改进批次:
- 透明窗口:GLFW_TRANSPARENT_FRAMEBUFFER + 窗口 opacity 0.5,
  swapchain composite alpha 按能力自适应(Opaque/Pre/PostMultiplied),
  Forward 清屏 alpha=0,tonemap 透传 HDR alpha——物体实体、背景透明
- 退出确认:Window 挂 WindowCloseCallback 发事件,App 侧 OnWindowClose
  置确认标记,OnRenderOverlay 弹 ImGui 确认框后 Close();
  主循环退出条件改为 m_Running(close 事件驱动)
- ImGui 总开关:` 键切换 ImGuiLayer::SetEnabled,禁用时跳过
  NewFrame/OnRenderOverlay/BuildDrawData 整段
- 帧率控制简化:去掉 LimitFps 开关,非 vsync 时恒按 target 限帧
  (默认 60,可调 ≥24);m_VSync 默认 false(Mailbox 不限速)
- 清理:Model.cpp const 修正,swapchain 日志带 present mode,
  GPU 遥测暂时注释

验证:0 error 0 warning,composite alpha 实测选 PreMultiplied(0x2),
present=Mailbox
2026-09-23 10:57:48 +08:00

297 lines
8.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Created by atdunbg on 2026/9/20.
//
// SandboxApp:第一个基于 Application 框架的程序。
//
// main 的全部职责:定义一个 Application 派生类并运行它。
// 窗口、渲染器、事件泵、帧计时、场景容器全部由框架管理;
// 这里只写"程序逻辑":创建实体、逐帧更新组件、事件响应。
//
#include <Engine/Core/Application.h>
#include <Engine/Core/Log.h>
#include <Engine/Core/Stats.h>
#include <Engine/ECS/Scene.h>
#include <Engine/ECS/EntityHandle.h>
#include <Engine/ECS/Components.h>
#include <Engine/Assets/AssetManager.h>
#include <Engine/Renderer/Renderer.h>
#include <imgui.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <GLFW/glfw3.h>
using namespace Engine;
using namespace Engine::ECS;
class SandboxApp final : public Application
{
public:
SandboxApp()
: Application("HelloVulkan", 800, 600)
{
}
protected:
// ---- 生命周期 ----
void OnInit() override
{
Scene& scene = GetScene();
// ImGui 叠加层(框架只做管线桥接,UI 内容全部在这里定义)
EnableImGui();
// 模型资产(AssetManager 负责加载/缓存/生命周期,返回句柄)
auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj");
// 地面:程序化纯色平面(半边长 15,中心 y=-2)
auto ground = GetAssets().CreatePlane(15.0f, 140, 140, 145);
m_Ground = scene.CreateEntity("Ground");
m_Ground.AddComponent<TransformComponent>()
.SetPosition(glm::vec3(0.0f, -4.5f, 0.0f))
.Compose();
m_Ground.AddComponent<MeshRendererComponent>(MeshRendererComponent{ground});
// 相机实体(primary = 当前渲染视角)
m_Camera = scene.CreateEntity("MainCamera");
auto& camera = m_Camera.AddComponent<CameraComponent>();
camera.primary = true;
camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f));
// 背包实体 × 3(悬浮在地面上方)
auto size = 1;
for (int i = 0; i < size; ++i)
{
for (int j = 0; j < size; j++)
{
auto e = scene.CreateEntity("Backpack" + std::to_string(i));
e.AddComponent<TransformComponent>()
.SetPosition(glm::vec3((i - size * 0.5) * 1.5f, 0.0f, (j - size * 0.5) * 1.5f))
.SetScale(glm::vec3(0.5f))
.Compose();
e.AddComponent<MeshRendererComponent>(MeshRendererComponent{backpack});
m_Spinners.push_back(e);
}
}
// 灯光实体(第一个灯 = 阴影光源)
m_KeyLight = scene.CreateEntity("KeyLight");
m_KeyLight.AddComponent<LightComponent>(
LightComponent{
glm::vec3(-0.5f, -0.6f, -0.35f),
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)
});
scene.CreateEntity("FillLight")
.AddComponent<LightComponent>(
LightComponent{
glm::vec3(0.5f, -0.5f, 0.3f),
glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)
});
}
void OnUpdate(f32 deltaTime) override
{
m_Time += deltaTime;
// 相机(WASD/Space/Shift 移动)
m_Camera.GetComponent<CameraComponent>()
.camera.Update(GetWindow(), deltaTime);
// 旋转动画:编辑 Transform 分量后 Compose 重算 world
for (size_t i = 0; i < m_Spinners.size(); ++i)
{
auto& t = m_Spinners[i].GetComponent<TransformComponent>();
t.rotation.y = m_Time * 60.0f + static_cast<f32>(i) * 40.0f;
t.scale = glm::vec3(0.5f);
t.Compose();
}
}
// ---- 事件 ----
// ---- 叠加层 UI(唯一允许出现 ImGui 控件的地方)----
void OnRenderOverlay() override
{
ImGui::ShowDemoWindow();
ImGui::Begin("Scene Debug");
// 阴影光源方向:拖动滑条实时看影子变化
if (auto* light = m_KeyLight.TryGetComponent<LightComponent>())
{
ImGui::Text("Key Light (shadow caster)");
ImGui::SliderFloat3("direction",
&light->direction.x, -1.0f, 1.0f);
if (ImGui::Button("reset"))
{
light->direction = glm::vec3(-0.5f, -0.6f, -0.35f);
}
}
// Tonemap + Bloom 参数(HDR 后处理输出端)
auto& renderer = GetRenderer();
ImGui::Separator();
ImGui::Text("Tonemap");
f32 exposure = renderer.GetExposure();
if (ImGui::SliderFloat("exposure", &exposure, 0.1f, 4.0f))
{
renderer.SetExposure(exposure);
}
bool aces = renderer.GetUseACES();
if (ImGui::Checkbox("ACES", &aces))
{
renderer.SetUseACES(aces);
}
ImGui::Text("Bloom");
f32 threshold = renderer.GetBloomThreshold();
if (ImGui::SliderFloat("threshold", &threshold, 0.0f, 2.0f))
{
renderer.SetBloomThreshold(threshold);
}
f32 strength = renderer.GetBloomStrength();
if (ImGui::SliderFloat("strength", &strength, 0.0f, 2.0f))
{
renderer.SetBloomStrength(strength);
}
// Pass 级 GPU 耗时(RG timestamp)
ImGui::Separator();
ImGui::Text("GPU Timings");
const auto& names = renderer.GetPassNames();
const auto& times = renderer.GetPassGpuMs();
for (size_t i = 0; i < names.size() && i < times.size(); ++i)
{
ImGui::Text(" %-12s %.3f ms", names[i].c_str(), times[i]);
}
// 帧率控制
auto& app = *this;
ImGui::Separator();
ImGui::Text("Frame");
bool vsync = app.GetVSync();
if (ImGui::Checkbox("VSync", &vsync))
{
app.SetVSync(vsync);
if (!vsync)
{
const f32 target = app.GetTargetFps();
app.SetTargetFps(target);
}
}
if (!vsync)
{
f32 target = app.GetTargetFps();
if (ImGui::SliderFloat("target fps", &target, 24.0f, 360.0f, "%.0f"))
{
app.SetTargetFps(target);
}
}
ImGui::Separator();
ImGui::Text("fps: %.1f frame: %llu",
1.0f / GetDeltaTime(),
static_cast<unsigned long long>(GetFrameCount()));
ImGui::End();
// 自定义重载事件: 重载关闭事件模态框
{
if (m_ShowQuitConfirm)
{
ImGui::OpenPopup("Should quit?");
m_ShowQuitConfirm = false;
}
ImGui::SetNextWindowPos(
ImGui::GetMainViewport()->GetCenter(),
ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Should quit?", nullptr,
ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::TextUnformatted("Quit the application?");
ImGui::Separator();
if (ImGui::Button("Yes", ImVec2(120, 0)))
{
m_QuitConfirmed = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No", ImVec2(120, 0)))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if (m_QuitConfirmed)
{
m_QuitConfirmed = false;
Close();
}
}
}
void OnWindowClose(const Event& event) override
{
m_ShowQuitConfirm = true;
}
void OnKeyPress(const Event& event) override
{
if (event.key == GLFW_KEY_GRAVE_ACCENT)
{
SetImGuiEnabled(!GetImGuiEnabled());
}
if (event.key == GLFW_KEY_F1)
{
STATS.PrintComparison();
}
}
void OnWindowResize(const Event& event) override
{
ENGINE_INFO("App: window resized to {}x{}", event.width, event.height);
}
private:
bool m_ShowQuitConfirm = false;
bool m_QuitConfirmed = false;
EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
EntityHandle m_Ground{};
EntityHandle m_KeyLight{};
std::vector<EntityHandle> m_Spinners;
f32 m_Time = 0.0f;
};
int main()
{
SandboxApp app;
try
{
app.Run();
}
catch (const std::exception& e)
{
ENGINE_FATAL("Exception: {}", e.what());
return -1;
}
return 0;
}