diff --git a/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h b/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h index ae61f5de..135929fb 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Core/EditorActions.h @@ -247,6 +247,11 @@ namespace OvEditor::Core */ void CompileShaders(); + /** + * Compile the given shader + */ + void CompileShader(OvRendering::Resources::Shader& p_shader); + /** * Save every materials to their respective files */ diff --git a/Sources/Overload/OvEditor/include/OvEditor/Panels/MaterialEditor.h b/Sources/Overload/OvEditor/include/OvEditor/Panels/MaterialEditor.h index 794e35cc..bfc25bee 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Panels/MaterialEditor.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Panels/MaterialEditor.h @@ -6,11 +6,12 @@ #pragma once -#include +#include #include +#include +#include #include #include -#include namespace OvCore::Resources { class Material; } @@ -85,6 +86,9 @@ namespace OvEditor::Panels OvUI::Widgets::Texts::Text* m_targetMaterialText = nullptr; OvUI::Widgets::Texts::Text* m_shaderText = nullptr; + OvUI::Widgets::Buttons::AButton* m_editShaderButton = nullptr; + OvUI::Widgets::Buttons::AButton* m_compileShaderButton = nullptr; + OvTools::Eventing::Event<> m_materialDroppedEvent; OvTools::Eventing::Event<> m_shaderDroppedEvent; diff --git a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp index a0af6e0c..8d92a15e 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Core/EditorActions.cpp @@ -709,6 +709,14 @@ void OvEditor::Core::EditorActions::MoveToTarget(OvCore::ECS::Actor& p_target) } void OvEditor::Core::EditorActions::CompileShaders() +{ + for (const auto shader : m_context.shaderManager.GetResources() | std::views::values) + { + CompileShader(*shader); + } +} + +void OvEditor::Core::EditorActions::CompileShader(OvRendering::Resources::Shader& p_shader) { using namespace OvRendering::Resources::Loaders; @@ -717,10 +725,7 @@ void OvEditor::Core::EditorActions::CompileShaders() newLoggingSettings.summary = true; // Force enable summary logging ShaderLoader::SetLoggingSettings(newLoggingSettings); - for (const auto shader : m_context.shaderManager.GetResources() | std::views::values) - { - m_context.shaderManager.ReloadResource(shader, GetRealPath(shader->path)); - } + m_context.shaderManager.ReloadResource(&p_shader, GetRealPath(p_shader.path)); ShaderLoader::SetLoggingSettings(previousLoggingSettings); } diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/MaterialEditor.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/MaterialEditor.cpp index f8df6a49..841c9c79 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/MaterialEditor.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/MaterialEditor.cpp @@ -11,6 +11,8 @@ #include #include +#include + #include #include #include @@ -232,6 +234,8 @@ void OvEditor::Panels::MaterialEditor::OnMaterialDropped() void OvEditor::Panels::MaterialEditor::OnShaderDropped() { m_materialProperties->enabled = m_shader; // Enable m_shaderSettings group if the shader of the target material is non-null + m_editShaderButton->disabled = m_shader == nullptr; + m_compileShaderButton->disabled = m_shader == nullptr; if (m_shader != m_target->GetShader()) { @@ -246,13 +250,16 @@ void OvEditor::Panels::MaterialEditor::OnShaderDropped() else { m_materialPropertiesColumns->RemoveAllWidgets(); + m_materialFeaturesColumns->RemoveAllWidgets(); } } void OvEditor::Panels::MaterialEditor::CreateHeaderButtons() { - auto& saveButton = CreateWidget("Save to file"); + auto& saveButton = CreateWidget("Save"); saveButton.idleBackgroundColor = { 0.0f, 0.5f, 0.0f }; + saveButton.tooltip = "Save the current material to file"; + saveButton.lineBreak = false; saveButton.ClickedEvent += [this] { if (m_target) { @@ -260,10 +267,9 @@ void OvEditor::Panels::MaterialEditor::CreateHeaderButtons() } }; - saveButton.lineBreak = false; - - auto& reloadButton = CreateWidget("Reload from file"); - reloadButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f }; + auto& reloadButton = CreateWidget("Reload"); + reloadButton.tooltip = "Reload the current material from file"; + reloadButton.lineBreak = false; reloadButton.ClickedEvent += [this] { if (m_target) { @@ -273,15 +279,45 @@ void OvEditor::Panels::MaterialEditor::CreateHeaderButtons() OnMaterialDropped(); }; - reloadButton.lineBreak = false; + auto& compileButton = CreateWidget("Compile"); + m_compileShaderButton = &compileButton; + compileButton.tooltip = "Compile the shader of the current material"; + compileButton.lineBreak = false; + compileButton.ClickedEvent += [this] { + if (m_target) + { + if (const auto shader = m_target->GetShader()) + { + EDITOR_EXEC(CompileShader(*shader)); + m_target->UpdateProperties(); + OnShaderDropped(); + } + } + }; + + auto& editShaderButton = CreateWidget("Edit Shader"); + m_editShaderButton = &editShaderButton; + editShaderButton.tooltip = "Edit the shader of the current material"; + editShaderButton.lineBreak = false; + editShaderButton.ClickedEvent += [this] { + if (m_target) + { + if (const auto shader = m_target->GetShader()) + { + const auto shaderFilePath = EDITOR_EXEC(GetRealPath(shader->path)); + OvTools::Utils::SystemCalls::OpenFile(shaderFilePath); + } + } + }; auto& previewButton = CreateWidget("Preview"); - previewButton.idleBackgroundColor = { 0.7f, 0.5f, 0.0f }; - previewButton.ClickedEvent += std::bind(&MaterialEditor::Preview, this); + previewButton.tooltip = "Preview the current material in the Asset View"; previewButton.lineBreak = false; + previewButton.ClickedEvent += std::bind(&MaterialEditor::Preview, this); - auto& resetButton = CreateWidget("Reset to default"); + auto& resetButton = CreateWidget("Reset"); resetButton.idleBackgroundColor = { 0.5f, 0.0f, 0.0f }; + resetButton.tooltip = "Reset the current material to its default state"; resetButton.ClickedEvent += std::bind(&MaterialEditor::Reset, this); } diff --git a/Sources/Overload/OvRendering/include/OvRendering/Data/Material.h b/Sources/Overload/OvRendering/include/OvRendering/Data/Material.h index 97c6719e..052d3640 100644 --- a/Sources/Overload/OvRendering/include/OvRendering/Data/Material.h +++ b/Sources/Overload/OvRendering/include/OvRendering/Data/Material.h @@ -75,9 +75,9 @@ namespace OvRendering::Data ) const; /** - * Fill uniform with default uniform values + * Add missing properties to the material based on the shader, and remove properties that are not used in the shader. */ - void FillUniform(); + void UpdateProperties(); /** * Bind the material and send its uniform data to the GPU diff --git a/Sources/Overload/OvRendering/src/OvRendering/Data/Material.cpp b/Sources/Overload/OvRendering/src/OvRendering/Data/Material.cpp index 6f8700e6..a0737cfb 100644 --- a/Sources/Overload/OvRendering/src/OvRendering/Data/Material.cpp +++ b/Sources/Overload/OvRendering/src/OvRendering/Data/Material.cpp @@ -74,7 +74,8 @@ void OvRendering::Data::Material::SetShader(OvRendering::Resources::Shader* p_sh if (m_shader) { - FillUniform(); + m_properties.clear(); + UpdateProperties(); } else { @@ -98,15 +99,23 @@ OvTools::Utils::OptRef OvRendering::Data::Mater return std::nullopt; } -void OvRendering::Data::Material::FillUniform() +void OvRendering::Data::Material::UpdateProperties() { - m_properties.clear(); + // Collect all uniform names currently used by the shader + std::unordered_set usedUniforms; + + auto variants_view = m_shader->GetVariants() + | std::views::values + | std::views::join + | std::views::values; - for (const auto& featureVariants : m_shader->GetVariants() | std::views::values) + for (const auto& variant : variants_view) { - for (const auto& variant : featureVariants | std::views::values) + for (const auto& [name, uniformInfo] : variant->GetUniforms()) { - for (const auto& [name, uniformInfo] : variant->GetUniforms()) + usedUniforms.insert(name); + + if (!m_properties.contains(name)) { m_properties.emplace(name, MaterialProperty{ .value = UniformToPropertyValue(uniformInfo.defaultValue), @@ -115,6 +124,10 @@ void OvRendering::Data::Material::FillUniform() } } } + + std::erase_if(m_properties, [&usedUniforms](const auto& property) { + return !usedUniforms.contains(property.first); + }); } // Note: this function is critical for performance, as it may be called many times during a frame.