add auto exposure

This commit is contained in:
2026-02-26 18:17:12 +08:00
parent 99bbf1eb5a
commit 56da5ebef7
17 changed files with 909 additions and 807 deletions

View File

@ -16,7 +16,7 @@ file(GLOB_RECURSE SRC_SOURCE ./**.cpp)
add_executable(${PROJECT_NAME} ${SRC_SOURCE}) add_executable(${PROJECT_NAME} ${SRC_SOURCE})
target_link_libraries(${PROJECT_NAME} PRIVATE Prism-shared) target_link_libraries(${PROJECT_NAME} PRIVATE Prism-static)
# Enable ImGui Docking space # Enable ImGui Docking space
target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_DOCKSPACE) target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_DOCKSPACE)

View File

@ -19,141 +19,8 @@
namespace Prism namespace Prism
{ {
enum class PropertyFlag
{
None = 0, ColorProperty = 1, SliderProperty = 2, DragProperty = 4
};
bool Property(const std::string& name, bool& value)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
const std::string id = "##" + name;
const bool isChanged = ImGui::Checkbox(id.c_str(), &value);
ImGui::PopItemWidth();
ImGui::NextColumn();
return isChanged;
}
bool Property(const std::string& name, float& value, const float min = -1.0f, const float max = 1.0f, PropertyFlag flags = PropertyFlag::None)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
const std::string id = "##" + name;
bool isChanged = false;
if (flags == PropertyFlag::SliderProperty)
isChanged = ImGui::SliderFloat(id.c_str(), &value, min, max);
else
isChanged = ImGui::DragFloat(id.c_str(), &value, 1.0f, min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
return isChanged;
}
bool Property(const std::string& name, glm::vec3& value, const float min = -1.0f, const float max = 1.0f, PropertyFlag flags = PropertyFlag::None)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
bool isChanged = false;
if (flags == PropertyFlag::ColorProperty)
isChanged = ImGui::ColorEdit3(id.c_str(), glm::value_ptr(value), ImGuiColorEditFlags_NoInputs);
else if (flags == PropertyFlag::SliderProperty)
isChanged = ImGui::SliderFloat3(id.c_str(), glm::value_ptr(value), min, max);
else
isChanged = ImGui::DragFloat3(id.c_str(), glm::value_ptr(value), 1.0f, min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
return isChanged;
}
bool Property(const std::string& name, glm::vec3& value, PropertyFlag flags)
{
return Property(name, value, -1.0f, 1.0f, flags);
}
bool Property(const std::string& name, glm::vec4& value, const float min = -1.0f, const float max = 1.0f, PropertyFlag flags = PropertyFlag::None)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
std::string id = "##" + name;
bool isChanged = false;
if (flags == PropertyFlag::ColorProperty)
isChanged = ImGui::ColorEdit4(id.c_str(), glm::value_ptr(value), ImGuiColorEditFlags_NoInputs);
else if (flags == PropertyFlag::SliderProperty)
isChanged = ImGui::SliderFloat4(id.c_str(), glm::value_ptr(value), min, max);
else
isChanged = ImGui::DragFloat4(id.c_str(), glm::value_ptr(value), 1.0f, min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
return isChanged;
}
bool Property(const std::string& name, glm::vec4& value, const PropertyFlag flags)
{
return Property(name, value, -1.0f, 1.0f, flags);
}
bool Property(const std::string& name, glm::vec2& value, const float min, const float max, PropertyFlag flags)
{
ImGui::Text(name.c_str());
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
const std::string id = "##" + name;
bool isChanged = false;
if (flags == PropertyFlag::SliderProperty)
isChanged = ImGui::SliderFloat2(id.c_str(), glm::value_ptr(value), min, max);
else
isChanged = ImGui::DragFloat2(id.c_str(), glm::value_ptr(value), 1.0f, min, max);
ImGui::PopItemWidth();
ImGui::NextColumn();
return isChanged;
}
bool Property(const std::string& name, glm::vec2& value, const PropertyFlag flags)
{
return Property(name, value, -1.0f, 1.0f, flags);
}
static void ImGuiShowHelpMarker(const char* desc)
{
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(desc);
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
EditorLayer::EditorLayer() EditorLayer::EditorLayer()
: m_SceneType(SceneType::Model), m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f)) : m_EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f)), m_SceneType(SceneType::Model)
{ {
} }
@ -166,13 +33,15 @@ namespace Prism
// Editor // Editor
m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga"); m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga");
m_PlayButtonTex = Texture2D::Create("assets/editor/PlayButton.png"); m_PlayButtonTex = Texture2D::Create("assets/editor/PlayButton.png");
m_PauseButtonTex = Texture2D::Create("assets/editor/PauseButton.png");
m_StopButtonTex = Texture2D::Create("assets/editor/StopButton.png");
m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(m_EditorScene); m_SceneHierarchyPanel = CreateScope<SceneHierarchyPanel>(m_EditorScene);
m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1)); m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1));
m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1)); m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1));
UpdateWindowTitle("untitled Scene"); UpdateWindowTitle("untitled Scene");
m_AssetManagerPanel = CreateScope<AssetsManagerPanel>(); m_ContentBrowserPanel = CreateScope<ContentBrowserPanel>();
m_ObjectsPanel = CreateScope<ObjectsPanel>(); m_ObjectsPanel = CreateScope<ObjectsPanel>();
// OpenScene("assets/scenes/FPSDemo.scene"); // OpenScene("assets/scenes/FPSDemo.scene");
@ -285,8 +154,6 @@ namespace Prism
void EditorLayer::OnImGuiRender() void EditorLayer::OnImGuiRender()
{ {
static bool p_open = true; static bool p_open = true;
static bool opt_fullscreen = true; static bool opt_fullscreen = true;
static bool opt_padding = false; static bool opt_padding = false;
@ -325,6 +192,7 @@ namespace Prism
if (!opt_padding) if (!opt_padding)
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("DockSpace Demo", &p_open, window_flags); ImGui::Begin("DockSpace Demo", &p_open, window_flags);
{
if (!opt_padding) if (!opt_padding)
ImGui::PopStyleVar(); ImGui::PopStyleVar();
@ -408,7 +276,6 @@ namespace Prism
ImGui::Begin("Materials"); ImGui::Begin("Materials");
if (!m_SelectionContext.empty()) if (!m_SelectionContext.empty())
{ {
Entity selectedEntity = m_SelectionContext.front().Entity; Entity selectedEntity = m_SelectionContext.front().Entity;
@ -609,163 +476,114 @@ namespace Prism
} }
} }
} }
ImGui::End(); ImGui::End();
}
ImGui::End(); ImGui::End();
// Editor Panel ------------------------------------------------------------------------------
ImGui::Begin("Model");
ImGui::Begin("Environment");
ImGui::SliderFloat("Skybox LOD", &m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f);
ImGui::Columns(2);
ImGui::AlignTextToFramePadding();
auto& light = m_EditorScene->GetLight();
Property("Light Direction", light.Direction, PropertyFlag::SliderProperty);
Property("Light Radiance", light.Radiance, PropertyFlag::ColorProperty);
Property("Light Multiplier", light.Multiplier, 0.0f, 5.0f, PropertyFlag::SliderProperty);
Property("Radiance Prefiltering", m_RadiancePrefilter);
Property("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f, PropertyFlag::SliderProperty);
if (m_SceneState == SceneState::Edit)
{
float physics2DGravity = m_EditorScene->GetPhysics2DGravity();
if (Property("Gravity", physics2DGravity, -10000.0f, 10000.0f, PropertyFlag::DragProperty))
{
m_EditorScene->SetPhysics2DGravity(physics2DGravity);
}
}
else if (m_SceneState == SceneState::Play)
{
float physics2DGravity = m_RuntimeScene->GetPhysics2DGravity();
if (Property("Gravity", physics2DGravity, -10000.0f, 10000.0f, PropertyFlag::DragProperty))
{
m_RuntimeScene->SetPhysics2DGravity(physics2DGravity);
}
}
if (Property("Show Bounding Boxes", m_UIShowBoundingBoxes))
ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop);
if (m_UIShowBoundingBoxes && Property("On Top", m_UIShowBoundingBoxesOnTop))
ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop);
m_SceneHierarchyPanel->OnImGuiRender(); m_SceneHierarchyPanel->OnImGuiRender();
PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings); PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings);
SceneRenderer::OnImGuiRender(); SceneRenderer::OnImGuiRender();
AssetEditorPanel::OnImGuiRender(); AssetEditorPanel::OnImGuiRender();
ScriptEngine::OnImGuiRender(); ScriptEngine::OnImGuiRender();
m_AssetManagerPanel->OnImGuiRender(); m_ContentBrowserPanel->OnImGuiRender();
m_ObjectsPanel->OnImGuiRender(); m_ObjectsPanel->OnImGuiRender();
// Editor Panel ------------------------------------------------------------------------------
ImGui::Begin("Environment");
{
UI::PropertySlider("Skybox LOD", m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f);
UI::BeginPropertyGrid();
ImGui::AlignTextToFramePadding();
auto& light = m_EditorScene->GetLight();
UI::PropertySlider("Light Direction", light.Direction, -1.0f, 1.0f);
UI::PropertyColor("Light Radiance", light.Radiance);
UI::PropertySlider("Light Multiplier", light.Multiplier, 0.0f, 5.0f);
UI::PropertySlider("Exposure", m_EditorCamera.GetExposure(), 0.0f, 5.0f);
UI::Property("Radiance Prefiltering", m_RadiancePrefilter);
UI::PropertySlider("Env Map Rotation", m_EnvMapRotation, -360.0f, 360.0f);
if (m_SceneState == SceneState::Edit)
{
float physics2DGravity = m_EditorScene->GetPhysics2DGravity();
if (UI::Property("2D World Gravity", physics2DGravity, -10000.0f, 10000.0f))
{
m_EditorScene->SetPhysics2DGravity(physics2DGravity);
}
}
else if (m_SceneState == SceneState::Play)
{
float physics2DGravity = m_RuntimeScene->GetPhysics2DGravity();
if (UI::Property("2D World Gravity", physics2DGravity, -10000.0f, 10000.0f))
{
m_RuntimeScene->SetPhysics2DGravity(physics2DGravity);
}
}
if (UI::Property("Show Bounding Boxes", m_UIShowBoundingBoxes))
ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop);
if (m_UIShowBoundingBoxes && UI::Property("On Top", m_UIShowBoundingBoxesOnTop))
ShowBoundingBoxes(m_UIShowBoundingBoxes, m_UIShowBoundingBoxesOnTop);
const char* label = m_SelectionMode == SelectionMode::Entity ? "Entity" : "Mesh"; const char* label = m_SelectionMode == SelectionMode::Entity ? "Entity" : "Mesh";
if (ImGui::Button(label)) if (ImGui::Button(label))
{ {
m_SelectionMode = m_SelectionMode == SelectionMode::Entity ? SelectionMode::SubMesh : SelectionMode::Entity; m_SelectionMode = m_SelectionMode == SelectionMode::Entity ? SelectionMode::SubMesh : SelectionMode::Entity;
} }
ImGui::Columns(1); UI::EndPropertyGrid();
ImGui::End();
ImGui::Separator();
{
ImGui::Text("Mesh");
/*
auto meshComponent = m_MeshEntity.GetComponent<MeshComponent>();
std::string fullpath = meshComponent.Mesh ? meshComponent.Mesh->GetFilePath() : "None";
size_t found = fullpath.find_last_of("/\\");
std::string path = found != std::string::npos ? fullpath.substr(found + 1) : fullpath;
ImGui::Text(path.c_str());
ImGui::SameLine();
if (ImGui::Button("...##Mesh"))
{
std::string filename = Application::Get().OpenFile("");
if (!filename.empty())
{
auto newMesh = Ref<Mesh>::Create(filename);
// m_MeshMaterial.reset(new MaterialInstance(newMesh->GetMaterial()));
// m_MeshEntity->SetMaterial(m_MeshMaterial);
meshComponent.Mesh = newMesh;
} }
}
*/
}
ImGui::Separator();
if (ImGui::TreeNode("Shaders"))
{
auto& shaders = Shader::GetAllShaders();
for (auto& shader : shaders)
{
if (ImGui::TreeNode(shader->GetName().c_str()))
{
const std::string buttonName = shader->GetName();
if (ImGui::Button(("Reload##" + buttonName).c_str()))
{
PM_CLIENT_INFO("Reloading Shader: {0}", buttonName);
shader->Reload();
}
ImGui::TreePop();
}
}
ImGui::TreePop();
}
ImGui::Separator();
ImGui::End(); ImGui::End();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12, 0));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 4));
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(0, 0));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8f, 0.8f, 0.8f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.305f, 0.31f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.15f, 0.1505f, 0.151f, 0.5f));
ImGui::Begin("Toolbar");
ImGui::Begin("##tool_bar", NULL, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
{
float size = ImGui::GetWindowHeight() - 4.0F;
ImGui::SameLine((ImGui::GetWindowContentRegionMax().x / 2.0f) - (1.5f * (ImGui::GetFontSize() + ImGui::GetStyle().ItemSpacing.x)) - (size / 2.0f));
Ref<Texture2D> buttonTex = m_SceneState == SceneState::Play ? m_StopButtonTex : m_PlayButtonTex;
if (UI::ImageButton(buttonTex, ImVec2(size, size), ImVec2(0, 0), ImVec2(1, 1)))
{
if (m_SceneState == SceneState::Edit) if (m_SceneState == SceneState::Edit)
{
if (ImGui::ImageButton("editbutton", (ImTextureID)(m_PlayButtonTex->GetRendererID()), ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0,0,0,0), ImVec4(0.9f, 0.9f, 0.9f, 1.0f)))
{
OnScenePlay(); OnScenePlay();
} else
}
else if (m_SceneState == SceneState::Play)
{
if (ImGui::ImageButton("playbutton", (ImTextureID)(m_PlayButtonTex->GetRendererID()), ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), ImVec4(1.0f, 1.0f, 1.0f, 0.2f)))
{
OnSceneStop(); OnSceneStop();
} }
}
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::ImageButton("a", (ImTextureID)(m_PlayButtonTex->GetRendererID()), ImVec2(32, 32), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1.0f, 1.0f, 1.0f, 0.6f)))
if (UI::ImageButton(m_PauseButtonTex, ImVec2(size, size), ImVec2(0, 0), ImVec2(1, 1)))
{ {
PM_CORE_INFO("PLAY!"); if (m_SceneState == SceneState::Play)
{
//OnScenePause();
m_SceneState = SceneState::Pause;
} }
else if (m_SceneState == SceneState::Pause)
{
//OnSceneResume();
m_SceneState = SceneState::Play;
}
}
}
ImGui::PopStyleColor(3);
ImGui::PopStyleVar(2);
ImGui::End(); ImGui::End();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleVar();
ImGui::PopStyleVar();
ImGui::PopStyleVar();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::Begin("Viewport"); ImGui::Begin("Viewport");
{
m_ViewportPanelHovered = ImGui::IsWindowHovered(); m_ViewportPanelHovered = ImGui::IsWindowHovered();
m_ViewportPanelFocused = ImGui::IsWindowFocused(); m_ViewportPanelFocused = ImGui::IsWindowFocused();
@ -888,6 +706,7 @@ namespace Prism
} }
ImGui::EndDragDropTarget(); ImGui::EndDragDropTarget();
} }
}
ImGui::End(); ImGui::End();
ImGui::PopStyleVar(); ImGui::PopStyleVar();
@ -1079,11 +898,16 @@ namespace Prism
void EditorLayer::SelectEntity(Entity entity) void EditorLayer::SelectEntity(Entity entity)
{ {
if (!entity) return;
SelectedSubmesh selection; SelectedSubmesh selection;
if (entity.HasComponent<MeshComponent>()) if (entity.HasComponent<MeshComponent>())
{ {
if (auto mesh = entity.GetComponent<MeshComponent>().Mesh) auto& meshComponent = entity.GetComponent<MeshComponent>();
selection.Mesh = &mesh->GetSubmeshes()[0];
if (meshComponent.Mesh && meshComponent.Mesh->Type == AssetType::Mesh)
selection.Mesh = &meshComponent.Mesh->GetSubmeshes()[0];
} }
selection.Entity = entity; selection.Entity = entity;
m_SelectionContext.clear(); m_SelectionContext.clear();

View File

@ -6,7 +6,7 @@
#define EDITORLAYER_H #define EDITORLAYER_H
#include "Prism.h" #include "Prism.h"
#include "Prism/Editor/AssetsManagerPanel.h" #include "Prism/Editor/ContentBrowserPanel.h"
#include "Prism/Editor/ObjectsPanel.h" #include "Prism/Editor/ObjectsPanel.h"
#include "Prism/Editor/SceneHierachyPanel.h" #include "Prism/Editor/SceneHierachyPanel.h"
@ -58,7 +58,7 @@ namespace Prism
float GetSnapValue() const; float GetSnapValue() const;
private: private:
Scope<SceneHierarchyPanel> m_SceneHierarchyPanel; Scope<SceneHierarchyPanel> m_SceneHierarchyPanel;
Scope<AssetsManagerPanel> m_AssetManagerPanel; Scope<ContentBrowserPanel> m_ContentBrowserPanel;
Scope<ObjectsPanel> m_ObjectsPanel; Scope<ObjectsPanel> m_ObjectsPanel;
Ref<Scene> m_CurrentScene; Ref<Scene> m_CurrentScene;
@ -133,7 +133,7 @@ namespace Prism
// Editor resources // Editor resources
Ref<Texture2D> m_CheckerboardTex; Ref<Texture2D> m_CheckerboardTex;
Ref<Texture2D> m_PlayButtonTex; Ref<Texture2D> m_PlayButtonTex, m_StopButtonTex, m_PauseButtonTex;
// configure button // configure button

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

View File

@ -1,6 +1,6 @@
[Window][DockSpace Demo] [Window][DockSpace Demo]
Pos=0,0 Pos=0,0
Size=2066,1198 Size=2560,1566
Collapsed=0 Collapsed=0
[Window][Debug##Default] [Window][Debug##Default]
@ -9,32 +9,32 @@ Size=400,400
Collapsed=0 Collapsed=0
[Window][Scene Hierarchy] [Window][Scene Hierarchy]
Pos=1595,24 Pos=2089,24
Size=471,428 Size=471,563
Collapsed=0 Collapsed=0
DockId=0x00000009,0 DockId=0x00000009,0
[Window][Properties] [Window][Properties]
Pos=1595,454 Pos=2089,589
Size=471,744 Size=471,977
Collapsed=0 Collapsed=0
DockId=0x0000000A,0 DockId=0x0000000A,0
[Window][Scene Renderer] [Window][Scene Renderer]
Pos=0,648 Pos=0,843
Size=481,550 Size=481,723
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Window][Materials] [Window][Materials]
Pos=0,24 Pos=0,24
Size=481,622 Size=481,817
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][Script Engine Debug] [Window][Script Engine Debug]
Pos=1595,454 Pos=2089,589
Size=471,744 Size=471,977
Collapsed=0 Collapsed=0
DockId=0x0000000A,2 DockId=0x0000000A,2
@ -52,25 +52,25 @@ DockId=0x00000001,0
[Window][Viewport] [Window][Viewport]
Pos=483,58 Pos=483,58
Size=1110,722 Size=1604,955
Collapsed=0 Collapsed=0
DockId=0x0000000B,0 DockId=0x0000000B,0
[Window][Environment] [Window][Environment]
Pos=1595,454 Pos=2089,589
Size=471,744 Size=471,977
Collapsed=0 Collapsed=0
DockId=0x0000000A,1 DockId=0x0000000A,1
[Window][Project] [Window][Project]
Pos=483,782 Pos=483,1015
Size=1110,416 Size=1604,551
Collapsed=0 Collapsed=0
DockId=0x0000000C,0 DockId=0x0000000C,0
[Window][Objects] [Window][Objects]
Pos=483,782 Pos=483,1015
Size=1110,416 Size=1604,551
Collapsed=0 Collapsed=0
DockId=0x0000000C,1 DockId=0x0000000C,1
@ -79,17 +79,23 @@ Pos=189,113
Size=468,371 Size=468,371
Collapsed=0 Collapsed=0
[Window][##tool_bar]
Pos=483,24
Size=1604,32
Collapsed=0
DockId=0x00000001,0
[Docking][Data] [Docking][Data]
DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=150,89 Size=2066,1174 Split=X Selected=0x0C01D6D5 DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=0,58 Size=2560,1542 Split=X Selected=0x0C01D6D5
DockNode ID=0x00000007 Parent=0xC0DFADC4 SizeRef=1557,1542 Split=X DockNode ID=0x00000007 Parent=0xC0DFADC4 SizeRef=1557,1542 Split=X
DockNode ID=0x00000003 Parent=0x00000007 SizeRef=481,1542 Split=Y Selected=0x5D711C2C DockNode ID=0x00000003 Parent=0x00000007 SizeRef=481,1542 Split=Y Selected=0x5D711C2C
DockNode ID=0x00000005 Parent=0x00000003 SizeRef=481,817 Selected=0x5D711C2C DockNode ID=0x00000005 Parent=0x00000003 SizeRef=481,817 Selected=0x5D711C2C
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=481,723 Selected=0x68D924E0 DockNode ID=0x00000006 Parent=0x00000003 SizeRef=481,723 Selected=0x68D924E0
DockNode ID=0x00000004 Parent=0x00000007 SizeRef=1074,1542 Split=Y DockNode ID=0x00000004 Parent=0x00000007 SizeRef=1074,1542 Split=Y
DockNode ID=0x00000001 Parent=0x00000004 SizeRef=2560,32 CentralNode=1 HiddenTabBar=1 Selected=0x0C01D6D5 DockNode ID=0x00000001 Parent=0x00000004 SizeRef=2560,32 CentralNode=1 HiddenTabBar=1 Selected=0xE8CD5B84
DockNode ID=0x00000002 Parent=0x00000004 SizeRef=2560,1508 Split=Y Selected=0xC450F867 DockNode ID=0x00000002 Parent=0x00000004 SizeRef=2560,1508 Split=Y Selected=0xC450F867
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1401,669 HiddenTabBar=1 Selected=0xC450F867 DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1401,955 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,386 Selected=0x9C21DE82 DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,551 Selected=0x9C21DE82
DockNode ID=0x00000008 Parent=0xC0DFADC4 SizeRef=471,1542 Split=Y Selected=0x8C72BEA8 DockNode ID=0x00000008 Parent=0xC0DFADC4 SizeRef=471,1542 Split=Y Selected=0x8C72BEA8
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=315,563 Selected=0xB8729153 DockNode ID=0x00000009 Parent=0x00000008 SizeRef=315,563 Selected=0xB8729153
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=315,977 Selected=0x73E3D51F DockNode ID=0x0000000A Parent=0x00000008 SizeRef=315,977 Selected=0x73E3D51F

View File

@ -66,7 +66,8 @@ elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif () endif ()
include_directories(vendor/PhysX/physx/include) include_directories(vendor/PhysX/physx/include)
link_directories("vendor/PhysX/physx/bin/win.x86_64.vc143.md/${PHYSX_BUILD_TYPE}") # This is the path where PhysX libraries are installed set(PHYSX_LIB_DIR "vendor/PhysX/physx/bin/win.x86_64.vc143.md/${PHYSX_BUILD_TYPE}") # This is the path where PhysX libraries are installed
link_directories(${PHYSX_LIB_DIR})
if(CMAKE_BUILD_TYPE STREQUAL "Debug") if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Building snippet in debug with PhysX ${PHYSX_BUILD_TYPE} configuration") message("Building snippet in debug with PhysX ${PHYSX_BUILD_TYPE} configuration")
@ -139,6 +140,11 @@ target_compile_definitions(${STATIC_LIBRARY} PRIVATE
target_include_directories(${STATIC_LIBRARY} PUBLIC target_include_directories(${STATIC_LIBRARY} PUBLIC
${TARGET_INCLUDE_DIR} ${TARGET_INCLUDE_DIR}
) )
target_link_directories(${STATIC_LIBRARY} INTERFACE
${PHYSX_LIB_DIR}
)
target_link_libraries(${STATIC_LIBRARY} target_link_libraries(${STATIC_LIBRARY}
PRIVATE PRIVATE
${LINK_LIBRARIES_PRIVATE} ${LINK_LIBRARIES_PRIVATE}

View File

@ -102,7 +102,6 @@ namespace Prism
{ {
m_ImGuiLayer->Begin(); m_ImGuiLayer->Begin();
/*
ImGui::Begin("Renderer"); ImGui::Begin("Renderer");
const auto& caps = RendererAPI::GetCapabilities(); const auto& caps = RendererAPI::GetCapabilities();
ImGui::Text("Vendor: %s", caps.Vendor.c_str()); ImGui::Text("Vendor: %s", caps.Vendor.c_str());
@ -110,7 +109,6 @@ namespace Prism
ImGui::Text("Version: %s", caps.Version.c_str()); ImGui::Text("Version: %s", caps.Version.c_str());
ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds()); ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds());
ImGui::End(); ImGui::End();
*/
for (Layer* layer : m_LayerStack) for (Layer* layer : m_LayerStack)
layer->OnImGuiRender(); layer->OnImGuiRender();

View File

@ -0,0 +1,31 @@
//
// Created by Atdunbg on 2026/2/21.
//
#include "ImGui.h"
#include "Prism/Renderer/RendererAPI.h"
#include "Prism/Renderer/Texture.h"
namespace Prism::UI
{
void Image(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGui::PushID(texture->Handle);
if (RendererAPI::Current() == RendererAPIType::OpenGL)
{
ImGui::Image((ImTextureID)texture->GetRendererID(), size, uv0, uv1, tint_col, border_col);
}
ImGui::PopID();
}
bool ImageButton(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
if (RendererAPI::Current() == RendererAPIType::OpenGL)
{
return ImGui::ImageButton(std::string_view("##" + std::to_string(texture->Handle)).data(), (ImTextureID)texture->GetRendererID(), size, uv0, uv1, bg_col, tint_col);
}
return false;
}
}

View File

@ -9,8 +9,13 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include "../../Asset/Asset.h" #include "Prism/Asset/Asset.h"
#include "../../Asset/AssetsManager.h" #include "Prism/Asset/AssetsManager.h"
namespace Prism
{
class Texture2D;
}
namespace Prism::UI { namespace Prism::UI {
@ -259,6 +264,157 @@ namespace Prism::UI {
return modified; return modified;
} }
static bool PropertySlider(const char* label, float& value, const float min, const float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat(s_IDBuffer, &value, min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, glm::vec2& value, const float min, const float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat2(s_IDBuffer, glm::value_ptr(value), min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, glm::vec3& value, const float min, const float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat3(s_IDBuffer, glm::value_ptr(value), min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, glm::vec4& value, float min, float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat4(s_IDBuffer, glm::value_ptr(value), min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertyDropdown(const char* label, const char** options, int32_t optionCount, int32_t* selected)
{
const char* current = options[*selected];
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
bool changed = false;
const std::string id = "##" + std::string(label);
if (ImGui::BeginCombo(id.c_str(), current))
{
for (int i = 0; i < optionCount; i++)
{
const bool is_selected = (current == options[i]);
if (ImGui::Selectable(options[i], is_selected))
{
current = options[i];
*selected = i;
changed = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::NextColumn();
return changed;
}
static bool PropertyDropdown(const char* label, const std::vector<std::string>& options, int32_t optionCount, int32_t* selected)
{
const char* current = options[*selected].c_str();
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
bool changed = false;
const std::string id = "##" + std::string(label);
if (ImGui::BeginCombo(id.c_str(), current))
{
for (int i = 0; i < optionCount; i++)
{
const bool is_selected = (current == options[i]);
if (ImGui::Selectable(options[i].c_str(), is_selected))
{
current = options[i].c_str();
*selected = i;
changed = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::NextColumn();
return changed;
}
template<typename T> template<typename T>
static bool PropertyAssetReference(const char* label, Ref<T>& object, const AssetType supportedType) static bool PropertyAssetReference(const char* label, Ref<T>& object, const AssetType supportedType)
{ {
@ -353,6 +509,10 @@ namespace Prism::UI {
s_CheckboxCount = 0; s_CheckboxCount = 0;
} }
void Image(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col = ImVec4(0,0,0,0), const ImVec4& border_col = ImVec4(1,1,1,1));
bool ImageButton(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1));
} }
#endif //IMGUI_H #endif //IMGUI_H

View File

@ -2,18 +2,19 @@
// Created by Atdunbg on 2026/1/20. // Created by Atdunbg on 2026/1/20.
// //
#include "AssetsManagerPanel.h" #include "ContentBrowserPanel.h"
#include <filesystem> #include <filesystem>
#include "AssetEditorPanel.h" #include "AssetEditorPanel.h"
#include "imgui_internal.h"
#include "Prism/Core/Application.h" #include "Prism/Core/Application.h"
#include "Prism/Core/Input.h" #include "Prism/Core/Input.h"
#include "Prism/Core/Log.h" #include "Prism/Core/Log.h"
namespace Prism namespace Prism
{ {
AssetsManagerPanel::AssetsManagerPanel() ContentBrowserPanel::ContentBrowserPanel()
{ {
AssetsManager::SetAssetChangeCallback([&]() AssetsManager::SetAssetChangeCallback([&]()
{ {
@ -47,7 +48,7 @@ namespace Prism
static int s_ColumnCount = 10; static int s_ColumnCount = 10;
void AssetsManagerPanel::OnImGuiRender() void ContentBrowserPanel::OnImGuiRender()
{ {
ImGui::Begin("Project", nullptr, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar); ImGui::Begin("Project", nullptr, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar);
{ {
@ -73,7 +74,6 @@ namespace Prism
// Grid and list files // Grid and list files
ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetWindowHeight() - 60)); ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetWindowHeight() - 60));
// ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetContentRegionAvail().y));
{ {
ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30)); ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30));
RenderBreadCrumbs(); RenderBreadCrumbs();
@ -195,26 +195,51 @@ namespace Prism
ImGui::End(); ImGui::End();
} }
void AssetsManagerPanel::DrawDirectoryInfo(const AssetHandle& directory) void ContentBrowserPanel::DrawDirectoryInfo(const AssetHandle& directory)
{ {
const Ref<Directory>& dir = AssetsManager::GetAsset<Directory>(directory); const Ref<Directory>& dir = AssetsManager::GetAsset<Directory>(directory);
if (ImGui::TreeNode(dir->FileName.c_str())) ImGui::PushID((int)directory);
{
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
UpdateCurrentDirectory(directory);
for (AssetHandle child : dir->ChildDirectories) // 保存调用 TreeNodeEx 前的光标位置(箭头起始位置)
const ImVec2 arrow_pos = ImGui::GetCursorScreenPos();
// 设置标志:仅箭头点击展开,双击文本也可展开(可选)
constexpr ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
const bool node_open = ImGui::TreeNodeEx(dir->FileName.c_str(), flags, "%s", dir->FileName.c_str());
const ImRect item_rect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
// 计算箭头区域矩形(宽度为字体大小,高度与项一致)
const float arrow_width = ImGui::GetFontSize();
const ImRect arrow_rect(arrow_pos, ImVec2(arrow_pos.x + arrow_width, item_rect.Max.y));
// 检测左键单击(排除双击)
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
const ImVec2 mouse_pos = ImGui::GetMousePos();
// 仅当点击位置不在箭头区域内时,才触发选择
if (!arrow_rect.Contains(mouse_pos))
{
UpdateCurrentDirectory(directory);
}
}
// 如果节点打开,绘制子文件夹
if (node_open)
{
for (const AssetHandle& child : dir->ChildDirectories)
{ {
DrawDirectoryInfo(child); DrawDirectoryInfo(child);
} }
ImGui::TreePop(); // 必须与 TreeNodeEx 匹配
ImGui::TreePop();
} }
ImGui::PopID();
} }
void AssetsManagerPanel::RenderAsset(Ref<Asset>& asset) void ContentBrowserPanel::RenderAsset(Ref<Asset>& asset)
{ {
// These caches are currently required for when we change directories // These caches are currently required for when we change directories
const AssetHandle assetHandle = asset->Handle; const AssetHandle assetHandle = asset->Handle;
@ -345,7 +370,7 @@ namespace Prism
ImGui::PopID(); ImGui::PopID();
} }
void AssetsManagerPanel::HandleDragDrop(const RendererID icon, const Ref<Asset>& asset) void ContentBrowserPanel::HandleDragDrop(const RendererID icon, const Ref<Asset>& asset)
{ {
if (asset->Type == AssetType::Directory && m_IsDragging) if (asset->Type == AssetType::Directory && m_IsDragging)
{ {
@ -392,7 +417,7 @@ namespace Prism
} }
void AssetsManagerPanel::RenderBreadCrumbs() void ContentBrowserPanel::RenderBreadCrumbs()
{ {
if (ImGui::ImageButton("##backbtn", (ImTextureID)m_BackbtnTex->GetRendererID(), ImVec2(20, 18))) if (ImGui::ImageButton("##backbtn", (ImTextureID)m_BackbtnTex->GetRendererID(), ImVec2(20, 18)))
{ {
@ -475,7 +500,7 @@ namespace Prism
} }
void AssetsManagerPanel::HandleRenaming(Ref<Asset>& asset) void ContentBrowserPanel::HandleRenaming(Ref<Asset>& asset)
{ {
if (m_SelectedAssets.SelectionCount() > 1) if (m_SelectedAssets.SelectionCount() > 1)
return; return;
@ -502,7 +527,7 @@ namespace Prism
} }
void AssetsManagerPanel::UpdateCurrentDirectory(AssetHandle directoryHandle) void ContentBrowserPanel::UpdateCurrentDirectory(AssetHandle directoryHandle)
{ {
m_UpdateBreadCrumbs = true; m_UpdateBreadCrumbs = true;

View File

@ -2,8 +2,8 @@
// Created by Atdunbg on 2026/1/20. // Created by Atdunbg on 2026/1/20.
// //
#ifndef PRISM_ASSETSMANAGERPANEL_H #ifndef PRISM_CONTENTBROWSERPANEL_H
#define PRISM_ASSETSMANAGERPANEL_H #define PRISM_CONTENTBROWSERPANEL_H
#include "Prism/Asset/AssetsManager.h" #include "Prism/Asset/AssetsManager.h"
#include "Prism/Renderer/Texture.h" #include "Prism/Renderer/Texture.h"
@ -64,10 +64,10 @@ namespace Prism
std::vector<T> m_Selections; std::vector<T> m_Selections;
}; };
class PRISM_API AssetsManagerPanel class PRISM_API ContentBrowserPanel
{ {
public: public:
AssetsManagerPanel(); ContentBrowserPanel();
void OnImGuiRender(); void OnImGuiRender();
private: private:
@ -116,4 +116,4 @@ namespace Prism
}; };
} }
#endif //PRISM_ASSETSMANAGERPANEL_H #endif //PRISM_CONTENTBROWSERPANEL_H

View File

@ -217,109 +217,6 @@ namespace Prism
glDrawBuffer(GL_NONE); glDrawBuffer(GL_NONE);
} }
#if 0
if (multisample)
{
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_ColorAttachment);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment);
// TODO: Create Hazel texture object based on format here
if (m_Specification.Format == FramebufferFormat::RGBA16F)
{
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA16F, m_Specification.Width, m_Specification.Height, GL_FALSE);
}
else if (m_Specification.Format == FramebufferFormat::RGBA8)
{
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_FALSE);
}
// glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
}
else
{
glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment);
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment);
// TODO: Create Hazel texture object based on format here
if (m_Specification.Format == FramebufferFormat::RGBA16F)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
}
else if (m_Specification.Format == FramebufferFormat::RG32F) // "Shadow" for now
{
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, m_Specification.Width, m_Specification.Height);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_ColorAttachment, 0);
glDrawBuffer(GL_NONE);
}
else if (m_Specification.Format == FramebufferFormat::RGBA8)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
else if (m_Specification.Format == FramebufferFormat::COMP)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment2);
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
if (m_Specification.Format != FramebufferFormat::RG32F)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0);
}
}
if (multisample)
{
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_DepthAttachment);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_FALSE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
}
else if (m_Specification.Format != FramebufferFormat::RG32F)
{
glCreateTextures(GL_TEXTURE_2D, 1, &m_DepthAttachment);
glBindTexture(GL_TEXTURE_2D, m_DepthAttachment);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0);
}
if (m_Specification.Format != FramebufferFormat::RG32F)
{
if (multisample)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0);
}
else
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0);
if (m_Specification.Format == FramebufferFormat::COMP)
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_ColorAttachment2, 0);
const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, buffers);
}
}
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_DepthAttachment, 0);
}
#endif
PM_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!"); PM_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!");
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);

