Merge pull request #504 from MihailRis/post-effects
Post-processing effects
This commit is contained in:
commit
8ae385e6e8
@ -5,10 +5,12 @@
|
|||||||
"main",
|
"main",
|
||||||
"lines",
|
"lines",
|
||||||
"entity",
|
"entity",
|
||||||
"screen",
|
|
||||||
"background",
|
"background",
|
||||||
"skybox_gen"
|
"skybox_gen"
|
||||||
],
|
],
|
||||||
|
"post-effects": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
"textures": [
|
"textures": [
|
||||||
"gui/menubg",
|
"gui/menubg",
|
||||||
"gui/delete_icon",
|
"gui/delete_icon",
|
||||||
|
|||||||
13
res/shaders/effect.glslf
Normal file
13
res/shaders/effect.glslf
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
in vec2 v_uv;
|
||||||
|
out vec4 f_color;
|
||||||
|
|
||||||
|
uniform sampler2D u_screen;
|
||||||
|
uniform ivec2 u_screenSize;
|
||||||
|
uniform float u_intensity;
|
||||||
|
|
||||||
|
#include <__effect__>
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
f_color = effect();
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,13 +1,10 @@
|
|||||||
layout (location = 0) in vec2 v_position;
|
layout (location = 0) in vec2 v_position;
|
||||||
|
|
||||||
out vec2 v_coord;
|
out vec2 v_uv;
|
||||||
|
|
||||||
uniform ivec2 u_screenSize;
|
uniform ivec2 u_screenSize;
|
||||||
uniform float u_timer;
|
|
||||||
uniform float u_dayTime;
|
|
||||||
|
|
||||||
void main(){
|
void main(){
|
||||||
v_coord = v_position * 0.5 + 0.5;
|
v_uv = v_position * 0.5 + 0.5;
|
||||||
gl_Position = vec4(v_position, 0.0, 1.0);
|
gl_Position = vec4(v_position, 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
3
res/shaders/effects/default.glsl
Normal file
3
res/shaders/effects/default.glsl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
vec4 effect() {
|
||||||
|
return texture(u_screen, v_uv);
|
||||||
|
}
|
||||||
5
res/shaders/effects/grayscale.glsl
Normal file
5
res/shaders/effects/grayscale.glsl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
vec4 effect() {
|
||||||
|
vec3 color = texture(u_screen, v_uv).rgb;
|
||||||
|
float m = (color.r + color.g + color.b) / 3.0;
|
||||||
|
return vec4(mix(color, vec3(m), u_intensity), 1.0);
|
||||||
|
}
|
||||||
6
res/shaders/effects/negative.glsl
Normal file
6
res/shaders/effects/negative.glsl
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
vec4 effect() {
|
||||||
|
vec4 color = texture(u_screen, v_uv);
|
||||||
|
color = mix(color, 1.0 - color, u_intensity);
|
||||||
|
color.a = 1.0;
|
||||||
|
return color;
|
||||||
|
}
|
||||||
15
res/shaders/effects/vignette.glsl
Normal file
15
res/shaders/effects/vignette.glsl
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <commons>
|
||||||
|
|
||||||
|
#param float p_radius = 1.1
|
||||||
|
#param float p_softness = 0.7
|
||||||
|
|
||||||
|
vec4 apply_vignette(vec4 color) {
|
||||||
|
vec2 position = v_uv - vec2(0.5);
|
||||||
|
float dist = length(position);
|
||||||
|
float vignette = smoothstep(p_radius, p_radius - p_softness, dist);
|
||||||
|
return vec4(color.rgb * vignette, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 effect() {
|
||||||
|
return apply_vignette(texture(u_screen, v_uv));
|
||||||
|
}
|
||||||
@ -1,25 +0,0 @@
|
|||||||
in vec2 v_coord;
|
|
||||||
out vec4 f_color;
|
|
||||||
|
|
||||||
uniform sampler2D u_texture0;
|
|
||||||
uniform ivec2 u_screenSize;
|
|
||||||
|
|
||||||
// Vignette
|
|
||||||
vec4 apply_vignette(vec4 color) {
|
|
||||||
vec2 position = (gl_FragCoord.xy / u_screenSize) - vec2(0.5);
|
|
||||||
float dist = length(position);
|
|
||||||
|
|
||||||
float radius = 1.3;
|
|
||||||
float softness = 1.0;
|
|
||||||
float vignette = smoothstep(radius, radius - softness, dist);
|
|
||||||
|
|
||||||
color.rgb = color.rgb * vignette;
|
|
||||||
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
f_color = texture(u_texture0, v_coord);
|
|
||||||
f_color = apply_vignette(f_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -16,7 +16,16 @@
|
|||||||
|
|
||||||
class Assets;
|
class Assets;
|
||||||
|
|
||||||
enum class AssetType { TEXTURE, SHADER, FONT, ATLAS, LAYOUT, SOUND, MODEL };
|
enum class AssetType {
|
||||||
|
TEXTURE,
|
||||||
|
SHADER,
|
||||||
|
FONT,
|
||||||
|
ATLAS,
|
||||||
|
LAYOUT,
|
||||||
|
SOUND,
|
||||||
|
MODEL,
|
||||||
|
POST_EFFECT
|
||||||
|
};
|
||||||
|
|
||||||
namespace assetload {
|
namespace assetload {
|
||||||
/// @brief final work to do in the main thread
|
/// @brief final work to do in the main thread
|
||||||
@ -91,6 +100,20 @@ public:
|
|||||||
return static_cast<T*>(found->second.get());
|
return static_cast<T*>(found->second.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::shared_ptr<T> getShared(const std::string& name) const {
|
||||||
|
const auto& mapIter = assets.find(typeid(T));
|
||||||
|
if (mapIter == assets.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto& map = mapIter->second;
|
||||||
|
const auto& found = map.find(name);
|
||||||
|
if (found == map.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::static_pointer_cast<T>(found->second);
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T& require(const std::string& name) const {
|
T& require(const std::string& name) const {
|
||||||
T* asset = get<T>(name);
|
T* asset = get<T>(name);
|
||||||
|
|||||||
@ -34,6 +34,7 @@ AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths
|
|||||||
addLoader(AssetType::LAYOUT, assetload::layout);
|
addLoader(AssetType::LAYOUT, assetload::layout);
|
||||||
addLoader(AssetType::SOUND, assetload::sound);
|
addLoader(AssetType::SOUND, assetload::sound);
|
||||||
addLoader(AssetType::MODEL, assetload::model);
|
addLoader(AssetType::MODEL, assetload::model);
|
||||||
|
addLoader(AssetType::POST_EFFECT, assetload::posteffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
||||||
@ -131,6 +132,8 @@ static std::string assets_def_folder(AssetType tag) {
|
|||||||
return SOUNDS_FOLDER;
|
return SOUNDS_FOLDER;
|
||||||
case AssetType::MODEL:
|
case AssetType::MODEL:
|
||||||
return MODELS_FOLDER;
|
return MODELS_FOLDER;
|
||||||
|
case AssetType::POST_EFFECT:
|
||||||
|
return POST_EFFECTS_FOLDER;
|
||||||
}
|
}
|
||||||
return "<error>";
|
return "<error>";
|
||||||
}
|
}
|
||||||
@ -196,6 +199,7 @@ void AssetsLoader::processPreloadConfig(const io::path& file) {
|
|||||||
processPreloadList(AssetType::TEXTURE, root["textures"]);
|
processPreloadList(AssetType::TEXTURE, root["textures"]);
|
||||||
processPreloadList(AssetType::SOUND, root["sounds"]);
|
processPreloadList(AssetType::SOUND, root["sounds"]);
|
||||||
processPreloadList(AssetType::MODEL, root["models"]);
|
processPreloadList(AssetType::MODEL, root["models"]);
|
||||||
|
processPreloadList(AssetType::POST_EFFECT, root["post-effects"]);
|
||||||
// layouts are loaded automatically
|
// layouts are loaded automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,6 +60,20 @@ assetload::postfunc assetload::texture(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static auto process_program(const ResPaths& paths, const std::string& filename) {
|
||||||
|
io::path vertexFile = paths.find(filename + ".glslv");
|
||||||
|
io::path fragmentFile = paths.find(filename + ".glslf");
|
||||||
|
|
||||||
|
std::string vertexSource = io::read_string(vertexFile);
|
||||||
|
std::string fragmentSource = io::read_string(fragmentFile);
|
||||||
|
|
||||||
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
|
||||||
|
auto vertex = preprocessor.process(vertexFile, vertexSource);
|
||||||
|
auto fragment = preprocessor.process(fragmentFile, fragmentSource);
|
||||||
|
return std::make_pair(vertex, fragment);
|
||||||
|
}
|
||||||
|
|
||||||
assetload::postfunc assetload::shader(
|
assetload::postfunc assetload::shader(
|
||||||
AssetsLoader*,
|
AssetsLoader*,
|
||||||
const ResPaths& paths,
|
const ResPaths& paths,
|
||||||
@ -67,15 +81,13 @@ assetload::postfunc assetload::shader(
|
|||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::shared_ptr<AssetCfg>&
|
const std::shared_ptr<AssetCfg>&
|
||||||
) {
|
) {
|
||||||
|
auto [vertex, fragment] = process_program(paths, filename);
|
||||||
|
|
||||||
io::path vertexFile = paths.find(filename + ".glslv");
|
io::path vertexFile = paths.find(filename + ".glslv");
|
||||||
io::path fragmentFile = paths.find(filename + ".glslf");
|
io::path fragmentFile = paths.find(filename + ".glslf");
|
||||||
|
|
||||||
std::string vertexSource = io::read_string(vertexFile);
|
std::string vertexSource = std::move(vertex.code);
|
||||||
std::string fragmentSource = io::read_string(fragmentFile);
|
std::string fragmentSource = std::move(fragment.code);
|
||||||
|
|
||||||
vertexSource = Shader::preprocessor->process(vertexFile, vertexSource);
|
|
||||||
fragmentSource =
|
|
||||||
Shader::preprocessor->process(fragmentFile, fragmentSource);
|
|
||||||
|
|
||||||
return [=](auto assets) {
|
return [=](auto assets) {
|
||||||
assets->store(
|
assets->store(
|
||||||
@ -90,6 +102,40 @@ assetload::postfunc assetload::shader(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetload::postfunc assetload::posteffect(
|
||||||
|
AssetsLoader*,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
|
) {
|
||||||
|
io::path effectFile = paths.find(file + ".glsl");
|
||||||
|
std::string effectSource = io::read_string(effectFile);
|
||||||
|
|
||||||
|
auto& preprocessor = *Shader::preprocessor;
|
||||||
|
preprocessor.addHeader(
|
||||||
|
"__effect__", preprocessor.process(effectFile, effectSource, true)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
|
||||||
|
auto params = std::move(fragment.params);
|
||||||
|
|
||||||
|
std::string vertexSource = std::move(vertex.code);
|
||||||
|
std::string fragmentSource = std::move(fragment.code);
|
||||||
|
|
||||||
|
return [=](auto assets) {
|
||||||
|
auto program = Shader::create(
|
||||||
|
effectFile.string(),
|
||||||
|
effectFile.string(),
|
||||||
|
vertexSource,
|
||||||
|
fragmentSource
|
||||||
|
);
|
||||||
|
assets->store(
|
||||||
|
std::make_shared<PostEffect>(std::move(program), params), name
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
|
static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
|
||||||
std::string name = file.stem();
|
std::string name = file.stem();
|
||||||
// skip duplicates
|
// skip duplicates
|
||||||
|
|||||||
@ -62,4 +62,11 @@ namespace assetload {
|
|||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::shared_ptr<AssetCfg>& settings
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
);
|
);
|
||||||
|
postfunc posteffect(
|
||||||
|
AssetsLoader*,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,9 @@ class BasicParser {
|
|||||||
using StringT = std::basic_string<CharT>;
|
using StringT = std::basic_string<CharT>;
|
||||||
using StringViewT = std::basic_string_view<CharT>;
|
using StringViewT = std::basic_string_view<CharT>;
|
||||||
|
|
||||||
|
void skipWhitespaceBasic(bool newline = true);
|
||||||
void skipWhitespaceHashComment(bool newline = true);
|
void skipWhitespaceHashComment(bool newline = true);
|
||||||
|
void skipWhitespaceCLikeComment(bool newline = true);
|
||||||
protected:
|
protected:
|
||||||
std::string_view filename;
|
std::string_view filename;
|
||||||
StringViewT source;
|
StringViewT source;
|
||||||
@ -16,6 +18,7 @@ protected:
|
|||||||
uint line = 1;
|
uint line = 1;
|
||||||
uint linestart = 0;
|
uint linestart = 0;
|
||||||
bool hashComment = false;
|
bool hashComment = false;
|
||||||
|
bool clikeComment = false;
|
||||||
|
|
||||||
void skipWhitespace(bool newline = true);
|
void skipWhitespace(bool newline = true);
|
||||||
void skip(size_t n);
|
void skip(size_t n);
|
||||||
@ -35,7 +38,7 @@ protected:
|
|||||||
StringT parseString(CharT chr, bool closeRequired = true);
|
StringT parseString(CharT chr, bool closeRequired = true);
|
||||||
|
|
||||||
parsing_error error(const std::string& message);
|
parsing_error error(const std::string& message);
|
||||||
public:
|
|
||||||
StringViewT readUntil(CharT c);
|
StringViewT readUntil(CharT c);
|
||||||
StringViewT readUntil(StringViewT s, bool nothrow);
|
StringViewT readUntil(StringViewT s, bool nothrow);
|
||||||
StringViewT readUntilWhitespace();
|
StringViewT readUntilWhitespace();
|
||||||
|
|||||||
@ -31,13 +31,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void BasicParser<CharT>::skipWhitespace(bool newline) {
|
void BasicParser<CharT>::skipWhitespaceBasic(bool newline) {
|
||||||
if (hashComment) {
|
|
||||||
skipWhitespaceHashComment(newline);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
char next = source[pos];
|
CharT next = source[pos];
|
||||||
if (next == '\n') {
|
if (next == '\n') {
|
||||||
if (!newline) {
|
if (!newline) {
|
||||||
break;
|
break;
|
||||||
@ -55,23 +51,20 @@ void BasicParser<CharT>::skipWhitespace(bool newline) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
|
void BasicParser<CharT>::skipWhitespace(bool newline) {
|
||||||
while (hasNext()) {
|
if (hashComment) {
|
||||||
char next = source[pos];
|
skipWhitespaceHashComment(newline);
|
||||||
if (next == '\n') {
|
return;
|
||||||
if (!newline) {
|
} else if (clikeComment) {
|
||||||
break;
|
skipWhitespaceCLikeComment(newline);
|
||||||
}
|
return;
|
||||||
line++;
|
|
||||||
linestart = ++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_whitespace(next)) {
|
|
||||||
pos++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
skipWhitespaceBasic(newline);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT>
|
||||||
|
void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
|
||||||
|
skipWhitespaceBasic(newline);
|
||||||
if (hasNext() && source[pos] == '#') {
|
if (hasNext() && source[pos] == '#') {
|
||||||
if (!newline) {
|
if (!newline) {
|
||||||
readUntilEOL();
|
readUntilEOL();
|
||||||
@ -84,6 +77,39 @@ void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename CharT>
|
||||||
|
void BasicParser<CharT>::skipWhitespaceCLikeComment(bool newline) {
|
||||||
|
skipWhitespaceBasic(newline);
|
||||||
|
if (hasNext() && source[pos] == '/' && pos + 1 < source.length()) {
|
||||||
|
pos++;
|
||||||
|
switch (source[pos]) {
|
||||||
|
case '*':
|
||||||
|
pos++;
|
||||||
|
while (hasNext()) {
|
||||||
|
if (source[pos] == '/' && source[pos-1] == '*') {
|
||||||
|
pos++;
|
||||||
|
skipWhitespace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
if (!newline) {
|
||||||
|
readUntilEOL();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skipLine();
|
||||||
|
if (hasNext() && (is_whitespace(source[pos]) || source[pos] == '/')) {
|
||||||
|
skipWhitespaceCLikeComment(newline);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
pos--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void BasicParser<CharT>::skip(size_t n) {
|
void BasicParser<CharT>::skip(size_t n) {
|
||||||
n = std::min(n, source.length() - pos);
|
n = std::min(n, source.length() - pos);
|
||||||
|
|||||||
@ -1,38 +1,47 @@
|
|||||||
#include "GLSLExtension.hpp"
|
#include "GLSLExtension.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
#include "io/engine_paths.hpp"
|
#include "io/engine_paths.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
|
#include "coders/json.hpp"
|
||||||
|
#include "data/dv_util.hpp"
|
||||||
|
#include "coders/BasicParser.hpp"
|
||||||
|
|
||||||
void GLSLExtension::setVersion(std::string version) {
|
static debug::Logger logger("glsl-extension");
|
||||||
this->version = std::move(version);
|
|
||||||
}
|
using Type = PostEffect::Param::Type;
|
||||||
|
using Value = PostEffect::Param::Value;
|
||||||
|
|
||||||
void GLSLExtension::setPaths(const ResPaths* paths) {
|
void GLSLExtension::setPaths(const ResPaths* paths) {
|
||||||
this->paths = paths;
|
this->paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::loadHeader(const std::string& name) {
|
void GLSLExtension::loadHeader(const std::string& name) {
|
||||||
|
if (paths == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
||||||
std::string source = io::read_string(file);
|
std::string source = io::read_string(file);
|
||||||
addHeader(name, "");
|
addHeader(name, {});
|
||||||
addHeader(name, process(file, source, true));
|
addHeader(name, process(file, source, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::addHeader(const std::string& name, std::string source) {
|
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
|
||||||
headers[name] = std::move(source);
|
headers[name] = std::move(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::define(const std::string& name, std::string value) {
|
void GLSLExtension::define(const std::string& name, std::string value) {
|
||||||
defines[name] = std::move(value);
|
defines[name] = std::move(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& GLSLExtension::getHeader(const std::string& name) const {
|
const GLSLExtension::ProcessingResult& GLSLExtension::getHeader(
|
||||||
|
const std::string& name
|
||||||
|
) const {
|
||||||
auto found = headers.find(name);
|
auto found = headers.find(name);
|
||||||
if (found == headers.end()) {
|
if (found == headers.end()) {
|
||||||
throw std::runtime_error("no header '" + name + "' loaded");
|
throw std::runtime_error("no header '" + name + "' loaded");
|
||||||
@ -48,6 +57,10 @@ const std::string& GLSLExtension::getDefine(const std::string& name) const {
|
|||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, std::string>& GLSLExtension::getDefines() const {
|
||||||
|
return defines;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLSLExtension::hasDefine(const std::string& name) const {
|
bool GLSLExtension::hasDefine(const std::string& name) const {
|
||||||
return defines.find(name) != defines.end();
|
return defines.find(name) != defines.end();
|
||||||
}
|
}
|
||||||
@ -72,76 +85,202 @@ inline std::runtime_error parsing_error(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void parsing_warning(
|
inline void parsing_warning(
|
||||||
const io::path& file, uint linenum, const std::string& message
|
std::string_view file, uint linenum, const std::string& message
|
||||||
) {
|
) {
|
||||||
std::cerr << "file " + file.string() + ": warning: " + message +
|
logger.warning() << "file " + std::string(file) + ": warning: " + message +
|
||||||
" at line " + std::to_string(linenum)
|
" at line " + std::to_string(linenum);
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void source_line(std::stringstream& ss, uint linenum) {
|
inline void source_line(std::stringstream& ss, uint linenum) {
|
||||||
ss << "#line " << linenum << "\n";
|
ss << "#line " << linenum << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GLSLExtension::process(
|
static std::optional<Type> param_type_from(
|
||||||
|
const std::string& name
|
||||||
|
) {
|
||||||
|
static const std::unordered_map<std::string, Type> typeNames {
|
||||||
|
{"float", Type::FLOAT},
|
||||||
|
{"vec2", Type::VEC2},
|
||||||
|
{"vec3", Type::VEC3},
|
||||||
|
{"vec4", Type::VEC4},
|
||||||
|
};
|
||||||
|
const auto& found = typeNames.find(name);
|
||||||
|
if (found == typeNames.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value default_value_for(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Type::FLOAT:
|
||||||
|
return 0.0f;
|
||||||
|
case Type::VEC2:
|
||||||
|
return glm::vec2 {0.0f, 0.0f};
|
||||||
|
case Type::VEC3:
|
||||||
|
return glm::vec3 {0.0f, 0.0f, 0.0f};
|
||||||
|
case Type::VEC4:
|
||||||
|
return glm::vec4 {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("unsupported type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GLSLParser : public BasicParser<char> {
|
||||||
|
public:
|
||||||
|
GLSLParser(GLSLExtension& glsl, std::string_view file, std::string_view source, bool header)
|
||||||
|
: BasicParser(file, source), glsl(glsl) {
|
||||||
|
if (!header) {
|
||||||
|
ss << "#version " << GLSLExtension::VERSION << '\n';
|
||||||
|
}
|
||||||
|
for (auto& entry : glsl.getDefines()) {
|
||||||
|
ss << "#define " << entry.first << " " << entry.second << '\n';
|
||||||
|
}
|
||||||
|
uint linenum = 1;
|
||||||
|
source_line(ss, linenum);
|
||||||
|
|
||||||
|
clikeComment = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processIncludeDirective() {
|
||||||
|
skipWhitespace(false);
|
||||||
|
if (peekNoJump() != '<') {
|
||||||
|
throw error("'<' expected");
|
||||||
|
}
|
||||||
|
skip(1);
|
||||||
|
skipWhitespace(false);
|
||||||
|
auto headerName = parseName();
|
||||||
|
skipWhitespace(false);
|
||||||
|
if (peekNoJump() != '>') {
|
||||||
|
throw error("'>' expected");
|
||||||
|
}
|
||||||
|
skip(1);
|
||||||
|
skipWhitespace(false);
|
||||||
|
skipLine();
|
||||||
|
|
||||||
|
if (!glsl.hasHeader(headerName)) {
|
||||||
|
glsl.loadHeader(headerName);
|
||||||
|
}
|
||||||
|
const auto& header = glsl.getHeader(headerName);
|
||||||
|
for (const auto& [name, param] : header.params) {
|
||||||
|
params[name] = param;
|
||||||
|
}
|
||||||
|
ss << header.code << '\n';
|
||||||
|
source_line(ss, line);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processVersionDirective() {
|
||||||
|
parsing_warning(filename, line, "removed #version directive");
|
||||||
|
source_line(ss, line);
|
||||||
|
skipLine();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int n>
|
||||||
|
Value parseVectorValue() {
|
||||||
|
if (peekNoJump() != '[') {
|
||||||
|
throw error("'[' expected");
|
||||||
|
}
|
||||||
|
// may be more efficient but ok
|
||||||
|
auto value = json::parse(
|
||||||
|
filename,
|
||||||
|
std::string_view(source.data() + pos, source.size() - pos)
|
||||||
|
);
|
||||||
|
glm::vec<n, float> vec {};
|
||||||
|
try {
|
||||||
|
dv::get_vec<n>(value, vec);
|
||||||
|
return vec;
|
||||||
|
} catch (const std::exception& err) {
|
||||||
|
throw error(err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value parseDefaultValue(Type type, const std::string& name) {
|
||||||
|
switch (type) {
|
||||||
|
case Type::FLOAT:
|
||||||
|
return static_cast<float>(parseNumber(1).asNumber());
|
||||||
|
case Type::VEC2:
|
||||||
|
return parseVectorValue<2>();
|
||||||
|
case Type::VEC3:
|
||||||
|
return parseVectorValue<3>();
|
||||||
|
case Type::VEC4:
|
||||||
|
return parseVectorValue<4>();
|
||||||
|
default:
|
||||||
|
throw error("unsupported default value for type " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processParamDirective() {
|
||||||
|
skipWhitespace(false);
|
||||||
|
// Parse type name
|
||||||
|
auto typeName = parseName();
|
||||||
|
auto type = param_type_from(typeName);
|
||||||
|
if (!type.has_value()) {
|
||||||
|
throw error("unsupported param type " + util::quote(typeName));
|
||||||
|
}
|
||||||
|
skipWhitespace(false);
|
||||||
|
// Parse parameter name
|
||||||
|
auto paramName = parseName();
|
||||||
|
if (params.find(paramName) != params.end()) {
|
||||||
|
throw error("duplicating param " + util::quote(paramName));
|
||||||
|
}
|
||||||
|
skipWhitespace(false);
|
||||||
|
ss << "uniform " << typeName << " " << paramName << ";\n";
|
||||||
|
|
||||||
|
auto defValue = default_value_for(type.value());
|
||||||
|
// Parse default value
|
||||||
|
if (peekNoJump() == '=') {
|
||||||
|
skip(1);
|
||||||
|
skipWhitespace(false);
|
||||||
|
defValue = parseDefaultValue(type.value(), typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipLine();
|
||||||
|
|
||||||
|
params[paramName] = PostEffect::Param(type.value(), std::move(defValue));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processPreprocessorDirective() {
|
||||||
|
skip(1);
|
||||||
|
|
||||||
|
auto name = parseName();
|
||||||
|
|
||||||
|
if (name == "version") {
|
||||||
|
return processVersionDirective();
|
||||||
|
} else if (name == "include") {
|
||||||
|
return processIncludeDirective();
|
||||||
|
} else if (name == "param") {
|
||||||
|
return processParamDirective();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLSLExtension::ProcessingResult process() {
|
||||||
|
while (hasNext()) {
|
||||||
|
skipWhitespace(false);
|
||||||
|
if (!hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (source[pos] != '#' || processPreprocessorDirective()) {
|
||||||
|
pos = linestart;
|
||||||
|
ss << readUntilEOL() << '\n';
|
||||||
|
skip(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {ss.str(), std::move(params)};
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
GLSLExtension& glsl;
|
||||||
|
std::unordered_map<std::string, PostEffect::Param> params;
|
||||||
|
std::stringstream ss;
|
||||||
|
};
|
||||||
|
|
||||||
|
GLSLExtension::ProcessingResult GLSLExtension::process(
|
||||||
const io::path& file, const std::string& source, bool header
|
const io::path& file, const std::string& source, bool header
|
||||||
) {
|
) {
|
||||||
std::stringstream ss;
|
std::string filename = file.string();
|
||||||
size_t pos = 0;
|
GLSLParser parser(*this, filename, source, header);
|
||||||
uint linenum = 1;
|
return parser.process();
|
||||||
if (!header) {
|
|
||||||
ss << "#version " << version << '\n';
|
|
||||||
}
|
|
||||||
for (auto& entry : defines) {
|
|
||||||
ss << "#define " << entry.first << " " << entry.second << '\n';
|
|
||||||
}
|
|
||||||
source_line(ss, linenum);
|
|
||||||
while (pos < source.length()) {
|
|
||||||
size_t endline = source.find('\n', pos);
|
|
||||||
if (endline == std::string::npos) {
|
|
||||||
endline = source.length();
|
|
||||||
}
|
|
||||||
// parsing preprocessor directives
|
|
||||||
if (source[pos] == '#') {
|
|
||||||
std::string line = source.substr(pos + 1, endline - pos);
|
|
||||||
util::trim(line);
|
|
||||||
// parsing 'include' directive
|
|
||||||
if (line.find("include") != std::string::npos) {
|
|
||||||
line = line.substr(7);
|
|
||||||
util::trim(line);
|
|
||||||
if (line.length() < 3) {
|
|
||||||
throw parsing_error(
|
|
||||||
file, linenum, "invalid 'include' syntax"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (line[0] != '<' || line[line.length() - 1] != '>') {
|
|
||||||
throw parsing_error(
|
|
||||||
file, linenum, "expected '#include <filename>' syntax"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
std::string name = line.substr(1, line.length() - 2);
|
|
||||||
if (!hasHeader(name)) {
|
|
||||||
loadHeader(name);
|
|
||||||
}
|
|
||||||
source_line(ss, 1);
|
|
||||||
ss << getHeader(name) << '\n';
|
|
||||||
pos = endline + 1;
|
|
||||||
linenum++;
|
|
||||||
source_line(ss, linenum);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// removing extra 'include' directives
|
|
||||||
else if (line.find("version") != std::string::npos) {
|
|
||||||
parsing_warning(file, linenum, "removed #version directive");
|
|
||||||
pos = endline + 1;
|
|
||||||
linenum++;
|
|
||||||
source_line(ss, linenum);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
linenum++;
|
|
||||||
ss << source.substr(pos, endline + 1 - pos);
|
|
||||||
pos = endline + 1;
|
|
||||||
}
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,33 +5,44 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "io/io.hpp"
|
#include "io/io.hpp"
|
||||||
|
#include "graphics/core/PostEffect.hpp"
|
||||||
|
|
||||||
class ResPaths;
|
class ResPaths;
|
||||||
|
|
||||||
class GLSLExtension {
|
class GLSLExtension {
|
||||||
std::unordered_map<std::string, std::string> headers;
|
|
||||||
std::unordered_map<std::string, std::string> defines;
|
|
||||||
std::string version = "330 core";
|
|
||||||
|
|
||||||
const ResPaths* paths = nullptr;
|
|
||||||
void loadHeader(const std::string& name);
|
|
||||||
public:
|
public:
|
||||||
|
using ParamsMap = std::unordered_map<std::string, PostEffect::Param>;
|
||||||
|
|
||||||
|
struct ProcessingResult {
|
||||||
|
std::string code;
|
||||||
|
ParamsMap params;
|
||||||
|
};
|
||||||
|
|
||||||
void setPaths(const ResPaths* paths);
|
void setPaths(const ResPaths* paths);
|
||||||
void setVersion(std::string version);
|
|
||||||
|
|
||||||
void define(const std::string& name, std::string value);
|
void define(const std::string& name, std::string value);
|
||||||
void undefine(const std::string& name);
|
void undefine(const std::string& name);
|
||||||
void addHeader(const std::string& name, std::string source);
|
void addHeader(const std::string& name, ProcessingResult header);
|
||||||
|
|
||||||
const std::string& getHeader(const std::string& name) const;
|
const ProcessingResult& getHeader(const std::string& name) const;
|
||||||
const std::string& getDefine(const std::string& name) const;
|
const std::string& getDefine(const std::string& name) const;
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, std::string>& getDefines() const;
|
||||||
|
|
||||||
bool hasHeader(const std::string& name) const;
|
bool hasHeader(const std::string& name) const;
|
||||||
bool hasDefine(const std::string& name) const;
|
bool hasDefine(const std::string& name) const;
|
||||||
|
void loadHeader(const std::string& name);
|
||||||
|
|
||||||
std::string process(
|
ProcessingResult process(
|
||||||
const io::path& file,
|
const io::path& file,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
bool header = false
|
bool header = false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static inline std::string VERSION = "330 core";
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, ProcessingResult> headers;
|
||||||
|
std::unordered_map<std::string, std::string> defines;
|
||||||
|
|
||||||
|
const ResPaths* paths = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ using namespace toml;
|
|||||||
class TomlReader : BasicParser<char> {
|
class TomlReader : BasicParser<char> {
|
||||||
dv::value root;
|
dv::value root;
|
||||||
|
|
||||||
// modified version of BaseParser.parseString
|
// modified version of BasicParser.parseString
|
||||||
// todo: extract common part
|
// todo: extract common part
|
||||||
std::string parseMultilineString() {
|
std::string parseMultilineString() {
|
||||||
pos += 2;
|
pos += 2;
|
||||||
|
|||||||
@ -68,5 +68,6 @@ inline const std::string LAYOUTS_FOLDER = "layouts";
|
|||||||
inline const std::string SOUNDS_FOLDER = "sounds";
|
inline const std::string SOUNDS_FOLDER = "sounds";
|
||||||
inline const std::string MODELS_FOLDER = "models";
|
inline const std::string MODELS_FOLDER = "models";
|
||||||
inline const std::string SKELETONS_FOLDER = "skeletons";
|
inline const std::string SKELETONS_FOLDER = "skeletons";
|
||||||
|
inline const std::string POST_EFFECTS_FOLDER = "shaders/effects";
|
||||||
|
|
||||||
inline const std::string FONT_DEFAULT = "normal";
|
inline const std::string FONT_DEFAULT = "normal";
|
||||||
|
|||||||
@ -189,6 +189,8 @@ constexpr const char* to_string(ResourceType type) {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case ResourceType::CAMERA:
|
case ResourceType::CAMERA:
|
||||||
return "camera";
|
return "camera";
|
||||||
|
case ResourceType::POST_EFFECT_SLOT:
|
||||||
|
return "post-effect-slot";
|
||||||
default:
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
@ -197,6 +199,8 @@ constexpr const char* to_string(ResourceType type) {
|
|||||||
inline std::optional<ResourceType> ResourceType_from(std::string_view str) {
|
inline std::optional<ResourceType> ResourceType_from(std::string_view str) {
|
||||||
if (str == "camera") {
|
if (str == "camera") {
|
||||||
return ResourceType::CAMERA;
|
return ResourceType::CAMERA;
|
||||||
|
} else if (str == "post-effect-slot") {
|
||||||
|
return ResourceType::POST_EFFECT_SLOT;
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,11 @@ class ContentPackRuntime;
|
|||||||
|
|
||||||
enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR };
|
enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR };
|
||||||
|
|
||||||
enum class ResourceType : size_t { CAMERA, LAST = CAMERA };
|
enum class ResourceType : size_t {
|
||||||
|
CAMERA,
|
||||||
|
POST_EFFECT_SLOT,
|
||||||
|
LAST = POST_EFFECT_SLOT
|
||||||
|
};
|
||||||
|
|
||||||
inline constexpr auto RESOURCE_TYPES_COUNT =
|
inline constexpr auto RESOURCE_TYPES_COUNT =
|
||||||
static_cast<size_t>(ResourceType::LAST) + 1;
|
static_cast<size_t>(ResourceType::LAST) + 1;
|
||||||
|
|||||||
@ -42,7 +42,9 @@ LevelScreen::LevelScreen(
|
|||||||
)
|
)
|
||||||
: Screen(engine),
|
: Screen(engine),
|
||||||
world(*levelPtr->getWorld()),
|
world(*levelPtr->getWorld()),
|
||||||
postProcessing(std::make_unique<PostProcessing>()),
|
postProcessing(std::make_unique<PostProcessing>(
|
||||||
|
levelPtr->content.getIndices(ResourceType::POST_EFFECT_SLOT).size()
|
||||||
|
)),
|
||||||
gui(engine.getGUI()),
|
gui(engine.getGUI()),
|
||||||
input(engine.getInput()) {
|
input(engine.getInput()) {
|
||||||
Level* level = levelPtr.get();
|
Level* level = levelPtr.get();
|
||||||
@ -115,7 +117,9 @@ void LevelScreen::initializeContent() {
|
|||||||
for (auto& entry : content.getPacks()) {
|
for (auto& entry : content.getPacks()) {
|
||||||
initializePack(entry.second.get());
|
initializePack(entry.second.get());
|
||||||
}
|
}
|
||||||
scripting::on_frontend_init(hud.get(), renderer.get());
|
scripting::on_frontend_init(
|
||||||
|
hud.get(), renderer.get(), postProcessing.get()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
||||||
|
|||||||
83
src/graphics/core/PostEffect.cpp
Normal file
83
src/graphics/core/PostEffect.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include "PostEffect.hpp"
|
||||||
|
|
||||||
|
#include "Shader.hpp"
|
||||||
|
#include "data/dv_util.hpp"
|
||||||
|
|
||||||
|
PostEffect::Param::Param() : type(Type::FLOAT) {}
|
||||||
|
|
||||||
|
PostEffect::Param::Param(Type type, Value defValue)
|
||||||
|
: type(type), defValue(defValue), value(defValue) {
|
||||||
|
}
|
||||||
|
|
||||||
|
PostEffect::PostEffect(
|
||||||
|
std::shared_ptr<Shader> shader,
|
||||||
|
std::unordered_map<std::string, Param> params
|
||||||
|
)
|
||||||
|
: shader(std::move(shader)), params(std::move(params)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader& PostEffect::use() {
|
||||||
|
shader->use();
|
||||||
|
shader->uniform1f("u_intensity", intensity);
|
||||||
|
for (auto& [name, param] : params) {
|
||||||
|
if (!param.dirty) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (param.type) {
|
||||||
|
case Param::Type::FLOAT:
|
||||||
|
shader->uniform1f(name, std::get<float>(param.value));
|
||||||
|
break;
|
||||||
|
case Param::Type::VEC2:
|
||||||
|
shader->uniform2f(name, std::get<glm::vec2>(param.value));
|
||||||
|
break;
|
||||||
|
case Param::Type::VEC3:
|
||||||
|
shader->uniform3f(name, std::get<glm::vec3>(param.value));
|
||||||
|
break;
|
||||||
|
case Param::Type::VEC4:
|
||||||
|
shader->uniform4f(name, std::get<glm::vec4>(param.value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
param.dirty = false;
|
||||||
|
}
|
||||||
|
return *shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
float PostEffect::getIntensity() const {
|
||||||
|
return intensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostEffect::setIntensity(float value) {
|
||||||
|
intensity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int n>
|
||||||
|
static void set_value(PostEffect::Param::Value& dst, const dv::value& value) {
|
||||||
|
glm::vec<n, float> vec;
|
||||||
|
dv::get_vec(value, vec);
|
||||||
|
dst = vec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostEffect::setParam(const std::string& name, const dv::value& value) {
|
||||||
|
const auto& found = params.find(name);
|
||||||
|
if (found == params.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& param = found->second;
|
||||||
|
switch (param.type) {
|
||||||
|
case Param::Type::FLOAT:
|
||||||
|
param.value = static_cast<float>(value.asNumber());
|
||||||
|
break;
|
||||||
|
case Param::Type::VEC2:
|
||||||
|
set_value<2>(param.value, value);
|
||||||
|
break;
|
||||||
|
case Param::Type::VEC3:
|
||||||
|
set_value<3>(param.value, value);
|
||||||
|
break;
|
||||||
|
case Param::Type::VEC4:
|
||||||
|
set_value<4>(param.value, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
param.dirty = true;
|
||||||
|
}
|
||||||
49
src/graphics/core/PostEffect.hpp
Normal file
49
src/graphics/core/PostEffect.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "data/dv_fwd.hpp"
|
||||||
|
|
||||||
|
class Shader;
|
||||||
|
|
||||||
|
class PostEffect {
|
||||||
|
public:
|
||||||
|
struct Param {
|
||||||
|
enum class Type { FLOAT, VEC2, VEC3, VEC4 };
|
||||||
|
using Value = std::variant<float, glm::vec2, glm::vec3, glm::vec4>;
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
Value defValue;
|
||||||
|
Value value;
|
||||||
|
bool dirty = true;
|
||||||
|
|
||||||
|
Param();
|
||||||
|
Param(Type type, Value defValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
PostEffect(
|
||||||
|
std::shared_ptr<Shader> shader,
|
||||||
|
std::unordered_map<std::string, Param> params
|
||||||
|
);
|
||||||
|
|
||||||
|
explicit PostEffect(const PostEffect&) = default;
|
||||||
|
|
||||||
|
Shader& use();
|
||||||
|
|
||||||
|
float getIntensity() const;
|
||||||
|
void setIntensity(float value);
|
||||||
|
|
||||||
|
void setParam(const std::string& name, const dv::value& value);
|
||||||
|
|
||||||
|
bool isActive() {
|
||||||
|
return intensity > 1e-4f;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Shader> shader;
|
||||||
|
std::unordered_map<std::string, Param> params;
|
||||||
|
float intensity = 0.0f;
|
||||||
|
};
|
||||||
@ -4,13 +4,16 @@
|
|||||||
#include "Texture.hpp"
|
#include "Texture.hpp"
|
||||||
#include "Framebuffer.hpp"
|
#include "Framebuffer.hpp"
|
||||||
#include "DrawContext.hpp"
|
#include "DrawContext.hpp"
|
||||||
|
#include "PostEffect.hpp"
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
PostProcessing::PostProcessing() {
|
PostProcessing::PostProcessing(size_t effectSlotsCount)
|
||||||
|
: effectSlots(effectSlotsCount) {
|
||||||
// Fullscreen quad mesh bulding
|
// Fullscreen quad mesh bulding
|
||||||
float vertices[] {
|
float vertices[] {
|
||||||
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
|
||||||
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f
|
||||||
};
|
};
|
||||||
VertexAttribute attrs[] {{2}, {0}};
|
VertexAttribute attrs[] {{2}, {0}};
|
||||||
@ -23,22 +26,64 @@ void PostProcessing::use(DrawContext& context) {
|
|||||||
const auto& vp = context.getViewport();
|
const auto& vp = context.getViewport();
|
||||||
if (fbo) {
|
if (fbo) {
|
||||||
fbo->resize(vp.x, vp.y);
|
fbo->resize(vp.x, vp.y);
|
||||||
|
fboSecond->resize(vp.x, vp.y);
|
||||||
} else {
|
} else {
|
||||||
fbo = std::make_unique<Framebuffer>(vp.x, vp.y);
|
fbo = std::make_unique<Framebuffer>(vp.x, vp.y);
|
||||||
|
fboSecond = std::make_unique<Framebuffer>(vp.x, vp.y);
|
||||||
}
|
}
|
||||||
context.setFramebuffer(fbo.get());
|
context.setFramebuffer(fbo.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PostProcessing::render(const DrawContext& context, Shader* screenShader) {
|
void PostProcessing::render(
|
||||||
|
const DrawContext& context, const Assets& assets, float timer
|
||||||
|
) {
|
||||||
if (fbo == nullptr) {
|
if (fbo == nullptr) {
|
||||||
throw std::runtime_error("'use(...)' was never called");
|
throw std::runtime_error("'use(...)' was never called");
|
||||||
}
|
}
|
||||||
|
int totalPasses = 0;
|
||||||
|
for (const auto& effect : effectSlots) {
|
||||||
|
totalPasses += (effect != nullptr && effect->isActive());
|
||||||
|
}
|
||||||
|
|
||||||
const auto& viewport = context.getViewport();
|
if (totalPasses == 0) {
|
||||||
screenShader->use();
|
auto& effect = assets.require<PostEffect>("default");
|
||||||
screenShader->uniform2i("u_screenSize", viewport);
|
effect.use();
|
||||||
fbo->getTexture()->bind();
|
fbo->getTexture()->bind();
|
||||||
quadMesh->draw();
|
quadMesh->draw();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentPass = 1;
|
||||||
|
for (const auto& effect : effectSlots) {
|
||||||
|
if (effect == nullptr || !effect->isActive()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& shader = effect->use();
|
||||||
|
|
||||||
|
const auto& viewport = context.getViewport();
|
||||||
|
shader.uniform1i("u_screen", 0);
|
||||||
|
shader.uniform2i("u_screenSize", viewport);
|
||||||
|
shader.uniform1f("u_timer", timer);
|
||||||
|
|
||||||
|
fbo->getTexture()->bind();
|
||||||
|
if (currentPass < totalPasses) {
|
||||||
|
fboSecond->bind();
|
||||||
|
}
|
||||||
|
quadMesh->draw();
|
||||||
|
if (currentPass < totalPasses) {
|
||||||
|
fboSecond->unbind();
|
||||||
|
std::swap(fbo, fboSecond);
|
||||||
|
}
|
||||||
|
currentPass++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostProcessing::setEffect(size_t slot, std::shared_ptr<PostEffect> effect) {
|
||||||
|
effectSlots.at(slot) = std::move(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
PostEffect* PostProcessing::getEffect(size_t slot) {
|
||||||
|
return effectSlots.at(slot).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<ImageData> PostProcessing::toImage() {
|
std::unique_ptr<ImageData> PostProcessing::toImage() {
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
class Mesh;
|
class Mesh;
|
||||||
class Shader;
|
class Assets;
|
||||||
class Framebuffer;
|
class Framebuffer;
|
||||||
class DrawContext;
|
class DrawContext;
|
||||||
class ImageData;
|
class ImageData;
|
||||||
|
class PostEffect;
|
||||||
|
|
||||||
/// @brief Framebuffer with blitting with shaders.
|
/// @brief Framebuffer with blitting with shaders.
|
||||||
/// @attention Current implementation does not support multiple render passes
|
/// @attention Current implementation does not support multiple render passes
|
||||||
@ -14,10 +16,12 @@ class ImageData;
|
|||||||
class PostProcessing {
|
class PostProcessing {
|
||||||
/// @brief Main framebuffer (lasy field)
|
/// @brief Main framebuffer (lasy field)
|
||||||
std::unique_ptr<Framebuffer> fbo;
|
std::unique_ptr<Framebuffer> fbo;
|
||||||
|
std::unique_ptr<Framebuffer> fboSecond;
|
||||||
/// @brief Fullscreen quad mesh as the post-processing canvas
|
/// @brief Fullscreen quad mesh as the post-processing canvas
|
||||||
std::unique_ptr<Mesh> quadMesh;
|
std::unique_ptr<Mesh> quadMesh;
|
||||||
|
std::vector<std::shared_ptr<PostEffect>> effectSlots;
|
||||||
public:
|
public:
|
||||||
PostProcessing();
|
PostProcessing(size_t effectSlotsCount);
|
||||||
~PostProcessing();
|
~PostProcessing();
|
||||||
|
|
||||||
/// @brief Prepare and bind framebuffer
|
/// @brief Prepare and bind framebuffer
|
||||||
@ -27,9 +31,12 @@ public:
|
|||||||
/// @brief Render fullscreen quad using the passed shader
|
/// @brief Render fullscreen quad using the passed shader
|
||||||
/// with framebuffer texture bound
|
/// with framebuffer texture bound
|
||||||
/// @param context graphics context
|
/// @param context graphics context
|
||||||
/// @param screenShader shader used for fullscreen quad
|
|
||||||
/// @throws std::runtime_error if use(...) wasn't called before
|
/// @throws std::runtime_error if use(...) wasn't called before
|
||||||
void render(const DrawContext& context, Shader* screenShader);
|
void render(const DrawContext& context, const Assets& assets, float timer);
|
||||||
|
|
||||||
|
void setEffect(size_t slot, std::shared_ptr<PostEffect> effect);
|
||||||
|
|
||||||
|
PostEffect* getEffect(size_t slot);
|
||||||
|
|
||||||
/// @brief Make an image from the last rendered frame
|
/// @brief Make an image from the last rendered frame
|
||||||
std::unique_ptr<ImageData> toImage();
|
std::unique_ptr<ImageData> toImage();
|
||||||
|
|||||||
@ -38,7 +38,7 @@ uint Shader::getUniformLocation(const std::string& name) {
|
|||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::uniformMatrix(const std::string& name, glm::mat4 matrix){
|
void Shader::uniformMatrix(const std::string& name, const glm::mat4& matrix){
|
||||||
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
|
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, glm::value_ptr(matrix));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,11 +54,11 @@ void Shader::uniform2f(const std::string& name, float x, float y){
|
|||||||
glUniform2f(getUniformLocation(name), x, y);
|
glUniform2f(getUniformLocation(name), x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::uniform2f(const std::string& name, glm::vec2 xy){
|
void Shader::uniform2f(const std::string& name, const glm::vec2& xy){
|
||||||
glUniform2f(getUniformLocation(name), xy.x, xy.y);
|
glUniform2f(getUniformLocation(name), xy.x, xy.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::uniform2i(const std::string& name, glm::ivec2 xy){
|
void Shader::uniform2i(const std::string& name, const glm::ivec2& xy){
|
||||||
glUniform2i(getUniformLocation(name), xy.x, xy.y);
|
glUniform2i(getUniformLocation(name), xy.x, xy.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,10 +66,14 @@ void Shader::uniform3f(const std::string& name, float x, float y, float z){
|
|||||||
glUniform3f(getUniformLocation(name), x,y,z);
|
glUniform3f(getUniformLocation(name), x,y,z);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shader::uniform3f(const std::string& name, glm::vec3 xyz){
|
void Shader::uniform3f(const std::string& name, const glm::vec3& xyz){
|
||||||
glUniform3f(getUniformLocation(name), xyz.x, xyz.y, xyz.z);
|
glUniform3f(getUniformLocation(name), xyz.x, xyz.y, xyz.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Shader::uniform4f(const std::string& name, const glm::vec4& xyzw) {
|
||||||
|
glUniform4f(getUniformLocation(name), xyzw.x, xyzw.y, xyzw.z, xyzw.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline auto shader_deleter = [](GLuint* shader) {
|
inline auto shader_deleter = [](GLuint* shader) {
|
||||||
glDeleteShader(*shader);
|
glDeleteShader(*shader);
|
||||||
|
|||||||
@ -21,14 +21,15 @@ public:
|
|||||||
~Shader();
|
~Shader();
|
||||||
|
|
||||||
void use();
|
void use();
|
||||||
void uniformMatrix(const std::string&, glm::mat4 matrix);
|
void uniformMatrix(const std::string&, const glm::mat4& matrix);
|
||||||
void uniform1i(const std::string& name, int x);
|
void uniform1i(const std::string& name, int x);
|
||||||
void uniform1f(const std::string& name, float x);
|
void uniform1f(const std::string& name, float x);
|
||||||
void uniform2f(const std::string& name, float x, float y);
|
void uniform2f(const std::string& name, float x, float y);
|
||||||
void uniform2f(const std::string& name, glm::vec2 xy);
|
void uniform2f(const std::string& name, const glm::vec2& xy);
|
||||||
void uniform2i(const std::string& name, glm::ivec2 xy);
|
void uniform2i(const std::string& name, const glm::ivec2& xy);
|
||||||
void uniform3f(const std::string& name, float x, float y, float z);
|
void uniform3f(const std::string& name, float x, float y, float z);
|
||||||
void uniform3f(const std::string& name, glm::vec3 xyz);
|
void uniform3f(const std::string& name, const glm::vec3& xyz);
|
||||||
|
void uniform4f(const std::string& name, const glm::vec4& xyzw);
|
||||||
|
|
||||||
/// @brief Create shader program using vertex and fragment shaders source.
|
/// @brief Create shader program using vertex and fragment shaders source.
|
||||||
/// @param vertexFile vertex shader file name
|
/// @param vertexFile vertex shader file name
|
||||||
|
|||||||
@ -388,12 +388,7 @@ void WorldRenderer::draw(
|
|||||||
renderBlockOverlay(wctx);
|
renderBlockOverlay(wctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rendering fullscreen quad
|
postProcessing.render(pctx, assets, timer);
|
||||||
auto screenShader = assets.get<Shader>("screen");
|
|
||||||
screenShader->use();
|
|
||||||
screenShader->uniform1f("u_timer", timer);
|
|
||||||
screenShader->uniform1f("u_dayTime", worldInfo.daytime);
|
|
||||||
postProcessing.render(pctx, screenShader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||||
|
|||||||
@ -3,11 +3,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <GL/glew.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
#include "presets/WeatherPreset.hpp"
|
#include "presets/WeatherPreset.hpp"
|
||||||
|
|||||||
@ -38,6 +38,7 @@ extern const luaL_Reg networklib[];
|
|||||||
extern const luaL_Reg packlib[];
|
extern const luaL_Reg packlib[];
|
||||||
extern const luaL_Reg particleslib[]; // gfx.particles
|
extern const luaL_Reg particleslib[]; // gfx.particles
|
||||||
extern const luaL_Reg playerlib[];
|
extern const luaL_Reg playerlib[];
|
||||||
|
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
||||||
extern const luaL_Reg quatlib[];
|
extern const luaL_Reg quatlib[];
|
||||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||||
extern const luaL_Reg timelib[];
|
extern const luaL_Reg timelib[];
|
||||||
@ -46,7 +47,7 @@ extern const luaL_Reg utf8lib[];
|
|||||||
extern const luaL_Reg vec2lib[]; // vecn.cpp
|
extern const luaL_Reg vec2lib[]; // vecn.cpp
|
||||||
extern const luaL_Reg vec3lib[]; // vecn.cpp
|
extern const luaL_Reg vec3lib[]; // vecn.cpp
|
||||||
extern const luaL_Reg vec4lib[]; // vecn.cpp
|
extern const luaL_Reg vec4lib[]; // vecn.cpp
|
||||||
extern const luaL_Reg weatherlib[];
|
extern const luaL_Reg weatherlib[]; // gfx.weather
|
||||||
extern const luaL_Reg worldlib[];
|
extern const luaL_Reg worldlib[];
|
||||||
extern const luaL_Reg yamllib[];
|
extern const luaL_Reg yamllib[];
|
||||||
|
|
||||||
|
|||||||
68
src/logic/scripting/lua/libs/libposteffects.cpp
Normal file
68
src/logic/scripting/lua/libs/libposteffects.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include "libhud.hpp"
|
||||||
|
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
|
#include "content/Content.hpp"
|
||||||
|
#include "graphics/core/PostEffect.hpp"
|
||||||
|
#include "graphics/core/PostProcessing.hpp"
|
||||||
|
|
||||||
|
using namespace scripting;
|
||||||
|
|
||||||
|
static int l_index(lua::State* L) {
|
||||||
|
auto name = lua::require_string(L, 1);
|
||||||
|
auto& indices = content->getIndices(ResourceType::POST_EFFECT_SLOT);
|
||||||
|
return lua::pushinteger(L, indices.indexOf(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_effect(lua::State* L) {
|
||||||
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
|
auto name = lua::require_string(L, 2);
|
||||||
|
auto& assets = *engine->getAssets();
|
||||||
|
auto effect = std::make_shared<PostEffect>(assets.require<PostEffect>(name));
|
||||||
|
post_processing->setEffect(index, std::move(effect));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_intensity(lua::State* L) {
|
||||||
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
|
auto effect = post_processing->getEffect(index);
|
||||||
|
return lua::pushnumber(L, effect ? effect->getIntensity() : 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_intensity(lua::State* L) {
|
||||||
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
|
float value = lua::tonumber(L, 2);
|
||||||
|
post_processing->getEffect(index)->setIntensity(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_is_active(lua::State* L) {
|
||||||
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
|
auto effect = post_processing->getEffect(index);
|
||||||
|
return lua::pushboolean(L, effect ? effect->isActive() : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_params(lua::State* L) {
|
||||||
|
size_t index = static_cast<size_t>(lua::tointeger(L, 1));
|
||||||
|
auto table = lua::tovalue(L, 2);
|
||||||
|
if (!table.isObject()) {
|
||||||
|
throw std::runtime_error("params table expected");
|
||||||
|
}
|
||||||
|
auto effect = post_processing->getEffect(index);
|
||||||
|
if (effect == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
for (const auto& [key, value] : table.asObject()) {
|
||||||
|
effect->setParam(key, value);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const luaL_Reg posteffectslib[] = {
|
||||||
|
{"index", lua::wrap<l_index>},
|
||||||
|
{"set_effect", lua::wrap<l_set_effect>},
|
||||||
|
{"get_intensity", lua::wrap<l_get_intensity>},
|
||||||
|
{"set_intensity", lua::wrap<l_set_intensity>},
|
||||||
|
{"is_active", lua::wrap<l_is_active>},
|
||||||
|
{"set_params", lua::wrap<l_set_params>},
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
@ -19,6 +19,7 @@ static debug::Logger logger("scripting-hud");
|
|||||||
|
|
||||||
Hud* scripting::hud = nullptr;
|
Hud* scripting::hud = nullptr;
|
||||||
WorldRenderer* scripting::renderer = nullptr;
|
WorldRenderer* scripting::renderer = nullptr;
|
||||||
|
PostProcessing* scripting::post_processing = nullptr;
|
||||||
|
|
||||||
static void load_script(const std::string& name) {
|
static void load_script(const std::string& name) {
|
||||||
auto file = io::path("res:scripts") / name;
|
auto file = io::path("res:scripts") / name;
|
||||||
@ -28,9 +29,12 @@ static void load_script(const std::string& name) {
|
|||||||
lua::execute(lua::get_main_state(), 0, src, file.string());
|
lua::execute(lua::get_main_state(), 0, src, file.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
|
void scripting::on_frontend_init(
|
||||||
|
Hud* hud, WorldRenderer* renderer, PostProcessing* postProcessing
|
||||||
|
) {
|
||||||
scripting::hud = hud;
|
scripting::hud = hud;
|
||||||
scripting::renderer = renderer;
|
scripting::renderer = renderer;
|
||||||
|
scripting::post_processing = postProcessing;
|
||||||
|
|
||||||
auto L = lua::get_main_state();
|
auto L = lua::get_main_state();
|
||||||
|
|
||||||
@ -39,6 +43,7 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
|
|||||||
lua::openlib(L, "gfx", "particles", particleslib);
|
lua::openlib(L, "gfx", "particles", particleslib);
|
||||||
lua::openlib(L, "gfx", "weather", weatherlib);
|
lua::openlib(L, "gfx", "weather", weatherlib);
|
||||||
lua::openlib(L, "gfx", "text3d", text3dlib);
|
lua::openlib(L, "gfx", "text3d", text3dlib);
|
||||||
|
lua::openlib(L, "gfx", "posteffects", posteffectslib);
|
||||||
|
|
||||||
load_script("hud_classes.lua");
|
load_script("hud_classes.lua");
|
||||||
|
|
||||||
@ -85,6 +90,7 @@ void scripting::on_frontend_close() {
|
|||||||
|
|
||||||
scripting::renderer = nullptr;
|
scripting::renderer = nullptr;
|
||||||
scripting::hud = nullptr;
|
scripting::hud = nullptr;
|
||||||
|
scripting::post_processing = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void scripting::load_hud_script(
|
void scripting::load_hud_script(
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
class Hud;
|
class Hud;
|
||||||
class WorldRenderer;
|
class WorldRenderer;
|
||||||
|
class PostProcessing;
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
class UINode;
|
class UINode;
|
||||||
@ -18,8 +19,11 @@ namespace gui {
|
|||||||
namespace scripting {
|
namespace scripting {
|
||||||
extern Hud *hud;
|
extern Hud *hud;
|
||||||
extern WorldRenderer* renderer;
|
extern WorldRenderer* renderer;
|
||||||
|
extern PostProcessing* post_processing;
|
||||||
|
|
||||||
void on_frontend_init(Hud* hud, WorldRenderer* renderer);
|
void on_frontend_init(
|
||||||
|
Hud* hud, WorldRenderer* renderer, PostProcessing* postProcessing
|
||||||
|
);
|
||||||
void on_frontend_render();
|
void on_frontend_render();
|
||||||
void on_frontend_close();
|
void on_frontend_close();
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdint.h>
|
#include <cstdint>
|
||||||
|
|
||||||
using scriptenv = std::shared_ptr<int>;
|
using scriptenv = std::shared_ptr<int>;
|
||||||
|
|
||||||
|
|||||||
36
test/coders/GLSLExtension.cpp
Normal file
36
test/coders/GLSLExtension.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "coders/commons.hpp"
|
||||||
|
#include "coders/GLSLExtension.hpp"
|
||||||
|
|
||||||
|
TEST(GLSLExtension, processing) {
|
||||||
|
GLSLExtension glsl;
|
||||||
|
glsl.addHeader("sum",
|
||||||
|
glsl.process("sum.glsl",
|
||||||
|
"// sum function for glsl\n"
|
||||||
|
"float sum(float a, float b) {\n"
|
||||||
|
" return a + b;\n"
|
||||||
|
"}\n",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
auto processed = glsl.process("test.glsl",
|
||||||
|
"in vec2 v_uv;\n"
|
||||||
|
"uniform sampler2D u_screen;\n"
|
||||||
|
"\n"
|
||||||
|
"#include /* hell\no */ < sum >\n"
|
||||||
|
"#param float p_intensity\n"
|
||||||
|
"#param vec3 p_pos = [0, 0, 0]\n"
|
||||||
|
"\n"
|
||||||
|
"vec4 effect() {\n"
|
||||||
|
" vec4 color = texture(u_screen, v_uv);\n"
|
||||||
|
" return mix(color, 1.0 - color, p_intensity);\n"
|
||||||
|
"}\n",
|
||||||
|
false);
|
||||||
|
std::cout << processed.code << std::endl;
|
||||||
|
} catch (const parsing_error& err) {
|
||||||
|
std::cerr << err.errorLog() << std::endl;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user