From 928f389637b120c69fe2ba0c7fe2e23f2898c15c Mon Sep 17 00:00:00 2001 From: Jeff Graw Date: Fri, 8 May 2026 21:34:25 -0600 Subject: [PATCH] Added 2/4x supersampling. --- TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp | 33 +++ .../Renderer/RClassic_GPU/rsectorGPU.cpp | 7 +- .../TFE_Jedi/Renderer/virtualFramebuffer.cpp | 7 +- .../Win32OpenGL/renderBackend.cpp | 216 +++++++++++++++--- .../Win32OpenGL/renderTarget.cpp | 26 ++- .../Win32OpenGL/renderTarget.h | 2 +- .../TFE_RenderBackend/renderBackend.h | 1 + TheForceEngine/TFE_Settings/settings.cpp | 6 + TheForceEngine/TFE_Settings/settings.h | 1 + 9 files changed, 254 insertions(+), 45 deletions(-) diff --git a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp index 31a447e5d..981654217 100644 --- a/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp +++ b/TheForceEngine/TFE_FrontEndUI/frontEndUi.cpp @@ -187,6 +187,20 @@ namespace TFE_FrontEndUI "True Color", // COLORMODE_TRUE_COLOR }; + static const char* c_supersampling[] = + { + "Off", + "2x", + "4x", + }; + + static const s32 c_supersamplingFactor[] = + { + 1, + 2, + 4, + }; + typedef void(*MenuItemSelected)(); static s32 s_resIndex = 0; @@ -3190,6 +3204,23 @@ namespace TFE_FrontEndUI ImGui::InputInt("##FOVText", &graphics->fov, 1, 20, ImGuiInputTextFlags_CharsDecimal); graphics->fov = clamp(graphics->fov, 5, 175); + s32 supersamplingIndex = 0; + if (graphics->supersampling == 2) + { + supersamplingIndex = 1; + } + else if (graphics->supersampling == 4) + { + supersamplingIndex = 2; + } + + ImGui::LabelText("##ConfigLabel", "Supersampling"); ImGui::SameLine(comboOffset); + ImGui::SetNextItemWidth(196 * s_uiScale); + if (ImGui::Combo("##Supersampling", &supersamplingIndex, c_supersampling, IM_ARRAYSIZE(c_supersampling))) + { + graphics->supersampling = c_supersamplingFactor[supersamplingIndex]; + } + // Sky rendering mode. s32 skyMode = graphics->skyMode; ImGui::LabelText("##ConfigLabel", "Sky Render Mode"); ImGui::SameLine(comboOffset); @@ -4056,6 +4087,7 @@ namespace TFE_FrontEndUI graphicsSettings->widescreen = true; graphicsSettings->gameResolution.x = displayInfo.width; graphicsSettings->gameResolution.z = displayInfo.height; + graphicsSettings->supersampling = 1; // Color mode and texture filtering are the main differences between modes. graphicsSettings->colorMode = (temp == TEMPLATE_MODERN) ? COLORMODE_TRUE_COLOR : COLORMODE_8BIT_INTERP; // Texture filtering. @@ -4106,6 +4138,7 @@ namespace TFE_FrontEndUI graphicsSettings->widescreen = false; graphicsSettings->gameResolution.x = 320; graphicsSettings->gameResolution.z = 200; + graphicsSettings->supersampling = 1; graphicsSettings->colorMode = COLORMODE_8BIT; // Reticle. graphicsSettings->reticleEnable = false; diff --git a/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp b/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp index 9ed090862..cf98b5f21 100644 --- a/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp +++ b/TheForceEngine/TFE_Jedi/Renderer/RClassic_GPU/rsectorGPU.cpp @@ -1886,6 +1886,7 @@ namespace TFE_Jedi { u32 dispWidth, dispHeight; vfb_getResolution(&dispWidth, &dispHeight); + const f32 ssFactor = f32(TFE_RenderBackend::getVirtualDisplaySupersampleFactor()); // Compute the camera yaw from the camera direction and rotate it 90 degrees. // This generates a value from 0 to 1. @@ -1901,12 +1902,12 @@ namespace TFE_Jedi cameraYaw * parallax[0], clamp(cameraPitch, -rad45, rad45) * parallax[1] * oneOverTwoPi, parallax[0] * oneOverTwoPi, - 200.0f / f32(dispHeight), + 200.0f / (f32(dispHeight) * ssFactor), }; const f32 skyParam1[2] = { -s_rcfltState.nearPlaneHalfLen, - s_rcfltState.nearPlaneHalfLen * 2.0f / f32(dispWidth), + s_rcfltState.nearPlaneHalfLen * 2.0f / (f32(dispWidth) * ssFactor), }; shader->setVariable(skyInputs->skyParam0Id, SVT_VEC4, skyParam0); shader->setVariable(skyInputs->skyParam1Id, SVT_VEC2, skyParam1); @@ -2238,4 +2239,4 @@ namespace TFE_Jedi assert(success); return success; } -} \ No newline at end of file +} diff --git a/TheForceEngine/TFE_Jedi/Renderer/virtualFramebuffer.cpp b/TheForceEngine/TFE_Jedi/Renderer/virtualFramebuffer.cpp index 0e2ba6a7b..ae50498f2 100644 --- a/TheForceEngine/TFE_Jedi/Renderer/virtualFramebuffer.cpp +++ b/TheForceEngine/TFE_Jedi/Renderer/virtualFramebuffer.cpp @@ -15,6 +15,7 @@ namespace TFE_Jedi static u32 s_prevWidth = 0; static u32 s_prevHeight = 0; static s32 s_widescreenOffset = 0; + static s32 s_supersampling = 1; static bool s_widescreen = false; static u32 s_palette[256]; @@ -39,11 +40,13 @@ namespace TFE_Jedi JBool vfb_setResolution(u32 width, u32 height) { TFE_Settings_Graphics* graphics = TFE_Settings::getGraphicsSettings(); - if (width == s_width && height == s_height && s_widescreen == graphics->widescreen && s_mode == s_nextMode) + const s32 supersampling = s_nextMode == VFB_RENDER_TRAGET ? graphics->supersampling : 1; + if (width == s_width && height == s_height && s_widescreen == graphics->widescreen && s_supersampling == supersampling && s_mode == s_nextMode) { return JFALSE; } s_widescreen = graphics->widescreen; + s_supersampling = supersampling; s_mode = s_nextMode; if (width == 320 && height == 200) @@ -273,4 +276,4 @@ namespace TFE_Jedi }; TFE_RenderBackend::createVirtualDisplay(vdisp); } -} // namespace TFE_Jedi \ No newline at end of file +} // namespace TFE_Jedi diff --git a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderBackend.cpp b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderBackend.cpp index a364e36bb..b14481c9c 100644 --- a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderBackend.cpp +++ b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderBackend.cpp @@ -45,6 +45,12 @@ namespace TFE_RenderBackend static TextureGpu* s_virtualRenderTexture = nullptr; static TextureGpu* s_materialRenderTexture = nullptr; static RenderTarget* s_virtualRenderTarget = nullptr; + static TextureGpu* s_resolvedRenderTexture = nullptr; + static TextureGpu* s_resolvedMaterialTexture = nullptr; + static RenderTarget* s_resolvedRenderTarget = nullptr; + static TextureGpu* s_downsampleRenderTexture = nullptr; + static TextureGpu* s_downsampleMaterialTexture = nullptr; + static RenderTarget* s_downsampleRenderTarget = nullptr; static ScreenCapture* s_screenCapture = nullptr; static RenderTarget* s_copyTarget = nullptr; @@ -52,6 +58,7 @@ namespace TFE_RenderBackend static u32 s_virtualWidth, s_virtualHeight; static u32 s_virtualWidthUi; static u32 s_virtualWidth3d; + static u32 s_supersampleFactor = 1; static bool s_widescreen = false; static bool s_asyncFrameBuffer = true; @@ -72,6 +79,9 @@ namespace TFE_RenderBackend void drawVirtualDisplay(); void setupPostEffectChain(bool useDynamicTexture, bool useBloom); + void freeVirtualDisplayResources(); + bool createVirtualRenderTargets(u32 factor); + RenderTarget* getPostProcessRenderTarget(); static GLuint s_globalVAO = 0; static bool s_isMacOS = false; @@ -285,16 +295,8 @@ namespace TFE_RenderBackend TFE_PostProcess::destroy(); TFE_Ui::shutdown(); - delete s_virtualDisplay; - delete s_virtualRenderTarget; - delete s_virtualRenderTexture; - delete s_materialRenderTexture; + freeVirtualDisplayResources(); SDL_DestroyWindow((SDL_Window*)m_window); - - s_virtualDisplay = nullptr; - s_virtualRenderTarget = nullptr; - s_virtualRenderTexture = nullptr; - s_materialRenderTexture = nullptr; m_window = nullptr; } @@ -544,48 +546,170 @@ namespace TFE_RenderBackend displayInfo->refreshRate = (m_windowState.flags & WINFLAG_VSYNC) != 0 ? m_windowState.refreshRate : 0.0f; } - bool recreateDisplay(bool setupPostFx) + u32 getRequestedSupersampleFactor() { - if (s_virtualDisplay) + if (!s_useRenderTarget) { - delete s_virtualDisplay; + return 1; } - if (s_virtualRenderTarget) + + const s32 supersampling = TFE_Settings::getGraphicsSettings()->supersampling; + return (supersampling == 2 || supersampling == 4) ? (u32)supersampling : 1u; + } + + u32 getValidSupersampleFactor(u32 width, u32 height) + { + u32 factor = getRequestedSupersampleFactor(); + if (factor <= 1) { - delete s_virtualRenderTarget; + return 1; } - // Sync issue? - if (s_virtualRenderTexture) + + GLint maxTextureSize = 0; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + GLint maxRenderBufferSize = 0; + glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderBufferSize); + GLint maxViewportDims[2] = { 0, 0 }; + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims); + const u32 maxWidth = std::min((u32)maxTextureSize, std::min((u32)maxRenderBufferSize, (u32)maxViewportDims[0])); + const u32 maxHeight = std::min((u32)maxTextureSize, std::min((u32)maxRenderBufferSize, (u32)maxViewportDims[1])); + + while (factor > 1 && (width * factor > maxWidth || height * factor > maxHeight)) { - delete s_virtualRenderTexture; + factor >>= 1; } - if (s_materialRenderTexture) + + if (factor != getRequestedSupersampleFactor()) { - delete s_materialRenderTexture; + TFE_System::logWrite(LOG_WARNING, "RenderBackend", "Supersampling reduced from %ux to %ux; requested render target exceeds OpenGL size limits.", getRequestedSupersampleFactor(), factor); } + return factor; + } + + RenderTarget* getPostProcessRenderTarget() + { + return s_resolvedRenderTarget ? s_resolvedRenderTarget : s_virtualRenderTarget; + } + + void freeVirtualDisplayResources() + { + delete s_virtualDisplay; + delete s_virtualRenderTarget; + delete s_resolvedRenderTarget; + delete s_downsampleRenderTarget; + delete s_virtualRenderTexture; + delete s_materialRenderTexture; + delete s_resolvedRenderTexture; + delete s_resolvedMaterialTexture; + delete s_downsampleRenderTexture; + delete s_downsampleMaterialTexture; + s_virtualDisplay = nullptr; s_virtualRenderTarget = nullptr; + s_resolvedRenderTarget = nullptr; + s_downsampleRenderTarget = nullptr; s_virtualRenderTexture = nullptr; s_materialRenderTexture = nullptr; + s_resolvedRenderTexture = nullptr; + s_resolvedMaterialTexture = nullptr; + s_downsampleRenderTexture = nullptr; + s_downsampleMaterialTexture = nullptr; + } - bool result = false; - if (s_useRenderTarget) + bool createVirtualRenderTargets(u32 factor) + { + const u32 renderWidth = s_virtualWidth * factor; + const u32 renderHeight = s_virtualHeight * factor; + + s_virtualRenderTarget = new RenderTarget(); + s_virtualRenderTexture = new TextureGpu(); + bool result = s_virtualRenderTexture->create(renderWidth, renderHeight); + + if (s_bloomEnable) // Output to two textures. + { + s_materialRenderTexture = new TextureGpu(); + result &= s_materialRenderTexture->create(renderWidth, renderHeight); + + TextureGpu* textures[] = { s_virtualRenderTexture, s_materialRenderTexture }; + result &= s_virtualRenderTarget->create(2, textures, true); + } + else + { + result &= s_virtualRenderTarget->create(1, &s_virtualRenderTexture, true); + } + + if (factor > 1) { - s_virtualRenderTarget = new RenderTarget(); - s_virtualRenderTexture = new TextureGpu(); - result = s_virtualRenderTexture->create(s_virtualWidth, s_virtualHeight); + if (factor == 4) + { + s_downsampleRenderTarget = new RenderTarget(); + s_downsampleRenderTexture = new TextureGpu(); + result &= s_downsampleRenderTexture->create(s_virtualWidth * 2, s_virtualHeight * 2); + + if (s_bloomEnable) + { + s_downsampleMaterialTexture = new TextureGpu(); + result &= s_downsampleMaterialTexture->create(s_virtualWidth * 2, s_virtualHeight * 2); + + TextureGpu* textures[] = { s_downsampleRenderTexture, s_downsampleMaterialTexture }; + result &= s_downsampleRenderTarget->create(2, textures, false); + } + else + { + result &= s_downsampleRenderTarget->create(1, &s_downsampleRenderTexture, false); + } + } - if (s_bloomEnable) // Output to two textures. + s_resolvedRenderTarget = new RenderTarget(); + s_resolvedRenderTexture = new TextureGpu(); + result &= s_resolvedRenderTexture->create(s_virtualWidth, s_virtualHeight); + + if (s_bloomEnable) { - s_materialRenderTexture = new TextureGpu(); - result &= s_materialRenderTexture->create(s_virtualWidth, s_virtualHeight); + s_resolvedMaterialTexture = new TextureGpu(); + result &= s_resolvedMaterialTexture->create(s_virtualWidth, s_virtualHeight); - TextureGpu* textures[] = { s_virtualRenderTexture, s_materialRenderTexture }; - result &= s_virtualRenderTarget->create(2, textures, true); + TextureGpu* textures[] = { s_resolvedRenderTexture, s_resolvedMaterialTexture }; + result &= s_resolvedRenderTarget->create(2, textures, false); } else { - result &= s_virtualRenderTarget->create(1, &s_virtualRenderTexture, true); + result &= s_resolvedRenderTarget->create(1, &s_resolvedRenderTexture, false); + } + } + + return result; + } + + bool recreateDisplay(bool setupPostFx) + { + freeVirtualDisplayResources(); + + bool result = false; + if (s_useRenderTarget) + { + const u32 requestedFactor = getValidSupersampleFactor(s_virtualWidth, s_virtualHeight); + for (u32 factor = requestedFactor; factor >= 1; factor >>= 1) + { + s_supersampleFactor = factor; + result = createVirtualRenderTargets(s_supersampleFactor); + if (result) + { + break; + } + + freeVirtualDisplayResources(); + if (factor == 1) + { + TFE_System::logWrite(LOG_ERROR, "RenderBackend", "Failed to create virtual render target."); + break; + } + TFE_System::logWrite(LOG_WARNING, "RenderBackend", "Supersampling reduced from %ux to %ux after render target creation failed.", factor, factor >> 1); + } + + if (!result) + { + return false; } // The renderer will handle this instead. @@ -602,6 +726,7 @@ namespace TFE_RenderBackend } else { + s_supersampleFactor = 1; s_virtualDisplay = new DynamicTexture(); if (s_gpuColorConvert) { @@ -663,6 +788,11 @@ namespace TFE_RenderBackend return (s_virtualWidth - s_virtualWidth3d) >> 1; } + u32 getVirtualDisplaySupersampleFactor() + { + return s_supersampleFactor; + } + void* getVirtualDisplayGpuPtr() { return (void*)(iptr)s_virtualDisplay->getTexture()->getHandle(); @@ -770,6 +900,19 @@ namespace TFE_RenderBackend TFE_ZONE("Draw Virtual Display"); if (!s_virtualDisplay && !s_virtualRenderTarget) { return; } + if (s_resolvedRenderTarget) + { + if (s_downsampleRenderTarget) + { + RenderTarget::copy(s_downsampleRenderTarget, s_virtualRenderTarget, MAG_FILTER_LINEAR); + RenderTarget::copy(s_resolvedRenderTarget, s_downsampleRenderTarget, MAG_FILTER_LINEAR); + } + else + { + RenderTarget::copy(s_resolvedRenderTarget, s_virtualRenderTarget, MAG_FILTER_LINEAR); + } + } + // Only clear if (1) s_virtualDisplay == null or (2) s_displayMode != DMODE_STRETCH if (s_displayMode != DMODE_STRETCH) { @@ -939,7 +1082,8 @@ namespace TFE_RenderBackend s_bloomBufferCount = 0; // Create new textures and stages. - const TextureGpu* baseTex = s_virtualRenderTarget->getTexture(); + RenderTarget* postProcessTarget = getPostProcessRenderTarget(); + const TextureGpu* baseTex = postProcessTarget->getTexture(); u32 width = baseTex->getWidth()/2; u32 height = baseTex->getHeight()/2; @@ -954,8 +1098,8 @@ namespace TFE_RenderBackend const PostEffectInput bloomTresholdInputs[] = { - { PTYPE_TEXTURE, (void*)s_virtualRenderTarget->getTexture(0) }, - { PTYPE_TEXTURE, (void*)s_virtualRenderTarget->getTexture(1) }, + { PTYPE_TEXTURE, (void*)postProcessTarget->getTexture(0) }, + { PTYPE_TEXTURE, (void*)postProcessTarget->getTexture(1) }, }; TFE_PostProcess::appendEffect(s_bloomTheshold, TFE_ARRAYSIZE(bloomTresholdInputs), bloomTresholdInputs, s_bloomTargets[index], 0, 0, width, height, true); @@ -1057,11 +1201,12 @@ namespace TFE_RenderBackend } else if (useBloom) { + RenderTarget* postProcessTarget = getPostProcessRenderTarget(); setupBloomStages(); const PostEffectInput blitInputs[] = { - { PTYPE_TEXTURE, (void*)s_virtualRenderTarget->getTexture(0) }, + { PTYPE_TEXTURE, (void*)postProcessTarget->getTexture(0) }, { PTYPE_TEXTURE, (void*)s_bloomTextures[s_bloomBufferCount-1] }, { PTYPE_DYNAMIC_TEX, s_palette } }; @@ -1069,9 +1214,10 @@ namespace TFE_RenderBackend } else { + RenderTarget* postProcessTarget = getPostProcessRenderTarget(); const PostEffectInput blitInputs[] = { - { PTYPE_TEXTURE, (void*)s_virtualRenderTarget->getTexture() }, + { PTYPE_TEXTURE, (void*)postProcessTarget->getTexture() }, { PTYPE_DYNAMIC_TEX, s_palette } }; TFE_PostProcess::appendEffect(s_postEffectBlit, TFE_ARRAYSIZE(blitInputs), blitInputs, nullptr, x, y, w, h); diff --git a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.cpp b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.cpp index 8865b8a64..4b646e003 100644 --- a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.cpp +++ b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.cpp @@ -3,6 +3,7 @@ #include #include "gl.h" #include +#include namespace { @@ -66,6 +67,7 @@ bool RenderTarget::create(s32 textureCount, TextureGpu** textures, bool depthBuf assert(status == GL_FRAMEBUFFER_COMPLETE); if (status != GL_FRAMEBUFFER_COMPLETE) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); return false; } @@ -124,17 +126,33 @@ void RenderTarget::unbind() glBindFramebuffer(GL_FRAMEBUFFER, 0); } -void RenderTarget::copy(RenderTarget* dst, RenderTarget* src) +void RenderTarget::copy(RenderTarget* dst, RenderTarget* src, MagFilter filter) { glBindFramebuffer(GL_READ_FRAMEBUFFER, src->m_gpuHandle); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->m_gpuHandle); - glBlitFramebuffer(0, 0, src->getTexture()->getWidth(), src->getTexture()->getHeight(), // src rect - 0, 0, dst->getTexture()->getWidth(), dst->getTexture()->getHeight(), // dst rect - GL_COLOR_BUFFER_BIT, GL_NEAREST); + const GLenum glFilter = filter == MAG_FILTER_LINEAR ? GL_LINEAR : GL_NEAREST; + const u32 count = std::min(src->m_textureCount, dst->m_textureCount); + for (u32 i = 0; i < count; i++) + { + glReadBuffer(GL_COLOR_ATTACHMENT0 + i); + glDrawBuffer(GL_COLOR_ATTACHMENT0 + i); + glBlitFramebuffer(0, 0, src->getTexture(i)->getWidth(), src->getTexture(i)->getHeight(), // src rect + 0, 0, dst->getTexture(i)->getWidth(), dst->getTexture(i)->getHeight(), // dst rect + GL_COLOR_BUFFER_BIT, glFilter); + } + + GLenum drawBuffers[MAX_ATTACHMENT] = { 0 }; + for (u32 i = 0; i < dst->m_textureCount; i++) + { + drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; + } + glDrawBuffers(dst->m_textureCount, drawBuffers); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glReadBuffer(GL_BACK); + glDrawBuffer(GL_BACK); } void RenderTarget::copyBackbufferToTarget(RenderTarget* dst) diff --git a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.h b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.h index 296eeb147..e1aeb45d0 100644 --- a/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.h +++ b/TheForceEngine/TFE_RenderBackend/Win32OpenGL/renderTarget.h @@ -18,7 +18,7 @@ class RenderTarget void clear(const f32* color, f32 depth, u8 stencil = 0, bool clearColor = true); void clearDepth(f32 depth); void clearStencil(u8 stencil); - static void copy(RenderTarget* dst, RenderTarget* src); + static void copy(RenderTarget* dst, RenderTarget* src, MagFilter filter = MAG_FILTER_NONE); static void copyBackbufferToTarget(RenderTarget* dst); static void unbind(); diff --git a/TheForceEngine/TFE_RenderBackend/renderBackend.h b/TheForceEngine/TFE_RenderBackend/renderBackend.h index cdfc30ef9..52b521788 100644 --- a/TheForceEngine/TFE_RenderBackend/renderBackend.h +++ b/TheForceEngine/TFE_RenderBackend/renderBackend.h @@ -136,6 +136,7 @@ namespace TFE_RenderBackend u32 getVirtualDisplayHeight(); u32 getVirtualDisplayOffset2D(); u32 getVirtualDisplayOffset3D(); + u32 getVirtualDisplaySupersampleFactor(); // core gpu functionality for UI and editor. // Render target. diff --git a/TheForceEngine/TFE_Settings/settings.cpp b/TheForceEngine/TFE_Settings/settings.cpp index f9f2ad51b..c1f6f35d7 100644 --- a/TheForceEngine/TFE_Settings/settings.cpp +++ b/TheForceEngine/TFE_Settings/settings.cpp @@ -470,6 +470,7 @@ namespace TFE_Settings writeKeyValue_Int(settings, "renderer", s_graphicsSettings.rendererIndex); writeKeyValue_Int(settings, "colorMode", s_graphicsSettings.colorMode); + writeKeyValue_Int(settings, "supersampling", s_graphicsSettings.supersampling); writeKeyValue_Int(settings, "skyMode", s_graphicsSettings.skyMode); writeKeyValue_Bool(settings, "forceGouraud", s_graphicsSettings.forceGouraudShading); } @@ -898,6 +899,11 @@ namespace TFE_Settings { s_graphicsSettings.colorMode = parseInt(value); } + else if (strcasecmp("supersampling", key) == 0) + { + const s32 supersampling = parseInt(value); + s_graphicsSettings.supersampling = (supersampling == 2 || supersampling == 4) ? supersampling : 1; + } else if (strcasecmp("skyMode", key) == 0) { s_graphicsSettings.skyMode = SkyMode(parseInt(value)); diff --git a/TheForceEngine/TFE_Settings/settings.h b/TheForceEngine/TFE_Settings/settings.h index a2e2bb788..2e368a164 100644 --- a/TheForceEngine/TFE_Settings/settings.h +++ b/TheForceEngine/TFE_Settings/settings.h @@ -81,6 +81,7 @@ struct TFE_Settings_Graphics s32 fov = 90; s32 rendererIndex = 0; s32 colorMode = COLORMODE_8BIT; + s32 supersampling = 1; // 8-bit options. bool ditheredBilinear = false;