added gyro thingy on the top right, attempted to fix Motion blur, (it did not go well 😭)

This commit is contained in:
Anemunt
2025-12-14 17:28:33 -05:00
parent cf63b25b16
commit b6323a0fdc
5 changed files with 439 additions and 43 deletions

View File

@@ -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);

View File

@@ -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<int>(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<long>(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<int>(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<int>(i);
}
ImGui::EndPopup();
}
for (size_t i = 0; i < obj.scripts.size(); ++i) {
ImGui::Separator();
ImGui::PushID(static_cast<int>(i));
ScriptComponent& sc = obj.scripts[i];
if (scriptToRemove == static_cast<int>(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<long>(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<long>(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<int>(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<ImVec2> {
glm::vec4 clip = proj * view * glm::vec4(p, 1.0f);

View File

@@ -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));

View File

@@ -1066,7 +1066,19 @@ PostFXSettings Renderer::gatherPostFX(const std::vector<SceneObject>& sceneObjec
void Renderer::applyPostProcessing(const std::vector<SceneObject>& 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<SceneObject>& 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);

View File

@@ -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<ScriptComponent> scripts;
std::vector<std::string> 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) {}