diff --git a/assets/opensb/rendering/opengl20.config b/assets/opensb/rendering/opengl20.config new file mode 100644 index 0000000..923578c --- /dev/null +++ b/assets/opensb/rendering/opengl20.config @@ -0,0 +1,38 @@ +{ + "effectParameters" : { + "lightMapEnabled" : { + "type" : "bool", + "default" : false, + "uniform" : "lightMapEnabled" + }, + "lightMapScale" : { + "type" : "vec2", + "default" : [1, 1], + "uniform" : "lightMapScale" + }, + "lightMapOffset" : { + "type" : "vec2", + "default" : [0, 0], + "uniform" : "lightMapOffset" + }, + "lightMapMultiplier" : { + "type" : "float", + "default" : 1.0, + "uniform" : "lightMapMultiplier" + } + }, + + "effectTextures" : { + "lightMap" : { + "textureUniform" : "lightMap", + "textureSizeUniform" : "lightMapSize", + "textureAddressing" : "clamp", + "textureFiltering" : "linear" + } + }, + + "effectShaders" : { + "vertex" : "opengl20.vert", + "fragment" : "opengl20.frag" + } +} diff --git a/assets/opensb/rendering/opengl20.frag b/assets/opensb/rendering/opengl20.frag new file mode 100644 index 0000000..92a121f --- /dev/null +++ b/assets/opensb/rendering/opengl20.frag @@ -0,0 +1,75 @@ +#version 110 + +uniform sampler2D texture0; +uniform sampler2D texture1; +uniform sampler2D texture2; +uniform sampler2D texture3; +uniform bool lightMapEnabled; +uniform vec2 lightMapSize; +uniform sampler2D lightMap; +uniform float lightMapMultiplier; + +varying vec2 fragmentTextureCoordinate; +varying float fragmentTextureIndex; +varying vec4 fragmentColor; +varying float fragmentLightMapMultiplier; +varying vec2 fragmentLightMapCoordinate; + +vec4 cubic(float v) { + vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; + vec4 s = n * n * n; + float x = s.x; + float y = s.y - 4.0 * s.x; + float z = s.z - 4.0 * s.y + 6.0 * s.x; + float w = 6.0 - x - y - z; + return vec4(x, y, z, w); +} + +vec4 bicubicSample(sampler2D texture, vec2 texcoord, vec2 texscale) { + texcoord = texcoord - vec2(0.5, 0.5); + + float fx = fract(texcoord.x); + float fy = fract(texcoord.y); + texcoord.x -= fx; + texcoord.y -= fy; + + vec4 xcubic = cubic(fx); + vec4 ycubic = cubic(fy); + + vec4 c = vec4(texcoord.x - 0.5, texcoord.x + 1.5, texcoord.y - 0.5, texcoord.y + 1.5); + vec4 s = vec4(xcubic.x + xcubic.y, xcubic.z + xcubic.w, ycubic.x + ycubic.y, ycubic.z + ycubic.w); + vec4 offset = c + vec4(xcubic.y, xcubic.w, ycubic.y, ycubic.w) / s; + + vec4 sample0 = texture2D(texture, vec2(offset.x, offset.z) * texscale); + vec4 sample1 = texture2D(texture, vec2(offset.y, offset.z) * texscale); + vec4 sample2 = texture2D(texture, vec2(offset.x, offset.w) * texscale); + vec4 sample3 = texture2D(texture, vec2(offset.y, offset.w) * texscale); + + float sx = s.x / (s.x + s.y); + float sy = s.z / (s.z + s.w); + + return mix( + mix(sample3, sample2, sx), + mix(sample1, sample0, sx), sy); +} + +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; + + vec4 finalColor = texColor * fragmentColor; + float finalLightMapMultiplier = fragmentLightMapMultiplier * lightMapMultiplier; + if (lightMapEnabled && finalLightMapMultiplier > 0.0) + finalColor.rgb *= bicubicSample(lightMap, 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/opengl20.vert new file mode 100644 index 0000000..67998bc --- /dev/null +++ b/assets/opensb/rendering/opengl20.vert @@ -0,0 +1,41 @@ +#version 110 + +uniform vec2 textureSize0; +uniform vec2 textureSize1; +uniform vec2 textureSize2; +uniform vec2 textureSize3; +uniform vec2 screenSize; +uniform mat3 vertexTransform; +uniform vec2 lightMapSize; +uniform vec2 lightMapScale; +uniform vec2 lightMapOffset; + +attribute vec2 vertexPosition; +attribute vec2 vertexTextureCoordinate; +attribute float vertexTextureIndex; +attribute vec4 vertexColor; +attribute float vertexParam1; + +varying vec2 fragmentTextureCoordinate; +varying float fragmentTextureIndex; +varying vec4 fragmentColor; +varying float fragmentLightMapMultiplier; +varying vec2 fragmentLightMapCoordinate; + +void main() { + vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy; + fragmentLightMapMultiplier = vertexParam1; + fragmentLightMapCoordinate = (screenPosition / lightMapScale) - lightMapOffset * lightMapSize / screenSize; + 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/source/application/StarRenderer.hpp b/source/application/StarRenderer.hpp index 1cd3223..e12590f 100644 --- a/source/application/StarRenderer.hpp +++ b/source/application/StarRenderer.hpp @@ -117,7 +117,7 @@ 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) = 0; + virtual void setEffectConfig(Json const& effectConfig, StringMap const& shaders) = 0; // The effect config will specify named parameters and textures which can be // set here. diff --git a/source/application/StarRenderer_opengl20.cpp b/source/application/StarRenderer_opengl20.cpp index bc58ee1..eea4e9f 100644 --- a/source/application/StarRenderer_opengl20.cpp +++ b/source/application/StarRenderer_opengl20.cpp @@ -7,71 +7,67 @@ namespace Star { size_t const MultiTextureCount = 4; -char const* DefaultEffectConfig = R"JSON( - { - "vertexShader" : " - #version 110 +char const* DefaultVertexShader = R"SHADER( +#version 110 - uniform vec2 textureSize0; - uniform vec2 textureSize1; - uniform vec2 textureSize2; - uniform vec2 textureSize3; - uniform vec2 screenSize; - uniform mat3 vertexTransform; +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; +attribute vec2 vertexPosition; +attribute vec2 vertexTextureCoordinate; +attribute float vertexTextureIndex; +attribute vec4 vertexColor; +attribute float vertexParam1; - varying vec2 fragmentTextureCoordinate; - varying float fragmentTextureIndex; - varying vec4 fragmentColor; +varying vec2 fragmentTextureCoordinate; +varying float fragmentTextureIndex; +varying vec4 fragmentColor; - void main() { - vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy; - gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0); - 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; - } - ", +void main() { + vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy; + gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0); + 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; +} +)SHADER"; - "fragmentShader" : " - #version 110 +char const* DefaultFragmentShader = R"SHADER( +#version 110 - uniform sampler2D texture0; - uniform sampler2D texture1; - uniform sampler2D texture2; - uniform sampler2D texture3; +uniform sampler2D texture0; +uniform sampler2D texture1; +uniform sampler2D texture2; +uniform sampler2D texture3; - varying vec2 fragmentTextureCoordinate; - varying float fragmentTextureIndex; - varying vec4 fragmentColor; +varying vec2 fragmentTextureCoordinate; +varying float fragmentTextureIndex; +varying vec4 fragmentColor; - void main() { - if (fragmentTextureIndex > 2.9) { - gl_FragColor = texture2D(texture3, fragmentTextureCoordinate) * fragmentColor; - } else if (fragmentTextureIndex > 1.9) { - gl_FragColor = texture2D(texture2, fragmentTextureCoordinate) * fragmentColor; - } else if (fragmentTextureIndex > 0.9) { - gl_FragColor = texture2D(texture1, fragmentTextureCoordinate) * fragmentColor; - } else { - gl_FragColor = texture2D(texture0, fragmentTextureCoordinate) * fragmentColor; - } - } - " - } - )JSON"; +void main() { + if (fragmentTextureIndex > 2.9) { + gl_FragColor = texture2D(texture3, fragmentTextureCoordinate) * fragmentColor; + } else if (fragmentTextureIndex > 1.9) { + gl_FragColor = texture2D(texture2, fragmentTextureCoordinate) * fragmentColor; + } else if (fragmentTextureIndex > 0.9) { + gl_FragColor = texture2D(texture1, fragmentTextureCoordinate) * fragmentColor; + } else { + gl_FragColor = texture2D(texture0, fragmentTextureCoordinate) * fragmentColor; + } +} +)SHADER"; OpenGl20Renderer::OpenGl20Renderer() { if (glewInit() != GLEW_OK) @@ -97,7 +93,7 @@ OpenGl20Renderer::OpenGl20Renderer() { TextureFiltering::Nearest); m_immediateRenderBuffer = createGlRenderBuffer(); - setEffectConfig(Json::parse(DefaultEffectConfig)); + setEffectConfig(JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}}); m_limitTextureGroupSize = false; m_useMultiTexturing = true; @@ -119,44 +115,44 @@ Vec2U OpenGl20Renderer::screenSize() const { return m_screenSize; } -void OpenGl20Renderer::setEffectConfig(Json const& effectConfig) { +void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap const& shaders) { flushImmediatePrimitives(); - GLint status = 0; char logBuffer[1024]; - GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); - String vertexSource = effectConfig.getString("vertexShader"); - char const* vertexSourcePtr = vertexSource.utf8Ptr(); - glShaderSource(vertexShader, 1, &vertexSourcePtr, NULL); - glCompileShader(vertexShader); + auto compileShader = [&](GLenum type, String const& name) -> GLuint { + GLuint shader = glCreateShader(type); + auto* source = shaders.ptr(name); + if (!source) + return 0; + char const* sourcePtr = source->utf8Ptr(); + glShaderSource(shader, 1, &sourcePtr, NULL); + glCompileShader(shader); - glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); - if (!status) { - glGetShaderInfoLog(vertexShader, sizeof(logBuffer), NULL, logBuffer); - throw RendererException(strf("Failed to compile vertex shader: %s\n", logBuffer)); - } + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status) { + glGetShaderInfoLog(shader, sizeof(logBuffer), NULL, logBuffer); + throw RendererException(strf("Failed to compile %s shader: %s\n", name, logBuffer)); + } - GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); - String fragmentSource = effectConfig.getString("fragmentShader"); - char const* fragmentSourcePtr = fragmentSource.utf8Ptr(); - glShaderSource(fragmentShader, 1, &fragmentSourcePtr, NULL); - glCompileShader(fragmentShader); + return shader; + }; - glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status); - if (!status) { - glGetShaderInfoLog(fragmentShader, sizeof(logBuffer), NULL, logBuffer); - throw RendererException(strf("Failed to compile fragment shader: %s\n", logBuffer)); - } + GLuint vertexShader = compileShader(GL_VERTEX_SHADER, "vertex"); + GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, "fragment"); GLuint program = glCreateProgram(); - glAttachShader(program, vertexShader); - glAttachShader(program, fragmentShader); + if (vertexShader) + glAttachShader(program, vertexShader); + if (fragmentShader) + glAttachShader(program, fragmentShader); glLinkProgram(program); - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); + if (vertexShader) + glDeleteShader(vertexShader); + if (fragmentShader) + glDeleteShader(fragmentShader); glGetProgramiv(program, GL_LINK_STATUS, &status); if (!status) { diff --git a/source/application/StarRenderer_opengl20.hpp b/source/application/StarRenderer_opengl20.hpp index 0bdb73a..dfab534 100644 --- a/source/application/StarRenderer_opengl20.hpp +++ b/source/application/StarRenderer_opengl20.hpp @@ -20,7 +20,7 @@ public: String rendererId() const override; Vec2U screenSize() const override; - void setEffectConfig(Json const& effectConfig) override; + void setEffectConfig(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; diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 4a156bd..b53a9c1 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -196,10 +196,25 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController) void ClientApplication::renderInit(RendererPtr renderer) { Application::renderInit(renderer); + auto assets = m_root->assets(); String rendererConfig = strf("/rendering/%s.config", renderer->rendererId()); - if (m_root->assets()->assetExists(rendererConfig)) - renderer->setEffectConfig(m_root->assets()->json(rendererConfig)); + 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()); + } + shaders[entry.first] = shader; + } + } + renderer->setEffectConfig(config, shaders); + } else Logger::warn("No rendering config found for renderer with id '%s'", renderer->rendererId()); diff --git a/source/game/CMakeLists.txt b/source/game/CMakeLists.txt index 1ff95ff..c7399b1 100644 --- a/source/game/CMakeLists.txt +++ b/source/game/CMakeLists.txt @@ -38,6 +38,7 @@ SET (star_game_HEADERS StarDamageManager.hpp StarDamageTypes.hpp StarDanceDatabase.hpp + StarDirectives.hpp StarDrawable.hpp StarDungeonGenerator.hpp StarDungeonImagePart.hpp @@ -296,6 +297,7 @@ SET (star_game_SOURCES StarDamageManager.cpp StarDamageTypes.cpp StarDanceDatabase.cpp + StarDirectives.cpp StarDrawable.cpp StarDungeonGenerator.cpp StarDungeonImagePart.cpp diff --git a/source/game/StarDirectives.cpp b/source/game/StarDirectives.cpp new file mode 100644 index 0000000..4dc661d --- /dev/null +++ b/source/game/StarDirectives.cpp @@ -0,0 +1,51 @@ +#include "StarImage.hpp" +#include "StarImageProcessing.hpp" +#include "StarDirectives.hpp" + +namespace Star { + +NestedDirectives::NestedDirectives() {} +NestedDirectives::NestedDirectives(String const& string) : m_root{ Leaf{ parseImageOperations(string), string} } {} + +void NestedDirectives::addBranch(const Branch& newBranch) { + convertToBranches(); + + m_root.value.get().emplace_back(newBranch); +} + +String NestedDirectives::toString() const { + String string; + buildString(string, m_root); + return string; +} + +void NestedDirectives::forEach() const { + +} + +Image NestedDirectives::apply(Image& image) const { + Image current = image; + + return current; +} + +void NestedDirectives::buildString(String& string, const Cell& cell) const { + if (auto leaf = cell.value.ptr()) + string += leaf->string; + else { + for (auto& branch : cell.value.get()) + buildString(string, *branch); + } +} + +void NestedDirectives::convertToBranches() { + if (m_root.value.is()) + return; + + Leaf& leaf = m_root.value.get(); + Branches newBranches; + newBranches.emplace_back(std::make_shared(move(leaf))); + m_root.value = move(newBranches); +} + +} \ No newline at end of file diff --git a/source/game/StarDirectives.hpp b/source/game/StarDirectives.hpp new file mode 100644 index 0000000..3540ee1 --- /dev/null +++ b/source/game/StarDirectives.hpp @@ -0,0 +1,50 @@ +#ifndef STAR_DIRECTIVES_HPP +#define STAR_DIRECTIVES_HPP + +#include "StarImageProcessing.hpp" + +namespace Star { + +STAR_CLASS(NestedDirectives); + +// Attempt at reducing memory allocation and per-frame string parsing for extremely long directives +class NestedDirectives { +public: + struct Leaf { + List operations; + String string; + }; + + struct Cell; + typedef std::shared_ptr Branch; + typedef List Branches; + + struct Cell { + Variant value; + + Cell() : value(Leaf()) {}; + Cell(Leaf&& leaf) : value(move(leaf)) {}; + Cell(Branches&& branches) : value(move(branches)) {}; + Cell(const Leaf& leaf) : value(leaf) {}; + Cell(const Branches& branches) : value(branches) {}; + }; + + + NestedDirectives(); + NestedDirectives(String const& string); + + void addBranch(const Branch& newBranch); + const Branch& branch() const; + String toString() const; + void forEach() const; + Image apply(Image& image) const; +private: + void buildString(String& string, const Cell& cell) const; + void convertToBranches(); + + Cell m_root; +}; + +} + +#endif diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp index 0dfddf8..91701e5 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -787,6 +787,10 @@ void WorldClient::handleIncomingPackets(List const& packets) { m_outgoingPackets.append(make_shared(makeLeft("Entity delivery error"), entityMessagePacket->uuid)); } else { + ConnectionId fromConnection = entityMessagePacket->fromConnection; + if (fromConnection == *m_clientId) // Kae: The server should not be able to forge entity messages that appear as if they're from us + fromConnection = ServerConnectionId; + auto response = entity->receiveMessage(entityMessagePacket->fromConnection, entityMessagePacket->message, entityMessagePacket->args); if (response) m_outgoingPackets.append(make_shared(makeRight(response.take()), entityMessagePacket->uuid)); @@ -805,7 +809,13 @@ void WorldClient::handleIncomingPackets(List const& packets) { response.fail(entityMessageResponsePacket->response.left()); } else if (auto updateWorldProperties = as(packet)) { - m_worldProperties.merge(updateWorldProperties->updatedProperties, true); + // Kae: Properties set to null (nil from Lua) should be erased instead of lingering around + for (auto& pair : updateWorldProperties->updatedProperties) { + if (pair.second.isNull()) + m_worldProperties.erase(pair.first); + else + m_worldProperties[pair.first] = pair.second; + } } else if (auto updateTileProtection = as(packet)) { setTileProtection(updateTileProtection->dungeonId, updateTileProtection->isProtected);