Split shaders into their own files

some unrelated directives thing too
This commit is contained in:
Kae 2023-06-23 23:01:25 +10:00
parent 4328119e1c
commit 6832c10ed5
11 changed files with 367 additions and 89 deletions

View File

@ -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"
}
}

View File

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

View File

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

View File

@ -117,7 +117,7 @@ public:
// specific to each type of renderer, so it will be necessary to key the // 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 // 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. // 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<String> const& shaders) = 0;
// The effect config will specify named parameters and textures which can be // The effect config will specify named parameters and textures which can be
// set here. // set here.

View File

@ -7,9 +7,7 @@ namespace Star {
size_t const MultiTextureCount = 4; size_t const MultiTextureCount = 4;
char const* DefaultEffectConfig = R"JSON( char const* DefaultVertexShader = R"SHADER(
{
"vertexShader" : "
#version 110 #version 110
uniform vec2 textureSize0; uniform vec2 textureSize0;
@ -44,9 +42,9 @@ char const* DefaultEffectConfig = R"JSON(
fragmentTextureIndex = vertexTextureIndex; fragmentTextureIndex = vertexTextureIndex;
fragmentColor = vertexColor; fragmentColor = vertexColor;
} }
", )SHADER";
"fragmentShader" : " char const* DefaultFragmentShader = R"SHADER(
#version 110 #version 110
uniform sampler2D texture0; uniform sampler2D texture0;
@ -69,9 +67,7 @@ char const* DefaultEffectConfig = R"JSON(
gl_FragColor = texture2D(texture0, fragmentTextureCoordinate) * fragmentColor; gl_FragColor = texture2D(texture0, fragmentTextureCoordinate) * fragmentColor;
} }
} }
" )SHADER";
}
)JSON";
OpenGl20Renderer::OpenGl20Renderer() { OpenGl20Renderer::OpenGl20Renderer() {
if (glewInit() != GLEW_OK) if (glewInit() != GLEW_OK)
@ -97,7 +93,7 @@ OpenGl20Renderer::OpenGl20Renderer() {
TextureFiltering::Nearest); TextureFiltering::Nearest);
m_immediateRenderBuffer = createGlRenderBuffer(); m_immediateRenderBuffer = createGlRenderBuffer();
setEffectConfig(Json::parse(DefaultEffectConfig)); setEffectConfig(JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}});
m_limitTextureGroupSize = false; m_limitTextureGroupSize = false;
m_useMultiTexturing = true; m_useMultiTexturing = true;
@ -119,43 +115,43 @@ Vec2U OpenGl20Renderer::screenSize() const {
return m_screenSize; return m_screenSize;
} }
void OpenGl20Renderer::setEffectConfig(Json const& effectConfig) { void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<String> const& shaders) {
flushImmediatePrimitives(); flushImmediatePrimitives();
GLint status = 0; GLint status = 0;
char logBuffer[1024]; char logBuffer[1024];
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); auto compileShader = [&](GLenum type, String const& name) -> GLuint {
String vertexSource = effectConfig.getString("vertexShader"); GLuint shader = glCreateShader(type);
char const* vertexSourcePtr = vertexSource.utf8Ptr(); auto* source = shaders.ptr(name);
glShaderSource(vertexShader, 1, &vertexSourcePtr, NULL); if (!source)
glCompileShader(vertexShader); return 0;
char const* sourcePtr = source->utf8Ptr();
glShaderSource(shader, 1, &sourcePtr, NULL);
glCompileShader(shader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &status); glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (!status) { if (!status) {
glGetShaderInfoLog(vertexShader, sizeof(logBuffer), NULL, logBuffer); glGetShaderInfoLog(shader, sizeof(logBuffer), NULL, logBuffer);
throw RendererException(strf("Failed to compile vertex shader: %s\n", logBuffer)); throw RendererException(strf("Failed to compile %s shader: %s\n", name, logBuffer));
} }
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); return shader;
String fragmentSource = effectConfig.getString("fragmentShader"); };
char const* fragmentSourcePtr = fragmentSource.utf8Ptr();
glShaderSource(fragmentShader, 1, &fragmentSourcePtr, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status); GLuint vertexShader = compileShader(GL_VERTEX_SHADER, "vertex");
if (!status) { GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, "fragment");
glGetShaderInfoLog(fragmentShader, sizeof(logBuffer), NULL, logBuffer);
throw RendererException(strf("Failed to compile fragment shader: %s\n", logBuffer));
}
GLuint program = glCreateProgram(); GLuint program = glCreateProgram();
if (vertexShader)
glAttachShader(program, vertexShader); glAttachShader(program, vertexShader);
if (fragmentShader)
glAttachShader(program, fragmentShader); glAttachShader(program, fragmentShader);
glLinkProgram(program); glLinkProgram(program);
if (vertexShader)
glDeleteShader(vertexShader); glDeleteShader(vertexShader);
if (fragmentShader)
glDeleteShader(fragmentShader); glDeleteShader(fragmentShader);
glGetProgramiv(program, GL_LINK_STATUS, &status); glGetProgramiv(program, GL_LINK_STATUS, &status);