View File

@ -154,6 +154,13 @@ namespace Prism
UploadUniformInt(name, value); UploadUniformInt(name, value);
}); });
} }
void OpenGLShader::SetUInt(const std::string& name, unsigned int value)
{
Renderer::Submit([=]() {
UploadUniformUInt(name, value);
});
}
void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value) void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value)
{ {
@ -709,16 +716,36 @@ namespace Prism
return GL_NONE; return GL_NONE;
} }
void OpenGLShader::UploadUniformBool(const std::string& name, const bool value) const
{
glUseProgram(m_RendererID);
const auto location = glGetUniformLocation(m_RendererID, name.c_str());
if (location != -1)
glUniform1i(location, value);
else
PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
}
void OpenGLShader::UploadUniformInt(const std::string& name, const int value) const void OpenGLShader::UploadUniformInt(const std::string& name, const int value) const
{ {
glUseProgram(m_RendererID); glUseProgram(m_RendererID);
auto location = glGetUniformLocation(m_RendererID, name.c_str()); const auto location = glGetUniformLocation(m_RendererID, name.c_str());
if (location != -1) if (location != -1)
glUniform1i(location, value); glUniform1i(location, value);
else else
PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name); PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
} }
void OpenGLShader::UploadUniformUInt(const std::string& name, int value) const
{
glUseProgram(m_RendererID);
const auto location = glGetUniformLocation(m_RendererID, name.c_str());
if (location != -1)
glUniform1ui(location, value);
else
PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
}
void OpenGLShader::UploadUniformFloat(const std::string& name, const float value) const void OpenGLShader::UploadUniformFloat(const std::string& name, const float value) const
{ {
glUseProgram(m_RendererID); glUseProgram(m_RendererID);

View File

@ -32,6 +32,7 @@ namespace Prism
virtual void SetFloat2(const std::string& name, const glm::vec2& value) override; virtual void SetFloat2(const std::string& name, const glm::vec2& value) override;
virtual void SetFloat3(const std::string& name, const glm::vec3& value) override; virtual void SetFloat3(const std::string& name, const glm::vec3& value) override;
virtual void SetInt(const std::string& name, int value) override; virtual void SetInt(const std::string& name, int value) override;
virtual void SetUInt(const std::string& name, unsigned int value) override;
virtual void SetMat4(const std::string& name, const glm::mat4& value) override; virtual void SetMat4(const std::string& name, const glm::mat4& value) override;
virtual void SetIntArray(const std::string& name, int* values, uint32_t size) override; virtual void SetIntArray(const std::string& name, int* values, uint32_t size) override;
@ -43,7 +44,6 @@ namespace Prism
const std::string& GetName() const override; const std::string& GetName() const override;
private: private:
void Load(const std::string& source); void Load(const std::string& source);
@ -64,7 +64,9 @@ namespace Prism
static GLenum ShaderTypeFromString(const std::string& type); static GLenum ShaderTypeFromString(const std::string& type);
void UploadUniformBool(const std::string& name, bool value) const;
void UploadUniformInt(const std::string& name, int value) const; void UploadUniformInt(const std::string& name, int value) const;
void UploadUniformUInt(const std::string& name, int value) const;
void UploadUniformFloat(const std::string& name, float value) const; void UploadUniformFloat(const std::string& name, float value) const;
void UploadUniformFloat2(const std::string& name, const glm::vec2& values) const; void UploadUniformFloat2(const std::string& name, const glm::vec2& values) const;
void UploadUniformFloat3(const std::string& name, const glm::vec3& values) const; void UploadUniformFloat3(const std::string& name, const glm::vec3& values) const;

View File

@ -13,6 +13,8 @@
#include "RenderPass.h" #include "RenderPass.h"
#include "glad/glad.h" #include "glad/glad.h"
#include "Prism/Core/Timer.h" #include "Prism/Core/Timer.h"
#include <cmath>
#include <algorithm>
namespace Prism namespace Prism
{ {
@ -40,6 +42,18 @@ namespace Prism
Ref<RenderPass> CompositePass; Ref<RenderPass> CompositePass;
Ref<RenderPass> BloomBlurPass[2]; Ref<RenderPass> BloomBlurPass[2];
Ref<RenderPass> BloomBlendPass; Ref<RenderPass> BloomBlendPass;
Ref<RenderPass> LuminancePass;
// Auto exposure
struct AutoExposure
{
float CurrentExposure = 1.0f;
bool EnableAutoExposure = true;
float Key = 0.18f; // middle gray
float AdaptationSpeed = 1.0f; // stops per second
Timer ExposureTimer;
float MaxExposure = 5.0f;
}AutoExposure;
Ref<Shader> ShadowMapShader, ShadowMapAnimShader; Ref<Shader> ShadowMapShader, ShadowMapAnimShader;
Ref<RenderPass> ShadowMapRenderPass[4]; Ref<RenderPass> ShadowMapRenderPass[4];
@ -89,15 +103,104 @@ namespace Prism
float ShadowPass = 0.0f; float ShadowPass = 0.0f;
float GeometryPass = 0.0f; float GeometryPass = 0.0f;
float CompositePass = 0.0f; float CompositePass = 0.0f;
float AutoExposurePass = 0.0f;
Timer ShadowPassTimer; Timer ShadowPassTimer;
Timer GeometryPassTimer; Timer GeometryPassTimer;
Timer CompositePassTimer; Timer CompositePassTimer;
Timer AutoExposurePassTimer;
}; };
static SceneRendererData s_Data; static SceneRendererData s_Data;
static SceneRendererStats s_Stats; static SceneRendererStats s_Stats;
static void ComputeAutoExposure()
{
if (!s_Data.AutoExposure.EnableAutoExposure)
return;
auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer;
auto dstFB = s_Data.LuminancePass->GetSpecification().TargetFramebuffer;
if (!srcFB || !dstFB)
return;
const uint32_t srcID = srcFB->GetColorAttachmentRendererID();
const uint32_t dstID = dstFB->GetColorAttachmentRendererID();
const uint32_t width = dstFB->GetWidth();
const uint32_t height = dstFB->GetHeight();
Renderer::Submit([srcID, dstID, width, height, srcFB]() mutable
{
// Use framebuffer blit to resolve multisampled source into non-multisampled luminance target.
GLuint srcFBO = srcFB->GetRendererID();
GLuint dstFBO = s_Data.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID();
// Bind read/draw FBOs and blit color attachment 0
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
// Source size — try to use srcFB dimensions if available
int srcWidth = srcFB->GetWidth();
int srcHeight = srcFB->GetHeight();
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Unbind
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Generate mipmaps so the smallest mip is the average color
glGenerateTextureMipmap(dstID);
// Determine highest mip level
int maxLevel = (int)std::floor(std::log2((float)std::max(width, height)));
if (maxLevel < 0) maxLevel = 0;
float pixel[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, sizeof(pixel), pixel);
// Sanitize pixel values (handle NaN/Inf or negative values coming from GPU)
for (int i = 0; i < 3; ++i)
{
if (!std::isfinite(pixel[i]) || pixel[i] < 0.0f)
pixel[i] = 0.0f;
}
// Convert to luminance
float lum = 0.2126f * pixel[0] + 0.7152f * pixel[1] + 0.0722f * pixel[2];
if (!std::isfinite(lum) || lum <= 0.0f)
lum = 1e-6f; // fallback minimum luminance
// Compute desired exposure (simple key/avg approach)
const float key = s_Data.AutoExposure.Key;
constexpr float minLum = 1e-6f;
float desiredExposure = key / std::max(lum, minLum);
desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposure.MaxExposure);
// Adapt exposure over time (exponential)
const float dt = s_Data.AutoExposure.ExposureTimer.ElapsedMillis() / 1000.0f;
s_Data.AutoExposure.ExposureTimer.Reset();
const float tau = s_Data.AutoExposure.AdaptationSpeed;
const float adaptFactor = 1.0f - std::exp(-tau * dt);
s_Data.AutoExposure.CurrentExposure = s_Data.AutoExposure.CurrentExposure + (desiredExposure - s_Data.AutoExposure.CurrentExposure) * adaptFactor;
s_Data.AutoExposure.CurrentExposure = std::clamp(s_Data.AutoExposure.CurrentExposure, 0.0001f, s_Data.AutoExposure.MaxExposure);
// Write exposure directly into composite shader program uniform so the subsequent composite pass uses it
/*
if (const GLuint prog = s_Data.CompositeShader->GetRendererID())
{
const GLint loc = glGetUniformLocation(prog, "u_Exposure");
if (loc >= 0)
glProgramUniform1f(prog, loc, s_Data.CurrentExposure);
}
*/
});
}
void SceneRenderer::Init() void SceneRenderer::Init()
{ {
FramebufferSpecification geoFramebufferSpec; FramebufferSpecification geoFramebufferSpec;
@ -140,6 +243,21 @@ namespace Prism
s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl"); s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl");
s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga"); s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga");
// Luminance pass: used to compute average scene luminance (for auto exposure)
FramebufferSpecification luminanceFramebufferSpec;
luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F };
luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
RenderPassSpecification luminanceRenderPassSpec;
luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec);
s_Data.LuminancePass = RenderPass::Create(luminanceRenderPassSpec);
// Auto exposure defaults
s_Data.AutoExposure.CurrentExposure = 1.0f;
s_Data.AutoExposure.EnableAutoExposure = true;
s_Data.AutoExposure.Key = 0.18f;
s_Data.AutoExposure.AdaptationSpeed = 1.0f;
s_Data.AutoExposure.ExposureTimer.Reset();
// Grid // Grid
const auto gridShader = Shader::Create("assets/shaders/Grid.glsl"); const auto gridShader = Shader::Create("assets/shaders/Grid.glsl");
@ -200,6 +318,8 @@ namespace Prism
{ {
s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height); s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height);
s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height); s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height);
if (s_Data.LuminancePass)
s_Data.LuminancePass->GetSpecification().TargetFramebuffer->Resize(width, height);
} }
void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera) void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera)
@ -361,39 +481,28 @@ namespace Prism
memset(&s_Stats, 0, sizeof(SceneRendererStats)); memset(&s_Stats, 0, sizeof(SceneRendererStats));
{ {
Renderer::Submit([]() Renderer::Submit([]() { s_Stats.ShadowPassTimer.Reset(); });
{
s_Stats.ShadowPassTimer.Reset();
});
ShadowMapPass(); ShadowMapPass();
Renderer::Submit([] Renderer::Submit([] { s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis(); });
{
s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis();
});
} }
{ {
Renderer::Submit([]() Renderer::Submit([]() { s_Stats.GeometryPassTimer.Reset(); });
{
s_Stats.GeometryPassTimer.Reset();
});
GeometryPass(); GeometryPass();
Renderer::Submit([] Renderer::Submit([] { s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis(); });
{
s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis();
});
} }
{
Renderer::Submit([]()
{
s_Stats.CompositePassTimer.Reset();
});
// Compute average luminance and update exposure (GPU-copy + mipmap -> read 1 texel)
{
Renderer::Submit([]() { s_Stats.AutoExposurePassTimer.Reset(); });
ComputeAutoExposure();
Renderer::Submit([] { s_Stats.AutoExposurePass = s_Stats.AutoExposurePassTimer.ElapsedMillis(); });
}
{
Renderer::Submit([]() { s_Stats.CompositePassTimer.Reset(); });
CompositePass(); CompositePass();
Renderer::Submit([] Renderer::Submit([] { s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis(); });
{
s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis();
});
BloomBlurPass(); BloomBlurPass();
} }
@ -684,7 +793,7 @@ namespace Prism
Renderer::BeginRenderPass(s_Data.CompositePass); Renderer::BeginRenderPass(s_Data.CompositePass);
s_Data.CompositeShader->Bind(); s_Data.CompositeShader->Bind();
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposure.EnableAutoExposure ? s_Data.AutoExposure.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
// s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight())); // s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight()));
// s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint); // s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint);
@ -741,7 +850,8 @@ namespace Prism
Renderer::BeginRenderPass(s_Data.BloomBlendPass); Renderer::BeginRenderPass(s_Data.BloomBlendPass);
s_Data.BloomBlendShader->Bind(); s_Data.BloomBlendShader->Bind();
auto & adad =s_Data; auto & adad =s_Data;
s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); // s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposure.EnableAutoExposure ? s_Data.AutoExposure.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom); s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom);
s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0); s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0);
@ -796,7 +906,7 @@ namespace Prism
cascadeSplits[i] = (d - nearClip) / clipRange; cascadeSplits[i] = (d - nearClip) / clipRange;
} }
cascadeSplits[3] = 0.3f; // cascadeSplits[3] = 0.3f;
// Manually set cascades here // Manually set cascades here
// cascadeSplits[0] = 0.05f; // cascadeSplits[0] = 0.05f;
@ -994,6 +1104,20 @@ namespace Prism
UI::EndTreeNode(); UI::EndTreeNode();
} }
if (UI::BeginTreeNode("Auto Exposure"))
{
UI::Property("Auto Exposure Pass time: %.2fs", s_Stats.AutoExposurePass);
UI::BeginPropertyGrid();
UI::Property("Enable Auto Exposure", s_Data.AutoExposure.EnableAutoExposure);
UI::Property("Key (middle gray)", s_Data.AutoExposure.Key, 0.001f, 0.001f, 2.5f);
UI::Property("Adaptation Speed", s_Data.AutoExposure.AdaptationSpeed, 0.01f, 0.001f, 5.0f);
UI::Property("Current Exposure", s_Data.AutoExposure.CurrentExposure, 0.01f, 0.0f, 0.0f, true);
UI::EndPropertyGrid();
UI::EndTreeNode();
}
ImGui::End(); ImGui::End();
} }

View File

@ -32,6 +32,7 @@ namespace Prism
{ {
public: public:
static void Init(); static void Init();
void InitAutoExposure();
static void SetViewportSize(uint32_t width, uint32_t height); static void SetViewportSize(uint32_t width, uint32_t height);

View File

@ -111,6 +111,7 @@ namespace Prism
virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0; virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0;
virtual void SetInt(const std::string& name, int value) = 0; virtual void SetInt(const std::string& name, int value) = 0;
virtual void SetUInt(const std::string& name, unsigned int value) = 0;
virtual void SetBool(const std::string& name, bool value) = 0; virtual void SetBool(const std::string& name, bool value) = 0;
virtual void SetFloat(const std::string& name, float value) = 0; virtual void SetFloat(const std::string& name, float value) = 0;
virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0; virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0;