diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index c5950db..1de9fd4 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -16,7 +16,7 @@ file(GLOB_RECURSE SRC_SOURCE ./**.cpp) 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 target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_DOCKSPACE) diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index 679c44f..4c9acce 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -19,141 +19,8 @@ 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() - : 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 m_CheckerboardTex = Texture2D::Create("assets/editor/Checkerboard.tga"); 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(m_EditorScene); m_SceneHierarchyPanel->SetSelectionChangedCallback(std::bind(&EditorLayer::SelectEntity, this, std::placeholders::_1)); m_SceneHierarchyPanel->SetEntityDeletedCallback(std::bind(&EditorLayer::OnEntityDeleted, this, std::placeholders::_1)); UpdateWindowTitle("untitled Scene"); - m_AssetManagerPanel = CreateScope(); + m_ContentBrowserPanel = CreateScope(); m_ObjectsPanel = CreateScope(); // OpenScene("assets/scenes/FPSDemo.scene"); @@ -285,8 +154,6 @@ namespace Prism void EditorLayer::OnImGuiRender() { - - static bool p_open = true; static bool opt_fullscreen = true; static bool opt_padding = false; @@ -325,568 +192,520 @@ namespace Prism if (!opt_padding) ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::Begin("DockSpace Demo", &p_open, window_flags); - if (!opt_padding) - ImGui::PopStyleVar(); - - if (opt_fullscreen) - ImGui::PopStyleVar(2); - - // Submit the DockSpace - // REMINDER: THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace()! - // MOST REGULAR APPLICATIONS WILL SIMPLY WANT TO CALL DockSpaceOverViewport(). READ COMMENTS ABOVE. - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { - ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); - } + if (!opt_padding) + ImGui::PopStyleVar(); - // Show demo options and help - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("file")) + if (opt_fullscreen) + ImGui::PopStyleVar(2); + + // Submit the DockSpace + // REMINDER: THIS IS A DEMO FOR ADVANCED USAGE OF DockSpace()! + // MOST REGULAR APPLICATIONS WILL SIMPLY WANT TO CALL DockSpaceOverViewport(). READ COMMENTS ABOVE. + ImGuiIO& io = ImGui::GetIO(); + if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable) { - if (ImGui::MenuItem("New Scene", "Ctrl+N")) - { - NewScene(); - } - - if (ImGui::MenuItem("Open Scene...", "Ctrl+O")) - { - OpenScene(); - } - ImGui::Separator(); - if (ImGui::MenuItem("Save Scene...", "Ctrl+S")) - { - SaveScene(); - } - if (ImGui::MenuItem("Save Scene As...", "Ctrl+Shift+S")) - { - SaveSceneAs(); - } - - ImGui::Separator(); - if (ImGui::MenuItem("Exit")) - p_open = false; - ImGui::EndMenu(); + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); } - if (ImGui::BeginMenu("Script")) + // Show demo options and help + if (ImGui::BeginMenuBar()) { - // temp - if (ImGui::MenuItem("Reload C# Assembly")) - ScriptEngine::ReloadAssembly("assets/scripts/ExampleApp.dll"); - - ImGui::MenuItem("Reload assembly on play", nullptr, &m_ReloadScriptOnPlay); - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Edit")) - { - ImGui::MenuItem("Physics Settings", nullptr, &m_ShowPhysicsSettings); - - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Tools")) - { - // PhysX Debugger - if (ImGui::MenuItem("Connect to PVD")) + if (ImGui::BeginMenu("file")) { - Physics3D::ConnectPVD(); - } - if (ImGui::MenuItem("Disconnect from PVD", nullptr, nullptr, Physics3D::IsPVDConnected())) - { - Physics3D::DisconnectPVD(); - } - - ImGui::EndMenu(); - } - - ImGui::EndMenuBar(); - } - - - ImGui::Begin("Materials"); - - if (!m_SelectionContext.empty()) - { - Entity selectedEntity = m_SelectionContext.front().Entity; - if (selectedEntity.HasComponent()) - { - Ref mesh = selectedEntity.GetComponent().Mesh; - if (mesh) - { - auto& materials = mesh->GetMaterials(); - static uint32_t selectedMaterialIndex = 0; - for (uint32_t i = 0; i < materials.size(); i++) + if (ImGui::MenuItem("New Scene", "Ctrl+N")) { - const auto& materialInstance = materials[i]; - - ImGuiTreeNodeFlags node_flags = (selectedMaterialIndex == i ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_Leaf; - bool opened = ImGui::TreeNodeEx((void*)(&materialInstance), node_flags, "%s", materialInstance->GetName().c_str()); - if (ImGui::IsItemClicked()) - { - selectedMaterialIndex = i; - } - if (opened) - ImGui::TreePop(); + NewScene(); + } + if (ImGui::MenuItem("Open Scene...", "Ctrl+O")) + { + OpenScene(); + } + ImGui::Separator(); + if (ImGui::MenuItem("Save Scene...", "Ctrl+S")) + { + SaveScene(); + } + if (ImGui::MenuItem("Save Scene As...", "Ctrl+Shift+S")) + { + SaveSceneAs(); } ImGui::Separator(); + if (ImGui::MenuItem("Exit")) + p_open = false; + ImGui::EndMenu(); + } - if (selectedMaterialIndex < materials.size()) + if (ImGui::BeginMenu("Script")) + { + // temp + if (ImGui::MenuItem("Reload C# Assembly")) + ScriptEngine::ReloadAssembly("assets/scripts/ExampleApp.dll"); + + ImGui::MenuItem("Reload assembly on play", nullptr, &m_ReloadScriptOnPlay); + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Edit")) + { + ImGui::MenuItem("Physics Settings", nullptr, &m_ShowPhysicsSettings); + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("Tools")) + { + // PhysX Debugger + if (ImGui::MenuItem("Connect to PVD")) { - auto& materialInstance = materials[selectedMaterialIndex]; - ImGui::Text("Shader: %s", materialInstance->GetShader()->GetName().c_str()); - // Textures ------------------------------------------------------------------------------ - { - // Albedo - if (ImGui::CollapsingHeader("Albedo", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + Physics3D::ConnectPVD(); + } + if (ImGui::MenuItem("Disconnect from PVD", nullptr, nullptr, Physics3D::IsPVDConnected())) + { + Physics3D::DisconnectPVD(); + } - auto& albedoColor = materialInstance->Get("u_AlbedoColor"); - bool useAlbedoMap = materialInstance->Get("u_AlbedoTexToggle"); - Ref albedoMap = materialInstance->TryGetResource("u_AlbedoTexture"); + ImGui::EndMenu(); + } - ImGui::Image(albedoMap ? (ImTextureRef)albedoMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); - ImGui::PopStyleVar(); - if (ImGui::IsItemHovered()) - { - if (albedoMap) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(albedoMap->GetPath().c_str()); - ImGui::PopTextWrapPos(); - ImGui::Image((ImTextureRef)albedoMap->GetRendererID(), ImVec2(384, 384)); - ImGui::EndTooltip(); - } - if (ImGui::IsItemClicked()) - { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") - { - albedoMap = Texture2D::Create(filename, true/*m_AlbedoInput.SRGB*/); - materialInstance->Set("u_AlbedoTexture", albedoMap); - } - } - } - ImGui::SameLine(); - ImGui::BeginGroup(); - if (ImGui::Checkbox("Use##AlbedoMap", &useAlbedoMap)) - materialInstance->Set("u_AlbedoTexToggle", useAlbedoMap ? 1.0f : 0.0f); + ImGui::EndMenuBar(); + } - /*if (ImGui::Checkbox("sRGB##AlbedoMap", &m_AlbedoInput.SRGB)) - { - if (m_AlbedoInput.TextureMap) - m_AlbedoInput.TextureMap = Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB); - }*/ - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::ColorEdit3("Color##Albedo", glm::value_ptr(albedoColor), ImGuiColorEditFlags_NoInputs); - } - } - { - // Normals - if (ImGui::CollapsingHeader("Normals", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); - bool useNormalMap = materialInstance->Get("u_NormalTexToggle"); - Ref normalMap = materialInstance->TryGetResource("u_NormalTexture"); + ImGui::Begin("Materials"); + if (!m_SelectionContext.empty()) + { + Entity selectedEntity = m_SelectionContext.front().Entity; + if (selectedEntity.HasComponent()) + { + Ref mesh = selectedEntity.GetComponent().Mesh; + if (mesh) + { + auto& materials = mesh->GetMaterials(); + static uint32_t selectedMaterialIndex = 0; + for (uint32_t i = 0; i < materials.size(); i++) + { + const auto& materialInstance = materials[i]; - ImGui::Image(normalMap ? (ImTextureRef)normalMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); - ImGui::PopStyleVar(); - if (ImGui::IsItemHovered()) - { - if (normalMap) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(normalMap->GetPath().c_str()); - ImGui::PopTextWrapPos(); - ImGui::Image((ImTextureRef)normalMap->GetRendererID(), ImVec2(384, 384)); - ImGui::EndTooltip(); - } - if (ImGui::IsItemClicked()) - { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") - { - normalMap = Texture2D::Create(filename); - materialInstance->Set("u_NormalTexture", normalMap); - } - } - } - ImGui::SameLine(); - if (ImGui::Checkbox("Use##NormalMap", &useNormalMap)) - materialInstance->Set("u_NormalTexToggle", useNormalMap ? 1.0f : 0.0f); - } - } - { - // Metalness - if (ImGui::CollapsingHeader("Metalness", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); - float& metalnessValue = materialInstance->Get("u_Metalness"); - bool useMetalnessMap = materialInstance->Get("u_MetalnessTexToggle"); + ImGuiTreeNodeFlags node_flags = (selectedMaterialIndex == i ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_Leaf; + bool opened = ImGui::TreeNodeEx((void*)(&materialInstance), node_flags, "%s", materialInstance->GetName().c_str()); + if (ImGui::IsItemClicked()) + { + selectedMaterialIndex = i; + } + if (opened) + ImGui::TreePop(); - Ref metalnessMap = materialInstance->TryGetResource("u_MetalnessTexture"); + } - ImGui::Image(metalnessMap ? (ImTextureRef)metalnessMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); - ImGui::PopStyleVar(); - if (ImGui::IsItemHovered()) - { - if (metalnessMap) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(metalnessMap->GetPath().c_str()); - ImGui::PopTextWrapPos(); - ImGui::Image((ImTextureRef)metalnessMap->GetRendererID(), ImVec2(384, 384)); - ImGui::EndTooltip(); - } - if (ImGui::IsItemClicked()) - { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") - { - metalnessMap = Texture2D::Create(filename); - materialInstance->Set("u_MetalnessTexture", metalnessMap); - } - } - } - ImGui::SameLine(); - if (ImGui::Checkbox("Use##MetalnessMap", &useMetalnessMap)) - materialInstance->Set("u_MetalnessTexToggle", useMetalnessMap ? 1.0f : 0.0f); - ImGui::SameLine(); - ImGui::SliderFloat("Value##MetalnessInput", &metalnessValue, 0.0f, 1.0f); - } - } - { - // Roughness - if (ImGui::CollapsingHeader("Roughness", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) - { - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); - float& roughnessValue = materialInstance->Get("u_Roughness"); - bool useRoughnessMap = materialInstance->Get("u_RoughnessTexToggle"); - Ref roughnessMap = materialInstance->TryGetResource("u_RoughnessTexture"); + ImGui::Separator(); - ImGui::Image(roughnessMap ? (ImTextureRef)roughnessMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); - ImGui::PopStyleVar(); - if (ImGui::IsItemHovered()) - { - if (roughnessMap) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(roughnessMap->GetPath().c_str()); - ImGui::PopTextWrapPos(); - ImGui::Image((ImTextureRef)roughnessMap->GetRendererID(), ImVec2(384, 384)); - ImGui::EndTooltip(); - } - if (ImGui::IsItemClicked()) - { - std::string filename = Application::Get().OpenFile(""); - if (filename != "") - { - roughnessMap = Texture2D::Create(filename); - materialInstance->Set("u_RoughnessTexture", roughnessMap); - } - } - } - ImGui::SameLine(); - if (ImGui::Checkbox("Use##RoughnessMap", &useRoughnessMap)) - materialInstance->Set("u_RoughnessTexToggle", useRoughnessMap ? 1.0f : 0.0f); - ImGui::SameLine(); - ImGui::SliderFloat("Value##RoughnessInput", &roughnessValue, 0.0f, 1.0f); - } - } + if (selectedMaterialIndex < materials.size()) + { + auto& materialInstance = materials[selectedMaterialIndex]; + ImGui::Text("Shader: %s", materialInstance->GetShader()->GetName().c_str()); + // Textures ------------------------------------------------------------------------------ + { + // Albedo + if (ImGui::CollapsingHeader("Albedo", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + + auto& albedoColor = materialInstance->Get("u_AlbedoColor"); + bool useAlbedoMap = materialInstance->Get("u_AlbedoTexToggle"); + Ref albedoMap = materialInstance->TryGetResource("u_AlbedoTexture"); + + ImGui::Image(albedoMap ? (ImTextureRef)albedoMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); + ImGui::PopStyleVar(); + if (ImGui::IsItemHovered()) + { + if (albedoMap) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(albedoMap->GetPath().c_str()); + ImGui::PopTextWrapPos(); + ImGui::Image((ImTextureRef)albedoMap->GetRendererID(), ImVec2(384, 384)); + ImGui::EndTooltip(); + } + if (ImGui::IsItemClicked()) + { + std::string filename = Application::Get().OpenFile(""); + if (filename != "") + { + albedoMap = Texture2D::Create(filename, true/*m_AlbedoInput.SRGB*/); + materialInstance->Set("u_AlbedoTexture", albedoMap); + } + } + } + ImGui::SameLine(); + ImGui::BeginGroup(); + if (ImGui::Checkbox("Use##AlbedoMap", &useAlbedoMap)) + materialInstance->Set("u_AlbedoTexToggle", useAlbedoMap ? 1.0f : 0.0f); + + /*if (ImGui::Checkbox("sRGB##AlbedoMap", &m_AlbedoInput.SRGB)) + { + if (m_AlbedoInput.TextureMap) + m_AlbedoInput.TextureMap = Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB); + }*/ + ImGui::EndGroup(); + ImGui::SameLine(); + ImGui::ColorEdit3("Color##Albedo", glm::value_ptr(albedoColor), ImGuiColorEditFlags_NoInputs); + } + } + { + // Normals + + if (ImGui::CollapsingHeader("Normals", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + bool useNormalMap = materialInstance->Get("u_NormalTexToggle"); + Ref normalMap = materialInstance->TryGetResource("u_NormalTexture"); + + ImGui::Image(normalMap ? (ImTextureRef)normalMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); + ImGui::PopStyleVar(); + if (ImGui::IsItemHovered()) + { + if (normalMap) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(normalMap->GetPath().c_str()); + ImGui::PopTextWrapPos(); + ImGui::Image((ImTextureRef)normalMap->GetRendererID(), ImVec2(384, 384)); + ImGui::EndTooltip(); + } + if (ImGui::IsItemClicked()) + { + std::string filename = Application::Get().OpenFile(""); + if (filename != "") + { + normalMap = Texture2D::Create(filename); + materialInstance->Set("u_NormalTexture", normalMap); + } + } + } + ImGui::SameLine(); + if (ImGui::Checkbox("Use##NormalMap", &useNormalMap)) + materialInstance->Set("u_NormalTexToggle", useNormalMap ? 1.0f : 0.0f); + } + } + { + // Metalness + if (ImGui::CollapsingHeader("Metalness", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + float& metalnessValue = materialInstance->Get("u_Metalness"); + bool useMetalnessMap = materialInstance->Get("u_MetalnessTexToggle"); + + Ref metalnessMap = materialInstance->TryGetResource("u_MetalnessTexture"); + + ImGui::Image(metalnessMap ? (ImTextureRef)metalnessMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); + ImGui::PopStyleVar(); + if (ImGui::IsItemHovered()) + { + if (metalnessMap) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(metalnessMap->GetPath().c_str()); + ImGui::PopTextWrapPos(); + ImGui::Image((ImTextureRef)metalnessMap->GetRendererID(), ImVec2(384, 384)); + ImGui::EndTooltip(); + } + if (ImGui::IsItemClicked()) + { + std::string filename = Application::Get().OpenFile(""); + if (filename != "") + { + metalnessMap = Texture2D::Create(filename); + materialInstance->Set("u_MetalnessTexture", metalnessMap); + } + } + } + ImGui::SameLine(); + if (ImGui::Checkbox("Use##MetalnessMap", &useMetalnessMap)) + materialInstance->Set("u_MetalnessTexToggle", useMetalnessMap ? 1.0f : 0.0f); + ImGui::SameLine(); + ImGui::SliderFloat("Value##MetalnessInput", &metalnessValue, 0.0f, 1.0f); + } + } + { + // Roughness + if (ImGui::CollapsingHeader("Roughness", nullptr, ImGuiTreeNodeFlags_DefaultOpen)) + { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10)); + float& roughnessValue = materialInstance->Get("u_Roughness"); + bool useRoughnessMap = materialInstance->Get("u_RoughnessTexToggle"); + Ref roughnessMap = materialInstance->TryGetResource("u_RoughnessTexture"); + + ImGui::Image(roughnessMap ? (ImTextureRef)roughnessMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64)); + ImGui::PopStyleVar(); + if (ImGui::IsItemHovered()) + { + if (roughnessMap) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(roughnessMap->GetPath().c_str()); + ImGui::PopTextWrapPos(); + ImGui::Image((ImTextureRef)roughnessMap->GetRendererID(), ImVec2(384, 384)); + ImGui::EndTooltip(); + } + if (ImGui::IsItemClicked()) + { + std::string filename = Application::Get().OpenFile(""); + if (filename != "") + { + roughnessMap = Texture2D::Create(filename); + materialInstance->Set("u_RoughnessTexture", roughnessMap); + } + } + } + ImGui::SameLine(); + if (ImGui::Checkbox("Use##RoughnessMap", &useRoughnessMap)) + materialInstance->Set("u_RoughnessTexToggle", useRoughnessMap ? 1.0f : 0.0f); + ImGui::SameLine(); + ImGui::SliderFloat("Value##RoughnessInput", &roughnessValue, 0.0f, 1.0f); + } + } + } } } } + 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(); PhysicsSettingsWindow::OnImGuiRender(m_ShowPhysicsSettings); SceneRenderer::OnImGuiRender(); AssetEditorPanel::OnImGuiRender(); ScriptEngine::OnImGuiRender(); - m_AssetManagerPanel->OnImGuiRender(); + m_ContentBrowserPanel->OnImGuiRender(); m_ObjectsPanel->OnImGuiRender(); - const char* label = m_SelectionMode == SelectionMode::Entity ? "Entity" : "Mesh"; - if (ImGui::Button(label)) + + // Editor Panel ------------------------------------------------------------------------------ + ImGui::Begin("Environment"); { - m_SelectionMode = m_SelectionMode == SelectionMode::Entity ? SelectionMode::SubMesh : SelectionMode::Entity; - } + UI::PropertySlider("Skybox LOD", m_EditorScene->GetSkyboxLod(), 0.0f, 11.0f); - ImGui::Columns(1); + UI::BeginPropertyGrid(); + ImGui::AlignTextToFramePadding(); - ImGui::End(); + 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); - ImGui::Separator(); - { - ImGui::Text("Mesh"); - /* - auto meshComponent = m_MeshEntity.GetComponent(); - 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")) + 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) { - std::string filename = Application::Get().OpenFile(""); - if (!filename.empty()) + float physics2DGravity = m_EditorScene->GetPhysics2DGravity(); + if (UI::Property("2D World Gravity", physics2DGravity, -10000.0f, 10000.0f)) { - auto newMesh = Ref::Create(filename); - // m_MeshMaterial.reset(new MaterialInstance(newMesh->GetMaterial())); - // m_MeshEntity->SetMaterial(m_MeshMaterial); - meshComponent.Mesh = newMesh; + m_EditorScene->SetPhysics2DGravity(physics2DGravity); } } - */ - } - ImGui::Separator(); - - if (ImGui::TreeNode("Shaders")) - { - auto& shaders = Shader::GetAllShaders(); - for (auto& shader : shaders) + else if (m_SceneState == SceneState::Play) { - if (ImGui::TreeNode(shader->GetName().c_str())) + float physics2DGravity = m_RuntimeScene->GetPhysics2DGravity(); + if (UI::Property("2D World Gravity", physics2DGravity, -10000.0f, 10000.0f)) { - - const std::string buttonName = shader->GetName(); - if (ImGui::Button(("Reload##" + buttonName).c_str())) - { - PM_CLIENT_INFO("Reloading Shader: {0}", buttonName); - shader->Reload(); - } - ImGui::TreePop(); + m_RuntimeScene->SetPhysics2DGravity(physics2DGravity); } } - ImGui::TreePop(); + + 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"; + if (ImGui::Button(label)) + { + m_SelectionMode = m_SelectionMode == SelectionMode::Entity ? SelectionMode::SubMesh : SelectionMode::Entity; + } + + UI::EndPropertyGrid(); } - - ImGui::Separator(); - ImGui::End(); - - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(12, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(12, 4)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 2)); ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(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_ButtonActive, ImVec4(0, 0, 0, 0)); - ImGui::Begin("Toolbar"); - if (m_SceneState == SceneState::Edit) + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.305f, 0.31f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.15f, 0.1505f, 0.151f, 0.5f)); + + ImGui::Begin("##tool_bar", NULL, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); { - 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))) + 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 buttonTex = m_SceneState == SceneState::Play ? m_StopButtonTex : m_PlayButtonTex; + if (UI::ImageButton(buttonTex, ImVec2(size, size), ImVec2(0, 0), ImVec2(1, 1))) { - OnScenePlay(); + if (m_SceneState == SceneState::Edit) + OnScenePlay(); + else + OnSceneStop(); + } + + ImGui::SameLine(); + + if (UI::ImageButton(m_PauseButtonTex, ImVec2(size, size), ImVec2(0, 0), ImVec2(1, 1))) + { + if (m_SceneState == SceneState::Play) + { + //OnScenePause(); + m_SceneState = SceneState::Pause; + } + else if (m_SceneState == SceneState::Pause) + { + //OnSceneResume(); + m_SceneState = SceneState::Play; + } } } - 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(); - } - } - 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))) - { - PM_CORE_INFO("PLAY!"); - } + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(2); ImGui::End(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleVar(); - ImGui::PopStyleVar(); - ImGui::PopStyleVar(); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::Begin("Viewport"); - - m_ViewportPanelHovered = ImGui::IsWindowHovered(); - m_ViewportPanelFocused = ImGui::IsWindowFocused(); - - auto viewportOffset = ImGui::GetCursorPos(); // includes tab bar - auto viewportSize = ImGui::GetContentRegionAvail(); - SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - if (m_RuntimeScene) - m_RuntimeScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); - m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); - ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); - - - static int counter = 0; - auto windowSize = ImGui::GetWindowSize(); - ImVec2 minBound = ImGui::GetWindowPos(); - minBound.x += viewportOffset.x; - minBound.y += viewportOffset.y; - - ImVec2 maxBound = { minBound.x + windowSize.x, minBound.y + windowSize.y }; - m_ViewportBounds[0] = { minBound.x, minBound.y }; - m_ViewportBounds[1] = { maxBound.x, maxBound.y }; - m_AllowViewportCameraEvents = ImGui::IsMouseHoveringRect(minBound, maxBound); - - // ImGuizmo - if (m_GizmoType != -1 && !m_SelectionContext.empty()) { - auto& selection = m_SelectionContext[0]; + m_ViewportPanelHovered = ImGui::IsWindowHovered(); + m_ViewportPanelFocused = ImGui::IsWindowFocused(); - const auto rw = (float)ImGui::GetWindowWidth(); - const auto rh = (float)ImGui::GetWindowHeight(); - ImGuizmo::SetOrthographic(false); - ImGuizmo::SetDrawlist(); - ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y, rw, rh); + auto viewportOffset = ImGui::GetCursorPos(); // includes tab bar + auto viewportSize = ImGui::GetContentRegionAvail(); + SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + if (m_RuntimeScene) + m_RuntimeScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f)); + m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y); + ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 }); - bool snap = Input::IsKeyPressed(Key::LEFT_CONTROL); - TransformComponent& entityTransform = selection.Entity.Transform(); - // glm::mat4 transform = entityTransform.GetTransform(); - glm::mat4 transform = m_CurrentScene->GetTransformRelativeToParent(selection.Entity); - float snapValue = GetSnapValue(); - float snapValues[3] = { snapValue, snapValue, snapValue }; - if (m_SelectionMode == SelectionMode::Entity) + static int counter = 0; + auto windowSize = ImGui::GetWindowSize(); + ImVec2 minBound = ImGui::GetWindowPos(); + minBound.x += viewportOffset.x; + minBound.y += viewportOffset.y; + + ImVec2 maxBound = { minBound.x + windowSize.x, minBound.y + windowSize.y }; + m_ViewportBounds[0] = { minBound.x, minBound.y }; + m_ViewportBounds[1] = { maxBound.x, maxBound.y }; + m_AllowViewportCameraEvents = ImGui::IsMouseHoveringRect(minBound, maxBound); + + // ImGuizmo + if (m_GizmoType != -1 && !m_SelectionContext.empty()) { - ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), - glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), - (ImGuizmo::OPERATION)m_GizmoType, - ImGuizmo::LOCAL, - glm::value_ptr(transform), - nullptr, - snap ? snapValues : nullptr); + auto& selection = m_SelectionContext[0]; - if (ImGuizmo::IsUsing()) + const auto rw = (float)ImGui::GetWindowWidth(); + const auto rh = (float)ImGui::GetWindowHeight(); + ImGuizmo::SetOrthographic(false); + ImGuizmo::SetDrawlist(); + ImGuizmo::SetRect(ImGui::GetWindowPos().x, ImGui::GetWindowPos().y, rw, rh); + + bool snap = Input::IsKeyPressed(Key::LEFT_CONTROL); + + TransformComponent& entityTransform = selection.Entity.Transform(); + // glm::mat4 transform = entityTransform.GetTransform(); + glm::mat4 transform = m_CurrentScene->GetTransformRelativeToParent(selection.Entity); + float snapValue = GetSnapValue(); + float snapValues[3] = { snapValue, snapValue, snapValue }; + if (m_SelectionMode == SelectionMode::Entity) { - glm::vec3 translation, rotation, scale; - Math::DecomposeTransform(transform, translation,rotation,scale); + ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), + glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), + (ImGuizmo::OPERATION)m_GizmoType, + ImGuizmo::LOCAL, + glm::value_ptr(transform), + nullptr, + snap ? snapValues : nullptr); - if (Entity parent = m_CurrentScene->FindEntityByUUID(selection.Entity.GetParentUUID())) + if (ImGuizmo::IsUsing()) { - glm::vec3 parentTranslation, parentRotation, parentScale; - Math::DecomposeTransform(m_CurrentScene->GetTransformRelativeToParent(parent), parentTranslation, parentRotation, parentScale); + glm::vec3 translation, rotation, scale; + Math::DecomposeTransform(transform, translation,rotation,scale); - glm::vec3 deltaRotation = (rotation - parentRotation) - entityTransform.Rotation; - entityTransform.Translation = translation - parentTranslation; - entityTransform.Rotation += deltaRotation; - entityTransform.Scale = scale; - } - else - { - glm::vec3 deltaRotation = rotation - entityTransform.Rotation; - entityTransform.Translation = translation; - entityTransform.Rotation += deltaRotation; - entityTransform.Scale = scale; - } - } - }else - { - glm::mat4 transformBase = transform * selection.Mesh->Transform; - ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), - glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), - (ImGuizmo::OPERATION)m_GizmoType, - ImGuizmo::LOCAL, - glm::value_ptr(transformBase), - nullptr, - snap ? snapValues : nullptr); + if (Entity parent = m_CurrentScene->FindEntityByUUID(selection.Entity.GetParentUUID())) + { + glm::vec3 parentTranslation, parentRotation, parentScale; + Math::DecomposeTransform(m_CurrentScene->GetTransformRelativeToParent(parent), parentTranslation, parentRotation, parentScale); - if (ImGuizmo::IsUsing()) + glm::vec3 deltaRotation = (rotation - parentRotation) - entityTransform.Rotation; + entityTransform.Translation = translation - parentTranslation; + entityTransform.Rotation += deltaRotation; + entityTransform.Scale = scale; + } + else + { + glm::vec3 deltaRotation = rotation - entityTransform.Rotation; + entityTransform.Translation = translation; + entityTransform.Rotation += deltaRotation; + entityTransform.Scale = scale; + } + } + }else { - selection.Mesh->Transform = glm::inverse(entityTransform.GetTransform()) * transformBase; - } - } - } + glm::mat4 transformBase = transform * selection.Mesh->Transform; + ImGuizmo::Manipulate(glm::value_ptr(m_EditorCamera.GetViewMatrix()), + glm::value_ptr(m_EditorCamera.GetProjectionMatrix()), + (ImGuizmo::OPERATION)m_GizmoType, + ImGuizmo::LOCAL, + glm::value_ptr(transformBase), + nullptr, + snap ? snapValues : nullptr); - if (ImGui::BeginDragDropTarget()) - { - auto payload = ImGui::AcceptDragDropPayload("asset_payload"); - if (payload) - { - int count = payload->DataSize / sizeof(AssetHandle); - - for (int i = 0; i < count; i++) - { - AssetHandle assetHandle = *(((AssetHandle*)payload->Data) + i); - Ref asset = AssetsManager::GetAsset(assetHandle); - - // We can't really support dragging and dropping scenes when we're dropping multiple assets - if (count == 1 && asset->Type == AssetType::Scene) + if (ImGuizmo::IsUsing()) { - OpenScene(asset->FilePath); - } - - if (asset->Type == AssetType::Mesh) - { - Entity entity = m_EditorScene->CreateEntity(asset->FileName); - entity.AddComponent(Ref(asset)); - SelectEntity(entity); + selection.Mesh->Transform = glm::inverse(entityTransform.GetTransform()) * transformBase; } } } - ImGui::EndDragDropTarget(); + + if (ImGui::BeginDragDropTarget()) + { + auto payload = ImGui::AcceptDragDropPayload("asset_payload"); + if (payload) + { + int count = payload->DataSize / sizeof(AssetHandle); + + for (int i = 0; i < count; i++) + { + AssetHandle assetHandle = *(((AssetHandle*)payload->Data) + i); + Ref asset = AssetsManager::GetAsset(assetHandle); + + // We can't really support dragging and dropping scenes when we're dropping multiple assets + if (count == 1 && asset->Type == AssetType::Scene) + { + OpenScene(asset->FilePath); + } + + if (asset->Type == AssetType::Mesh) + { + Entity entity = m_EditorScene->CreateEntity(asset->FileName); + entity.AddComponent(Ref(asset)); + SelectEntity(entity); + } + } + } + ImGui::EndDragDropTarget(); + } } ImGui::End(); @@ -1079,11 +898,16 @@ namespace Prism void EditorLayer::SelectEntity(Entity entity) { + if (!entity) return; + + SelectedSubmesh selection; if (entity.HasComponent()) { - if (auto mesh = entity.GetComponent().Mesh) - selection.Mesh = &mesh->GetSubmeshes()[0]; + auto& meshComponent = entity.GetComponent(); + + if (meshComponent.Mesh && meshComponent.Mesh->Type == AssetType::Mesh) + selection.Mesh = &meshComponent.Mesh->GetSubmeshes()[0]; } selection.Entity = entity; m_SelectionContext.clear(); diff --git a/Editor/Editor/EditorLayer.h b/Editor/Editor/EditorLayer.h index 9bd5ab4..e33163c 100644 --- a/Editor/Editor/EditorLayer.h +++ b/Editor/Editor/EditorLayer.h @@ -6,7 +6,7 @@ #define EDITORLAYER_H #include "Prism.h" -#include "Prism/Editor/AssetsManagerPanel.h" +#include "Prism/Editor/ContentBrowserPanel.h" #include "Prism/Editor/ObjectsPanel.h" #include "Prism/Editor/SceneHierachyPanel.h" @@ -58,7 +58,7 @@ namespace Prism float GetSnapValue() const; private: Scope m_SceneHierarchyPanel; - Scope m_AssetManagerPanel; + Scope m_ContentBrowserPanel; Scope m_ObjectsPanel; Ref m_CurrentScene; @@ -133,7 +133,7 @@ namespace Prism // Editor resources Ref m_CheckerboardTex; - Ref m_PlayButtonTex; + Ref m_PlayButtonTex, m_StopButtonTex, m_PauseButtonTex; // configure button diff --git a/Editor/assets/editor/PauseButton.png b/Editor/assets/editor/PauseButton.png new file mode 100644 index 0000000..1345ae0 Binary files /dev/null and b/Editor/assets/editor/PauseButton.png differ diff --git a/Editor/imgui.ini b/Editor/imgui.ini index ac066b7..a7496db 100644 --- a/Editor/imgui.ini +++ b/Editor/imgui.ini @@ -1,6 +1,6 @@ [Window][DockSpace Demo] Pos=0,0 -Size=2066,1198 +Size=2560,1566 Collapsed=0 [Window][Debug##Default] @@ -9,32 +9,32 @@ Size=400,400 Collapsed=0 [Window][Scene Hierarchy] -Pos=1595,24 -Size=471,428 +Pos=2089,24 +Size=471,563 Collapsed=0 DockId=0x00000009,0 [Window][Properties] -Pos=1595,454 -Size=471,744 +Pos=2089,589 +Size=471,977 Collapsed=0 DockId=0x0000000A,0 [Window][Scene Renderer] -Pos=0,648 -Size=481,550 +Pos=0,843 +Size=481,723 Collapsed=0 DockId=0x00000006,0 [Window][Materials] Pos=0,24 -Size=481,622 +Size=481,817 Collapsed=0 DockId=0x00000005,0 [Window][Script Engine Debug] -Pos=1595,454 -Size=471,744 +Pos=2089,589 +Size=471,977 Collapsed=0 DockId=0x0000000A,2 @@ -52,25 +52,25 @@ DockId=0x00000001,0 [Window][Viewport] Pos=483,58 -Size=1110,722 +Size=1604,955 Collapsed=0 DockId=0x0000000B,0 [Window][Environment] -Pos=1595,454 -Size=471,744 +Pos=2089,589 +Size=471,977 Collapsed=0 DockId=0x0000000A,1 [Window][Project] -Pos=483,782 -Size=1110,416 +Pos=483,1015 +Size=1604,551 Collapsed=0 DockId=0x0000000C,0 [Window][Objects] -Pos=483,782 -Size=1110,416 +Pos=483,1015 +Size=1604,551 Collapsed=0 DockId=0x0000000C,1 @@ -79,17 +79,23 @@ Pos=189,113 Size=468,371 Collapsed=0 +[Window][##tool_bar] +Pos=483,24 +Size=1604,32 +Collapsed=0 +DockId=0x00000001,0 + [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=0x00000003 Parent=0x00000007 SizeRef=481,1542 Split=Y Selected=0x5D711C2C DockNode ID=0x00000005 Parent=0x00000003 SizeRef=481,817 Selected=0x5D711C2C DockNode ID=0x00000006 Parent=0x00000003 SizeRef=481,723 Selected=0x68D924E0 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=0x0000000B Parent=0x00000002 SizeRef=1401,669 HiddenTabBar=1 Selected=0xC450F867 - DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,386 Selected=0x9C21DE82 + DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1401,955 HiddenTabBar=1 Selected=0xC450F867 + DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,551 Selected=0x9C21DE82 DockNode ID=0x00000008 Parent=0xC0DFADC4 SizeRef=471,1542 Split=Y Selected=0x8C72BEA8 DockNode ID=0x00000009 Parent=0x00000008 SizeRef=315,563 Selected=0xB8729153 DockNode ID=0x0000000A Parent=0x00000008 SizeRef=315,977 Selected=0x73E3D51F diff --git a/Prism/CMakeLists.txt b/Prism/CMakeLists.txt index d4d05d2..a6d3d98 100644 --- a/Prism/CMakeLists.txt +++ b/Prism/CMakeLists.txt @@ -66,7 +66,8 @@ elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") endif () 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") 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_DIR} ) + +target_link_directories(${STATIC_LIBRARY} INTERFACE + ${PHYSX_LIB_DIR} +) + target_link_libraries(${STATIC_LIBRARY} PRIVATE ${LINK_LIBRARIES_PRIVATE} diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index f7b4f65..990fc0a 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -102,7 +102,6 @@ namespace Prism { m_ImGuiLayer->Begin(); - /* ImGui::Begin("Renderer"); const auto& caps = RendererAPI::GetCapabilities(); ImGui::Text("Vendor: %s", caps.Vendor.c_str()); @@ -110,7 +109,6 @@ namespace Prism ImGui::Text("Version: %s", caps.Version.c_str()); ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds()); ImGui::End(); - */ for (Layer* layer : m_LayerStack) layer->OnImGuiRender(); diff --git a/Prism/src/Prism/Core/ImGui/ImGui.cpp b/Prism/src/Prism/Core/ImGui/ImGui.cpp new file mode 100644 index 0000000..6dca9c9 --- /dev/null +++ b/Prism/src/Prism/Core/ImGui/ImGui.cpp @@ -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& 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& 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; + } +} diff --git a/Prism/src/Prism/Core/ImGui/ImGui.h b/Prism/src/Prism/Core/ImGui/ImGui.h index 91860aa..68a1847 100644 --- a/Prism/src/Prism/Core/ImGui/ImGui.h +++ b/Prism/src/Prism/Core/ImGui/ImGui.h @@ -9,8 +9,13 @@ #include #include -#include "../../Asset/Asset.h" -#include "../../Asset/AssetsManager.h" +#include "Prism/Asset/Asset.h" +#include "Prism/Asset/AssetsManager.h" + +namespace Prism +{ + class Texture2D; +} namespace Prism::UI { @@ -259,6 +264,157 @@ namespace Prism::UI { 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& 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 static bool PropertyAssetReference(const char* label, Ref& object, const AssetType supportedType) { @@ -353,6 +509,10 @@ namespace Prism::UI { s_CheckboxCount = 0; } + void Image(const Ref& 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& 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 diff --git a/Prism/src/Prism/Editor/AssetsManagerPanel.cpp b/Prism/src/Prism/Editor/ContentBrowserPanel.cpp similarity index 88% rename from Prism/src/Prism/Editor/AssetsManagerPanel.cpp rename to Prism/src/Prism/Editor/ContentBrowserPanel.cpp index 447f7ca..764dd6c 100644 --- a/Prism/src/Prism/Editor/AssetsManagerPanel.cpp +++ b/Prism/src/Prism/Editor/ContentBrowserPanel.cpp @@ -2,18 +2,19 @@ // Created by Atdunbg on 2026/1/20. // -#include "AssetsManagerPanel.h" +#include "ContentBrowserPanel.h" #include #include "AssetEditorPanel.h" +#include "imgui_internal.h" #include "Prism/Core/Application.h" #include "Prism/Core/Input.h" #include "Prism/Core/Log.h" namespace Prism { - AssetsManagerPanel::AssetsManagerPanel() + ContentBrowserPanel::ContentBrowserPanel() { AssetsManager::SetAssetChangeCallback([&]() { @@ -47,7 +48,7 @@ namespace Prism static int s_ColumnCount = 10; - void AssetsManagerPanel::OnImGuiRender() + void ContentBrowserPanel::OnImGuiRender() { ImGui::Begin("Project", nullptr, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar); { @@ -73,7 +74,6 @@ namespace Prism // Grid and list files 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)); RenderBreadCrumbs(); @@ -195,26 +195,51 @@ namespace Prism ImGui::End(); } - void AssetsManagerPanel::DrawDirectoryInfo(const AssetHandle& directory) + void ContentBrowserPanel::DrawDirectoryInfo(const AssetHandle& directory) { const Ref& dir = AssetsManager::GetAsset(directory); - if (ImGui::TreeNode(dir->FileName.c_str())) - { - if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) - UpdateCurrentDirectory(directory); + ImGui::PushID((int)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); } - - ImGui::TreePop(); + ImGui::TreePop(); // 必须与 TreeNodeEx 匹配 } + + ImGui::PopID(); } - void AssetsManagerPanel::RenderAsset(Ref& asset) + void ContentBrowserPanel::RenderAsset(Ref& asset) { // These caches are currently required for when we change directories const AssetHandle assetHandle = asset->Handle; @@ -345,7 +370,7 @@ namespace Prism ImGui::PopID(); } - void AssetsManagerPanel::HandleDragDrop(const RendererID icon, const Ref& asset) + void ContentBrowserPanel::HandleDragDrop(const RendererID icon, const Ref& asset) { 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))) { @@ -475,7 +500,7 @@ namespace Prism } - void AssetsManagerPanel::HandleRenaming(Ref& asset) + void ContentBrowserPanel::HandleRenaming(Ref& asset) { if (m_SelectedAssets.SelectionCount() > 1) return; @@ -502,7 +527,7 @@ namespace Prism } - void AssetsManagerPanel::UpdateCurrentDirectory(AssetHandle directoryHandle) + void ContentBrowserPanel::UpdateCurrentDirectory(AssetHandle directoryHandle) { m_UpdateBreadCrumbs = true; diff --git a/Prism/src/Prism/Editor/AssetsManagerPanel.h b/Prism/src/Prism/Editor/ContentBrowserPanel.h similarity index 93% rename from Prism/src/Prism/Editor/AssetsManagerPanel.h rename to Prism/src/Prism/Editor/ContentBrowserPanel.h index 46c64c9..bec4890 100644 --- a/Prism/src/Prism/Editor/AssetsManagerPanel.h +++ b/Prism/src/Prism/Editor/ContentBrowserPanel.h @@ -2,8 +2,8 @@ // Created by Atdunbg on 2026/1/20. // -#ifndef PRISM_ASSETSMANAGERPANEL_H -#define PRISM_ASSETSMANAGERPANEL_H +#ifndef PRISM_CONTENTBROWSERPANEL_H +#define PRISM_CONTENTBROWSERPANEL_H #include "Prism/Asset/AssetsManager.h" #include "Prism/Renderer/Texture.h" @@ -64,10 +64,10 @@ namespace Prism std::vector m_Selections; }; - class PRISM_API AssetsManagerPanel + class PRISM_API ContentBrowserPanel { public: - AssetsManagerPanel(); + ContentBrowserPanel(); void OnImGuiRender(); private: @@ -116,4 +116,4 @@ namespace Prism }; } -#endif //PRISM_ASSETSMANAGERPANEL_H \ No newline at end of file +#endif //PRISM_CONTENTBROWSERPANEL_H \ No newline at end of file diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp index c79097b..4acabb9 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLFrameBuffer.cpp @@ -217,109 +217,6 @@ namespace Prism 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!"); glBindFramebuffer(GL_FRAMEBUFFER, 0); diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp index 14f5058..75d51bd 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.cpp @@ -154,6 +154,13 @@ namespace Prism 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) { @@ -709,16 +716,36 @@ namespace Prism 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 { glUseProgram(m_RendererID); - auto location = glGetUniformLocation(m_RendererID, name.c_str()); + 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::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 { glUseProgram(m_RendererID); diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h index a8eb036..bedf04f 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLShader.h @@ -32,6 +32,7 @@ namespace Prism 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 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 SetIntArray(const std::string& name, int* values, uint32_t size) override; @@ -43,7 +44,6 @@ namespace Prism const std::string& GetName() const override; - private: void Load(const std::string& source); @@ -64,7 +64,9 @@ namespace Prism 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 UploadUniformUInt(const std::string& name, int value) const; void UploadUniformFloat(const std::string& name, float value) const; void UploadUniformFloat2(const std::string& name, const glm::vec2& values) const; void UploadUniformFloat3(const std::string& name, const glm::vec3& values) const; diff --git a/Prism/src/Prism/Renderer/SceneRenderer.cpp b/Prism/src/Prism/Renderer/SceneRenderer.cpp index d0f670e..1a91905 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.cpp +++ b/Prism/src/Prism/Renderer/SceneRenderer.cpp @@ -13,6 +13,8 @@ #include "RenderPass.h" #include "glad/glad.h" #include "Prism/Core/Timer.h" +#include +#include namespace Prism { @@ -40,6 +42,18 @@ namespace Prism Ref CompositePass; Ref BloomBlurPass[2]; Ref BloomBlendPass; + Ref 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 ShadowMapShader, ShadowMapAnimShader; Ref ShadowMapRenderPass[4]; @@ -89,15 +103,104 @@ namespace Prism float ShadowPass = 0.0f; float GeometryPass = 0.0f; float CompositePass = 0.0f; + float AutoExposurePass = 0.0f; Timer ShadowPassTimer; Timer GeometryPassTimer; Timer CompositePassTimer; + Timer AutoExposurePassTimer; }; static SceneRendererData s_Data; 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() { FramebufferSpecification geoFramebufferSpec; @@ -140,6 +243,21 @@ namespace Prism s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl"); 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 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.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) @@ -361,40 +481,29 @@ namespace Prism memset(&s_Stats, 0, sizeof(SceneRendererStats)); { - Renderer::Submit([]() - { - s_Stats.ShadowPassTimer.Reset(); - }); + Renderer::Submit([]() { s_Stats.ShadowPassTimer.Reset(); }); ShadowMapPass(); - Renderer::Submit([] - { - s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis(); - }); + Renderer::Submit([] { s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis(); }); } + { - Renderer::Submit([]() - { - s_Stats.GeometryPassTimer.Reset(); - }); + Renderer::Submit([]() { s_Stats.GeometryPassTimer.Reset(); }); GeometryPass(); - Renderer::Submit([] - { - s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis(); - }); + Renderer::Submit([] { s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis(); }); } + + // Compute average luminance and update exposure (GPU-copy + mipmap -> read 1 texel) { - Renderer::Submit([]() - { - s_Stats.CompositePassTimer.Reset(); - }); + Renderer::Submit([]() { s_Stats.AutoExposurePassTimer.Reset(); }); + ComputeAutoExposure(); + Renderer::Submit([] { s_Stats.AutoExposurePass = s_Stats.AutoExposurePassTimer.ElapsedMillis(); }); + } + { + Renderer::Submit([]() { s_Stats.CompositePassTimer.Reset(); }); CompositePass(); - Renderer::Submit([] - { - s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis(); - }); - - BloomBlurPass(); + Renderer::Submit([] { s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis(); }); + BloomBlurPass(); } s_Data.DrawList.clear(); @@ -684,7 +793,7 @@ namespace Prism Renderer::BeginRenderPass(s_Data.CompositePass); 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->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight())); // s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint); @@ -741,7 +850,8 @@ namespace Prism Renderer::BeginRenderPass(s_Data.BloomBlendPass); s_Data.BloomBlendShader->Bind(); 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.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0); @@ -796,7 +906,7 @@ namespace Prism cascadeSplits[i] = (d - nearClip) / clipRange; } - cascadeSplits[3] = 0.3f; + // cascadeSplits[3] = 0.3f; // Manually set cascades here // cascadeSplits[0] = 0.05f; @@ -994,6 +1104,20 @@ namespace Prism 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(); } diff --git a/Prism/src/Prism/Renderer/SceneRenderer.h b/Prism/src/Prism/Renderer/SceneRenderer.h index f36fdc5..1e91147 100644 --- a/Prism/src/Prism/Renderer/SceneRenderer.h +++ b/Prism/src/Prism/Renderer/SceneRenderer.h @@ -32,6 +32,7 @@ namespace Prism { public: static void Init(); + void InitAutoExposure(); static void SetViewportSize(uint32_t width, uint32_t height); diff --git a/Prism/src/Prism/Renderer/Shader.h b/Prism/src/Prism/Renderer/Shader.h index a5eb9c2..86e5adf 100644 --- a/Prism/src/Prism/Renderer/Shader.h +++ b/Prism/src/Prism/Renderer/Shader.h @@ -111,6 +111,7 @@ namespace Prism virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 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 SetFloat(const std::string& name, float value) = 0; virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0;