View File

@ -20,7 +20,7 @@ public:
String rendererId() const override; String rendererId() const override;
Vec2U screenSize() const override; Vec2U screenSize() const override;
void setEffectConfig(Json const& effectConfig) override; void setEffectConfig(Json const& effectConfig, StringMap<String> const& shaders) override;
void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override; void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override;
void setEffectTexture(String const& textureName, Image const& image) override; void setEffectTexture(String const& textureName, Image const& image) override;

View File

@ -196,10 +196,25 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController)
void ClientApplication::renderInit(RendererPtr renderer) { void ClientApplication::renderInit(RendererPtr renderer) {
Application::renderInit(renderer); Application::renderInit(renderer);
auto assets = m_root->assets();
String rendererConfig = strf("/rendering/%s.config", renderer->rendererId()); String rendererConfig = strf("/rendering/%s.config", renderer->rendererId());
if (m_root->assets()->assetExists(rendererConfig)) if (assets->assetExists(rendererConfig)) {
renderer->setEffectConfig(m_root->assets()->json(rendererConfig)); StringMap<String> 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 else
Logger::warn("No rendering config found for renderer with id '%s'", renderer->rendererId()); Logger::warn("No rendering config found for renderer with id '%s'", renderer->rendererId());

View File

@ -38,6 +38,7 @@ SET (star_game_HEADERS
StarDamageManager.hpp StarDamageManager.hpp
StarDamageTypes.hpp StarDamageTypes.hpp
StarDanceDatabase.hpp StarDanceDatabase.hpp
StarDirectives.hpp
StarDrawable.hpp StarDrawable.hpp
StarDungeonGenerator.hpp StarDungeonGenerator.hpp
StarDungeonImagePart.hpp StarDungeonImagePart.hpp
@ -296,6 +297,7 @@ SET (star_game_SOURCES
StarDamageManager.cpp StarDamageManager.cpp
StarDamageTypes.cpp StarDamageTypes.cpp
StarDanceDatabase.cpp StarDanceDatabase.cpp
StarDirectives.cpp
StarDrawable.cpp StarDrawable.cpp
StarDungeonGenerator.cpp StarDungeonGenerator.cpp
StarDungeonImagePart.cpp StarDungeonImagePart.cpp

View File

@ -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<Branches>().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<Leaf>())
string += leaf->string;
else {
for (auto& branch : cell.value.get<Branches>())
buildString(string, *branch);
}
}
void NestedDirectives::convertToBranches() {
if (m_root.value.is<Branches>())
return;
Leaf& leaf = m_root.value.get<Leaf>();
Branches newBranches;
newBranches.emplace_back(std::make_shared<Cell const>(move(leaf)));
m_root.value = move(newBranches);
}
}

View File

@ -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<ImageOperation> operations;
String string;
};
struct Cell;
typedef std::shared_ptr<Cell const> Branch;
typedef List<Branch> Branches;
struct Cell {
Variant<Leaf, Branches> 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

View File

@ -787,6 +787,10 @@ void WorldClient::handleIncomingPackets(List<PacketPtr> const& packets) {
m_outgoingPackets.append(make_shared<EntityMessageResponsePacket>(makeLeft("Entity delivery error"), entityMessagePacket->uuid)); m_outgoingPackets.append(make_shared<EntityMessageResponsePacket>(makeLeft("Entity delivery error"), entityMessagePacket->uuid));
} else { } 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); auto response = entity->receiveMessage(entityMessagePacket->fromConnection, entityMessagePacket->message, entityMessagePacket->args);
if (response) if (response)
m_outgoingPackets.append(make_shared<EntityMessageResponsePacket>(makeRight(response.take()), entityMessagePacket->uuid)); m_outgoingPackets.append(make_shared<EntityMessageResponsePacket>(makeRight(response.take()), entityMessagePacket->uuid));
@ -805,7 +809,13 @@ void WorldClient::handleIncomingPackets(List<PacketPtr> const& packets) {
response.fail(entityMessageResponsePacket->response.left()); response.fail(entityMessageResponsePacket->response.left());
} else if (auto updateWorldProperties = as<UpdateWorldPropertiesPacket>(packet)) { } else if (auto updateWorldProperties = as<UpdateWorldPropertiesPacket>(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<UpdateTileProtectionPacket>(packet)) { } else if (auto updateTileProtection = as<UpdateTileProtectionPacket>(packet)) {
setTileProtection(updateTileProtection->dungeonId, updateTileProtection->isProtected); setTileProtection(updateTileProtection->dungeonId, updateTileProtection->isProtected);