From b6323a0fdcaa692bf8097011607d3fa3fd8a6908 Mon Sep 17 00:00:00 2001 From: Anemunt <69436164+darkresident55@users.noreply.github.com> Date: Sun, 14 Dec 2025 17:28:33 -0500 Subject: [PATCH] =?UTF-8?q?added=20gyro=20thingy=20on=20the=20top=20right,?= =?UTF-8?q?=20attempted=20to=20fix=20Motion=20blur,=20(it=20did=20not=20go?= =?UTF-8?q?=20well=20=F0=9F=98=AD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Resources/Shaders/postfx_frag.glsl | 101 +++++++++- src/EnginePanels.cpp | 314 +++++++++++++++++++++++++---- src/ProjectManager.cpp | 36 ++++ src/Rendering.cpp | 22 +- src/SceneObject.h | 9 + 5 files changed, 439 insertions(+), 43 deletions(-) diff --git a/Resources/Shaders/postfx_frag.glsl b/Resources/Shaders/postfx_frag.glsl index 650826d..d2047ba 100644 --- a/Resources/Shaders/postfx_frag.glsl +++ b/Resources/Shaders/postfx_frag.glsl @@ -20,6 +20,17 @@ uniform bool enableMotionBlur = false; uniform bool hasHistory = false; uniform float motionBlurStrength = 0.15; +uniform bool enableVignette = false; +uniform float vignetteIntensity = 0.35; +uniform float vignetteSmoothness = 0.25; + +uniform bool enableChromatic = false; +uniform float chromaticAmount = 0.0025; + +uniform bool enableAO = false; +uniform float aoRadius = 0.0035; +uniform float aoStrength = 0.6; + vec3 applyColorAdjust(vec3 color) { if (enableColorAdjust) { color *= exp2(exposure); @@ -31,18 +42,94 @@ vec3 applyColorAdjust(vec3 color) { return color; } -void main() { - vec3 color = texture(sceneTex, TexCoord).rgb; +vec3 sampleCombined(vec2 uv) { + vec3 c = texture(sceneTex, uv).rgb; if (enableBloom) { - vec3 glow = texture(bloomTex, TexCoord).rgb; - color += glow * bloomIntensity; + vec3 glow = texture(bloomTex, uv).rgb * bloomIntensity; + c += glow; + } + return c; +} + +vec3 sampleAdjusted(vec2 uv) { + return applyColorAdjust(sampleCombined(uv)); +} + +float luminance(vec3 c) { + return dot(c, vec3(0.299, 0.587, 0.114)); +} + +float computeVignette(vec2 uv) { + float dist = length(uv - vec2(0.5)); + float vig = smoothstep(0.8 - vignetteIntensity, 1.0 + vignetteSmoothness, dist); + return clamp(1.0 - vig * vignetteIntensity, 0.0, 1.0); +} + +vec3 applyChromatic(vec2 uv) { + vec3 base = sampleAdjusted(uv); + vec2 dir = uv - vec2(0.5); + float dist = max(length(dir), 0.0001); + vec2 offset = normalize(dir) * chromaticAmount * (1.0 + dist * 2.0); + vec3 rSample = sampleAdjusted(uv + offset); + vec3 bSample = sampleAdjusted(uv - offset); + vec3 ca = vec3(rSample.r, base.g, bSample.b); + return mix(base, ca, 0.85); +} + +float computeAOFactor(vec2 uv) { + vec3 centerColor = sampleAdjusted(uv); + float centerLum = luminance(centerColor); + float occlusion = 0.0; + vec2 directions[4] = vec2[](vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0)); + for (int i = 0; i < 4; ++i) { + vec2 sampleUv = uv + directions[i] * aoRadius; + vec3 sampleColor = sampleAdjusted(sampleUv); + float sampleLum = luminance(sampleColor); + occlusion += max(0.0, centerLum - sampleLum); + } + occlusion /= 4.0; + return clamp(1.0 - occlusion * aoStrength, 0.0, 1.0); +} + +void main() { + vec3 color = sampleAdjusted(TexCoord); + + if (enableChromatic) { + color = applyChromatic(TexCoord); } - color = applyColorAdjust(color); + if (enableAO) { + color *= computeAOFactor(TexCoord); + } + + if (enableVignette) { + color *= computeVignette(TexCoord); + } if (enableMotionBlur && hasHistory) { - vec3 history = texture(historyTex, TexCoord).rgb; - color = mix(color, history, clamp(motionBlurStrength, 0.0, 0.98)); + vec2 dir = TexCoord - vec2(0.5); + float len = length(dir); + dir = (len > 0.0001) ? dir / len : vec2(0.0); + float smear = clamp(motionBlurStrength, 0.0, 0.98) * 0.035; // subtle default + + vec3 accum = vec3(0.0); + float weightSum = 0.0; + for (int i = 0; i < 3; ++i) { + float t = (float(i) + 1.0) / 3.0; + float w = 1.0 - t * 0.4; + vec2 offsetUv = TexCoord - dir * smear * t; + offsetUv = clamp(offsetUv, vec2(0.002), vec2(0.998)); + vec3 sampleCol = texture(historyTex, offsetUv).rgb; + accum += sampleCol * w; + weightSum += w; + } + vec3 history = (weightSum > 0.0) ? accum / weightSum : texture(historyTex, TexCoord).rgb; + float diff = length(color - history); + float motionWeight = smoothstep(0.01, 0.08, diff); // suppress blur when camera still + float mixAmt = clamp(motionBlurStrength * 0.85, 0.0, 0.9) * motionWeight; + if (mixAmt > 0.0001) { + color = mix(color, history, mixAmt); + } } FragColor = vec4(color, 1.0); diff --git a/src/EnginePanels.cpp b/src/EnginePanels.cpp index 2ca1d92..da7e111 100644 --- a/src/EnginePanels.cpp +++ b/src/EnginePanels.cpp @@ -1784,6 +1784,7 @@ void Engine::renderInspectorPanel() { } SceneObject& obj = *it; + bool addComponentButtonShown = false; if (selectedObjectIds.size() > 1) { ImGui::Text("Multiple objects selected: %zu", selectedObjectIds.size()); @@ -1968,10 +1969,50 @@ void Engine::renderInspectorPanel() { } ImGui::EndDisabled(); + ImGui::Separator(); + ImGui::TextDisabled("Vignette"); + if (ImGui::Checkbox("Enable Vignette", &obj.postFx.vignetteEnabled)) { + changed = true; + } + ImGui::BeginDisabled(!obj.postFx.vignetteEnabled); + if (ImGui::SliderFloat("Intensity", &obj.postFx.vignetteIntensity, 0.0f, 1.5f, "%.2f")) { + changed = true; + } + if (ImGui::SliderFloat("Smoothness", &obj.postFx.vignetteSmoothness, 0.05f, 1.0f, "%.2f")) { + changed = true; + } + ImGui::EndDisabled(); + + ImGui::Separator(); + ImGui::TextDisabled("Ambient Occlusion"); + if (ImGui::Checkbox("Enable AO", &obj.postFx.ambientOcclusionEnabled)) { + changed = true; + } + ImGui::BeginDisabled(!obj.postFx.ambientOcclusionEnabled); + if (ImGui::SliderFloat("AO Radius", &obj.postFx.aoRadius, 0.0005f, 0.01f, "%.4f")) { + changed = true; + } + if (ImGui::SliderFloat("AO Strength", &obj.postFx.aoStrength, 0.0f, 2.0f, "%.2f")) { + changed = true; + } + ImGui::EndDisabled(); + + ImGui::Separator(); + ImGui::TextDisabled("Chromatic Aberration"); + if (ImGui::Checkbox("Enable Chromatic", &obj.postFx.chromaticAberrationEnabled)) { + changed = true; + } + ImGui::BeginDisabled(!obj.postFx.chromaticAberrationEnabled); + if (ImGui::SliderFloat("Fringe Amount", &obj.postFx.chromaticAmount, 0.0f, 0.01f, "%.4f")) { + changed = true; + } + ImGui::EndDisabled(); + if (changed) { projectManager.currentProject.hasUnsavedChanges = true; } ImGui::TextDisabled("Nodes stack in hierarchy order; latest node overrides previous settings."); + ImGui::TextDisabled("Wireframe/line mode auto-disables post effects."); ImGui::Unindent(10.0f); } ImGui::PopStyleColor(); @@ -2095,6 +2136,22 @@ void Engine::renderInspectorPanel() { ImGui::Spacing(); ImGui::Separator(); + ImGui::Text("Material"); + ImGui::SameLine(); + ImVec4 previewColor(obj.material.color.x, obj.material.color.y, obj.material.color.z, 1.0f); + ImVec2 sphereStart = ImGui::GetCursorScreenPos(); + float sphereRadius = 12.0f; + ImDrawList* dl = ImGui::GetWindowDrawList(); + ImU32 shadowCol = ImGui::ColorConvertFloat4ToU32(ImVec4(previewColor.x * 0.3f, previewColor.y * 0.3f, previewColor.z * 0.3f, 1.0f)); + ImU32 baseCol = ImGui::ColorConvertFloat4ToU32(previewColor); + ImU32 highlightCol = ImGui::ColorConvertFloat4ToU32(ImVec4(std::min(1.0f, previewColor.x + 0.25f), std::min(1.0f, previewColor.y + 0.25f), std::min(1.0f, previewColor.z + 0.25f), 1.0f)); + ImVec2 center = ImVec2(sphereStart.x + sphereRadius, sphereStart.y + sphereRadius); + dl->AddCircleFilled(center, sphereRadius, shadowCol); + dl->AddCircleFilled(ImVec2(center.x, center.y - 1.5f), sphereRadius - 1.5f, baseCol); + dl->AddCircleFilled(ImVec2(center.x - sphereRadius * 0.35f, center.y - sphereRadius * 0.5f), sphereRadius * 0.35f, highlightCol); + ImGui::Dummy(ImVec2(sphereRadius * 2.0f, sphereRadius * 2.0f)); + ImGui::SameLine(); + ImGui::TextDisabled("%s", obj.materialPath.empty() ? "Unsaved Material" : fs::path(obj.materialPath).filename().string().c_str()); ImGui::Text("Material Asset"); char matPathBuf[512] = {}; @@ -2125,6 +2182,63 @@ void Engine::renderInspectorPanel() { } ImGui::EndDisabled(); + ImGui::Spacing(); + ImGui::TextDisabled("Material Slots"); + for (size_t slot = 0; slot < obj.additionalMaterialPaths.size(); ++slot) { + ImGui::PushID(static_cast(slot)); + char slotBuf[512] = {}; + std::snprintf(slotBuf, sizeof(slotBuf), "%s", obj.additionalMaterialPaths[slot].c_str()); + ImGui::SetNextItemWidth(-140); + if (ImGui::InputText("##AdditionalMat", slotBuf, sizeof(slotBuf))) { + obj.additionalMaterialPaths[slot] = slotBuf; + materialChanged = true; + } + ImGui::SameLine(); + if (ImGui::SmallButton("Use Selection / Blender")) { + if (!fileBrowser.selectedFile.empty() && fs::exists(fileBrowser.selectedFile)) { + fs::directory_entry entry(fileBrowser.selectedFile); + if (fileBrowser.getFileCategory(entry) == FileCategory::Material) { + obj.additionalMaterialPaths[slot] = entry.path().string(); + materialChanged = true; + } + } + } + ImGui::SameLine(); + if (ImGui::SmallButton("Remove")) { + obj.additionalMaterialPaths.erase(obj.additionalMaterialPaths.begin() + static_cast(slot)); + materialChanged = true; + ImGui::PopID(); + break; + } + ImGui::PopID(); + } + if (ImGui::SmallButton("Add Material Slot")) { + obj.additionalMaterialPaths.push_back(""); + materialChanged = true; + } + + ImGui::Spacing(); + ImGui::Separator(); + ImGui::TextDisabled("Preview"); + ImVec4 previewColorBar(obj.material.color.x, obj.material.color.y, obj.material.color.z, 1.0f); + ImGui::ColorButton("##MaterialPreview", previewColorBar, ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetContentRegionAvail().x, 32.0f)); + + ImGui::Spacing(); + addComponentButtonShown = true; + ImGui::PushID("MaterialAddComponent"); + if (ImGui::Button("Add Component", ImVec2(-1, 0))) { + ImGui::OpenPopup("AddComponentPopup"); + } + if (ImGui::BeginPopup("AddComponentPopup")) { + if (ImGui::MenuItem("Script")) { + obj.scripts.push_back(ScriptComponent{}); + materialChanged = true; + addComponentButtonShown = true; + } + ImGui::EndPopup(); + } + ImGui::PopID(); + if (materialChanged) { projectManager.currentProject.hasUnsavedChanges = true; } @@ -2311,29 +2425,72 @@ void Engine::renderInspectorPanel() { ImGui::PopStyleColor(); } - ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.4f, 0.35f, 0.55f, 1.0f)); - if (ImGui::CollapsingHeader("Scripts", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Indent(10.0f); + if (!addComponentButtonShown) { + ImGui::Spacing(); + ImGui::Separator(); + ImGui::TextDisabled("Components"); + addComponentButtonShown = true; + ImGui::PushID("FallbackAddComponent"); + if (ImGui::Button("Add Component", ImVec2(-1, 0))) { + ImGui::OpenPopup("AddComponentPopup"); + } + if (ImGui::BeginPopup("AddComponentPopup")) { + if (ImGui::MenuItem("Script")) { + obj.scripts.push_back(ScriptComponent{}); + projectManager.currentProject.hasUnsavedChanges = true; + } + ImGui::EndPopup(); + } + ImGui::PopID(); + } - bool changed = false; - if (ImGui::Button("Add Script", ImVec2(-1, 0))) { - obj.scripts.push_back(ScriptComponent{}); - changed = true; + ImGui::Spacing(); + ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.9f, 1.0f), "Scripts"); + + bool scriptsChanged = false; + int scriptToRemove = -1; + + for (size_t i = 0; i < obj.scripts.size(); ++i) { + ImGui::Separator(); + ImGui::PushID(static_cast(i)); + ScriptComponent& sc = obj.scripts[i]; + + std::string headerLabel = sc.path.empty() ? "Script" : fs::path(sc.path).filename().string(); + std::string headerId = headerLabel + "##ScriptHeader" + std::to_string(i); + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen; + bool open = ImGui::CollapsingHeader(headerId.c_str(), flags); + + ImVec2 headerMin = ImGui::GetItemRectMin(); + ImVec2 headerMax = ImGui::GetItemRectMax(); + float menuWidth = ImGui::GetFrameHeight(); + ImGui::SameLine(); + ImGui::SetCursorScreenPos(ImVec2(headerMax.x - menuWidth - ImGui::GetStyle().ItemSpacing.x, headerMin.y + (headerMax.y - headerMin.y - menuWidth) * 0.5f)); + if (ImGui::SmallButton("...")) { + ImGui::OpenPopup("ScriptComponentMenu"); + } + if (ImGui::BeginPopup("ScriptComponentMenu")) { + if (ImGui::MenuItem("Compile", nullptr, false, !sc.path.empty())) { + compileScriptFile(sc.path); + } + if (ImGui::MenuItem("Remove")) { + scriptToRemove = static_cast(i); + } + ImGui::EndPopup(); } - for (size_t i = 0; i < obj.scripts.size(); ++i) { - ImGui::Separator(); - ImGui::PushID(static_cast(i)); - ScriptComponent& sc = obj.scripts[i]; + if (scriptToRemove == static_cast(i)) { + ImGui::PopID(); + continue; + } + if (open) { char pathBuf[512] = {}; std::snprintf(pathBuf, sizeof(pathBuf), "%s", sc.path.c_str()); - ImGui::Text("Script %zu", i + 1); + ImGui::TextDisabled("Path"); ImGui::SetNextItemWidth(-140); if (ImGui::InputText("##ScriptPath", pathBuf, sizeof(pathBuf))) { sc.path = pathBuf; - changed = true; + scriptsChanged = true; } ImGui::SameLine(); @@ -2342,7 +2499,7 @@ void Engine::renderInspectorPanel() { fs::directory_entry entry(fileBrowser.selectedFile); if (fileBrowser.getFileCategory(entry) == FileCategory::Script) { sc.path = entry.path().string(); - changed = true; + scriptsChanged = true; } } } @@ -2354,14 +2511,6 @@ void Engine::renderInspectorPanel() { } ImGui::EndDisabled(); - ImGui::SameLine(); - if (ImGui::SmallButton("Remove")) { - obj.scripts.erase(obj.scripts.begin() + static_cast(i)); - changed = true; - ImGui::PopID(); - break; - } - if (!sc.path.empty()) { fs::path binary = resolveScriptBinary(sc.path); sc.lastBinaryPath = binary.string(); @@ -2391,18 +2540,18 @@ void Engine::renderInspectorPanel() { ImGui::SetNextItemWidth(140); if (ImGui::InputText("##Key", keyBuf, sizeof(keyBuf))) { sc.settings[s].key = keyBuf; - changed = true; + scriptsChanged = true; } ImGui::SameLine(); ImGui::SetNextItemWidth(-100); if (ImGui::InputText("##Value", valBuf, sizeof(valBuf))) { sc.settings[s].value = valBuf; - changed = true; + scriptsChanged = true; } ImGui::SameLine(); if (ImGui::SmallButton("X")) { sc.settings.erase(sc.settings.begin() + static_cast(s)); - changed = true; + scriptsChanged = true; ImGui::PopID(); break; } @@ -2411,19 +2560,21 @@ void Engine::renderInspectorPanel() { if (ImGui::SmallButton("Add Setting")) { sc.settings.push_back(ScriptSetting{"", ""}); - changed = true; + scriptsChanged = true; } - - ImGui::PopID(); } - if (changed) { - projectManager.currentProject.hasUnsavedChanges = true; - } - - ImGui::Unindent(10.0f); + ImGui::PopID(); + } + + if (scriptToRemove >= 0 && scriptToRemove < static_cast(obj.scripts.size())) { + obj.scripts.erase(obj.scripts.begin() + scriptToRemove); + scriptsChanged = true; + } + + if (scriptsChanged) { + projectManager.currentProject.hasUnsavedChanges = true; } - ImGui::PopStyleColor(); if (browserHasMaterial) { ImGui::Spacing(); @@ -2750,6 +2901,99 @@ void Engine::renderViewport() { ImVec2 imageMin = ImGui::GetItemRectMin(); ImVec2 imageMax = ImGui::GetItemRectMax(); mouseOverViewportImage = ImGui::IsItemHovered(); + ImDrawList* viewportDrawList = ImGui::GetWindowDrawList(); + + auto setCameraFacing = [&](const glm::vec3& dir) { + glm::vec3 worldUp = glm::vec3(0, 1, 0); + glm::vec3 n = glm::normalize(dir); + glm::vec3 up = worldUp; + if (std::abs(glm::dot(n, worldUp)) > 0.98f) { + up = glm::vec3(0, 0, 1); + } + glm::vec3 right = glm::normalize(glm::cross(up, n)); + if (glm::length(right) < 1e-4f) { + right = glm::vec3(1, 0, 0); + } + up = glm::normalize(glm::cross(n, right)); + + camera.front = n; + camera.up = up; + camera.pitch = glm::degrees(std::asin(glm::clamp(n.y, -1.0f, 1.0f))); + camera.pitch = glm::clamp(camera.pitch, -89.0f, 89.0f); + camera.yaw = glm::degrees(std::atan2(n.z, n.x)); + camera.firstMouse = true; + }; + + // Draw small axis widget in top-right of viewport + { + const float widgetSize = 94.0f; + const float padding = 12.0f; + ImVec2 center = ImVec2( + imageMax.x - padding - widgetSize * 0.5f, + imageMin.y + padding + widgetSize * 0.5f + ); + float radius = widgetSize * 0.46f; + ImU32 ringCol = ImGui::GetColorU32(ImVec4(0.07f, 0.07f, 0.1f, 0.9f)); + ImU32 ringBorder = ImGui::GetColorU32(ImVec4(1, 1, 1, 0.18f)); + viewportDrawList->AddCircleFilled(center, radius + 10.0f, ringCol, 48); + viewportDrawList->AddCircle(center, radius + 10.0f, ringBorder, 48); + viewportDrawList->AddCircle(center, radius + 3.0f, ImGui::GetColorU32(ImVec4(1,1,1,0.08f)), 32); + viewportDrawList->AddCircleFilled(center, 5.5f, ImGui::GetColorU32(ImVec4(1,1,1,0.6f)), 24); + + glm::mat3 viewRot = glm::mat3(view); + ImVec2 widgetMin = ImVec2(center.x - widgetSize * 0.5f, center.y - widgetSize * 0.5f); + ImVec2 widgetMax = ImVec2(center.x + widgetSize * 0.5f, center.y + widgetSize * 0.5f); + bool widgetHover = ImGui::IsMouseHoveringRect(widgetMin, widgetMax); + struct AxisArrow { + glm::vec3 dir; + ImU32 color; + const char* label; + }; + AxisArrow arrows[] = { + { glm::vec3(1, 0, 0), ImGui::GetColorU32(ImVec4(0.9f, 0.2f, 0.2f, 1.0f)), "X" }, + { glm::vec3(-1, 0, 0), ImGui::GetColorU32(ImVec4(0.6f, 0.2f, 0.2f, 1.0f)), "-X" }, + { glm::vec3(0, 1, 0), ImGui::GetColorU32(ImVec4(0.2f, 0.9f, 0.2f, 1.0f)), "Y" }, + { glm::vec3(0,-1, 0), ImGui::GetColorU32(ImVec4(0.2f, 0.6f, 0.2f, 1.0f)), "-Y" }, + { glm::vec3(0, 0, 1), ImGui::GetColorU32(ImVec4(0.2f, 0.4f, 0.9f, 1.0f)), "Z" }, + { glm::vec3(0, 0,-1), ImGui::GetColorU32(ImVec4(0.2f, 0.3f, 0.6f, 1.0f)), "-Z" }, + }; + + ImVec2 mouse = ImGui::GetIO().MousePos; + int clickedIdx = -1; + float clickRadius = 12.0f; + + for (int i = 0; i < 6; ++i) { + glm::vec3 camSpace = viewRot * arrows[i].dir; + glm::vec2 dir2 = glm::normalize(glm::vec2(camSpace.x, -camSpace.y)); + float depthScale = glm::clamp(0.35f + 0.65f * ((camSpace.z + 1.0f) * 0.5f), 0.25f, 1.0f); + float len = radius * depthScale; + ImVec2 tip = ImVec2(center.x + dir2.x * len, center.y + dir2.y * len); + + ImVec2 base1 = ImVec2(center.x + dir2.x * (len * 0.55f) + dir2.y * (len * 0.12f), + center.y + dir2.y * (len * 0.55f) - dir2.x * (len * 0.12f)); + ImVec2 base2 = ImVec2(center.x + dir2.x * (len * 0.55f) - dir2.y * (len * 0.12f), + center.y + dir2.y * (len * 0.55f) + dir2.x * (len * 0.12f)); + + viewportDrawList->AddTriangleFilled(base1, tip, base2, arrows[i].color); + viewportDrawList->AddTriangle(base1, tip, base2, ImGui::GetColorU32(ImVec4(0,0,0,0.35f))); + + ImVec2 labelPos = ImVec2(center.x + dir2.x * (len * 0.78f), center.y + dir2.y * (len * 0.78f)); + viewportDrawList->AddCircleFilled(labelPos, 6.0f, ImGui::GetColorU32(ImVec4(0,0,0,0.5f)), 12); + viewportDrawList->AddText(ImVec2(labelPos.x - 4.0f, labelPos.y - 7.0f), ImGui::GetColorU32(ImVec4(1,1,1,0.95f)), arrows[i].label); + + if (widgetHover) { + float dx = mouse.x - tip.x; + float dy = mouse.y - tip.y; + if (std::sqrt(dx*dx + dy*dy) <= clickRadius && ImGui::IsMouseReleased(0)) { + clickedIdx = i; + } + } + } + + if (clickedIdx >= 0) { + setCameraFacing(arrows[clickedIdx].dir); + } + } auto projectToScreen = [&](const glm::vec3& p) -> std::optional { glm::vec4 clip = proj * view * glm::vec4(p, 1.0f); diff --git a/src/ProjectManager.cpp b/src/ProjectManager.cpp index 6ee4e56..af6b0dc 100644 --- a/src/ProjectManager.cpp +++ b/src/ProjectManager.cpp @@ -284,6 +284,10 @@ bool SceneSerializer::saveScene(const fs::path& filePath, file << "vertexShader=" << obj.vertexShaderPath << "\n"; file << "fragmentShader=" << obj.fragmentShaderPath << "\n"; file << "useOverlay=" << (obj.useOverlay ? 1 : 0) << "\n"; + file << "additionalMaterialCount=" << obj.additionalMaterialPaths.size() << "\n"; + for (size_t mi = 0; mi < obj.additionalMaterialPaths.size(); ++mi) { + file << "additionalMaterial" << mi << "=" << obj.additionalMaterialPaths[mi] << "\n"; + } file << "scripts=" << obj.scripts.size() << "\n"; for (size_t si = 0; si < obj.scripts.size(); ++si) { const auto& sc = obj.scripts[si]; @@ -317,6 +321,14 @@ bool SceneSerializer::saveScene(const fs::path& filePath, file << "postColorFilter=" << obj.postFx.colorFilter.r << "," << obj.postFx.colorFilter.g << "," << obj.postFx.colorFilter.b << "\n"; file << "postMotionBlurEnabled=" << (obj.postFx.motionBlurEnabled ? 1 : 0) << "\n"; file << "postMotionBlurStrength=" << obj.postFx.motionBlurStrength << "\n"; + file << "postVignetteEnabled=" << (obj.postFx.vignetteEnabled ? 1 : 0) << "\n"; + file << "postVignetteIntensity=" << obj.postFx.vignetteIntensity << "\n"; + file << "postVignetteSmoothness=" << obj.postFx.vignetteSmoothness << "\n"; + file << "postChromaticEnabled=" << (obj.postFx.chromaticAberrationEnabled ? 1 : 0) << "\n"; + file << "postChromaticAmount=" << obj.postFx.chromaticAmount << "\n"; + file << "postAOEnabled=" << (obj.postFx.ambientOcclusionEnabled ? 1 : 0) << "\n"; + file << "postAORadius=" << obj.postFx.aoRadius << "\n"; + file << "postAOStrength=" << obj.postFx.aoStrength << "\n"; } file << "scriptCount=" << obj.scripts.size() << "\n"; @@ -448,6 +460,14 @@ bool SceneSerializer::loadScene(const fs::path& filePath, currentObj->fragmentShaderPath = value; } else if (key == "useOverlay") { currentObj->useOverlay = (std::stoi(value) != 0); + } else if (key == "additionalMaterialCount") { + int count = std::stoi(value); + currentObj->additionalMaterialPaths.resize(std::max(0, count)); + } else if (key.rfind("additionalMaterial", 0) == 0) { + int idx = std::stoi(key.substr(18)); // length of "additionalMaterial" + if (idx >= 0 && idx < (int)currentObj->additionalMaterialPaths.size()) { + currentObj->additionalMaterialPaths[idx] = value; + } } else if (key == "scripts") { int count = std::stoi(value); currentObj->scripts.resize(std::max(0, count)); @@ -534,6 +554,22 @@ bool SceneSerializer::loadScene(const fs::path& filePath, currentObj->postFx.motionBlurEnabled = (std::stoi(value) != 0); } else if (key == "postMotionBlurStrength") { currentObj->postFx.motionBlurStrength = std::stof(value); + } else if (key == "postVignetteEnabled") { + currentObj->postFx.vignetteEnabled = (std::stoi(value) != 0); + } else if (key == "postVignetteIntensity") { + currentObj->postFx.vignetteIntensity = std::stof(value); + } else if (key == "postVignetteSmoothness") { + currentObj->postFx.vignetteSmoothness = std::stof(value); + } else if (key == "postChromaticEnabled") { + currentObj->postFx.chromaticAberrationEnabled = (std::stoi(value) != 0); + } else if (key == "postChromaticAmount") { + currentObj->postFx.chromaticAmount = std::stof(value); + } else if (key == "postAOEnabled") { + currentObj->postFx.ambientOcclusionEnabled = (std::stoi(value) != 0); + } else if (key == "postAORadius") { + currentObj->postFx.aoRadius = std::stof(value); + } else if (key == "postAOStrength") { + currentObj->postFx.aoStrength = std::stof(value); } else if (key == "scriptCount") { int count = std::stoi(value); currentObj->scripts.resize(std::max(0, count)); diff --git a/src/Rendering.cpp b/src/Rendering.cpp index b6c0e6d..1a011a2 100644 --- a/src/Rendering.cpp +++ b/src/Rendering.cpp @@ -1066,7 +1066,19 @@ PostFXSettings Renderer::gatherPostFX(const std::vector& sceneObjec void Renderer::applyPostProcessing(const std::vector& sceneObjects) { PostFXSettings settings = gatherPostFX(sceneObjects); - bool wantsEffects = settings.enabled && (settings.bloomEnabled || settings.colorAdjustEnabled || settings.motionBlurEnabled); + GLint polygonMode[2] = { GL_FILL, GL_FILL }; +#ifdef GL_POLYGON_MODE + glGetIntegerv(GL_POLYGON_MODE, polygonMode); +#endif + bool wireframe = (polygonMode[0] == GL_LINE || polygonMode[1] == GL_LINE); + + bool wantsEffects = settings.enabled && + (settings.bloomEnabled || settings.colorAdjustEnabled || settings.motionBlurEnabled || + settings.vignetteEnabled || settings.chromaticAberrationEnabled || settings.ambientOcclusionEnabled); + + if (wireframe) { + wantsEffects = false; + } if (!wantsEffects || !postShader || currentWidth <= 0 || currentHeight <= 0) { displayTexture = viewportTexture; @@ -1142,6 +1154,14 @@ void Renderer::applyPostProcessing(const std::vector& sceneObjects) postShader->setBool("enableMotionBlur", settings.motionBlurEnabled); postShader->setFloat("motionBlurStrength", settings.motionBlurStrength); postShader->setBool("hasHistory", historyValid); + postShader->setBool("enableVignette", settings.vignetteEnabled); + postShader->setFloat("vignetteIntensity", settings.vignetteIntensity); + postShader->setFloat("vignetteSmoothness", settings.vignetteSmoothness); + postShader->setBool("enableChromatic", settings.chromaticAberrationEnabled); + postShader->setFloat("chromaticAmount", settings.chromaticAmount); + postShader->setBool("enableAO", settings.ambientOcclusionEnabled); + postShader->setFloat("aoRadius", settings.aoRadius); + postShader->setFloat("aoStrength", settings.aoStrength); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, viewportTexture); diff --git a/src/SceneObject.h b/src/SceneObject.h index be50305..9a3ac83 100644 --- a/src/SceneObject.h +++ b/src/SceneObject.h @@ -69,6 +69,14 @@ struct PostFXSettings { glm::vec3 colorFilter = glm::vec3(1.0f); bool motionBlurEnabled = false; float motionBlurStrength = 0.15f; // 0..1 blend with previous frame + bool vignetteEnabled = false; + float vignetteIntensity = 0.35f; + float vignetteSmoothness = 0.25f; + bool chromaticAberrationEnabled = false; + float chromaticAmount = 0.0025f; + bool ambientOcclusionEnabled = false; + float aoRadius = 0.0035f; + float aoStrength = 0.6f; }; enum class ConsoleMessageType { @@ -115,6 +123,7 @@ public: CameraComponent camera; // Only used when type is camera PostFXSettings postFx; // Only used when type is PostFXNode std::vector scripts; + std::vector additionalMaterialPaths; SceneObject(const std::string& name, ObjectType type, int id) : name(name), type(type), position(0.0f), rotation(0.0f), scale(1.0f), id(id) {}