Environment settings become scene entities (UE Skybox/SkyLight semantics)
Skybox/SkyLight are now Actor-carried components instead of resident global settings, matching UE where SkyAtmosphere/SkyLight/fog are Actors added from Place Actors and configured in Details. - New SkyboxComponent (active + envPath asset slot) and SkyLightComponent (active + ambientColor); first-active entity wins per frame - SceneRenderer collects env from components into FrameSceneData (DrawSkybox/envPath/ambient); Renderer performs delta sync - env reloads only when the resolved path changes, neutral fallback when empty or unloadable - App-side env plumbing removed: kEnvPaths table and getEnvIndex/ setEnvIndex callbacks deleted; StatsPanel keeps ClearColor + debug view, env params move to Details - Serializer: scene-level Settings stores ClearColor only; env components serialize per entity; legacy v1 scenes (Environment header + Settings.AmbientColor/DrawSkybox) auto-migrate into an Environment entity on load for visual parity - Inspector: Skybox/Sky Light sections with .hdr asset slot dropdown and Add Component entries; Outliner "+ Create" popup offers Empty/Light/Camera/Environment
This commit is contained in:
+3
-39
@@ -108,9 +108,8 @@ void Editor::DoStop()
|
||||
{
|
||||
if (!m_Ctx.scene || !m_Ctx.assets || !m_Ctx.renderer) return;
|
||||
|
||||
std::string envPath;
|
||||
if (!Serialization::SceneSerializer::DeserializeFromString(
|
||||
*m_Ctx.scene, *m_Ctx.assets, m_PlaySnapshot, envPath))
|
||||
*m_Ctx.scene, *m_Ctx.assets, m_PlaySnapshot))
|
||||
{
|
||||
ENGINE_ERROR("PIE: snapshot restore failed (scene is now the "
|
||||
"played-out state)");
|
||||
@@ -445,16 +444,8 @@ void Editor::DoSaveScene()
|
||||
std::filesystem::create_directories(
|
||||
std::filesystem::path(path).parent_path(), ec);
|
||||
|
||||
// 环境路径:引擎序列化器只认字符串,查表由编辑器侧负责
|
||||
std::string envPath;
|
||||
if (m_Ctx.getEnvIndex)
|
||||
{
|
||||
const int idx = m_Ctx.getEnvIndex();
|
||||
if (idx >= 0 && idx < kEnvCount) envPath = kEnvPaths[idx];
|
||||
}
|
||||
|
||||
if (Serialization::SceneSerializer::Serialize(*m_Ctx.scene, *m_Ctx.assets,
|
||||
path, envPath))
|
||||
path))
|
||||
{
|
||||
ENGINE_INFO("Editor: scene saved to '{}'", path);
|
||||
}
|
||||
@@ -472,10 +463,8 @@ void Editor::DoLoadScene()
|
||||
return;
|
||||
}
|
||||
|
||||
std::string envPath;
|
||||
if (!Serialization::SceneSerializer::Deserialize(*m_Ctx.scene,
|
||||
*m_Ctx.assets, path,
|
||||
envPath))
|
||||
*m_Ctx.assets, path))
|
||||
{
|
||||
ENGINE_ERROR("Editor: failed to load scene '{}'", path);
|
||||
return;
|
||||
@@ -484,29 +473,4 @@ void Editor::DoLoadScene()
|
||||
|
||||
// 旧实体已销毁:清选中(连带清描边,见 SelectEntity)
|
||||
SelectEntity(entt::null);
|
||||
|
||||
// 环境恢复:场景带环境且命中环境表才切换(镜像 StatsPanel 同步路径)
|
||||
if (!envPath.empty() && m_Ctx.getEnvIndex && m_Ctx.setEnvIndex)
|
||||
{
|
||||
int found = -1;
|
||||
for (int i = 0; i < kEnvCount; ++i)
|
||||
{
|
||||
if (envPath == kEnvPaths[i]) { found = i; break; }
|
||||
}
|
||||
if (found >= 0)
|
||||
{
|
||||
m_Ctx.setEnvIndex(found);
|
||||
const auto env = m_Ctx.assets->LoadEnvironment(kEnvPaths[found]);
|
||||
if (env.Valid())
|
||||
{
|
||||
m_Ctx.renderer->SetEnvironment(
|
||||
m_Ctx.assets->GetEnvironment(env));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ENGINE_WARN("Editor: scene env '{}' not in env table, "
|
||||
"keeping current", envPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,6 @@
|
||||
#include <Engine/Renderer/EditorCamera.h>
|
||||
#include <Engine/Renderer/SceneTypes.h> // kDisplayViewport / kDisplayDirect
|
||||
|
||||
#include <functional>
|
||||
|
||||
// 可切换环境(IBL 动态切换):StatsPanel 的 Combo 与 SandboxApp 的
|
||||
// 加载逻辑共用这一张表(下标即身份)。
|
||||
inline constexpr const char *kEnvPaths[] = {
|
||||
"assets/textures/venice_dawn_1_4k.hdr",
|
||||
"assets/textures/rooitou_park_4k.hdr",
|
||||
"assets/textures/pink_sunrise_4k.hdr",
|
||||
"assets/textures/birchwood_4k.hdr",
|
||||
};
|
||||
inline constexpr const char *kEnvNames[] = {
|
||||
"Venice Dawn", "Rooitou Park", "Pink Sunrise", "Birchwood",
|
||||
};
|
||||
inline constexpr int kEnvCount = 4;
|
||||
inline constexpr int kDefaultEnv = 0;
|
||||
|
||||
// 沙盒特有状态(面板可选读取,空指针 = 该面板区块不显示)
|
||||
struct EditorContext {
|
||||
// 引擎门面
|
||||
@@ -45,9 +29,6 @@ struct EditorContext {
|
||||
// PIE 运行态(Play-In-Editor):Editor 持有,只读指针。
|
||||
// 面板门控用——运行态禁 Gizmo/编辑器热键,渲染源已是 primary 相机
|
||||
const bool *playState = nullptr;
|
||||
|
||||
std::function<int()> getEnvIndex; // 当前环境表下标
|
||||
std::function<void(int)> setEnvIndex;
|
||||
};
|
||||
|
||||
#endif //HELLOVULKAN_EDITORCONTEXT_H
|
||||
|
||||
@@ -148,11 +148,39 @@ void HierarchyPanel::Draw(const EditorContext& ctx, entt::entity& selected)
|
||||
{
|
||||
ImGui::Begin("Outliner");
|
||||
|
||||
// 工具栏:创建实体(在遍历之前,不修改遍历中的注册表结构)
|
||||
if (ImGui::Button("+ Create Entity"))
|
||||
// 工具栏:创建实体(在遍历之前,不修改遍历中的注册表结构)。
|
||||
// Place Actors 简化语义:常用类型一键带组件,其余 Add Component 补
|
||||
if (ImGui::Button("+ Create"))
|
||||
{
|
||||
const auto e = ctx.scene->CreateEntity("Entity");
|
||||
selected = e;
|
||||
ImGui::OpenPopup("create_entity");
|
||||
}
|
||||
if (ImGui::BeginPopup("create_entity"))
|
||||
{
|
||||
if (ImGui::MenuItem("Empty"))
|
||||
{
|
||||
selected = ctx.scene->CreateEntity("Entity");
|
||||
}
|
||||
if (ImGui::MenuItem("Light"))
|
||||
{
|
||||
auto e = ctx.scene->CreateEntity("Light");
|
||||
e.AddComponent<Engine::ECS::LightComponent>();
|
||||
selected = e;
|
||||
}
|
||||
if (ImGui::MenuItem("Camera"))
|
||||
{
|
||||
auto e = ctx.scene->CreateEntity("Camera");
|
||||
e.AddComponent<Engine::ECS::CameraComponent>();
|
||||
selected = e;
|
||||
}
|
||||
if (ImGui::MenuItem("Environment"))
|
||||
{
|
||||
// UE 新关卡环境集简化:Skybox(背景)+ SkyLight(环境光)一体
|
||||
auto e = ctx.scene->CreateEntity("Environment");
|
||||
e.AddComponent<Engine::ECS::SkyboxComponent>();
|
||||
e.AddComponent<Engine::ECS::SkyLightComponent>();
|
||||
selected = e;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
@@ -58,6 +58,39 @@ namespace
|
||||
return s_Files;
|
||||
}
|
||||
|
||||
// 递归收集 assets/ 下环境贴图(.hdr,Skybox 资产槽用;会话级缓存同上)
|
||||
const std::vector<std::string>& AssetEnvFiles()
|
||||
{
|
||||
static std::vector<std::string> s_Files;
|
||||
static bool s_Cached = false;
|
||||
if (!s_Cached)
|
||||
{
|
||||
s_Cached = true;
|
||||
namespace fs = std::filesystem;
|
||||
if (fs::exists("assets"))
|
||||
{
|
||||
for (const auto& entry :
|
||||
fs::recursive_directory_iterator("assets"))
|
||||
{
|
||||
if (!entry.is_regular_file())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::string ext = entry.path().extension().string();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(),
|
||||
[](unsigned char c)
|
||||
{ return std::tolower(c); });
|
||||
if (ext == ".hdr")
|
||||
{
|
||||
s_Files.push_back(entry.path().generic_string());
|
||||
}
|
||||
}
|
||||
std::sort(s_Files.begin(), s_Files.end());
|
||||
}
|
||||
}
|
||||
return s_Files;
|
||||
}
|
||||
|
||||
// InputText 需要 char 缓冲;提交时写回 std::string
|
||||
bool InputTextString(const char* label, std::string& str)
|
||||
{
|
||||
@@ -198,6 +231,9 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
menuItem(static_cast<Engine::ECS::MeshRendererComponent*>(nullptr),
|
||||
"Mesh Renderer");
|
||||
menuItem(static_cast<Engine::ECS::LightComponent*>(nullptr), "Light");
|
||||
menuItem(static_cast<Engine::ECS::SkyboxComponent*>(nullptr), "Skybox");
|
||||
menuItem(static_cast<Engine::ECS::SkyLightComponent*>(nullptr),
|
||||
"Sky Light");
|
||||
menuItem(static_cast<Engine::ECS::CameraComponent*>(nullptr), "Camera");
|
||||
menuItem(static_cast<Engine::ECS::AnimationComponent*>(nullptr), "Animation");
|
||||
menuItem(static_cast<Engine::ECS::NameComponent*>(nullptr), "Name");
|
||||
@@ -417,6 +453,78 @@ void InspectorPanel::Draw(const EditorContext& ctx, entt::entity selected)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Skybox(UE Skybox Actor 语义:环境贴图 = 实体资产槽)----
|
||||
if (auto* sb = reg.try_get<Engine::ECS::SkyboxComponent>(selected))
|
||||
{
|
||||
bool removeSb = false;
|
||||
if (ComponentHeader("Skybox", &sb->active, true, &removeSb))
|
||||
{
|
||||
if (!sb->active)
|
||||
{
|
||||
ImGui::BeginDisabled(true);
|
||||
}
|
||||
// 环境贴图资产槽(模型槽同款:下拉列 assets/ 下 .hdr,选中
|
||||
// 即加载;空 = 无环境,只露清屏色)。渲染侧由 Renderer 按
|
||||
// 帧数据差量同步(路径变化才重载),此处零额外同步
|
||||
{
|
||||
const auto& s_EnvFiles = AssetEnvFiles();
|
||||
std::vector<const char*> opts;
|
||||
opts.push_back("<none>");
|
||||
for (const auto& f : s_EnvFiles)
|
||||
{
|
||||
opts.push_back(f.c_str());
|
||||
}
|
||||
int sel = 0;
|
||||
for (size_t k = 0; k < s_EnvFiles.size(); ++k)
|
||||
{
|
||||
if (s_EnvFiles[k] == sb->envPath)
|
||||
{
|
||||
sel = static_cast<int>(k) + 1;
|
||||
}
|
||||
}
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
if (ImGui::Combo("##env_slot", &sel, opts.data(),
|
||||
static_cast<int>(opts.size())))
|
||||
{
|
||||
sb->envPath = (sel <= 0)
|
||||
? std::string{}
|
||||
: s_EnvFiles[static_cast<size_t>(sel) - 1];
|
||||
}
|
||||
ImGui::TextDisabled("(empty = clear color only)");
|
||||
}
|
||||
if (!sb->active)
|
||||
{
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
if (removeSb)
|
||||
{
|
||||
reg.remove<Engine::ECS::SkyboxComponent>(selected);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- SkyLight(UE SkyLight 语义:全局环境光)----
|
||||
if (auto* sl = reg.try_get<Engine::ECS::SkyLightComponent>(selected))
|
||||
{
|
||||
bool removeSl = false;
|
||||
if (ComponentHeader("Sky Light", &sl->active, true, &removeSl))
|
||||
{
|
||||
if (!sl->active)
|
||||
{
|
||||
ImGui::BeginDisabled(true);
|
||||
}
|
||||
ImGui::ColorEdit4("ambient", &sl->ambientColor.x);
|
||||
if (!sl->active)
|
||||
{
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
if (removeSl)
|
||||
{
|
||||
reg.remove<Engine::ECS::SkyLightComponent>(selected);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Camera ----
|
||||
if (auto* cam = reg.try_get<Engine::ECS::CameraComponent>(selected))
|
||||
{
|
||||
|
||||
+10
-24
@@ -120,34 +120,20 @@ void StatsPanel::Draw(const EditorContext& ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Environment:IBL 环境 + 调试视图(决定"看什么")----
|
||||
// ---- Environment:清屏色 + 调试视图(决定"看什么")。
|
||||
// 环境贴图/环境光已迁为实体组件(Skybox/SkyLight),在 Details 配 ----
|
||||
if (ImGui::CollapsingHeader("Environment", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
bool& drawSkybox = ctx.scene->GetSettings().DrawSkybox;
|
||||
ImGui::Checkbox("DrawSkybox", &drawSkybox);
|
||||
if (drawSkybox && ctx.getEnvIndex)
|
||||
{
|
||||
int envIdx = ctx.getEnvIndex();
|
||||
if (ImGui::Combo("env", &envIdx, kEnvNames, kEnvCount))
|
||||
{
|
||||
ctx.setEnvIndex(envIdx);
|
||||
const auto env = ctx.assets->LoadEnvironment(kEnvPaths[envIdx]);
|
||||
ctx.renderer->SetEnvironment(ctx.assets->GetEnvironment(env));
|
||||
}
|
||||
ImGui::ColorEdit4("ClearColor",
|
||||
glm::value_ptr(ctx.scene->GetSettings().ClearColor));
|
||||
|
||||
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
||||
const auto& viewNames = renderer.GetDebugViewNames();
|
||||
int view = renderer.GetDebugView();
|
||||
if (ImGui::Combo("Debug View", &view, viewNames.data(),
|
||||
static_cast<int>(viewNames.size())))
|
||||
{
|
||||
renderer.SetDebugView(view);
|
||||
}
|
||||
}
|
||||
else
|
||||
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
||||
const auto& viewNames = renderer.GetDebugViewNames();
|
||||
int view = renderer.GetDebugView();
|
||||
if (ImGui::Combo("Debug View", &view, viewNames.data(),
|
||||
static_cast<int>(viewNames.size())))
|
||||
{
|
||||
ImGui::ColorEdit4("ClearColor",
|
||||
glm::value_ptr(ctx.scene->GetSettings().ClearColor));
|
||||
renderer.SetDebugView(view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -27,7 +27,7 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "Editor.h"
|
||||
#include "EditorContext.h" // kEnvPaths / kDefaultEnv(与 StatsPanel 共用)
|
||||
#include "EditorContext.h"
|
||||
|
||||
using namespace Engine;
|
||||
|
||||
@@ -117,8 +117,6 @@ namespace
|
||||
ctx.assets = &GetAssets();
|
||||
ctx.app = this;
|
||||
ctx.editorCamera = &GetRenderer().GetEditorCamera();
|
||||
ctx.getEnvIndex = [this] { return m_EnvIndex; };
|
||||
ctx.setEnvIndex = [this](int index) { m_EnvIndex = index; };
|
||||
// 继承原沙盒默认:初始暂停(StatsPanel 的 Pause Simulation 开关恢复)
|
||||
Time::SetTimeScale(0.0f);
|
||||
m_Editor->Init(ctx);
|
||||
@@ -135,7 +133,6 @@ namespace
|
||||
|
||||
private:
|
||||
std::unique_ptr<Editor> m_Editor = std::make_unique<Editor>();
|
||||
int m_EnvIndex = kDefaultEnv;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user