diff --git a/assets/opensb/client.config.patch b/assets/opensb/client.config.patch index a0de983..619a767 100644 --- a/assets/opensb/client.config.patch +++ b/assets/opensb/client.config.patch @@ -9,5 +9,6 @@ "deployCinematicBase" : { "scissor" : false, "letterbox" : false - } -} \ No newline at end of file + }, + "postProcessLayers": [] +} diff --git a/assets/opensb/rendering/effects/basic.vert b/assets/opensb/rendering/effects/basic.vert new file mode 100644 index 0000000..362dc6c --- /dev/null +++ b/assets/opensb/rendering/effects/basic.vert @@ -0,0 +1,7 @@ +#version 140 + +in vec2 vertexPosition; + +void main() { + gl_Position = vec4(vertexPosition, 0.0, 1.0); +} diff --git a/source/application/StarRenderer_opengl.cpp b/source/application/StarRenderer_opengl.cpp index 7ebcf4c..7f4dcaa 100644 --- a/source/application/StarRenderer_opengl.cpp +++ b/source/application/StarRenderer_opengl.cpp @@ -153,12 +153,32 @@ OpenGlRenderer::GlFrameBuffer::GlFrameBuffer(Json const& fbConfig) : config(fbCo GLenum target = multisample ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; glBindTexture(target, texture->glTextureId()); - Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 })); + sizeDiv = config.getUInt("sizeDiv",1); + Vec2U size = jsonToVec2U(config.getArray("size", { 256, 256 }))/sizeDiv; if (multisample) glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, size[0], size[1], GL_TRUE); - else + else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, size[0], size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + } + auto addressing = TextureAddressingNames.getLeft(config.getString("textureAddressing", "clamp")); + auto filtering = TextureFilteringNames.getLeft(config.getString("textureFiltering", "nearest")); + if (addressing == TextureAddressing::Clamp) { + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + if (!multisample) { + if (filtering == TextureFiltering::Nearest) { + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } else { + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + } glGenFramebuffers(1, &id); if (!id) @@ -184,6 +204,7 @@ void OpenGlRenderer::loadConfig(Json const& config) { for (auto& pair : config.getObject("frameBuffers", {})) { Json config = pair.second; config = config.set("multisample", m_multiSampling); + Logger::info("Creating framebuffer {}", pair.first); m_frameBuffers[pair.first] = make_ref(config); } @@ -257,8 +278,9 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf auto& effect = m_effects.emplace(name, Effect()).first->second; effect.program = m_program; effect.config = effectConfig; + effect.includeVBTextures = effectConfig.getBool("includeVBTextures",true); m_currentEffect = &effect; - setupGlUniforms(effect); + setupGlUniforms(effect,m_screenSize); for (auto const& p : effectConfig.getObject("effectParameters", {})) { EffectParameter effectParameter; @@ -307,7 +329,7 @@ void OpenGlRenderer::loadEffectConfig(String const& name, Json const& effectConf // Assign each texture parameter a texture unit starting with MultiTextureCount, the first // few texture units are used by the primary textures being drawn. Currently, // maximum texture units are not checked. - unsigned parameterTextureUnit = MultiTextureCount; + unsigned parameterTextureUnit = effect.includeVBTextures ? MultiTextureCount : 0; for (auto const& p : effectConfig.getObject("effectTextures", {})) { EffectTexture effectTexture; @@ -394,19 +416,39 @@ bool OpenGlRenderer::switchEffectConfig(String const& name) { if (auto blitFrameBufferId = effect.config.optString("blitFrameBuffer")) blitGlFrameBuffer(getGlFrameBuffer(*blitFrameBufferId)); - if (auto frameBufferId = effect.config.optString("frameBuffer")) - switchGlFrameBuffer(getGlFrameBuffer(*frameBufferId)); - else { + auto effectScreenSize = m_screenSize; + if (auto frameBufferId = effect.config.optString("frameBuffer")) { + auto buf = getGlFrameBuffer(*frameBufferId); + switchGlFrameBuffer(buf); + effectScreenSize = m_screenSize/(buf->sizeDiv); + } else { m_currentFrameBuffer.reset(); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } glUseProgram(m_program = effect.program); - setupGlUniforms(effect); + setupGlUniforms(effect,effectScreenSize); m_currentEffect = &effect; setEffectParameter("vertexRounding", m_multiSampling > 0); - + if (auto fbts = effect.config.optArray("frameBufferTextures")) { + for (auto const& fbt : *fbts) { + if (auto frameBufferId = fbt.optString("framebuffer")) { + auto textureUniform=fbt.getString("texture"); + auto ptr = m_currentEffect->textures.ptr(textureUniform); + if (ptr) { + if (!ptr->textureValue || ptr->textureValue->textureId == 0) { + auto texture = getGlFrameBuffer(*frameBufferId)->texture; + ptr->textureValue = texture; + if (ptr->textureSizeUniform != -1) { + auto textureSize = ptr->textureValue->glTextureSize(); + glUniform2f(ptr->textureSizeUniform, textureSize[0], textureSize[1]); + } + } + } + } + } + } return true; } @@ -507,10 +549,10 @@ void OpenGlRenderer::setScreenSize(Vec2U screenSize) { for (auto& frameBuffer : m_frameBuffers) { if (unsigned multisample = frameBuffer.second->multisample) { glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, frameBuffer.second->texture->glTextureId()); - glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, m_screenSize[0], m_screenSize[1], GL_TRUE); + glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisample, GL_RGBA8, m_screenSize[0]/frameBuffer.second->sizeDiv, m_screenSize[1]/frameBuffer.second->sizeDiv, GL_TRUE); } else { glBindTexture(GL_TEXTURE_2D, frameBuffer.second->texture->glTextureId()); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_screenSize[0], m_screenSize[1], 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_screenSize[0]/frameBuffer.second->sizeDiv, m_screenSize[1]/frameBuffer.second->sizeDiv, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); } } } @@ -963,10 +1005,12 @@ void OpenGlRenderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F co for (auto const& vb : renderBuffer.vertexBuffers) { glUniformMatrix3fv(m_vertexTransformUniform, 1, GL_TRUE, transformation.ptr()); - for (size_t i = 0; i < vb.textures.size(); ++i) { - glUniform2f(m_textureSizeUniforms[i], vb.textures[i].size[0], vb.textures[i].size[1]); - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture); + if (m_currentEffect->includeVBTextures) { + for (size_t i = 0; i < vb.textures.size(); ++i) { + glUniform2f(m_textureSizeUniforms[i], vb.textures[i].size[0], vb.textures[i].size[1]); + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture); + } } for (auto const& p : m_currentEffect->textures) { @@ -993,7 +1037,7 @@ void OpenGlRenderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F co } //Assumes the passed effect program is currently in use. -void OpenGlRenderer::setupGlUniforms(Effect& effect) { +void OpenGlRenderer::setupGlUniforms(Effect& effect, Vec2U screenSize) { m_positionAttribute = effect.getAttribute("vertexPosition"); m_colorAttribute = effect.getAttribute("vertexColor"); m_texCoordAttribute = effect.getAttribute("vertexTextureCoordinate"); @@ -1001,17 +1045,21 @@ void OpenGlRenderer::setupGlUniforms(Effect& effect) { m_textureUniforms.clear(); m_textureSizeUniforms.clear(); - for (size_t i = 0; i < MultiTextureCount; ++i) { - m_textureUniforms.append(effect.getUniform(strf("texture{}", i).c_str())); - m_textureSizeUniforms.append(effect.getUniform(strf("textureSize{}", i).c_str())); + if (effect.includeVBTextures) { + for (size_t i = 0; i < MultiTextureCount; ++i) { + m_textureUniforms.append(effect.getUniform(strf("texture{}", i).c_str())); + m_textureSizeUniforms.append(effect.getUniform(strf("textureSize{}", i).c_str())); + } } m_screenSizeUniform = effect.getUniform("screenSize"); m_vertexTransformUniform = effect.getUniform("vertexTransform"); - for (size_t i = 0; i < MultiTextureCount; ++i) - glUniform1i(m_textureUniforms[i], i); + if (effect.includeVBTextures) { + for (size_t i = 0; i < MultiTextureCount; ++i) + glUniform1i(m_textureUniforms[i], i); + } - glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]); + glUniform2f(m_screenSizeUniform, screenSize[0], screenSize[1]); } RefPtr OpenGlRenderer::getGlFrameBuffer(String const& id) { diff --git a/source/application/StarRenderer_opengl.hpp b/source/application/StarRenderer_opengl.hpp index 035b906..36b8354 100644 --- a/source/application/StarRenderer_opengl.hpp +++ b/source/application/StarRenderer_opengl.hpp @@ -180,6 +180,7 @@ private: Json config; bool blitted = false; unsigned multisample = 0; + unsigned sizeDiv = 1; GlFrameBuffer(Json const& config); ~GlFrameBuffer(); @@ -197,6 +198,7 @@ private: GLuint getAttribute(String const& name); GLuint getUniform(String const& name); + bool includeVBTextures; }; static bool logGlErrorSummary(String prefix); @@ -211,7 +213,7 @@ private: void renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F const& transformation); - void setupGlUniforms(Effect& effect); + void setupGlUniforms(Effect& effect, Vec2U screenSize); RefPtr getGlFrameBuffer(String const& id); void blitGlFrameBuffer(RefPtr const& frameBuffer); diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 5f3d119..80f0e01 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -400,6 +400,17 @@ void ClientApplication::render() { }); LogMap::set("client_render_world_painter", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - paintStart)); LogMap::set("client_render_world_total", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - totalStart)); + + auto size = Vec2F(renderer->screenSize()); + auto quad = renderFlatRect(RectF::withSize(size/-2, size), Vec4B(0.0f,0.0f,0.0f,0.0f), 0.0f); + for (auto& layer : m_postProcessLayers) { + for(unsigned i = 0; i < layer.passes; i++) { + for (auto& effect : layer.effects) { + renderer->switchEffectConfig(effect); + renderer->render(quad); + } + } + } } renderer->switchEffectConfig("interface"); auto start = Time::monotonicMicroseconds(); @@ -449,8 +460,20 @@ void ClientApplication::renderReload() { }; renderer->loadConfig(assets->json("/rendering/opengl.config")); - + loadEffectConfig("world"); + + m_postProcessLayers.clear(); + auto postProcessLayers = assets->json("/client.config:postProcessLayers").toArray(); + for (auto& layer : postProcessLayers) { + List effects; + for (auto& effect : layer.getArray("effects")) { + auto effectStr = effect.toString(); + loadEffectConfig(effectStr); + effects.append(effectStr); + } + m_postProcessLayers.append(PostProcessLayer{effects,layer.getUInt("passes",1)}); + } loadEffectConfig("interface"); } diff --git a/source/client/StarClientApplication.hpp b/source/client/StarClientApplication.hpp index fe68f22..840dc9a 100644 --- a/source/client/StarClientApplication.hpp +++ b/source/client/StarClientApplication.hpp @@ -52,6 +52,11 @@ private: String account; String password; }; + + struct PostProcessLayer { + List effects; + unsigned passes; + }; void renderReload(); @@ -98,6 +103,8 @@ private: WorldPainterPtr m_worldPainter; WorldRenderData m_renderData; MainInterfacePtr m_mainInterface; + + List m_postProcessLayers; // Valid if main app state == SinglePlayer UniverseServerPtr m_universeServer;