diff --git a/assets/opensb/rendering/default.config b/assets/opensb/rendering/default.config new file mode 100644 index 0000000..5e51afc --- /dev/null +++ b/assets/opensb/rendering/default.config @@ -0,0 +1,9 @@ +{ + "effectParameters" : {}, + "effectTextures" : {}, + + "effectShaders" : { + "vertex" : "default.vert", + "fragment" : "default.frag" + } +} \ No newline at end of file diff --git a/assets/opensb/rendering/default.frag b/assets/opensb/rendering/default.frag new file mode 100644 index 0000000..0ba2d60 --- /dev/null +++ b/assets/opensb/rendering/default.frag @@ -0,0 +1,27 @@ +#version 110 + +uniform sampler2D texture0; +uniform sampler2D texture1; +uniform sampler2D texture2; +uniform sampler2D texture3; + +varying vec2 fragmentTextureCoordinate; +varying float fragmentTextureIndex; +varying vec4 fragmentColor; + +void main() { + vec4 texColor; + if (fragmentTextureIndex > 2.9) { + texColor = texture2D(texture3, fragmentTextureCoordinate); + } else if (fragmentTextureIndex > 1.9) { + texColor = texture2D(texture2, fragmentTextureCoordinate); + } else if (fragmentTextureIndex > 0.9) { + texColor = texture2D(texture1, fragmentTextureCoordinate); + } else { + texColor = texture2D(texture0, fragmentTextureCoordinate); + } + if (texColor.a <= 0.0) + discard; + + gl_FragColor = texColor * fragmentColor; +} \ No newline at end of file diff --git a/assets/opensb/rendering/default.vert b/assets/opensb/rendering/default.vert new file mode 100644 index 0000000..a8b2751 --- /dev/null +++ b/assets/opensb/rendering/default.vert @@ -0,0 +1,35 @@ +#version 110 + +uniform vec2 textureSize0; +uniform vec2 textureSize1; +uniform vec2 textureSize2; +uniform vec2 textureSize3; +uniform vec2 screenSize; +uniform mat3 vertexTransform; + +attribute vec2 vertexPosition; +attribute vec2 vertexTextureCoordinate; +attribute float vertexTextureIndex; +attribute vec4 vertexColor; +attribute float vertexParam1; + +varying vec2 fragmentTextureCoordinate; +varying float fragmentTextureIndex; +varying vec4 fragmentColor; + +void main() { + vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy; + + if (vertexTextureIndex > 2.9) { + fragmentTextureCoordinate = vertexTextureCoordinate / textureSize3; + } else if (vertexTextureIndex > 1.9) { + fragmentTextureCoordinate = vertexTextureCoordinate / textureSize2; + } else if (vertexTextureIndex > 0.9) { + fragmentTextureCoordinate = vertexTextureCoordinate / textureSize1; + } else { + fragmentTextureCoordinate = vertexTextureCoordinate / textureSize0; + } + fragmentTextureIndex = vertexTextureIndex; + fragmentColor = vertexColor; + gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/assets/opensb/rendering/opengl20.config b/assets/opensb/rendering/world.config similarity index 90% rename from assets/opensb/rendering/opengl20.config rename to assets/opensb/rendering/world.config index 70ff6c7..8dcc3cb 100644 --- a/assets/opensb/rendering/opengl20.config +++ b/assets/opensb/rendering/world.config @@ -2,7 +2,7 @@ "effectParameters" : { "lightMapEnabled" : { "type" : "bool", - "default" : false, + "default" : true, "uniform" : "lightMapEnabled" }, "lightMapScale" : { @@ -38,7 +38,7 @@ }, "effectShaders" : { - "vertex" : "opengl20.vert", - "fragment" : "opengl20.frag" + "vertex" : "world.vert", + "fragment" : "world.frag" } -} +} \ No newline at end of file diff --git a/assets/opensb/rendering/opengl20.frag b/assets/opensb/rendering/world.frag similarity index 95% rename from assets/opensb/rendering/opengl20.frag rename to assets/opensb/rendering/world.frag index a57c029..ed197c3 100644 --- a/assets/opensb/rendering/opengl20.frag +++ b/assets/opensb/rendering/world.frag @@ -77,7 +77,9 @@ void main() { vec4 finalColor = texColor * fragmentColor; float finalLightMapMultiplier = fragmentLightMapMultiplier * lightMapMultiplier; - if (lightMapEnabled && finalLightMapMultiplier > 0.0) + if (texColor.a == 0.99607843137) + finalColor.a = fragmentColor.a; + else if (lightMapEnabled && finalLightMapMultiplier > 0.0) finalColor.rgb *= sampleLightMap(fragmentLightMapCoordinate, 1.0 / lightMapSize).rgb * finalLightMapMultiplier; gl_FragColor = finalColor; } \ No newline at end of file diff --git a/assets/opensb/rendering/opengl20.vert b/assets/opensb/rendering/world.vert similarity index 100% rename from assets/opensb/rendering/opengl20.vert rename to assets/opensb/rendering/world.vert diff --git a/source/application/StarRenderer.hpp b/source/application/StarRenderer.hpp index 908f121..58c1c27 100644 --- a/source/application/StarRenderer.hpp +++ b/source/application/StarRenderer.hpp @@ -135,13 +135,15 @@ public: // specific to each type of renderer, so it will be necessary to key the // configuration off of the renderId string. This should not be called every // frame, because it will result in a recompile of the underlying shader set. - virtual void setEffectConfig(Json const& effectConfig, StringMap const& shaders) = 0; + virtual void loadEffectConfig(String const& name, Json const& effectConfig, StringMap const& shaders) = 0; // The effect config will specify named parameters and textures which can be // set here. virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0; virtual void setEffectTexture(String const& textureName, Image const& image) = 0; + virtual bool switchEffectConfig(String const& name) = 0; + // Any further rendering will be scissored based on this rect, specified in // pixels virtual void setScissorRect(Maybe const& scissorRect) = 0; diff --git a/source/application/StarRenderer_opengl20.cpp b/source/application/StarRenderer_opengl20.cpp index 375149e..70185cc 100644 --- a/source/application/StarRenderer_opengl20.cpp +++ b/source/application/StarRenderer_opengl20.cpp @@ -93,7 +93,7 @@ OpenGl20Renderer::OpenGl20Renderer() { TextureFiltering::Nearest); m_immediateRenderBuffer = createGlRenderBuffer(); - setEffectConfig(JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}}); + loadEffectConfig("internal", JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}}); m_limitTextureGroupSize = false; m_useMultiTexturing = true; @@ -102,7 +102,8 @@ OpenGl20Renderer::OpenGl20Renderer() { } OpenGl20Renderer::~OpenGl20Renderer() { - glDeleteProgram(m_program); + for (auto& effect : m_effects) + glDeleteProgram(effect.second.program); logGlErrorSummary("OpenGL errors during shutdown"); } @@ -115,8 +116,13 @@ Vec2U OpenGl20Renderer::screenSize() const { return m_screenSize; } -void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap const& shaders) { - flushImmediatePrimitives(); +void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectConfig, StringMap const& shaders) { + if (m_effects.contains(name)) { + Logger::warn("OpenGL effect {} already exists", name); + switchEffectConfig(name); + return; + } + GLint status = 0; char logBuffer[1024]; @@ -161,32 +167,11 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMapsecond; + m_currentEffect = &effect; for (auto const& p : effectConfig.getObject("effectParameters", {})) { EffectParameter effectParameter; @@ -212,7 +197,7 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMapparameters.ptr(parameterName); if (!ptr || (ptr->parameterValue && *ptr->parameterValue == value)) return; @@ -291,7 +274,7 @@ void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEff } void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const& image) { - auto ptr = m_effectTextures.ptr(textureName); + auto ptr = m_currentEffect->textures.ptr(textureName); if (!ptr) return; @@ -311,6 +294,21 @@ void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const& } } +bool OpenGl20Renderer::switchEffectConfig(String const& name) { + flushImmediatePrimitives(); + auto find = m_effects.find(name); + if (find == m_effects.end()) + return false; + + Effect& effect = find->second; + + glUseProgram(m_program = effect.program); + setupGlUniforms(m_program); + m_currentEffect = &effect; + + return true; +} + void OpenGl20Renderer::setScissorRect(Maybe const& scissorRect) { if (scissorRect == m_scissorRect) return; @@ -798,7 +796,7 @@ void OpenGl20Renderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture); } - for (auto const& p : m_effectTextures) { + for (auto const& p : m_currentEffect->textures) { if (p.second.textureValue) { glActiveTexture(GL_TEXTURE0 + p.second.textureUnit); glBindTexture(GL_TEXTURE_2D, p.second.textureValue->textureId); @@ -823,4 +821,26 @@ void OpenGl20Renderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F } } +void OpenGl20Renderer::setupGlUniforms(GLuint program) { + m_positionAttribute = glGetAttribLocation(program, "vertexPosition"); + m_texCoordAttribute = glGetAttribLocation(program, "vertexTextureCoordinate"); + m_texIndexAttribute = glGetAttribLocation(program, "vertexTextureIndex"); + m_colorAttribute = glGetAttribLocation(program, "vertexColor"); + m_param1Attribute = glGetAttribLocation(program, "vertexParam1"); + + m_textureUniforms.clear(); + m_textureSizeUniforms.clear(); + for (size_t i = 0; i < MultiTextureCount; ++i) { + m_textureUniforms.append(glGetUniformLocation(program, strf("texture{}", i).c_str())); + m_textureSizeUniforms.append(glGetUniformLocation(program, strf("textureSize{}", i).c_str())); + } + m_screenSizeUniform = glGetUniformLocation(program, "screenSize"); + m_vertexTransformUniform = glGetUniformLocation(program, "vertexTransform"); + + for (size_t i = 0; i < MultiTextureCount; ++i) + glUniform1i(m_textureUniforms[i], i); + + glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]); +} + } diff --git a/source/application/StarRenderer_opengl20.hpp b/source/application/StarRenderer_opengl20.hpp index ff82d3a..5add227 100644 --- a/source/application/StarRenderer_opengl20.hpp +++ b/source/application/StarRenderer_opengl20.hpp @@ -20,12 +20,15 @@ public: String rendererId() const override; Vec2U screenSize() const override; - void setEffectConfig(Json const& effectConfig, StringMap const& shaders) override; + void loadEffectConfig(String const& name, Json const& effectConfig, StringMap const& shaders) override; + void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override; void setEffectTexture(String const& textureName, Image const& image) override; void setScissorRect(Maybe const& scissorRect) override; + bool switchEffectConfig(String const& name) override; + TexturePtr createTexture(Image const& texture, TextureAddressing addressing, TextureFiltering filtering) override; void setSizeLimitEnabled(bool enabled) override; void setMultiTexturingEnabled(bool enabled) override; @@ -155,6 +158,13 @@ private: RefPtr textureValue; }; + struct Effect { + GLuint program; + Json config; + StringMap parameters; + StringMap textures; + }; + static void logGlErrorSummary(String prefix); static void uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data); @@ -166,6 +176,8 @@ private: void renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F const& transformation); + void setupGlUniforms(GLuint program); + Vec2U m_screenSize; GLuint m_program = 0; @@ -181,10 +193,11 @@ private: GLint m_screenSizeUniform = -1; GLint m_vertexTransformUniform = -1; + StringMap m_effects; + Effect* m_currentEffect; + RefPtr m_whiteTexture; - StringMap m_effectParameters; - StringMap m_effectTextures; Maybe m_scissorRect; bool m_limitTextureGroupSize; diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 14af82e..562128c 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -207,25 +207,31 @@ void ClientApplication::renderInit(RendererPtr renderer) { Application::renderInit(renderer); auto assets = m_root->assets(); - String rendererConfig = strf("/rendering/{}.config", renderer->rendererId()); - if (assets->assetExists(rendererConfig)) { - StringMap shaders; - auto config = assets->json(rendererConfig); - auto shaderConfig = config.getObject("effectShaders"); - for (auto& entry : shaderConfig) { - if (entry.second.isType(Json::Type::String)) { - String shader = entry.second.toString(); - if (!shader.hasChar('\n')) { - auto shaderBytes = assets->bytes(AssetPath::relativeTo(rendererConfig, shader)); - shader = std::string(shaderBytes->ptr(), shaderBytes->size()); + auto loadConfig = [&](String const& name) { + String path = strf("/rendering/{}.config", name); + if (assets->assetExists(path)) { + StringMap shaders; + auto config = assets->json(path); + auto shaderConfig = config.getObject("effectShaders"); + for (auto& entry : shaderConfig) { + if (entry.second.isType(Json::Type::String)) { + String shader = entry.second.toString(); + if (!shader.hasChar('\n')) { + auto shaderBytes = assets->bytes(AssetPath::relativeTo(path, shader)); + shader = std::string(shaderBytes->ptr(), shaderBytes->size()); + } + shaders[entry.first] = shader; } - shaders[entry.first] = shader; } + + renderer->loadEffectConfig(name, config, shaders); } - renderer->setEffectConfig(config, shaders); - } - else - Logger::warn("No rendering config found for renderer with id '{}'", renderer->rendererId()); + else + Logger::warn("No rendering config found for renderer with id '{}'", renderer->rendererId()); + }; + + loadConfig("world"); + loadConfig("default"); if (m_root->configuration()->get("limitTextureAtlasSize").optBool().value(false)) renderer->setSizeLimitEnabled(true); @@ -365,13 +371,13 @@ void ClientApplication::render() { RendererPtr renderer = Application::renderer(); if (worldClient) { int64_t start = Time::monotonicMilliseconds(); - if (renderer) - renderer->setEffectParameter("lightMapEnabled", true); + + renderer->switchEffectConfig("world"); worldClient->render(m_renderData, TilePainter::BorderTileSize); m_worldPainter->render(m_renderData, [&]() { worldClient->waitForLighting(); }); m_mainInterface->renderInWorldElements(); - if (renderer) - renderer->setEffectParameter("lightMapEnabled", false); + renderer->switchEffectConfig("default"); + LogMap::set("render_world", strf("{}ms", Time::monotonicMilliseconds() - start)); } int64_t start = Time::monotonicMilliseconds(); diff --git a/source/rendering/StarEnvironmentPainter.cpp b/source/rendering/StarEnvironmentPainter.cpp index 94bf364..6d1f029 100644 --- a/source/rendering/StarEnvironmentPainter.cpp +++ b/source/rendering/StarEnvironmentPainter.cpp @@ -231,6 +231,7 @@ void EnvironmentPainter::renderSky(Vec2F const& screenSize, SkyRenderData const& m_renderer->flush(); } +// TODO: Fix this to work with decimal zoom levels. Currently, the clouds shake rapidly when interpolating between zoom levels. void EnvironmentPainter::renderParallaxLayers( Vec2F parallaxWorldPosition, WorldCamera const& camera, ParallaxLayers const& layers, SkyRenderData const& sky) {