From acd44bb898996ff4e36b39b10e870e40bcaf2634 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Fri, 10 Nov 2023 19:24:56 -0500 Subject: One last push Signed-off-by: Andrew Opalach --- src/assets.cc | 188 ++++++++-------- src/assets.h | 71 +++--- src/context.cc | 34 ++- src/context.h | 57 ++--- src/context_glfw.cc | 144 ++++++------- src/context_glfw.h | 18 +- src/context_null.h | 40 ++-- src/context_x11.cc | 263 ++++++++++++----------- src/context_x11.h | 65 +++--- src/engine.cc | 211 +++++++++--------- src/engine.h | 64 +++--- src/framebuffer.cc | 23 +- src/framebuffer.h | 9 +- src/gl.cc | 559 +++++++++++++++++++++++++---------------------- src/gl.h | 70 +++--- src/json.h | 130 +++++------ src/log.h | 22 +- src/mauri.cc | 87 ++++++-- src/objects/effect.cc | 89 ++++---- src/objects/effect.h | 14 +- src/objects/material.cc | 57 ++--- src/objects/material.h | 12 +- src/objects/model.cc | 36 ++-- src/objects/model.h | 4 +- src/objects/object.cc | 314 ++++++++++++++------------- src/objects/object.h | 71 +++--- src/objects/pass.cc | 412 +++++++++++++++++------------------ src/objects/pass.h | 51 ++--- src/objects/scene.cc | 85 ++++---- src/objects/scene.h | 20 +- src/shader.cc | 561 +++++++++++++++++++++++++++++------------------- src/shader.h | 53 +++-- src/texture.cc | 270 +++++++++++++---------- src/texture.h | 58 ++--- src/util.cc | 16 +- src/util.h | 23 +- src/vis.cc | 286 ++++++++++++++++++++++++ src/vis.h | 87 ++++++++ src/visualizer.cc | 303 -------------------------- src/visualizer.h | 79 ------- 40 files changed, 2558 insertions(+), 2398 deletions(-) create mode 100644 src/vis.cc create mode 100644 src/vis.h delete mode 100644 src/visualizer.cc delete mode 100644 src/visualizer.h (limited to 'src') diff --git a/src/assets.cc b/src/assets.cc index d48cffd..f892662 100644 --- a/src/assets.cc +++ b/src/assets.cc @@ -8,11 +8,11 @@ namespace Mauri { -static AssetManager _asset_manager; +static AssetManager asset_manager_; -AssetManager *asset_manager() +auto asset_manager() -> AssetManager * { - return &_asset_manager; + return &asset_manager_; } } // namespace Mauri @@ -21,25 +21,20 @@ using namespace Mauri; Asset::Asset() { - type = ASSET_VOID; - size = 0; - pos = 0; - data = nullptr; - ltype = NONE; - lasset = nullptr; + this->type = ASSET_VOID; + this->size = 0; + this->data = nullptr; } static Asset asset_void = Asset(); -Asset::Asset(const std::string &path, byte *begin, byte *end) +Asset::Asset(const std::string &path, byte *start, byte *end) : path(path) { - type = ASSET_SLICE; - size = end - begin; - pos = 0; - data = begin; - ltype = NONE; - lasset = nullptr; + this->type = ASSET_SLICE; + this->size = end - start; + this->data = start; + this->hash = std::hash{}(path); } Asset::Asset(const std::string &path) @@ -49,44 +44,39 @@ Asset::Asset(const std::string &path) if (file.bad() || file.fail()) { - type = ASSET_VOID; + this->type = ASSET_VOID; file.close(); return; } - size = file.tellg(); - data = new byte[size]; + this->size = file.tellg(); + this->data = new byte[size]; file.seekg(0, std::ios::beg); - if (!file.read(reinterpret_cast(data), size)) + + if (!file.read(reinterpret_cast(this->data), this->size)) { - type = ASSET_VOID; + this->type = ASSET_VOID; file.close(); + delete[] this->data; return; } file.close(); - pos = 0; - type = ASSET_FILE; - - ltype = NONE; - lasset = nullptr; + this->type = ASSET_FILE; } Asset::~Asset() { - if (type == ASSET_FILE) - { - delete[] data; - } - - if (lasset != nullptr) + if (this->type == ASSET_VOID) return; + if (this->type == ASSET_FILE) delete[] this->data; + if (this->lasset) { - switch (ltype) + switch (this->ltype) { case TEXTURE: - delete (Texture *)lasset; + delete (Texture *)this->lasset; break; default: break; @@ -94,70 +84,67 @@ Asset::~Asset() } } -bool AssetManager::load_package(const std::string &path) +auto AssetManager::arm_package(const std::string &path, bool can_output) -> bool { - pkg = new Asset(path); + this->package = new Asset(path); - if (pkg->type == ASSET_VOID) + if (this->package->type == ASSET_VOID) { - log_error("failed to load pkg file: (%s)", path.c_str()); + error("failed to load package file: (%s)", path.c_str()); return false; - }; + } - auto signature = pkg->reads(pkg->read()); + std::string_view signature = this->package->reads(this->package->read()); if (signature.compare(0, 4, "PKGV") != 0) { - log_error("invalid pkg file (%s)", path.c_str()); + error("invalid package file: (%s)", path.c_str()); return false; } - auto count = pkg->read(); + u32 count = this->package->read(); + + this->assets.resize(count); - std::vector pkg_files; - pkg_files.reserve(count); + std::vector package_assets; + package_assets.resize(count); - for (auto i = 0u; i < count; i++) + for (u32 i = 0; i < count; i++) { - pkg_files.emplace_back(PkgFile { - pkg->reads(pkg->read()), - pkg->read(), - pkg->read() - }); + package_assets[i] = PackageFile{ + this->package->reads(this->package->read()), + this->package->read(), + this->package->read() + }; } - for (auto file : pkg_files) + for (u32 i = 0; i < count; i++) { - auto path = std::string(file.path); - - auto asset = new Asset(path, - pkg->ptr() + file.offset, - pkg->ptr() + file.offset + file.length - ); - - asset->hash = std::hash{}(path); - - files.push_back(asset); - - log_debug("loaded file (%s)", std::string(file.path).c_str()); + const PackageFile &asset = package_assets[i]; + byte *start = this->package->ptr() + asset.offset; + byte *end = start + asset.length; + this->assets[i] = new Asset(std::string(asset.path), start, end); + debug("package file: (%s)", std::string(asset.path).c_str()); } + this->prepare_output = can_output; + return true; } -void AssetManager::unload_package() +auto AssetManager::unload_package() -> void { - for (auto &asset : files) + for (Asset *asset : this->assets) { delete asset; } - files.clear(); + this->assets.clear(); - delete pkg; + delete this->package; } -Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) +auto AssetManager::get_file(const std::string &part, AssetTypeHint hint) -> Asset * { std::string path = part; @@ -170,33 +157,33 @@ Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) case SHADER: path.insert(0, "shaders/"); break; - case FRAGMENT_SHADER: - path.insert(0, "shaders/"); - path.append(".frag"); - break; case VERTEX_SHADER: path.insert(0, "shaders/"); path.append(".vert"); break; + case FRAGMENT_SHADER: + path.insert(0, "shaders/"); + path.append(".frag"); + break; case NONE: default: break; } Asset *asset = nullptr; - + u64 hash = std::hash{}(path); - for (auto &file : files) + for (u32 i = 0; i < this->assets.size(); i++) { - if (file->hash == hash) + if (this->assets[i]->hash == hash) { - asset = file; + asset = this->assets[i]; break; } } - if (asset == nullptr) + if (!asset) { path.insert(0, "assets/"); @@ -205,21 +192,25 @@ Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) if (asset->type == ASSET_VOID) { + error("failed to load asset: (%s)", path.c_str()); delete asset; return &asset_void; } - files.push_back(asset); + debug("fetched asset: (%s)", path.c_str()); + + this->assets.push_back(asset); } asset->ltype = hint; - if (asset->lasset == nullptr) + if (!asset->lasset) { switch (asset->ltype) { case TEXTURE: asset->lasset = new Texture(asset); + debug("processed texture: (%s)", path.c_str()); break; default: break; @@ -229,23 +220,23 @@ Asset *AssetManager::get_file(const std::string &part, FileTypeHint hint) return asset; } -void AssetManager::write_files(const std::string &output_path) +auto AssetManager::write_files(const std::string &output_path) -> void { - if (!std::filesystem::create_directories(output_path)) + if (!std::filesystem::is_directory(output_path) && !std::filesystem::create_directories(output_path)) + { + error("failed to create directory: (%s)", output_path.c_str()); return; - - for (auto &asset : files) + } + + for (Asset *asset : this->assets) { std::stringstream path; path << output_path; - if (output_path.back() != '/') - { - path << '/'; - } + if (output_path.back() != '/') path << '/'; - auto index = asset->path.find_last_of('/'); + size_t index = asset->path.find_last_of('/'); if (index != std::string::npos) { @@ -253,28 +244,31 @@ void AssetManager::write_files(const std::string &output_path) } path << asset->path; - + switch (asset->ltype) { - case TEXTURE: { - auto texture = ((Texture *)asset->lasset); + case TEXTURE: + { + Texture *texture = ((Texture *)asset->lasset); texture->save_to_file(path.str() + ".png"); std::ofstream file(path.str() + ".flags"); - file << "NO_INTERPOLATION " << texture->flags.map.NO_INTERPOLATION << "\n"; - file << "CLAMP_UV " << texture->flags.map.CLAMP_UV << "\n"; - file << "IS_GIF " << texture->flags.map.IS_GIF << "\n"; + file << "NO_INTERPOLATION " << texture->flags.m.NO_INTERPOLATION << "\n"; + file << "CLAMP_UV " << texture->flags.m.CLAMP_UV << "\n"; + file << "IS_GIF " << texture->flags.m.IS_GIF << "\n"; file.close(); break; } - case FRAGMENT_SHADER: case VERTEX_SHADER: - case SHADER: { + case FRAGMENT_SHADER: + case SHADER: + { std::ofstream file(path.str()); file << asset->as_string(); file.close(); break; } - case NONE: { + case NONE: + { std::ofstream file(path.str(), std::ios::binary); file << asset->as_string(); file.close(); diff --git a/src/assets.h b/src/assets.h index 72665d3..34e31df 100644 --- a/src/assets.h +++ b/src/assets.h @@ -9,21 +9,9 @@ namespace Mauri { -enum AssetType -{ - ASSET_FILE, - ASSET_SLICE, - ASSET_VOID -}; -enum FileTypeHint -{ - TEXTURE, - SHADER, - FRAGMENT_SHADER, - VERTEX_SHADER, - NONE -}; +enum AssetType { ASSET_VOID, ASSET_SLICE, ASSET_FILE }; +enum AssetTypeHint { NONE, TEXTURE, SHADER, FRAGMENT_SHADER, VERTEX_SHADER }; class Asset { @@ -31,66 +19,64 @@ class Asset Asset(); Asset(const std::string &path, byte *begin, byte *end); Asset(const std::string &path); - ~Asset(); - // If this is ASSET_FILE, the data should be - // free'd along with this asset. AssetType type; std::string path; - // Info about loaded asset - FileTypeHint ltype; - void *lasset; + AssetTypeHint ltype = NONE; + void *lasset = nullptr; u32 size; u64 hash; - byte *ptr() const + auto ptr() const -> byte * { return &this->data[this->pos]; } - void seek(s32 n) + auto seek(s32 n) -> void { this->pos += n; } - void reset() + auto reset() -> void { this->pos = 0; } template - T read() + auto read() -> T { T val = *((T *)this->ptr()); this->seek(sizeof(T)); return val; } - std::string_view reads(u32 n) + auto reads(u32 n) -> std::string_view { std::string_view s((char *)this->ptr(), n); this->seek(n); return s; } - - const std::string &as_string() + + auto as_string() -> const std::string_view & { - if (_str.empty()) - _str = std::string((char *)this->ptr(), this->size); - return _str; + if (str.empty()) + { + str = std::string_view((char *)this->ptr(), this->size); + } + return str; } private: - u32 pos; + u32 pos = 0; byte *data; - std::string _str; + std::string_view str; }; -struct PkgFile +struct PackageFile { std::string_view path; u32 offset; @@ -100,23 +86,24 @@ struct PkgFile class AssetManager { public: - AssetManager() {}; - ~AssetManager() {}; + AssetManager() {} + ~AssetManager() {} - std::vector files; + auto get_file(const std::string &part, AssetTypeHint hint = NONE) -> Asset *; - Asset *get_file(const std::string &part, FileTypeHint hint); + auto arm_package(const std::string &path, bool can_output) -> bool; + auto unload_package() -> void; - bool load_package(const std::string &path); - void unload_package(); + bool prepare_output; - void write_files(const std::string &output_path); + auto write_files(const std::string &output_path) -> void; private: - Asset *pkg; + Asset *package; + std::vector assets; }; -extern AssetManager *asset_manager(); +extern auto asset_manager() -> AssetManager *; } // namespace Mauri diff --git a/src/context.cc b/src/context.cc index f40790f..a89f8b0 100644 --- a/src/context.cc +++ b/src/context.cc @@ -1,38 +1,36 @@ #include "context.h" -namespace Mauri { +namespace Mauri +{ -static Context *_context = nullptr; +static Context *context_ = nullptr; -Context *context() +auto context() -> Context * { - return _context; + return context_; } -void set_context(Context *c) +auto set_context(Context *c) -> void { - if (_context != nullptr) + if (context_) { - _context->close_window(); - delete _context; + delete context_; } - - _context = c; + context_ = c; } -} +} // namespace Mauri using namespace Mauri; -void Context::print_gl_info() +auto Context::print_gl_info() -> void { - log_info("%s", "OpenGL loaded"); - log_info("Vendor: %s", glGetString(GL_VENDOR)); - log_info("Renderer: %s", glGetString(GL_RENDERER)); - log_info("Version: %s", glGetString(GL_VERSION)); + info("vendor: %s", glGetString(GL_VENDOR)); + info("renderer: %s", glGetString(GL_RENDERER)); + info("version: %s", glGetString(GL_VERSION)); } -GLenum Context::check_error() +auto Context::check_error() -> GLenum { GLenum error_code; while ((error_code = glGetError()) != GL_NO_ERROR) @@ -56,7 +54,7 @@ GLenum Context::check_error() error = "INVALID_FRAMEBUFFER_OPERATION"; break; } - log_error("%s", error.c_str()); + error("gl error: %s", error.c_str()); } return error_code; } diff --git a/src/context.h b/src/context.h index bc824b6..93e5290 100644 --- a/src/context.h +++ b/src/context.h @@ -1,13 +1,9 @@ #ifndef _CONTEXT_H #define _CONTEXT_H -#include -#include +#include -#include "util.h" - -#define GL_VERSION_MAJOR 3 -#define GL_VERSION_MINOR 3 +#include "gl.h" namespace Mauri { @@ -15,45 +11,50 @@ namespace Mauri class Context { public: - Context(s32 width, s32 height, const std::string &name, bool wallpaper) - : width(width), height(height) {} - + Context() : start(std::chrono::system_clock::now()) {} virtual ~Context() {} - s32 width, height; + s32 width; + s32 height; + + virtual auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool = 0; + virtual auto resize_window(s32 width, s32 height) -> void = 0; + virtual auto should_close_window() const -> bool = 0; + virtual auto close_window() -> void = 0; + + virtual auto process_input() -> void = 0; + virtual auto cursor_pos(f32 *x, f32 *y) -> void = 0; - bool draw_enabled() const + auto is_draw_enabled() const -> bool { - return _draw_enabled; + return this->draw_enabled; } - void set_draw_enabled(bool enabled) + auto set_draw_enabled(bool enabled) -> void { - _draw_enabled = enabled; + this->draw_enabled = enabled; } - virtual void resize_window(s32 w, s32 h) = 0; + virtual auto swap_buffers() -> void = 0; - virtual void close_window() = 0; - virtual bool should_close() const = 0; + using us = std::chrono::microseconds; - virtual void swap_buffers() = 0; - - virtual void process_input() = 0; - virtual void cursor_pos(f32 *x, f32 *y) = 0; - - virtual f64 current_time() = 0; + auto current_time() const -> f64 + { + return std::chrono::duration_cast(std::chrono::system_clock::now() - this->start).count() / 1000000.f; + } protected: - GLenum check_error(); - void print_gl_info(); + auto print_gl_info() -> void; + auto check_error() -> GLenum; private: - bool _draw_enabled = false; + bool draw_enabled = false; + std::chrono::time_point start; }; -extern Context *context(); -extern void set_context(Context *c); +extern auto context() -> Context *; +extern auto set_context(Context *c) -> void; } // namespace Mauri diff --git a/src/context_glfw.cc b/src/context_glfw.cc index c4f7d17..cbaea23 100644 --- a/src/context_glfw.cc +++ b/src/context_glfw.cc @@ -2,84 +2,45 @@ using namespace Mauri; -void ContextGLFW::close_window() -{ - glfwSetWindowShouldClose(window, GLFW_TRUE); -} - -void ContextGLFW::resize_window(s32 w, s32 h) -{ - width = w; - height = h; - glfwSetWindowSize(window, width, height); -} - -bool ContextGLFW::should_close() const -{ - return glfwWindowShouldClose(window); -} - -void ContextGLFW::cursor_pos(f32 *x, f32 *y) -{ - f64 _x, _y; - glfwGetCursorPos(window, &_x, &_y); - *x = (f32)_x; - *y = (f32)_y; -} - -void ContextGLFW::swap_buffers() -{ - glfwSwapBuffers(window); -} - -static void key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 mods) +static auto key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 mods) -> void { if (key == GLFW_KEY_B && action == GLFW_PRESS) { - context()->set_draw_enabled(!context()->draw_enabled()); + context()->set_draw_enabled(!context()->is_draw_enabled()); } } -void ContextGLFW::process_input() -{ - glfwPollEvents(); -} - -double ContextGLFW::current_time() -{ - return glfwGetTime(); -} - -ContextGLFW::ContextGLFW(s32 width, s32 height, const std::string &name, bool wallpaper) - : Context(width, height, name, wallpaper) +auto ContextGLFW::create_window(s32 width, s32 height, const std::string &name, bool background) -> bool { if (!glfwInit()) { - log_error("%s", "failed to init glfw"); - return; + error("failed to init glfw"); + return false; } + this->width = width; + this->height = height; + // https://github.com/haasn/libplacebo/blob/master/demos/window_glfw.c#L178 - struct { + struct + { s32 api; s32 major, minor; s32 glsl_ver; s32 profile; } gl_vers[] = { - { GLFW_OPENGL_API, 4, 6, 460, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 4, 5, 450, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 4, 4, 440, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 4, 0, 400, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 3, 3, 330, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 3, 2, 150, GLFW_OPENGL_CORE_PROFILE }, - { GLFW_OPENGL_API, 3, 1, 140, 0 }, - { GLFW_OPENGL_API, 3, 0, 130, 0 }, + {GLFW_OPENGL_API, 4, 6, 460, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 4, 5, 450, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 4, 4, 440, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 4, 0, 400, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 3, 3, 330, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 3, 2, 150, GLFW_OPENGL_CORE_PROFILE}, + {GLFW_OPENGL_API, 3, 1, 140, 0}, + {GLFW_OPENGL_API, 3, 0, 130, 0}, }; - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); - glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); - glfwWindowHint(GLFW_SAMPLES, 4); + //glfwWindowHint(GLFW_SAMPLES, 4); for (u32 i = 0; i < ARRAY_SIZE(gl_vers); i++) { @@ -89,43 +50,82 @@ ContextGLFW::ContextGLFW(s32 width, s32 height, const std::string &name, bool wa glfwWindowHint(GLFW_OPENGL_PROFILE, gl_vers[i].profile); #ifdef __APPLE__ if (gl_vers[i].profile == GLFW_OPENGL_CORE_PROFILE) + { glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + } #endif - - window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); - - if (window) + if ((this->window = glfwCreateWindow(this->width, this->height, name.c_str(), nullptr, nullptr))) { break; } } - if (!window) + if (!this->window) { - log_error("%s", "could not create opengl context"); - return; + error("could not create gl context"); + return false; } - glfwMakeContextCurrent(window); + glfwMakeContextCurrent(this->window); if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) { - log_error("%s", "could not load opengl"); - return; + error("could not load gl"); + return false; } glEnable(GL_BLEND); glEnable(GL_MULTISAMPLE); - print_gl_info(); + this->print_gl_info(); + + this->set_draw_enabled(true); - set_draw_enabled(true); + glfwSetKeyCallback(this->window, key_callback); - glfwSetKeyCallback(window, key_callback); + return true; +} + +auto ContextGLFW::resize_window(s32 width, s32 height) -> void +{ + if (width != this->width || height != this->height) + { + this->width = width; + this->height = height; + glfwSetWindowSize(this->window, this->width, this->height); + } +} + +auto ContextGLFW::should_close_window() const -> bool +{ + return glfwWindowShouldClose(this->window); +} + +auto ContextGLFW::close_window() -> void +{ + glfwSetWindowShouldClose(this->window, GLFW_TRUE); +} + +auto ContextGLFW::process_input() -> void +{ + glfwPollEvents(); +} + +auto ContextGLFW::cursor_pos(f32 *x, f32 *y) -> void +{ + f64 x_, y_; + glfwGetCursorPos(this->window, &x_, &y_); + *x = (f32)x_; + *y = (f32)y_; +} + +auto ContextGLFW::swap_buffers() -> void +{ + glfwSwapBuffers(this->window); } ContextGLFW::~ContextGLFW() { - glfwDestroyWindow(this->window); + if (this->window) glfwDestroyWindow(this->window); glfwTerminate(); } diff --git a/src/context_glfw.h b/src/context_glfw.h index 3d11775..1eb2991 100644 --- a/src/context_glfw.h +++ b/src/context_glfw.h @@ -12,20 +12,18 @@ namespace Mauri class ContextGLFW : public Context { public: - ContextGLFW(s32 width, s32 height, const std::string &name, bool wallpaper); + ContextGLFW() : Context() {} ~ContextGLFW(); - void close_window(); - void resize_window(s32 w, s32 h); + auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool; + auto resize_window(s32 width, s32 height) -> void; + auto should_close_window() const -> bool; + auto close_window() -> void; - bool should_close() const; - void process_input(); + auto process_input() -> void; + auto cursor_pos(f32 *x, f32 *y) -> void; - void cursor_pos(f32 *x, f32 *y); - - void swap_buffers(); - - f64 current_time(); + auto swap_buffers() -> void; private: GLFWwindow *window; diff --git a/src/context_null.h b/src/context_null.h index f95375e..bcae058 100644 --- a/src/context_null.h +++ b/src/context_null.h @@ -9,47 +9,45 @@ namespace Mauri class ContextNull : public Context { public: - ContextNull(s32 width, s32 height, const std::string &name, bool wallpaper) - : Context(width, height, name, wallpaper) - { - set_draw_enabled(false); - } - + ContextNull() : Context() { this->set_draw_enabled(false); } ~ContextNull() {} - void resize_window(s32 w, s32 h) + auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool { - width = w; - height = h; + return true; } - void close_window() + auto resize_window(s32 width, s32 height) -> void { - _should_close = true; + if (width != this->width || height != this->height) + { + this->width = width; + this->height = height; + } } - bool should_close() const + auto should_close_window() const -> bool { - return _should_close; + return this->should_close; } - void swap_buffers() {} + auto close_window() -> void + { + this->should_close = true; + } - void process_input() {} + auto process_input() -> void {} - void cursor_pos(f32 *x, f32 *y) + auto cursor_pos(f32 *x, f32 *y) -> void { *y = 0.f; *x = 0.f; } - f64 current_time() - { - return 0.f; - } + auto swap_buffers() -> void {} private: - bool _should_close = true; + bool should_close = true; }; } // namespace Mauri diff --git a/src/context_x11.cc b/src/context_x11.cc index a35a9f2..7975a4a 100644 --- a/src/context_x11.cc +++ b/src/context_x11.cc @@ -4,149 +4,150 @@ using namespace Mauri; static Atom ATOM_WM_DELETE_WINDOW, ATOM__NET_ACTIVE_WINDOW; -void ContextX11::disable_input() +auto ContextX11::disable_input() -> void { XWMHints wmHint; wmHint.flags = InputHint | StateHint; wmHint.input = false; wmHint.initial_state = NormalState; - XSetWMProperties(x11_d, window, NULL, NULL, NULL, 0, NULL, &wmHint, NULL); + XSetWMProperties(this->x11_d, this->window, NULL, NULL, NULL, 0, NULL, &wmHint, NULL); } -ContextX11::ContextX11(s32 width, s32 height, const std::string &name, bool wallpaper) - : Context(width, height, name, wallpaper) +auto ContextX11::create_window(s32 width, s32 height, const std::string &name, bool background) -> bool { - x11_d = XOpenDisplay(NULL); - - ATOM_WM_DELETE_WINDOW = XInternAtom(x11_d, "WM_DELETE_WINDOW", true); + this->x11_d = XOpenDisplay(NULL); - if (!x11_d) + ATOM_WM_DELETE_WINDOW = XInternAtom(this->x11_d, "WM_DELETE_WINDOW", true); + + if (!this->x11_d) { - log_error("%s", "could not open display"); - return; + error("could not open display"); + return false; } - - s32 screen = XDefaultScreen(x11_d); - Window root = RootWindow(x11_d, screen); + + s32 screen = XDefaultScreen(this->x11_d); + Window root = RootWindow(this->x11_d, screen); static s32 gl_attrs[] = { GLX_X_RENDERABLE, True, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, 8, + GLX_ALPHA_SIZE, 0, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_SAMPLES, 0, None }; GLint context_attrs[] = { - GLX_CONTEXT_MAJOR_VERSION_ARB, GL_VERSION_MAJOR, - GLX_CONTEXT_MINOR_VERSION_ARB, GL_VERSION_MINOR, + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, None }; - + // https://github.com/jarcode-foss/glava/blob/master/glava/glx_wcb.c#L383 s32 fb_sz, best = -1, samp = -1; + GLXFBConfig *fbc = glXChooseFBConfig(this->x11_d, screen, gl_attrs, &fb_sz); + if (!fbc) + { + error("no glX FB configs"); + return false; + } - GLXFBConfig *fbc = glXChooseFBConfig(x11_d, screen, gl_attrs, &fb_sz); - if (!fbc) - return; - - for (auto t = 0; t < fb_sz; ++t) + for (s32 t = 0; t < fb_sz; t++) { - XVisualInfo* xvi = glXGetVisualFromFBConfig(x11_d, fbc[t]); - if (xvi) + XVisualInfo *xvi = glXGetVisualFromFBConfig(this->x11_d, fbc[t]); + if (!xvi) continue; + + s32 samp_buf, samples; + glXGetFBConfigAttrib(this->x11_d, fbc[t], GLX_SAMPLE_BUFFERS, &samp_buf); + glXGetFBConfigAttrib(this->x11_d, fbc[t], GLX_SAMPLES, &samples); + + XRenderPictFormat *fmt = XRenderFindVisualFormat(this->x11_d, xvi->visual); + if (!fmt) continue; + + if (best < 0 || (samp_buf && samples > samp)) { - s32 samp_buf, samples; - glXGetFBConfigAttrib(x11_d, fbc[t], GLX_SAMPLE_BUFFERS, &samp_buf); - glXGetFBConfigAttrib(x11_d, fbc[t], GLX_SAMPLES, &samples); - XRenderPictFormat* fmt = XRenderFindVisualFormat(x11_d, xvi->visual); - - if (!fmt) continue; - - if (best < 0 || (samp_buf && samples > samp)) - { - best = t; - samp = samples; - } - XFree(xvi); + best = t; + samp = samples; } + + XFree(xvi); } - + if (best == -1) { - log_error("%s", "XRender could not find suitable format"); - return; + error("could not find suitable XRender format"); + return false; } GLXFBConfig config = fbc[best]; XFree(fbc); - XVisualInfo *vi = glXGetVisualFromFBConfig(x11_d, config); - Colormap cmap = XCreateColormap(x11_d, root, vi->visual, AllocNone); + XVisualInfo *vi = glXGetVisualFromFBConfig(this->x11_d, config); + Colormap cmap = XCreateColormap(this->x11_d, root, vi->visual, AllocNone); XSetWindowAttributes swa; swa.colormap = cmap; - swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | + swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask | PropertyChangeMask | VisibilityChangeMask; swa.background_pixmap = None; swa.border_pixel = 0; - - window = XCreateWindow(x11_d, root, 0, 0, width, height, 0, vi->depth, InputOutput, - vi->visual, CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel, &swa); + + this->window = XCreateWindow(this->x11_d, root, 0, 0, width, height, 0, vi->depth, + InputOutput, vi->visual, CWColormap | CWEventMask | CWBackPixmap | CWBorderPixel, &swa); XFree(vi); - //disable_input(); + //this->disable_input(); - XStoreName(x11_d, window, name.c_str()); + XStoreName(this->x11_d, this->window, name.c_str()); - XChangeProperty(x11_d, window, - XInternAtom(x11_d, "_NET_WM_NAME", False), - XInternAtom(x11_d, "UTF8_STRING", False), + XChangeProperty(this->x11_d, this->window, + XInternAtom(this->x11_d, "_NET_WM_NAME", False), + XInternAtom(this->x11_d, "UTF8_STRING", False), 8, PropModeReplace, (unsigned char *)name.c_str(), name.length()); XClassHint class_hint; class_hint.res_class = const_cast(name.c_str()); class_hint.res_name = const_cast(name.c_str()); - - XSetClassHint(x11_d, window, &class_hint); + + XSetClassHint(this->x11_d, this->window, &class_hint); glXCreateContextAttribsARBProc glXCreateContextAttribsARB = NULL; glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc) glXGetProcAddressARB((const GLubyte*) "glXCreateContextAttribsARB"); - - if (!glXCreateContextAttribsARB) - return; - - if (!(glc = glXCreateContextAttribsARB(x11_d, config, 0, True, context_attrs))) - return; - - glXMakeCurrent(x11_d, window, glc); + + if (!glXCreateContextAttribsARB) return false; + if (!(this->glc = glXCreateContextAttribsARB(this->x11_d, config, 0, True, context_attrs))) + { + return false; + } + + glXMakeCurrent(this->x11_d, this->window, this->glc); if (!gladLoadGL((GLADloadfunc)glXGetProcAddressARB)) { - log_error("%s", "failed to load glad"); - return; + error("could not load gl"); + return false; } - if (wallpaper) + if (background) { /* - Atom desktop = XInternAtom(x11_d, "_NET_WM_DESKTOP", false); - + Atom desktop = XInternAtom(this->x11_d, "_NET_WM_DESKTOP", false); unsigned long all_desktops = XWIN_ALL_DESKTOPS; - XChangeProperty(x11_d, window, desktop, - XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&all_desktops, 1); + XChangeProperty(this->x11_d, this->window, desktop, + XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&all_desktops, 1); */ - Atom desktop_type = XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", false); - - XChangeProperty(x11_d, window, XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE", false), + Atom net_wm_type = XInternAtom(this->x11_d, "_NET_WM_WINDOW_TYPE", false); + Atom desktop_type = XInternAtom(this->x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", false); + XChangeProperty(this->x11_d, this->window, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&desktop_type, 1); /* @@ -154,142 +155,148 @@ ContextX11::ContextX11(s32 width, s32 height, const std::string &name, bool wall event.xclient.type = ClientMessage; event.xclient.serial = 0; event.xclient.send_event = True; - event.xclient.display = x11_d; - event.xclient.window = window; - event.xclient.message_type = XInternAtom(x11_d, "_NET_WM_STATE", false); + event.xclient.display = this->x11_d; + event.xclient.window = this->window; + event.xclient.message_type = XInternAtom(this->x11_d, "_NET_WM_STATE", false); event.xclient.format = 32; event.xclient.data.l[0] = _NET_WM_STATE_ADD; - event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_BELOW", false); + event.xclient.data.l[1] = XInternAtom(this->x11_d, "_NET_WM_STATE_BELOW", false); event.xclient.data.l[2] = 0; //unused. event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; - XSendEvent(x11_d, root, false, + XSendEvent(this->x11_d, root, false, SubstructureRedirectMask|SubstructureNotifyMask, &event); - event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_STICKY", false); + event.xclient.data.l[1] = XInternAtom(this->x11_d, "_NET_WM_STATE_STICKY", false); XSendEvent(x11_d, root, false, SubstructureRedirectMask|SubstructureNotifyMask, &event); - event.xclient.data.l[1] = XInternAtom(x11_d, "_NET_WM_STATE_FULLSCREEN", false); + event.xclient.data.l[1] = XInternAtom(this->x11_d, "_NET_WM_STATE_FULLSCREEN", false); */ } - XSetWMProtocols(x11_d, window, &ATOM_WM_DELETE_WINDOW, 1); - - XMoveWindow(x11_d, window, 0, 0); - - XMapWindow(x11_d, window); - XFlush(x11_d); - + XSetWMProtocols(this->x11_d, this->window, &ATOM_WM_DELETE_WINDOW, 1); + + XMoveWindow(this->x11_d, this->window, 0, 0); + + XMapWindow(this->x11_d, this->window); + XFlush(this->x11_d); + glEnable(GL_BLEND); glEnable(GL_MULTISAMPLE); - - print_gl_info(); - - start = system_clock::now(); - set_draw_enabled(true); -} + this->print_gl_info(); -void ContextX11::resize_window(s32 w, s32 h) -{ - width = w; - height = h; + this->set_draw_enabled(true); + + return true; } -void ContextX11::swap_buffers() +auto ContextX11::resize_window(s32 width, s32 height) -> void { - glXSwapBuffers(x11_d, window); + if (width != this->width || height != this->height) + { + this->width = width; + this->height = height; + } } -void ContextX11::raise() +auto ContextX11::raise() -> void { XClientMessageEvent ev = { .type = ClientMessage, .serial = 0, .send_event = true, - .display = x11_d, - .window = window, + .display = this->x11_d, + .window = this->window, .message_type = ATOM__NET_ACTIVE_WINDOW, .format = 32, - .data = { .l = { 1, 0, (long)window } } + .data = {.l = {1, 0, (long)this->window}} }; - XSendEvent(x11_d, DefaultRootWindow(x11_d), false, StructureNotifyMask, (XEvent *)&ev); - XRaiseWindow(x11_d, window); - XFlush(x11_d); + XSendEvent(this->x11_d, DefaultRootWindow(this->x11_d), false, StructureNotifyMask, (XEvent *)&ev); + XRaiseWindow(this->x11_d, this->window); + XFlush(this->x11_d); } -void ContextX11::set_clickthrough() +auto ContextX11::set_clickthrough() -> void { - Window win = window, root = DefaultRootWindow(x11_d); + Window win = this->window, root = DefaultRootWindow(this->x11_d); while (win != None) { Region region; if ((region = XCreateRegion())) { - XShapeCombineRegion(x11_d, window, ShapeInput, 0, 0, region, ShapeSet); + XShapeCombineRegion(this->x11_d, this->window, ShapeInput, 0, 0, region, ShapeSet); XDestroyRegion(region); } Window parent, *children = NULL; u32 num_children; - if (XQueryTree(x11_d, win, &root, &parent, &children, &num_children)) + if (XQueryTree(this->x11_d, win, &root, &parent, &children, &num_children)) { - if (children) - XFree(children); + if (children) XFree(children); } win = (parent == root ? None : parent); } - XFlush(x11_d); + XFlush(this->x11_d); } -void ContextX11::close_window() +auto ContextX11::close_window() -> void { - this->_should_close = true; + this->should_close = true; } -void ContextX11::cursor_pos(float *x, float *y) +auto ContextX11::cursor_pos(float *x, float *y) -> void { u32 ret; s32 root_return_x, root_return_y; s32 win_return_x, win_return_y; Window root_return, child_return; - XQueryPointer(x11_d, window, &root_return, &child_return, - &root_return_x, &root_return_y, - &win_return_x, &win_return_y, &ret); + XQueryPointer(this->x11_d, this->window, &root_return, &child_return, + &root_return_x, &root_return_y, &win_return_x, &win_return_y, &ret); *x = (f32)win_return_x; *y = (f32)win_return_y; } -void ContextX11::process_input() +auto ContextX11::process_input() -> void { XEvent xev; - while (XPending(x11_d) > 0) + while (XPending(this->x11_d) > 0) { - XNextEvent(x11_d, &xev); + XNextEvent(this->x11_d, &xev); switch (xev.type) { case ClientMessage: if ((u64)xev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) - _should_close = true; + { + this->should_close = true; + } break; - case MapNotify: // make window not clickable + case MapNotify: //set_clickthrough(); - XFlush(x11_d); + XFlush(this->x11_d); break; } } } +auto ContextX11::swap_buffers() -> void +{ + glXSwapBuffers(this->x11_d, this->window); +} + ContextX11::~ContextX11() { - glXMakeCurrent(x11_d, None, NULL); - glXDestroyContext(x11_d, glc); - XDestroyWindow(x11_d, window); - XCloseDisplay(x11_d); + if (this->x11_d) + { + glXMakeCurrent(this->x11_d, None, NULL); + glXDestroyContext(this->x11_d, this->glc); + XDestroyWindow(this->x11_d, this->window); + XCloseDisplay(this->x11_d); + } } diff --git a/src/context_x11.h b/src/context_x11.h index 7e4a5a8..6bf898f 100644 --- a/src/context_x11.h +++ b/src/context_x11.h @@ -1,18 +1,15 @@ #ifndef _CONTEXT_X11_H #define _CONTEXT_X11_H -#include - #include #include #include #include -#include -#include - #include "context.h" +#include + #define XWIN_ALL_DESKTOPS 0xFFFFFFFF #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 @@ -24,51 +21,41 @@ typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const s32 *); -using std::chrono::time_point; -using std::chrono::system_clock; - -namespace Mauri { +namespace Mauri +{ -class ContextX11 : public Context { +class ContextX11 : public Context +{ public: - ContextX11(s32 width, s32 height, const std::string &name, bool wallpaper); + ContextX11() : Context() {} ~ContextX11(); - void close_window(); - - void resize_window(s32 w, s32 h); - - bool should_close() const + auto create_window(s32 width, s32 height, const std::string &name, bool background) -> bool; + auto resize_window(s32 width, s32 height) -> void; + auto should_close_window() const -> bool { - return _should_close; + return should_close; } + auto close_window() -> void; - void swap_buffers(); - void process_input(); + auto process_input() -> void; + auto cursor_pos(f32 *x, f32 *y) -> void; - void cursor_pos(f32 *x, f32 *y); - - f64 current_time() - { - return (system_clock::now() - start).count(); - } + auto swap_buffers() -> void; private: - bool _should_close = false; - - time_point start; - - void raise(); - - // Make the window shape 0x0 so that its unclickable. - void set_clickthrough(); - - // Attempt to disable input by setting window hints. - void disable_input(); - + bool should_close = false; + + auto raise() -> void; + + // Try to make the window shape 0x0 so that its unclickable. + auto set_clickthrough() -> void; + + // Try to disable input by setting window hints. + auto disable_input() -> void; + + Display *x11_d = nullptr; Window window; - Display *x11_d; - GLXContext glc; }; diff --git a/src/engine.cc b/src/engine.cc index d6e597b..641ac15 100644 --- a/src/engine.cc +++ b/src/engine.cc @@ -8,192 +8,181 @@ using namespace Mauri; -void View::center_scale_viewport() +auto View::center_scale_viewport() -> void { - Render::set_viewport(camera_pos[0], camera_pos[1], width / scale, height / scale); + Render::set_viewport(this->camera_pos[0], this->camera_pos[1], + this->ortho_width / this->scale, this->ortho_height / this->scale); } -void View::center_viewport(f32 w1, f32 h1) +auto View::center_viewport(f32 buffer_width, f32 buffer_height) -> void { - auto wpad = (width - w1) / 2.f; - auto hpad = (height - h1) / 2.f; - - Render::set_viewport(wpad, hpad, w1, h1); + f32 w_pad = (this->width - buffer_width) / -2.f; + f32 h_pad = (this->height - buffer_height) / -2.f; + Render::set_viewport(w_pad, h_pad, buffer_width, buffer_height); } -void View::default_viewport(f32 w0, f32 h0) +auto View::default_viewport(f32 buffer_width, f32 buffer_height) -> void { - Render::set_viewport(0, 0, w0, h0); + Render::set_viewport(0, 0, buffer_width, buffer_height); } -void View::center(f32 w1, f32 h1) +auto View::center(f32 buffer_width, f32 buffer_height) -> void { - auto w_scale = width / w1; - auto h_scale = height / h1; + f32 w_scale = this->width / buffer_width; + f32 h_scale = this->height / buffer_height; - scale = (w_scale <= h_scale) ? w_scale : h_scale; + this->scale = (w_scale <= h_scale) ? w_scale : h_scale; - auto scaled_width = width / scale; - auto scaled_height = height / scale; + f32 scaled_width = this->width / this->scale; + f32 scaled_height = this->height / this->scale; - camera_pos[0] = (scaled_width - w1) / -2.f; - camera_pos[1] = (scaled_height - h1) / -2.f; + //this->camera_pos[0] = ((scaled_width - buffer_width) / -2.f) + ((scaled_width - buffer_width) / 2.f); + //this->camera_pos[1] = ((scaled_height - buffer_height) / -2.f) + ((scaled_height - buffer_height) / 2.f); + this->camera_pos[0] = ((scaled_width - buffer_width) / -2.f); + this->camera_pos[1] = ((scaled_height - buffer_height) / -2.f); } -Engine::Engine(Scene *scene, Parser *parser, bool audio) - : scene(scene), parser(parser), audio_enabled(audio) +Engine::Engine(Scene *scene, f32 rate, bool audio) + : scene(scene), rate(rate), audio_enabled(audio) { - view.width = scene->width; - view.height = scene->height; + /* + for (Object *object : scene->objects) + { + if (object->size[0] > this->view.width) this->view.width = object->size[0]; + if (object->size[1] > this->view.height) this->view.height = object->size[1]; + } + */ + //std::cout << this->view.width << " " << this->view.height << "\n"; - view.texel_size[0] = 1.f / view.width; - view.texel_size[1] = 1.f / view.height; + this->view.width = scene->width; + this->view.height = scene->height; + this->view.ortho_width = scene->width; + this->view.ortho_height = scene->height; - view.texel_half_size[0] = .5f / view.width; - view.texel_half_size[1] = .5f / view.height; + this->view.texel_size[0] = 1.f / this->view.width; + this->view.texel_size[1] = 1.f / this->view.height; - view.center(context()->width, context()->height); + this->view.texel_half_size[0] = .5f / this->view.width; + this->view.texel_half_size[1] = .5f / this->view.height; + + this->view.center(context()->width, context()->height); #ifdef BUILD_AUDIO - if (audio_enabled) + if (this->audio_enabled) { - visualizer = new Visualizer(); + this->vis = new Visualizer(); + if (!this->vis->run()) + { + this->audio_enabled = false; + } } #else - audio_enabled = false; + this->audio_enabled = false; #endif - //rate = 1.f; - rate = 0.55f; - - minimal_shader = new Shader(parser, "minimal"); - minimal_shader->compile(); + this->shader = get_shader("hdr_downsample"); + this->render_shader = this->shader->compile({}); - create_framebuffer(COMBINE_BUFFER, view.width, view.height, 1.f); - combine_buffer = get_framebuffer(COMBINE_BUFFER); + this->create_framebuffer(COMBINE_BUFFER, this->view.width, this->view.height, 1.f); + this->combine_buffer = this->get_framebuffer(COMBINE_BUFFER); - default_object = new RenderObject(DEFAULT); + this->default_mat = glm::ortho(-1.f, 1.f, -1.f, 1.f, -3.f, 1.f); + this->default_mat = glm::scale(this->default_mat, vec3{1.f, 1.f, 0.001f}); + this->default_mat_flipped = glm::scale(this->default_mat, vec3{1.f, -1.f, 1.f}); - default_model = mat4x4(1.f); - default_model_flipped = glm::scale(default_model, vec3{1.f, -1.f, 1.f}); - - scene->load(this); + this->default_object = new RenderObject(DEFAULT); + this->default_object_flipped = new RenderObject(DEFAULT_FLIPPED); + this->fullscreen_object = new RenderObject(FULLSCREEN); } -Framebuffer *Engine::get_framebuffer(const std::string &name) +auto Engine::create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale) -> void { - for (auto &buffer : framebuffers) - { - if (buffer->name == name) - { - return buffer; - } - } - - return nullptr; + this->framebuffers.emplace_back(new Framebuffer(name, width, height, scale)); } -Texture *Engine::get_framebuffer_texture(const std::string &name) +auto Engine::get_framebuffer(const std::string &name) -> Framebuffer * { - auto buffer = get_framebuffer(name); - - if (buffer != nullptr) - return buffer->texture; - + // Most likely wrong... + if (name == "_rt_MipMappedFrameBuffer") + { + return this->combine_buffer; + } + for (Framebuffer *buffer : this->framebuffers) + { + if (buffer->name == name) return buffer; + } return nullptr; } -void Engine::create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale) +auto Engine::get_framebuffer_texture(const std::string &name) -> Texture * { - framebuffers.emplace_back(new Framebuffer(name, width, height, scale)); + Framebuffer *buffer = this->get_framebuffer(name); + return ((buffer) ? buffer->texture : nullptr); } -void Engine::update() +auto Engine::update() -> void { context()->process_input(); - context()->cursor_pos(&view.cursor_pos[0], &view.cursor_pos[1]); - - view.cursor_pos[0] *= view.scale; - view.cursor_pos[1] *= view.scale; + context()->cursor_pos(&this->view.cursor_pos[0], &this->view.cursor_pos[1]); - view.cursor_pos[0] -= view.camera_pos[0]; - view.cursor_pos[1] -= view.camera_pos[1]; + this->view.cursor_pos[0] -= this->view.camera_pos[0]; + this->view.cursor_pos[1] -= this->view.camera_pos[1]; - view.cursor_pos[0] /= view.width; - view.cursor_pos[1] /= view.height; + this->view.cursor_pos[0] *= this->view.scale; + this->view.cursor_pos[1] *= this->view.scale; - view.cursor_pos[0] = (view.cursor_pos[0] / 2.f) + 0.25f; - view.cursor_pos[1] = ((1 - view.cursor_pos[1]) / 2.f) + 0.25f; - - for (auto &object: scene->objects) - { - object->update(this); - } + this->view.cursor_pos[1] = this->view.height - this->view.cursor_pos[1]; } -void Engine::draw() +auto Engine::draw() -> void { - for (auto& buffer : framebuffers) - { - buffer->buffer->clear(vec4{0.f, 0.f, 0.f, 1.f}); - } - - if (scene->clear_enabled) + if (this->scene->clear_enabled) { - combine_buffer->buffer->clear(scene->clear_color); + this->combine_buffer->buffer->clear(this->scene->clear_color); } - scene->draw(this); + this->scene->draw(this); Render::bind_framebuffer(0); + this->shader->bind(this->render_shader, this, nullptr, &this->default_mat, nullptr); - default_object->bind(); - - minimal_shader->bind(this, nullptr, &default_model_flipped, nullptr); - - combine_buffer->texture->bind(0); - - Render::blend_func(GL_ONE, GL_ZERO); - Render::color_mask(1.f, 1.f, 1.f, 1.f); + this->combine_buffer->texture->bind(0); - view.center_scale_viewport(); + this->view.center_scale_viewport(); - default_object->draw(); + this->default_object_flipped->bind(); + this->default_object_flipped->draw(); } -void Engine::run() +auto Engine::run() -> void { - const f32 TARGET_FPS = 90.f; - - time = context()->current_time(); - - while (!context()->should_close()) + const f32 TARGET_FPS = 60.f; + while (!context()->should_close_window()) { - update(); - draw(); + this->time = context()->current_time(); + this->update(); + this->draw(); context()->swap_buffers(); - - while (context()->current_time() < time + 1.f / TARGET_FPS) + while (context()->current_time() < this->time + 1.0f / TARGET_FPS) { - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + std::this_thread::sleep_for(std::chrono::microseconds(500)); } - - time = context()->current_time(); } } Engine::~Engine() { - for (auto &buffer : framebuffers) + for (Framebuffer *buffer : this->framebuffers) { delete buffer; } - delete default_object; - delete minimal_shader; + delete this->shader; + delete this->default_object; + delete this->fullscreen_object; #ifdef BUILD_AUDIO - delete visualizer; + delete this->vis; #endif } diff --git a/src/engine.h b/src/engine.h index 95b92ab..a1a063e 100644 --- a/src/engine.h +++ b/src/engine.h @@ -1,11 +1,11 @@ #ifndef _ENGINE_H #define _ENGINE_H -#include "framebuffer.h" #include "shader.h" +#include "framebuffer.h" #ifdef BUILD_AUDIO -#include "visualizer.h" +#include "vis.h" #endif static const std::string COMBINE_BUFFER = "_rt_FullFrameBuffer"; @@ -13,13 +13,13 @@ static const std::string COMBINE_BUFFER = "_rt_FullFrameBuffer"; namespace Mauri { -class Scene; -class Object; - struct View { - f32 width; - f32 height; + f32 width = 0.f; + f32 height = 0.f; + + s32 ortho_width; + s32 ortho_height; vec2 texel_size; vec2 texel_half_size; @@ -29,49 +29,57 @@ struct View vec2 camera_pos; vec2 cursor_pos; - void center(f32 w1, f32 h1); + f32 adjusted_width; + f32 adjusted_height; - void center_scale_viewport(); - void center_viewport(f32 w0, f32 h0); - void default_viewport(f32 w0, f32 h0); + auto center(f32 buffer_width, f32 buffer_height) -> void; + + auto center_scale_viewport() -> void; + auto center_viewport(f32 buffer_width, f32 buffer_height) -> void; + auto default_viewport(f32 buffer_width, f32 buffer_height) -> void; }; +class Scene; + class Engine { public: - Engine(Scene *scene, Parser *parser, bool audio); + Engine(Scene *scene, f32 rate, bool audio); ~Engine(); - View view; Scene *scene; - Parser *parser; + + f32 rate; + f32 time; + + bool audio_enabled; #ifdef BUILD_AUDIO - Visualizer *visualizer; + Visualizer *vis; #endif - bool audio_enabled; + View view; - f32 time; - f32 rate; + mat4x4 default_mat; + mat4x4 default_mat_flipped; RenderObject *default_object; + RenderObject *default_object_flipped; + RenderObject *fullscreen_object; - mat4x4 default_model; - mat4x4 default_model_flipped; - - Shader *minimal_shader; + Shader *shader; + RenderShader *render_shader; Framebuffer *combine_buffer; std::vector framebuffers; - void create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale); + auto create_framebuffer(const std::string &name, f32 width, f32 height, f32 scale) -> void; - Framebuffer *get_framebuffer(const std::string &name); - Texture *get_framebuffer_texture(const std::string &name); + auto get_framebuffer(const std::string &name) -> Framebuffer *; + auto get_framebuffer_texture(const std::string &name) -> Texture *; - void run(); - void draw(); - void update(); + auto update() -> void; + auto draw() -> void; + auto run() -> void; }; } // namespace Mauri diff --git a/src/framebuffer.cc b/src/framebuffer.cc index 6f65ab0..210a052 100644 --- a/src/framebuffer.cc +++ b/src/framebuffer.cc @@ -2,28 +2,15 @@ using namespace Mauri; -Framebuffer::Framebuffer(std::string name, f32 width, f32 height, f32 scale) +Framebuffer::Framebuffer(std::string name, s32 width, s32 height, f32 scale) : name(name) { - buffer = new RenderFramebuffer(width, height, scale); - - texture = new Texture(); - - texture->wrapped = true; - - texture->texture = buffer->texture; - - texture->texture_resolution[0] = buffer->width; - texture->texture_resolution[1] = buffer->height; - texture->texture_resolution[2] = buffer->width; - texture->texture_resolution[3] = buffer->height; + this->buffer = new RenderFramebuffer(width, height, scale); + this->texture = new Texture(this->buffer); } Framebuffer::~Framebuffer() { - if (texture != nullptr) - delete texture; - - if (buffer != nullptr) - delete buffer; + if (this->buffer) delete this->buffer; + if (this->texture) delete this->texture; } diff --git a/src/framebuffer.h b/src/framebuffer.h index 9b373fa..734715a 100644 --- a/src/framebuffer.h +++ b/src/framebuffer.h @@ -1,7 +1,6 @@ #ifndef _FRAMEBUFFER_H #define _FRAMEBUFFER_H -#include "gl.h" #include "texture.h" namespace Mauri @@ -10,17 +9,15 @@ namespace Mauri class Framebuffer { public: - Framebuffer(std::string name, f32 width, f32 height, f32 scale); - + Framebuffer(std::string name, s32 width, s32 height, f32 scale); ~Framebuffer(); std::string name; - Texture *texture; - RenderFramebuffer *buffer; + Texture *texture; }; -} +} // namespace Mauri #endif // _FRAMEBUFFER_H diff --git a/src/gl.cc b/src/gl.cc index c45d641..66690ba 100644 --- a/src/gl.cc +++ b/src/gl.cc @@ -1,267 +1,307 @@ #include "log.h" -#include "texture.h" #include "context.h" +#include "texture.h" #include "gl.h" -using namespace Mauri; +#include "objects/object.h" -static u32 pack_rgba(byte r, byte g, byte b, byte a) +namespace Mauri { - return (a << 24 | b << 16 | g << 8 | r); -} -static u32 pack_rg(byte r, byte g) +auto uniform_type_to_string(UniformType type) -> std::string { - return (1 << 24 | 0 << 16 | g << 8 | r); + switch (type) + { + case TYPE_FLOAT: + return "float"; + case TYPE_VEC4: + return "vec4"; + case TYPE_VEC3: + return "vec3"; + case TYPE_VEC2: + return "vec2"; + case TYPE_MAT4: + return "mat4"; + case TYPE_MAT3: + return "mat3"; + case TYPE_SAMPLER2D: + return "sampler2D"; + case TYPE_INVALID: + default: + return "INVALID"; + } } -static u32 pack_red(byte r) +auto uniform_type_from_string(const std::string_view &s) -> UniformType { - return (1 << 24 | 0 << 16 | 0 << 8 | r); + if (s.compare(0, 5, "float") == 0) return TYPE_FLOAT; + else if (s.compare(0, 4, "vec4") == 0) return TYPE_VEC4; + else if (s.compare(0, 4, "vec3") == 0) return TYPE_VEC3; + else if (s.compare(0, 4, "vec2") == 0) return TYPE_VEC2; + else if (s.compare(0, 4, "mat4") == 0) return TYPE_MAT4; + else if (s.compare(0, 4, "mat3") == 0) return TYPE_MAT3; + else if (s.compare(0, 9, "sampler2D") == 0) return TYPE_SAMPLER2D; + return TYPE_INVALID; } -void RenderObject::set_data(f32 *vertices, s32 vertex_count) -{ - if (!context()->draw_enabled()) - return; - - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); +} // namespace Mauri - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)12); - - glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); -} +using namespace Mauri; -void RenderObject::bind() +RenderObject::RenderObject(ObjectType type, Object *object, Texture *texture) { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - glBindVertexArray(vao); - glBindBuffer(GL_ARRAY_BUFFER, vbo); -} + f32 width = (object) ? object->size[0] : 0.f; + f32 height = (object) ? object->size[1] : 0.f; -void RenderObject::draw() -{ - if (!context()->draw_enabled()) - return; + f32 x_scale = 1.f; + f32 y_scale = 1.f; - glDrawArrays(GL_TRIANGLES, 0, 6); -} - -RenderObject::RenderObject(RenderObjType type, f32 width, f32 height) -{ - if (!context()->draw_enabled()) - return; + if (texture && !texture->is_gif() && !(type == COMBINE && object->effects.size() != 0)) + { + x_scale = (*texture->texture_resolution)[2] / (*texture->texture_resolution)[0]; + y_scale = (*texture->texture_resolution)[3] / (*texture->texture_resolution)[1]; + } switch (type) { - case RenderObjType::DEFAULT: { + case DEFAULT: + { f32 vertices[] = { + -1.f, -1.f, 0.f, 0.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, 1.f, -1.f, 0.f, 1.f, 0.f, - -1.f, -1.f, 0.f, 0.f, 0.f, + 1.f, -1.f, 0.f, 1.f, 0.f, -1.f, 1.f, 0.f, 0.f, 1.f, - 1.f, 1.f, 0.f, 1.f, 1.f, - 1.f, -1.f, 0.f, 1.f, 0.f + 1.f, 1.f, 0.f, 1.f, 1.f }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; break; } - case RenderObjType::UV_SCALE: { - // Adjust UV coords here so that on the pass where this object is loadn - // the UV cuts out the padding used to make the texture dimensions powers of two. - f32 x_scale = width / (f32)next_power_of_two(width); - f32 y_scale = height / (f32)next_power_of_two(height); + case DEFAULT_FLIPPED: + { f32 vertices[] = { - -width, height, 0.f, 0.f, y_scale, - width, -height, 0.f, x_scale, 0.f, - -width, -height, 0.f, 0.f, 0.f, - -width, height, 0.f, 0.f, y_scale, - width, height, 0.f, x_scale, y_scale, - width, -height, 0.f, x_scale, 0.f + -1.f, 1.f, 0.f, 0.f, 0.f, + -1.f, -1.f, 0.f, 0.f, 1.f, + 1.f, 1.f, 0.f, 1.f, 0.f, + 1.f, 1.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 0.f, 1.f, + 1.f, -1.f, 0.f, 1.f, 1.f }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; break; } - case RenderObjType::OBJECT: { + case FULLSCREEN: + { f32 vertices[] = { - -width, -height, 0.f, 0.f, 1.f, - width, height, 0.f, 1.f, 0.f, - -width, height, 0.f, 0.f, 0.f, - -width, -height, 0.f, 0.f, 1.f, - width, -height, 0.f, 1.f, 1.f, - width, height, 0.f, 1.f, 0.f + -1.f, -1.f, 0.f, 0.f, 0.f, + -1.f, 3.f, 0.f, 0.f, 2.f, + 3.f, -1.f, 0.f, 2.f, 0.f }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 3; break; } - case RenderObjType::OBJECT_FLIPPED: { + case MODEL: + { f32 vertices[] = { - -width, height, 0.f, 0.f, 1.f, - width, -height, 0.f, 1.f, 0.f, - -width, -height, 0.f, 0.f, 0.f, - -width, height, 0.f, 0.f, 1.f, - width, height, 0.f, 1.f, 1.f, - width, -height, 0.f, 1.f, 0.f + 0.f, height, 0.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 0.f, y_scale, + width, height, 0.f, x_scale, 0.f, + width, height, 0.f, x_scale, 0.f, + 0.f, 0.f, 0.f, 0.f, y_scale, + width, 0.f, 0.f, x_scale, y_scale }; - set_data(vertices, sizeof(vertices)); + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; + break; + } + case COMBINE: + { + f32 half_width = width / 2.f; + f32 half_height = height / 2.f; + f32 vertices[] = { + -half_width, half_height, 0.f, 0.f, 0.f, + -half_width, -half_height, 0.f, 0.f, y_scale, + half_width, half_height, 0.f, x_scale, 0.f, + half_width, half_height, 0.f, x_scale, 0.f, + -half_width, -half_height, 0.f, 0.f, y_scale, + half_width, -half_height, 0.f, x_scale, y_scale + }; + this->set_data(vertices, sizeof(vertices)); + this->vertex_count = 6; break; } } } -RenderObject::~RenderObject() +auto RenderObject::set_data(f32 *vertices, s32 vertex_count) -> void { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - glDeleteVertexArrays(1, &vao); - glDeleteBuffers(1, &vbo); -} + glGenVertexArrays(1, &this->vao); + glBindVertexArray(this->vao); -bool RenderShader::compile(u32 program, const char *source, char *error) -{ - if (!context()->draw_enabled()) - return true; + glGenBuffers(1, &this->vbo); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); - glShaderSource(program, 1, &source, 0); - glCompileShader(program); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); - s32 status; + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(f32), (void *)12); - glGetShaderiv(program, GL_COMPILE_STATUS, &status); + glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); +} - if (status == GL_FALSE) - { - glGetShaderInfoLog(program, 256, NULL, error); - return false; - } +auto RenderObject::bind() -> void +{ + if (!context()->is_draw_enabled()) return; + glBindVertexArray(this->vao); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); +} - return true; +auto RenderObject::draw() -> void +{ + if (!context()->is_draw_enabled()) return; + glDrawArrays(GL_TRIANGLES, 0, this->vertex_count); +} + +RenderObject::~RenderObject() +{ + if (!context()->is_draw_enabled()) return; + glDeleteVertexArrays(1, &this->vao); + glDeleteBuffers(1, &this->vbo); } RenderShader::RenderShader(const std::string &vs_source, const std::string &fs_source) { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - uniform_locations.clear(); - program = glCreateProgram(); + this->program = glCreateProgram(); char error[256]; u32 vs_shader = glCreateShader(GL_VERTEX_SHADER); if (!compile(vs_shader, vs_source.c_str(), error)) { - std::cout << vs_source << "\n"; - log_error("failed to compile vertex shader (%s)", error); + error("failed to compile vertex shader: (%s)", error); + debug("\n%s", vs_source.c_str()); return; } u32 fs_shader = glCreateShader(GL_FRAGMENT_SHADER); if (!compile(fs_shader, fs_source.c_str(), error)) { - log_error("failed to compile fragment shader (%s)", error); + error("failed to compile fragment shader: (%s)", error); + debug("\n%s", fs_source.c_str()); return; } - glAttachShader(program, vs_shader); - glAttachShader(program, fs_shader); + glAttachShader(this->program, vs_shader); + glAttachShader(this->program, fs_shader); - glLinkProgram(program); + glLinkProgram(this->program); } -void RenderShader::bind() +auto RenderShader::compile(u32 shader, const char *source, char *error) -> bool { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return true; - glUseProgram(program); -} + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); -RenderShader::~RenderShader() -{ - if (!context()->draw_enabled()) - return; + s32 status; + + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - if (program != 0) - glDeleteShader(program); + if (status == GL_FALSE) + { + glGetShaderInfoLog(shader, 256, NULL, error); + return false; + } + + return true; } -s32 RenderShader::uniform_loc(const std::string &name) +auto RenderShader::bind() -> void { - if (!context()->draw_enabled()) - return 0; + if (!context()->is_draw_enabled()) return; + glUseProgram(this->program); +} - auto loc = uniform_locations.find(name); +auto RenderShader::get_uniform_location(const std::string &name) -> s32 +{ + if (!context()->is_draw_enabled()) return 0; - if (loc == uniform_locations.end()) + auto loc = this->uniform_locations.find(name); + if (loc == this->uniform_locations.end()) { - uniform_locations[name] = glGetUniformLocation(program, name.c_str()); - return uniform_locations[name]; + this->uniform_locations[name] = glGetUniformLocation(this->program, name.c_str()); + return this->uniform_locations[name]; } else + { return loc->second; + } } -void RenderShader::set_uniform(const std::string &name, UniformType type, f32 *value) +auto RenderShader::set_uniform(const std::string &name, UniformType type, void *value) -> void { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; + + s32 loc = this->get_uniform_location(name); + if (loc == -1) return; - auto loc = uniform_loc(name); +#if UNIFORM_DEBUG + std::cout << name << "(" << type << ")" << " " + << ((f32 *)value)[0] << " " << ((f32 *)value)[1] << " " + << ((f32 *)value)[2] << " " << ((f32 *)value)[3] << "\n"; +#endif switch (type) { case TYPE_FLOAT: - glUniform1f(loc, *value); + glUniform1f(loc, *((f32 *)value)); break; case TYPE_SAMPLER2D: - glUniform1i(loc, (s32)*value); + glUniform1i(loc, (u32)(*((f32 *)value))); break; case TYPE_VEC2: - glUniform2fv(loc, 1, value); + glUniform2fv(loc, 1, (f32 *)value); break; case TYPE_VEC3: - glUniform3fv(loc, 1, value); + glUniform3fv(loc, 1, (f32 *)value); break; case TYPE_VEC4: - glUniform4fv(loc, 1, value); + glUniform4fv(loc, 1, (f32 *)value); break; case TYPE_MAT4: - glUniformMatrix4fv(loc, 1, GL_FALSE, value); + glUniformMatrix4fv(loc, 1, GL_FALSE, (f32 *)value); + break; + case TYPE_MAT3: + glUniformMatrix3fv(loc, 1, GL_FALSE, (f32 *)value); break; case TYPE_INVALID: break; } } -void RenderTexture::bind(s32 index) +RenderShader::~RenderShader() { - if (!context()->draw_enabled()) - return; - - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, id); + if (!context()->is_draw_enabled()) return; + if (this->program != 0) glDeleteShader(this->program); } -RenderTexture::RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 mm_count) +RenderTexture::RenderTexture(bool no_interp, bool clamp, bool filtering, s32 mm_count) { - if (!context()->draw_enabled()) - return; - - glGenTextures(1, &id); + if (!context()->is_draw_enabled()) return; - glBindTexture(GL_TEXTURE_2D, id); + glGenTextures(1, &this->id); + glBindTexture(GL_TEXTURE_2D, this->id); if (filtering) { @@ -273,25 +313,25 @@ RenderTexture::RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 if (no_interp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (clamp_uv) + if (clamp) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT); } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); @@ -300,168 +340,171 @@ RenderTexture::RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 glBindTexture(GL_TEXTURE_2D, 0); } -void RenderTexture::upload(byte *data, s32 width, s32 height, s32 level, bool dxt, bool scale) +auto RenderTexture::upload(byte *data, s32 width, s32 height, s32 level, bool dxt) -> void { - // Save pixels in RGBA format as a simple utility. - // NOTE: This is pretty wasteful on memory. - if (data != nullptr && level == 0) + if (asset_manager()->prepare_output && data && level == 0) { - pixels.resize(width * height); - - switch (format) - { - case GL_RGBA: { - byte *dp = data; - for (auto i = 0; i < width * height; i++) - { - pixels[i] = pack_rgba(*dp, *(dp + 1), *(dp + 2), *(dp + 3)); - dp += 4; - } - break; - } - case GL_RG: { - byte *dp = data; - for (auto i = 0; i < width * height; i++) - { - pixels[i] = pack_rg(*dp, *(dp + 1)); - dp += 2; - } - break; - } - case GL_RED: { - byte *dp = data; - for (auto i = 0; i < width * height; i++) - { - pixels[i] = pack_red(*dp); - dp += 1; - } - break; - } - } + this->save_pixels(data, width, height); } - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - bind(0); + this->bind(0); - s32 iformat = GL_UNSIGNED_BYTE; + s32 iformat = (dxt) ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_BYTE; - if (dxt) - { - iformat = GL_UNSIGNED_INT_8_8_8_8; - } + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, + format, iformat, data); +} - if (scale) - { - s32 scaled_width = next_power_of_two(width); - s32 scaled_height = next_power_of_two(height); +auto RenderTexture::bind(s32 index) -> void +{ + if (!context()->is_draw_enabled()) return; + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, this->id); +} - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, scaled_width, scaled_height, 0, - format, iformat, nullptr); +static auto pack_rgba(byte r, byte g, byte b, byte a) -> u32 +{ + return (a << 24 | b << 16 | g << 8 | r); +} + +static auto pack_rgb(byte r, byte g, byte b) -> u32 +{ + return (255 << 24 | b << 16 | g << 8 | r); +} + +static auto pack_rg(byte r, byte g) -> u32 +{ + return (255 << 24 | 0 << 16 | g << 8 | r); +} + +static auto pack_red(byte r) -> u32 +{ + return (255 << 24 | 0 << 16 | 0 << 8 | r); +} - glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, - format, iformat, data); +auto RenderTexture::save_pixels(byte *data, s32 width, s32 height) -> void +{ + this->pixels.resize(width * height); + byte *dp = data; + switch (format) + { + case GL_RGBA: + { + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_rgba(*dp, *(dp + 1), *(dp + 2), *(dp + 3)); + dp += 4; + } + break; } - else + case GL_RGB: { - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, - format, iformat, data); + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_rgb(*dp, *(dp + 1), *(dp + 2)); + dp += 3; + } + break; + } + case GL_RG: + { + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_rg(*dp, *(dp + 1)); + dp += 2; + } + break; + } + case GL_RED: + { + for (s32 i = 0; i < width * height; i++) + { + this->pixels[i] = pack_red(*dp); + dp += 1; + } + break; + } + default: + break; } } RenderTexture::~RenderTexture() { - if (!context()->draw_enabled()) - return; - - glDeleteTextures(1, &id); + if (!context()->is_draw_enabled()) return; + glDeleteTextures(1, &this->id); } -RenderFramebuffer::RenderFramebuffer(f32 width0, f32 height0, f32 scale) - : width(width0 / scale), height(height0 / scale) +RenderFramebuffer::RenderFramebuffer(s32 width, s32 height, f32 scale) + : width(width / scale), height(height / scale) { - if (!context()->draw_enabled()) - return; + if (!context()->is_draw_enabled()) return; - texture = new RenderTexture(false, true, false, 1); + this->texture = new RenderTexture(false, true, false, 1); - texture->format = GL_RGBA; - texture->upload(nullptr, width, height, 0, false, false); + this->texture->format = GL_RGBA; + this->texture->upload(nullptr, this->width, this->height, 0); - glGenFramebuffers(1, &id); - glBindFramebuffer(GL_FRAMEBUFFER, id); + glGenFramebuffers(1, &this->id); + glBindFramebuffer(GL_FRAMEBUFFER, this->id); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->id, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->texture->id, 0); GLenum attatchment[] = {GL_COLOR_ATTACHMENT0}; glDrawBuffers(1, attatchment); } -void RenderFramebuffer::bind() +auto RenderFramebuffer::bind() -> void { - if (!context()->draw_enabled()) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, id); + if (!context()->is_draw_enabled()) return; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->id); } -void RenderFramebuffer::blit(u32 dest, f32 w0, f32 h0) +auto RenderFramebuffer::blit(u32 dest, s32 width, s32 height) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dest); - glBindFramebuffer(GL_READ_FRAMEBUFFER, id); - glBlitFramebuffer(0, h0, w0, 0, 0, 0, w0, h0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glBindFramebuffer(GL_READ_FRAMEBUFFER, this->id); + glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); } -void RenderFramebuffer::clear(vec4 color) +auto RenderFramebuffer::clear(vec4 color) -> void { - if (!context()->draw_enabled()) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, id); + if (!context()->is_draw_enabled()) return; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, this->id); glClearBufferfv(GL_COLOR, 0, glm::value_ptr(color)); } RenderFramebuffer::~RenderFramebuffer() { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; delete texture; - glDeleteFramebuffers(1, &id); + glDeleteFramebuffers(1, &this->id); } -void Render::blend_func(s32 sfactor, s32 dfactor) +auto Render::blend_func(s32 sfactor, s32 dfactor) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glBlendFunc(sfactor, dfactor); } -void Render::color_mask(f32 r, f32 g, f32 b, f32 a) +auto Render::color_mask(f32 r, f32 g, f32 b, f32 a) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glColorMask(r, g, b, a); } -void Render::bind_framebuffer(s32 id) +auto Render::bind_framebuffer(s32 id) -> void { - if (!context()->draw_enabled()) - return; - - glBindFramebuffer(GL_FRAMEBUFFER, id); + if (!context()->is_draw_enabled()) return; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id); } -void Render::set_viewport(u32 wpad, u32 hpad, u32 width, u32 height) +auto Render::set_viewport(u32 wpad, u32 hpad, u32 width, u32 height) -> void { - if (!context()->draw_enabled()) - return; - + if (!context()->is_draw_enabled()) return; glViewport(wpad, hpad, width, height); } diff --git a/src/gl.h b/src/gl.h index 3ee6ab6..9da72e7 100644 --- a/src/gl.h +++ b/src/gl.h @@ -8,38 +8,48 @@ namespace Mauri { -enum RenderObjType +enum ObjectType { DEFAULT, - UV_SCALE, - OBJECT, - OBJECT_FLIPPED + DEFAULT_FLIPPED, + FULLSCREEN, + MODEL, + COMBINE }; enum UniformType { + TYPE_INVALID, TYPE_FLOAT, TYPE_VEC4, TYPE_VEC3, TYPE_VEC2, TYPE_MAT4, - TYPE_SAMPLER2D, - TYPE_INVALID + TYPE_MAT3, + TYPE_SAMPLER2D }; +extern auto uniform_type_to_string(UniformType type) -> std::string; +extern auto uniform_type_from_string(const std::string_view &s) -> UniformType; + +class Object; +class Texture; + class RenderObject { public: - RenderObject(RenderObjType type, f32 width = 0.f, f32 height = 0.f); + RenderObject(ObjectType type, Object *object = nullptr, Texture *texture = nullptr); ~RenderObject(); - void bind(); - void draw(); - void set_data(f32 *vertices, s32 vertex_count); + auto set_data(f32 *vertices, s32 vertex_count) -> void; + + auto bind() -> void; + auto draw() -> void; private: u32 vao; u32 vbo; + u32 vertex_count; }; class RenderShader @@ -48,43 +58,43 @@ class RenderShader RenderShader(const std::string &vs_source, const std::string &fs_source); ~RenderShader(); - bool compile(u32 program, const char *source, char *error); + auto compile(u32 shader, const char *source, char *error) -> bool; + auto bind() -> void; - void set_uniform(const std::string &name, UniformType type, f32 *value); - s32 uniform_loc(const std::string &name); - - void bind(); + auto get_uniform_location(const std::string &name) -> s32; + auto set_uniform(const std::string &name, UniformType type, void *value) -> void; private: u32 program = 0; - std::unordered_map uniform_locations; }; class RenderTexture { public: - RenderTexture(bool no_interp, bool clamp_uv, bool filtering, s32 mm_count); + RenderTexture(bool no_interp, bool clamp, bool filtering, s32 mm_count); ~RenderTexture(); u32 id = 0; - f32 width; - f32 height; + s32 width; + s32 height; s32 format; std::vector pixels; - void bind(s32 index); - void upload(byte *data, s32 width, s32 height, s32 level, - bool dxt = false, bool scale = true); + auto upload(byte *data, s32 width, s32 height, s32 level, bool dxt = false) -> void; + auto bind(s32 index) -> void; + + private: + auto save_pixels(byte *data, s32 width, s32 height) -> void; }; class RenderFramebuffer { public: - RenderFramebuffer(f32 width, f32 height, f32 scale); + RenderFramebuffer(s32 width, s32 height, f32 scale); ~RenderFramebuffer(); u32 id = 0; @@ -94,18 +104,18 @@ class RenderFramebuffer RenderTexture *texture = nullptr; - void bind(); - void blit(u32 dest, f32 w0, f32 h0); - void clear(vec4 color); + auto bind() -> void; + auto blit(u32 dest, s32 width, s32 height) -> void; + auto clear(vec4 color) -> void; }; class Render { public: - static void blend_func(s32 sfactor, s32 dfactor); - static void color_mask(f32 r, f32 g, f32 b, f32 a); - static void bind_framebuffer(s32 id); - static void set_viewport(u32 wpad, u32 hpad, u32 width, u32 height); + static auto blend_func(s32 sfactor, s32 dfactor) -> void; + static auto color_mask(f32 r, f32 g, f32 b, f32 a) -> void; + static auto bind_framebuffer(s32 id) -> void; + static auto set_viewport(u32 wpad, u32 hpad, u32 width, u32 height) -> void; }; } // namespace Mauri diff --git a/src/json.h b/src/json.h index fc55c02..34b5e72 100644 --- a/src/json.h +++ b/src/json.h @@ -9,130 +9,102 @@ namespace Mauri { -enum JsonValueType -{ - FLOAT, - INT, - VEC2, - VEC3, - VEC4, - BOOL, - STRING -}; +enum JsonValueType { FLOAT, INT, VEC2, VEC3, VEC4, BOOL, STRING }; struct JsonValue { - JsonValueType type; void *v; + JsonValueType type; std::string name; }; class Parser { public: + Parser() {} + ~Parser() {} + template - void get_value(json &root, const std::string &name, void *res, const T &_default) + static auto parse_value(const json &root, const std::string &name, void *out, const T &default_value) -> void { - json jvalue; - - if (name.empty()) - jvalue = root; - else - jvalue = root[name]; - - std::string user; - - if (jvalue.is_object()) - { - auto _user = jvalue["user"]; - if (_user.is_string()) - user = _user; - jvalue = jvalue["value"]; - } - else - user = "root"; + *((T *)out) = default_value; - *((T *)res) = _default; - - JsonValueType type; + const json &value = (!name.empty() && root.contains(name)) ? root[name] : root; if constexpr (std::is_same::value) { - if (jvalue.is_number()) - { - *((u32 *)res) = jvalue.get(); - } - type = INT; + if (value.is_number()) *((u32 *)out) = value.get(); } else if constexpr (std::is_same::value) { - if (jvalue.is_number()) - { - *((s32 *)res) = jvalue.get(); - } - type = INT; + if (value.is_number()) *((s32 *)out) = value.get(); } else if constexpr (std::is_same::value) { - if (jvalue.is_number()) - { - *((f32 *)res) = jvalue.get(); - } - type = FLOAT; + if (value.is_number()) *((f32 *)out) = value.get(); } else if constexpr (std::is_same::value) { - if (jvalue.is_string()) - { - *((std::string *)res) = jvalue.get(); - } - type = STRING; + if (value.is_string()) *((std::string *)out) = value.get(); } else if constexpr (std::is_same::value) { - if (jvalue.is_string()) - { - str_vec2(jvalue.get().c_str(), *((vec2 *)res)); - } - type = VEC2; + if (value.is_string()) str_vec2(value.get().c_str(), *((vec2 *)out)); } else if constexpr (std::is_same::value) { - if (jvalue.is_string()) - { - str_vec3(jvalue.get().c_str(), *((vec3 *)res)); - } - type = VEC3; + if (value.is_string()) str_vec3(value.get().c_str(), *((vec3 *)out)); } else if constexpr (std::is_same::value) { - // Special case for because VEC4 is used for all - // uniform values and some are written as a single number, not a string. - if (jvalue.is_number()) - { - (*((vec4 *)res))[0] = jvalue.get(); - } - else if (jvalue.is_string()) - { - str_vec4(jvalue.get().c_str(), *((vec4 *)res)); - } - type = VEC4; + // Some uniforms are saved as a single number. + if (value.is_number()) (*((vec4 *)out))[0] = value.get(); + else if (value.is_string()) str_vec4(value.get().c_str(), *((vec4 *)out)); } else if constexpr (std::is_same::value) { - if (jvalue.is_boolean()) + if (value.is_boolean()) *((bool *)out) = value.get(); + } + } + + template + auto map_value(const json &root, const std::string &name, void *out, const T &default_value) -> void + { + const json &value = (!name.empty() && root.contains(name)) ? root[name] : root; + + std::string user = "root"; + + if (value.is_object()) + { + if (value.contains("user")) { - *((bool *)res) = jvalue.get(); + const json &user_value = value["user"]; + if (user_value.is_string()) user = user_value; } - type = BOOL; + this->parse_value(value, "value", out, default_value); } + else + { + this->parse_value(root, name, out, default_value); + } + + JsonValueType type; - values[user].push_back(JsonValue { type, res, name }); + if constexpr (std::is_same::value) type = INT; + else if constexpr (std::is_same::value) type = INT; + else if constexpr (std::is_same::value) type = FLOAT; + else if constexpr (std::is_same::value) type = STRING; + else if constexpr (std::is_same::value) type = VEC2; + else if constexpr (std::is_same::value) type = VEC3; + else if constexpr (std::is_same::value) type = VEC4; + else if constexpr (std::is_same::value) type = BOOL; + + this->values[user].emplace_back(JsonValue{out, type, name}); } std::unordered_map> values; }; - + } // namespace Mauri #endif // _JSON_H diff --git a/src/log.h b/src/log.h index 8c97bc5..34f6cd1 100644 --- a/src/log.h +++ b/src/log.h @@ -4,25 +4,25 @@ #include #include -#define PRINT(x, ...) printf(x, ##__VA_ARGS__) +#define _PRINT(x, ...) printf(x, ##__VA_ARGS__) #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#define log_print(t, fmt, ...) \ - do \ - { \ - PRINT("%s:%d %s(): ", __FILENAME__, __LINE__, __FUNCTION__); \ - PRINT("%s -> " fmt "\n", t, ##__VA_ARGS__); \ +#define log_print(t, fmt, ...) \ + do \ + { \ + _PRINT("%s:%d %s(): ", __FILENAME__, __LINE__, __FUNCTION__); \ + _PRINT("%s -> " fmt "\n", t, ##__VA_ARGS__); \ } while (0) -#define log_info(fmt, ...) log_print("info", fmt, ##__VA_ARGS__) -#define log_warn(fmt, ...) log_print("warn", fmt, ##__VA_ARGS__) -#define log_error(fmt, ...) log_print("error", fmt, ##__VA_ARGS__) +#define info(fmt, ...) log_print("info", fmt, ##__VA_ARGS__) +#define warn(fmt, ...) log_print("warn", fmt, ##__VA_ARGS__) +#define error(fmt, ...) log_print("error", fmt, ##__VA_ARGS__) #ifdef _DEBUG_ -#define log_debug(fmt, ...) log_print("debug", fmt, ##__VA_ARGS__) +#define debug(fmt, ...) log_print("debug", fmt, ##__VA_ARGS__) #else -#define log_debug(fmt, ...) +#define debug(fmt, ...) #endif #endif // _LOG_H diff --git a/src/mauri.cc b/src/mauri.cc index 330daa9..fb0cc4b 100644 --- a/src/mauri.cc +++ b/src/mauri.cc @@ -1,3 +1,5 @@ +#include + #include "assets.h" #include "engine.h" @@ -16,44 +18,99 @@ using namespace Mauri; -static const std::string scene_path = "scene.json"; - -s32 main(s32 argc, char *argv[]) +auto main(s32 argc, char *argv[]) -> s32 { - if (argc != 2) + args::ArgParser args("Usage: ...", "0.12"); + + args.option("path p", ""); + args.option("width w", ""); + args.option("height h", ""); + args.option("background b", ""); + args.option("output o", ""); + args.parse(argc, argv); + + if (!args.found("path")) { - printf("Usage: %s \n", argv[0]); + std::cout << args.helptext << "\n"; return EXIT_FAILURE; } - if (!asset_manager()->load_package(std::string(argv[1]))) + s32 width = -1; + s32 height = -1; + + if (args.found("width")) width = args.value("width"); + if (args.found("height")) height = args.value("height"); + + if (width == 0 || height == 0) { + error("invalid width and/or height in arguments"); return EXIT_FAILURE; } + bool background = false; + + if (args.found("background")) + { + std::string opt = args.value("background"); + str_to_lower(opt); + background = opt == "true"; + } + #ifdef BUILD_AUDIO gst_init(&argc, &argv); #endif - set_context(new ContextGLFW(1920, 1080, "mauri", false)); - //set_context(new ContextX11(1920, 1080, "mauri", false)); - //set_context(new ContextNull(1920, 1080, "mauri", false)); + //Context *new_context = new ContextGLFW(); + Context *new_context = new ContextX11(); + if (!new_context->create_window(width > 0 ? width : 640, height > 0 ? height : 480, "mauri", background)) + { + return EXIT_FAILURE; + } + + if (!asset_manager()->arm_package(args.value("path"), args.found("output"))) + { + return EXIT_FAILURE; + } + + set_context(new_context); + + Scene *scene = new Scene("scene.json"); - Parser parser; - Scene *scene = new Scene(parser, scene_path); - //context()->resize_window(scene->width, scene->height); - Engine *engine = new Engine(scene, &parser, true); + if (width <= 0) width = scene->width; + if (height <= 0) height = scene->height; - //asset_manager()->write_files("./output"); + context()->resize_window(width, height); - engine->run(); + Engine *engine = new Engine(scene, 1.f, true); + + std::chrono::time_point begin = std::chrono::system_clock::now(); + + scene->load(engine); + + using us = std::chrono::microseconds; + u64 duration = std::chrono::duration_cast(std::chrono::system_clock::now() - begin).count(); + info("scene loaded in %fs", duration / 1000000.f); + + if (asset_manager()->prepare_output) + { + asset_manager()->write_files(args.value("output")); + } + else + { + engine->run(); + } delete scene; delete engine; asset_manager()->unload_package(); + context()->close_window(); set_context(nullptr); +#ifdef BUILD_AUDIO + //gst_deinit(); +#endif + return EXIT_SUCCESS; } diff --git a/src/objects/effect.cc b/src/objects/effect.cc index 72d9e7e..5542ccc 100644 --- a/src/objects/effect.cc +++ b/src/objects/effect.cc @@ -3,86 +3,93 @@ using namespace Mauri; -Effect::Effect(Parser &p, json &root) +Effect::Effect(Parser &pr, const json &root) { - auto asset = asset_manager()->get_file(root["file"], NONE); + Asset *asset = asset_manager()->get_file(root["file"]); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - return; + const json &file = json::parse(asset->as_string()); + if (file.is_discarded()) return; - auto file = json::parse(asset->as_string()); + pr.map_value(root, "id", &this->id, 0); + pr.map_value(root, "visible", &this->visible, true); + pr.map_value(file, "name", &this->name, ""); - p.get_value(root, "id", &id, 0); - p.get_value(root, "visible", &visible, true); - p.get_value(file, "name", &name, ""); - - for (auto &pass0 : root["passes"]) + if (root.contains("passes")) { - passes0.emplace_back(new Pass(p, pass0, EFFECT0_PASS)); + for (const json &pass0 : root["passes"]) + { + this->passes0.emplace_back(new Pass(pr, pass0, EFFECT0_PASS)); + } } - for (auto &pass1 : file["passes"]) + if (file.contains("passes")) { - passes1.emplace_back(new Pass(p, pass1, EFFECT1_PASS)); + for (const json &pass1 : file["passes"]) + { + this->passes1.emplace_back(new Pass(pr, pass1, EFFECT1_PASS)); + } } - for (auto &fbo : file["fbos"]) + if (file.contains("fbos")) { - fbos.emplace_back(EffectBuffer { fbo["name"], fbo["scale"] }); + for (const json &fbo : file["fbos"]) + { + this->fbos.emplace_back(EffectBuffer{fbo["name"], fbo.contains("scale") ? (f32)fbo["scale"] : 1.f }); + } } } -void Effect::load(Engine *engine, Object *object, bool last) +auto Effect::load(Engine *engine, Object *object, bool last) -> void { - std::stringstream _buffer_id; - _buffer_id << "_" << object->id << "_" << id; - - buffer_id = _buffer_id.str(); + this->buffer_id = str_format("_%i_%i", object->id, this->id); - for (auto &fbo : fbos) + for (const EffectBuffer &fbo : this->fbos) { - engine->create_framebuffer(fbo.name + buffer_id, object->size[0], object->size[1], fbo.scale); + engine->create_framebuffer(fbo.name + this->buffer_id, object->size[0], object->size[1], fbo.scale); } u32 index = 0; - - for (auto i = 0u; i < passes1.size(); i++) + + for (u32 i = 0; i < this->passes1.size(); i++) { Pass pass; - pass.intermidiate = false; + pass.intermediate = false; - pass.object = object; pass.effect = this; + pass.object = object; - pass.combine = object->visible && i == (passes1.size() - 1) && last; + pass.model = false; + pass.combine = object->visible && object->color_blend_mode == 0 && + i == (this->passes1.size() - 1) && last; + //pass.combine = object->visible && + // i == (this->passes1.size() - 1) && last; - if (passes1[i]->command != COPY) + if (this->passes1[i]->command != COPY) { - passes0[index]->load(engine, EFFECT0_PASS, &pass); + this->passes0[index]->load(engine, EFFECT0_PASS, &pass); index++; } - passes1[i]->load(engine, EFFECT1_PASS, &pass); + this->passes1[i]->load(engine, EFFECT1_PASS, &pass); this->passes.push_back(new RenderPass(engine, &pass)); } } -void Effect::draw(Engine *engine) +auto Effect::draw(Engine *engine) -> void { - if (!visible) - return; + if (!this->visible) return; #if FRAME_STEP - std::cout << name << ":\n"; + std::cout << this->name << ":\n"; #endif - - for (auto &pass : passes) + + for (RenderPass *pass : this->passes) { #if FRAME_STEP - if (pass->shader != nullptr) - std::cout << "\t" << pass->shader->name << "\n"; + if (pass->shader) std::cout << "\t" << pass->shader->origin()->name << "\n"; #endif pass->draw(engine); } @@ -90,17 +97,17 @@ void Effect::draw(Engine *engine) Effect::~Effect() { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { delete pass0; } - for (auto &pass1 : passes1) + for (Pass *pass1 : this->passes1) { delete pass1; } - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { delete pass; } diff --git a/src/objects/effect.h b/src/objects/effect.h index 558396b..cb53a04 100644 --- a/src/objects/effect.h +++ b/src/objects/effect.h @@ -1,7 +1,6 @@ #ifndef _EFFECT_H #define _EFFECT_H -#include "../util.h" #include "../json.h" #include "../engine.h" #include "../texture.h" @@ -9,19 +8,19 @@ namespace Mauri { -class Pass; -class RenderPass; - struct EffectBuffer { std::string name; f32 scale; }; +class Pass; +class RenderPass; + class Effect { public: - Effect(Parser &p, json &root); + Effect(Parser &pr, const json &root); ~Effect(); u32 id; @@ -33,13 +32,12 @@ class Effect std::vector passes; - void load(Engine *engine, Object *object, bool last); - void draw(Engine *engine); + auto load(Engine *engine, Object *object, bool last) -> void; + auto draw(Engine *engine) -> void; private: std::vector passes0; std::vector passes1; - std::vector fbos; }; diff --git a/src/objects/material.cc b/src/objects/material.cc index 0957214..b17a102 100644 --- a/src/objects/material.cc +++ b/src/objects/material.cc @@ -3,59 +3,64 @@ using namespace Mauri; -Material::Material(Parser &p, const std::string &path) +Material::Material(Parser &pr, const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - { - log_error("failed to load material.json (%s)", path.c_str()); - return; - } - - auto root = json::parse(asset->as_string()); + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - for (auto &pass : root["passes"]) + if (root.contains("passes")) { - passes0.push_back(new Pass(p, pass, MATERIAL_PASS)); + for (const json &pass : root["passes"]) + { + this->passes0.push_back(new Pass(pr, pass, MATERIAL_PASS)); + } } } -void Material::load(Engine *engine, Object *object, MaterialType type, Pass *pass) +auto Material::load(Engine *engine, Object *object, MaterialType type, Pass *pass) -> void { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { switch (type) { case MATERIAL_EFFECT: + { pass0->load(engine, MATERIAL_PASS, pass); break; + } case MATERIAL_MODEL: - Pass _pass; + { + Pass model_pass; + + model_pass.intermediate = false; - _pass.intermidiate = false; + model_pass.object = object; + model_pass.effect = nullptr; - _pass.object = object; - _pass.effect = nullptr; + model_pass.model = true; + model_pass.combine = object->visible && object->effects.size() == 0 && !object->passthrough; - if (object->effects.size() == 0 - && !object->passthrough && object->visible) - _pass.combine = true; - else - _pass.combine = false; - - pass0->load(engine, MATERIAL_ONLY_PASS, &_pass); + if (model_pass.combine) + { + model_pass.combos.push_back(Combo("BLENDMODE", object->color_blend_mode)); + } - object->passes.push_back(new RenderPass(engine, &_pass)); + pass0->load(engine, MATERIAL_ONLY_PASS, &model_pass); + + object->passes.push_back(new RenderPass(engine, &model_pass)); break; } + } } } Material::~Material() { - for (auto &pass0 : passes0) + for (Pass *pass0 : this->passes0) { delete pass0; } diff --git a/src/objects/material.h b/src/objects/material.h index 5bc7c3a..cfea216 100644 --- a/src/objects/material.h +++ b/src/objects/material.h @@ -7,23 +7,19 @@ namespace Mauri { -class Pass; +enum MaterialType { MATERIAL_MODEL, MATERIAL_EFFECT }; -enum MaterialType -{ - MATERIAL_MODEL, - MATERIAL_EFFECT -}; +class Pass; class Material { public: - Material(Parser &p, const std::string &path); + Material(Parser &pr, const std::string &path); ~Material(); std::vector passes0; - void load(Engine *engine, Object *object, MaterialType type, Pass *pass); + auto load(Engine *engine, Object *object, MaterialType type, Pass *pass) -> void; }; } // namespace Mauri diff --git a/src/objects/model.cc b/src/objects/model.cc index 1517b23..b916ec7 100644 --- a/src/objects/model.cc +++ b/src/objects/model.cc @@ -2,38 +2,30 @@ using namespace Mauri; -Model::Model(Parser &p, const std::string &path) +Model::Model(Parser &pr, const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; - if (asset->type == ASSET_VOID) - { - log_error("failed to load model.json (%s)", path.c_str()); - return; - } + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - auto root = json::parse(asset->as_string()); + pr.map_value(root, "autosize", &this->autosize, false); + pr.map_value(root, "fullscreen", &this->fullscreen, false); + pr.map_value(root, "passthrough", &this->passthrough, false); - p.get_value(root, "autosize", &autosize, false); - p.get_value(root, "fullscreen", &fullscreen, false); - p.get_value(root, "passthrough", &passthrough, false); + pr.map_value(root, "width", &this->width, 0.f); + pr.map_value(root, "height", &this->height, 0.f); - p.get_value(root, "width", &width, 0.f); - p.get_value(root, "height", &height, 0.f); - - auto _material = root["material"]; - - if (_material.is_string()) - material = new Material(p, _material); + if (root.contains("material")) this->material = new Material(pr, root["material"]); } -void Model::load(Engine *engine, Object *object) +auto Model::load(Engine *engine, Object *object) -> void { - material->load(engine, object, MATERIAL_MODEL, NULL); + this->material->load(engine, object, MATERIAL_MODEL, NULL); } Model::~Model() { - if (material != nullptr) - delete material; + if (this->material) delete this->material; } diff --git a/src/objects/model.h b/src/objects/model.h index 0d5b313..f6b532a 100644 --- a/src/objects/model.h +++ b/src/objects/model.h @@ -9,7 +9,7 @@ namespace Mauri class Model { public: - Model(Parser &p, const std::string &path); + Model(Parser &pr, const std::string &path); ~Model(); bool autosize = true; @@ -19,7 +19,7 @@ class Model f32 width; f32 height; - void load(Engine *engine, Object *object); + auto load(Engine *engine, Object *object) -> void; private: Material *material = nullptr; diff --git a/src/objects/object.cc b/src/objects/object.cc index 73a16b3..ac64a25 100644 --- a/src/objects/object.cc +++ b/src/objects/object.cc @@ -1,32 +1,33 @@ +#include "../context.h" + #include "pass.h" #include "scene.h" #include "object.h" using namespace Mauri; -Object::Object(Parser &p, json &root) +Object::Object(Parser &pr, const json &root) { - p.get_value(root, "id", &id, 0); - p.get_value(root, "name", &name, ""); + pr.map_value(root, "id", &this->id, 0); + pr.map_value(root, "name", &this->name, ""); - for (auto &id : root["dependencies"]) - deps.push_back(id); + if (root.contains("dependencies")) + { + for (const json &id : root["dependencies"]) + { + this->deps.push_back(id); + } + } // Filter deps that are either have the same id as this // object or are repeated. This could be handled be sorting objects // on the scene in order of deps but I'm not sure if that would // break other things. - std::vector::const_iterator it = deps.begin(); - while (it != deps.end()) + auto it = this->deps.begin(); + while (it != this->deps.end()) { - bool remove = false; - - if ((u32)(*it) == id) - { - remove = true; - } - - for (auto rt = deps.begin(); rt != deps.end(); ++rt) + bool remove = ((u32)(*it) == id); + for (auto rt = this->deps.begin(); rt != this->deps.end(); ++rt) { if (rt != it && *rt == *it) { @@ -34,217 +35,228 @@ Object::Object(Parser &p, json &root) break; } } - - if (remove) deps.erase(it); - else ++it; + if (remove) this->deps.erase(it); + else it++; } - p.get_value(root, "angles", &angles, vec3(0.f)); - p.get_value(root, "color", &color, vec3(0.f)); - p.get_value(root, "size", &size, vec2(0.f)); - p.get_value(root, "scale", &scale, vec3(0.f)); - p.get_value(root, "origin", &origin, vec3(0.f)); - //origin[2] = 0.f; - - p.get_value(root, "colorBlendMode", &color_blend_mode, 0); - p.get_value(root, "alpha", &alpha, 1.f); - p.get_value(root, "visible", &visible, true); - - auto _image = root["image"]; + pr.map_value(root, "visible", &this->visible, true); + pr.map_value(root, "angles", &this->angles, vec3(0.f)); + pr.map_value(root, "size", &this->size, vec2(0.f)); + pr.map_value(root, "scale", &this->scale, vec3(0.f)); + pr.map_value(root, "origin", &this->origin, vec3(0.f)); + if (this->origin[2] != 0.f) + { + warn("non 0 origin[2]"); + this->origin[2] = 0.f; + } + pr.map_value(root, "color", &this->color, vec3(0.f)); + pr.map_value(root, "alpha", &this->alpha, 1.f); + pr.map_value(root, "colorBlendMode", &this->color_blend_mode, 0); - if (_image.is_string()) - image = new Model(p, _image); + this->color4 = vec4{this->color[0], this->color[1], this->color[2], this->alpha}; - auto config = root["config"]; + if (root.contains("image")) + { + const json &image = root["image"]; + if (image.is_string()) this->model = new Model(pr, image); + } - if (!config.is_null()) - p.get_value(config, "passthrough", &passthrough, false); + if (root.contains("config")) + { + pr.map_value(root["config"], "passthrough", &this->passthrough, false); + } - for (auto &effect : root["effects"]) + if (root.contains("effects")) { - effects.emplace_back(new Effect(p, effect)); + for (const json &effect : root["effects"]) + { + this->effects.emplace_back(new Effect(pr, effect)); + } } } -void Object::get_model(Engine *engine, f32 width, f32 height, bool flip) +auto Object::load(Engine *engine) -> void { - model = mat4x4(1.f); - - model = glm::translate(model, vec3{-width, -height, -origin[2]}); - model = glm::translate(model, origin * 2.f); - - model = glm::rotate(model, angles[0], vec3{1.f, 0.f, 0.f}); - model = glm::rotate(model, angles[1], vec3{0.f, 1.f, 0.f}); - model = glm::rotate(model, angles[2], vec3{0.f, 0.f, 1.f}); + if (this->loaded_as_dep || this->loaded) return; - model = glm::scale(model, scale); - - mat4x4 _ortho = glm::ortho(-width, width, height, -height, 0.f, 1.f); + for (u32 dep : this->deps) + { + if (dep == this->id) continue; + Object *object = engine->scene->get_object_by_id(dep); + if (object) + { + object->load(engine); + object->loaded_as_dep = true; + } + } - if (flip) + if (this->model) { - model = glm::scale(model, vec3{1.f, -1.f, 1.f}); + if (this->model->fullscreen) this->fullscreen = true; + if (this->model->passthrough) this->passthrough = true; } - model = _ortho * model; -} + if (this->size[0] == 0.f) this->size[0] = this->model->width; + if (this->size[1] == 0.f) this->size[1] = this->model->height; -void Object::load(Engine *engine) -{ - if (loaded_as_dep || loaded) - return; - - for (auto &dep : deps) + if (this->fullscreen) { - auto object = engine->scene->get_object_by_id(dep); - object->load(engine); - object->loaded_as_dep = true; + this->size[0] = engine->scene->width; + this->size[1] = engine->scene->height; } - - if (image != nullptr) + else if (this->passthrough) { - if (image->passthrough) - { - passthrough = true; - } + if (this->size[0] == 0.f) this->size[0] = engine->scene->width; + if (this->size[1] == 0.f) this->size[1] = engine->scene->height; + } - if (image->fullscreen) - { - fullscreen = true; - } + this->buffer_a = str_format("_rt_imageLayerComposite_%i_a", this->id); + this->buffer_b = str_format("_rt_imageLayerComposite_%i_b", this->id); + engine->create_framebuffer(this->buffer_a, this->size[0], this->size[1], 1.f); + engine->create_framebuffer(this->buffer_b, this->size[0], this->size[1], 1.f); - if (image->width != 0.f && image->height != 0.f) + if (this->effects.size() > 0) + { + for (auto it = this->effects.begin(); it != this->effects.end();) { - size[0] = image->width; - size[1] = image->height; + if (!(*it)->visible) it = this->effects.erase(it); + else it++; } } - if (size[0] == 0 && size[1] == 0) + if (this->model) this->model->load(engine, this); + + for (u32 i = 0; i < this->effects.size(); i++) { - size[0] = engine->view.width; - size[1] = engine->view.height; - origin[0] = size[0] / 2.f; - origin[1] = size[1] / 2.f; + this->effects[i]->load(engine, this, i == (this->effects.size() - 1)); } - std::stringstream _buffer_a, _buffer_b; + if (this->effects.size() != 0 && this->color_blend_mode != 0) + { + Pass blend_pass; - _buffer_a << "_rt_imageLayerComposite_" << id << "_a"; - _buffer_b << "_rt_imageLayerComposite_" << id << "_b"; + blend_pass.intermediate = false; - buffer_a = _buffer_a.str(); - buffer_b = _buffer_b.str(); + blend_pass.object = this; + blend_pass.effect = nullptr; - engine->create_framebuffer(buffer_a, size[0], size[1], 1.f); - engine->create_framebuffer(buffer_b, size[0], size[1], 1.f); + blend_pass.model = false; + blend_pass.combine = true; - get_model(engine, engine->view.width, engine->view.height, effects.size() == 0); + blend_pass.target = "previous"; + blend_pass.textures[0] = "previous"; + blend_pass.textures[1] = COMBINE_BUFFER; + blend_pass.shader = "passthroughblend"; - ortho = glm::ortho(-size[0], size[0], -size[1], size[1], 0.f, 1.f); + blend_pass.combos.push_back(Combo("BLENDMODE", this->color_blend_mode)); + blend_pass.combos.push_back(Combo("TRANSFORM", 1)); - gl_uv_object = new RenderObject(UV_SCALE, size[0], size[1]); + RenderPass *pass = new RenderPass(engine, &blend_pass); + this->effects.back()->passes.emplace_back(pass); - gl_object = new RenderObject(OBJECT, size[0], size[1]); - gl_object_flipped = new RenderObject(OBJECT_FLIPPED, size[0], size[1]); + /* + blend_pass.combine = true; - if (image != nullptr) - { - image->load(engine, this); - } + blend_pass.target = ""; + blend_pass.textures[1] = ""; + blend_pass.shader = "minimal"; - if (effects.size() != 0) - { - // Get last effect that is visible - // to know when to make a combine pass. - Effect *last_effect = nullptr; + blend_pass.combos.clear(); - for (auto &effect : effects) - { - if (effect->visible) - { - last_effect = effect; - } - } - - if (last_effect != nullptr) - { - for (auto &effect : effects) - { - effect->load(engine, this, effect == last_effect); - } - } - else if (visible) - { - // If there are effects but no visible effects, make sure - // combine is set on the background pass. - passes.back()->combine = true; - } + pass = new RenderPass(engine, &blend_pass); + //pass->mat = &engine->default_mat; + //pass->render_object = engine->default_object; + this->effects.back()->passes.emplace_back(pass); + */ } - loaded = true; + this->loaded = true; } -void Object::update(Engine *engine) +auto Object::create_objects(Engine *engine, Texture *texture) -> void { -} - -void Object::draw(Engine *engine) -{ - for (auto &dep : deps) + this->model_object = new RenderObject(MODEL, this, texture); + this->combine_object = new RenderObject(COMBINE, this, texture); + this->combine_model = glm::ortho(0.f, (f32)engine->scene->width, (f32)engine->scene->height, 0.f, -3.f, 1.f); + this->combine_model = glm::translate(this->combine_model, this->origin); + if (texture && texture->is_gif()) { - engine->scene->get_object_by_id(dep)->draw(engine); + // Something about positioning is wrong, affects most gifs. } + if (this->passthrough) + { + this->model_model = this->combine_model; + this->model_model = glm::rotate(this->model_model, this->angles[0], vec3{1.f, 0.f, 0.f}); + this->model_model = glm::rotate(this->model_model, this->angles[1], vec3{0.f, 1.f, 0.f}); + this->model_model = glm::rotate(this->model_model, this->angles[2], vec3{0.f, 0.f, 1.f}); + this->model_model = glm::scale(this->model_model, this->scale); + this->model_model = glm::translate(this->model_model, vec3{this->size[0] / -2.f, this->size[1] / -2.f, 0.f}); + } + else + { + this->model_model = glm::ortho(0.f, this->size[0], this->size[1], 0.f, -3.f, 1.f); + } + this->combine_model = glm::rotate(this->combine_model, this->angles[0], vec3{1.f, 0.f, 0.f}); + this->combine_model = glm::rotate(this->combine_model, this->angles[1], vec3{0.f, 1.f, 0.f}); + this->combine_model = glm::rotate(this->combine_model, this->angles[2], vec3{0.f, 0.f, 1.f}); + this->combine_model = glm::scale(this->combine_model, this->scale); +} +auto Object::draw_internal(Engine *engine) -> void +{ #if FRAME_STEP - std::cout << name << " (model):\n"; + std::cout << this->name << " (model):\n"; #endif - // Reset this every frame so avoid passes that target one of - // this objects buffers to get a different result every other - // frame. - buffer_switch = true; + this->buffer_switch = true; - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { #if FRAME_STEP - std::cout << "\t" << pass->shader->name << "\n";; + std::cout << "\t" << pass->shader->origin()->name << "\n";; #endif pass->draw(engine); } - - for (auto &effect : effects) + + for (Effect *effect : this->effects) { effect->draw(engine); } } -std::string Object::get_target_buffer() const +auto Object::draw(Engine *engine) -> void +{ + for (s32 dep : this->deps) + { + Object *object = engine->scene->get_object_by_id(dep); + if (object) object->draw_internal(engine); + } + this->draw_internal(engine); +} + +auto Object::get_target_buffer() const -> const std::string & { - return (buffer_switch) ? buffer_a : buffer_b; + return (this->buffer_switch) ? this->buffer_a : this->buffer_b; } -std::string Object::get_texture_buffer() const +auto Object::get_texture_buffer() const -> const std::string & { - return (buffer_switch) ? buffer_b : buffer_a; + return (this->buffer_switch) ? this->buffer_b : this->buffer_a; } Object::~Object() { - if (image != nullptr) - delete image; - - delete gl_uv_object; + if (this->model) delete this->model; - delete gl_object; - delete gl_object_flipped; + if (this->model_object) delete this->model_object; + if (this->combine_object) delete this->combine_object; - for (auto &pass : passes) + for (RenderPass *pass : this->passes) { delete pass; } - for (auto &effect : effects) + for (Effect *effect : this->effects) { delete effect; } diff --git a/src/objects/object.h b/src/objects/object.h index 1a79c6c..a171fae 100644 --- a/src/objects/object.h +++ b/src/objects/object.h @@ -7,21 +7,13 @@ namespace Mauri { -enum ObjectAlignment -{ - CENTER, -}; - -enum BlendMode -{ - TRANSLUCENT = 0, - NORMAL = 1 -}; +enum ObjectAlignment { CENTER }; +enum BlendMode { TRANSLUCENT, NORMAL }; class Object { public: - Object(Parser &p, json &root); + Object(Parser &pr, const json &root); ~Object(); u32 id; @@ -29,58 +21,51 @@ class Object bool visible; - std::vector deps; + std::vector deps; - bool passthrough = false; bool fullscreen = false; + bool passthrough = false; + vec3 angles; vec2 size; - - void get_model(Engine *engine, f32 width, f32 height, bool flip); - - mat4x4 ortho; - mat4x4 model; - - RenderObject *gl_uv_object = nullptr; - RenderObject *gl_object = nullptr; - RenderObject *gl_object_flipped = nullptr; - + vec3 scale; + vec3 origin; + vec3 color; + f32 alpha; s32 color_blend_mode; - f32 alpha; - vec3 color; + vec4 color4; - std::string get_target_buffer() const; - std::string get_texture_buffer() const; + mat4x4 model_model; + mat4x4 combine_model; - void swap_buffer() - { - buffer_switch = !buffer_switch; - } + RenderObject *model_object = nullptr; + RenderObject *combine_object = nullptr; - std::vector passes; + auto get_target_buffer() const -> const std::string &; + auto get_texture_buffer() const -> const std::string &; - std::vector effects; + auto swap_buffers() -> void { buffer_switch = !buffer_switch; } bool loaded = false; bool loaded_as_dep = false; - void load(Engine *engine); - void update(Engine *engine); - void draw(Engine *engine); + Model *model = nullptr; + std::vector effects; - vec3 origin; + std::vector passes; - private: - vec3 angles; - vec3 scale; + auto load(Engine *engine) -> void; + auto create_objects(Engine *engine, Texture *texture) -> void; - std::string buffer_a; - std::string buffer_b; + auto draw(Engine *engine) -> void; + private: bool buffer_switch; + std::string buffer_a; + std::string buffer_b; - Model *image = nullptr; + auto draw_internal(Engine *engine) -> void; }; } // namespace Mauri diff --git a/src/objects/pass.cc b/src/objects/pass.cc index e6a9bfa..49533e1 100644 --- a/src/objects/pass.cc +++ b/src/objects/pass.cc @@ -1,146 +1,139 @@ #include #include "../gl.h" +#include "../context.h" +#include "scene.h" #include "pass.h" using namespace Mauri; -Pass::Pass(Parser &p, json &root, PassStage stage) +Pass::Pass(Parser &pr, const json &root, PassStage stage) { switch (stage) { - case EFFECT0_PASS: { - auto _uniforms = root["constantshadervalues"]; - for (auto &value : _uniforms.items()) - { - uniforms.emplace_back(new UniformOverride({vec4(0.f), value.key()})); - p.get_value(_uniforms, value.key(), &uniforms.back()->value, vec4(0.f)); - } + case EFFECT0_PASS: break; - } - case EFFECT1_PASS: { - auto _material = root["material"]; - if (_material.is_string()) - { - material = new Material(p, _material); - } - - auto _command = root["command"]; - if (_command.is_string() && _command == "copy") - { - command = COPY; - } + case EFFECT1_PASS: + { + if (root.contains("material")) this->material = new Material(pr, root["material"]); - auto _target = root["target"]; - if (_target.is_string()) + if (root.contains("command")) { - target = _target; + const json command = root["command"]; + if (command == "copy") this->command = COPY; } - auto _source = root["source"]; - if (_source.is_string()) - { - source = _source; - } + if (root.contains("target")) this->target = root["target"]; + if (root.contains("source")) this->source = root["source"]; - binds[0] = "previous"; - for (auto &bind : root["bind"]) + this->binds[0] = "previous"; + if (root.contains("bind")) { - u32 index = bind["index"]; - binds[index] = bind["name"]; + for (const json &bind : root["bind"]) + { + this->binds[bind["index"]] = bind["name"]; + } } break; } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: - shader = root["shader"]; + this->shader = root["shader"]; break; } - auto _textures = root["textures"]; - for (auto i = 0u; i < _textures.size(); i++) + if (root.contains("constantshadervalues")) + { + const json uniforms = root["constantshadervalues"]; + for (const auto &value : uniforms.items()) + { + UniformOverride *uniform = new UniformOverride{vec4(0.f), value.key()}; + pr.map_value(uniforms, value.key(), &uniform->value, vec4(0.f)); + this->uniforms.push_back(uniform); + } + } + + if (root.contains("textures")) { - auto texture = _textures[i]; - if (texture.is_string()) + const json textures = root["textures"]; + for (u32 i = 0; i < textures.size(); i++) { - textures[i] = texture; + if (textures[i].is_string()) this->textures[i] = textures[i]; } } - for (auto &combo : root["combos"].items()) + if (root.contains("combos")) { - combos.emplace_back(Combo { combo.key(), combo.value() }); + for (const auto &combo : root["combos"].items()) + { + this->combos.emplace_back(Combo(combo.key(), combo.value())); + } } } -void Pass::load(Engine *engine, PassStage stage, Pass *pass) +auto Pass::load(Engine *engine, PassStage stage, Pass *pass) -> void { - intermidiate = true; + this->intermediate = true; - for (auto i = 0; i < MAX_TEXTURES; i++) - { - if (textures[i].empty()) - continue; + pass->uniforms.insert(pass->uniforms.end(), this->uniforms.begin(), this->uniforms.end()); - pass->textures[i] = textures[i]; - } - - for (auto &combo : combos) + for (u32 i = 0; i < MAX_TEXTURES; i++) { - pass->combos.push_back(combo); + if (!this->textures[i].empty()) + { + pass->textures[i] = this->textures[i]; + } } + pass->combos.insert(pass->combos.end(), this->combos.begin(), this->combos.end()); + switch (stage) { case EFFECT0_PASS: - for (auto &uniform : uniforms) - { - pass->uniforms.push_back(uniform); - } break; case EFFECT1_PASS: - pass->command = command; + { + pass->command = this->command; - pass->source = source + pass->effect->buffer_id; - pass->target = target; + pass->target = this->target; + pass->source = this->source + pass->effect->buffer_id; if (!pass->target.empty()) + { pass->target.append(pass->effect->buffer_id); + } - for (auto i = 0; i < MAX_TEXTURES; i++) + for (u32 i = 0; i < MAX_TEXTURES; i++) { - if (binds[i].empty()) - continue; + if (this->binds[i].empty()) continue; + + pass->textures[i] = this->binds[i]; - pass->textures[i] = binds[i]; - if (pass->textures[i] != "previous") - pass->textures[i] += pass->effect->buffer_id; + { + pass->textures[i].append(pass->effect->buffer_id); + } } - if (material != nullptr) - { - material->load(engine, pass->object, MATERIAL_EFFECT, pass); - } + if (this->material) this->material->load(engine, pass->object, MATERIAL_EFFECT, pass); break; + } case MATERIAL_ONLY_PASS: case MATERIAL_PASS: - pass->shader = shader; + pass->shader = this->shader; break; } } Pass::~Pass() { - if (material != nullptr) - delete material; - - if (intermidiate) + if (this->material) delete this->material; + if (this->intermediate) { - for (auto &uniform : uniforms) + for (UniformOverride *uniform : this->uniforms) { delete uniform; } @@ -149,235 +142,220 @@ Pass::~Pass() RenderPass::RenderPass(Engine *engine, Pass *pass) { - command = pass->command; - combine = pass->combine; - - // This object contains information needed for getting previous - // framebuffer textures and targets. - object = pass->object; + this->command = pass->command; - target = engine->get_framebuffer(pass->target); + this->target = engine->get_framebuffer(pass->target); - if (command == COPY) + if (this->command == COPY) { - // Target and source is the only needed information - // for a COPY pass. - source = engine->get_framebuffer(pass->source); + // Target and source is the only needed information for a COPY pass. + this->source = engine->get_framebuffer(pass->source); return; } - shader = new Shader(engine->parser, pass->shader); + this->object = pass->object; + + this->model = pass->model; + this->combine = pass->combine; - for (auto i = 0u; i < shader->texture_count; i++) + this->shader = get_shader(pass->shader); + + bool objects_created = false; + + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { - if (!shader->textures[i].empty()) + if (!this->shader->origin()->texture_combos[i].empty()) { - // If there is no texture at "i" use default shader from the texture. - //if (pass->textures[i].empty()) - // pass->textures[i] = shader->textures[i]; + pass->combos.push_back(Combo(this->shader->origin()->texture_combos[i], !pass->textures[i].empty())); + } - if (!shader->texture_combos[i].empty() && !pass->textures[i].empty()) - pass->combos.push_back(Combo { shader->texture_combos[i], 1 }); + if (!this->shader->origin()->textures[i].empty() && pass->textures[i].empty()) + { + pass->textures[i] = this->shader->origin()->textures[i]; } - if (pass->textures[i].empty()) - continue; + if (pass->textures[i].empty()) continue; if (pass->textures[i] == "previous") { - texture_types[i] = PREVIOUS; + this->texture_types[i] = PREVIOUS; continue; } - // Check if texture is referencing a framebuffer. - textures[i] = engine->get_framebuffer_texture(pass->textures[i]); - if (textures[i] == nullptr) + this->textures[i] = engine->get_framebuffer_texture(pass->textures[i]); + + if (!this->textures[i]) { - // The texture wasn't a framebuffer so try to load it from an asset. - auto asset = asset_manager()->get_file(pass->textures[i], TEXTURE); + Asset *asset = asset_manager()->get_file(pass->textures[i], TEXTURE); if (asset->type != ASSET_VOID) { - textures[i] = (Texture *)asset->lasset; + this->textures[i] = (Texture *)asset->lasset; } } + + if (i == 0 && this->textures[0] && this->model) + { + if (this->textures[0]->is_gif()) pass->combos.push_back(Combo("SPRITESHEET", 1)); + this->object->create_objects(engine, this->textures[0]); + objects_created = true; + } } - for (auto &shader_uniform : shader->uniforms) + if (this->model && !objects_created) { - for (auto &uniform_override : pass->uniforms) + this->object->create_objects(engine, nullptr); + } + + for (Uniform *uniform : this->shader->uniforms) + { + for (UniformOverride *uniform_override : pass->uniforms) { - if (shader_uniform->name.empty()) - continue; - if (shader_uniform->name == uniform_override->name) + if (uniform->name.empty()) continue; + if (uniform->name == uniform_override->name) { // Take value as reference because "uniform_override->value" points // to the value stored in the parser. This allows changing that value // to have an instant effect. - shader_uniform->value = &uniform_override->value; + uniform->value = &uniform_override->value; break; } } } - for (auto &pass_combo : pass->combos) + this->render_shader = this->shader->compile(pass->combos); + + if (this->model && this->object->fullscreen) { - auto add = true; - for (auto &shader_combo : shader->combos) - { - if (pass_combo.name == shader_combo.name) - { - shader_combo.value = pass_combo.value; - add = false; - break; - } - } - if (add) - shader->combos.push_back(pass_combo); + this->mat = &engine->default_mat; + this->render_object = engine->fullscreen_object; } - - // If visualizer is not enabled disable audio logic - // on the shader. - if (!engine->audio_enabled) + else if (this->object->passthrough) { - for (auto &combo : shader->combos) + if (this->combine) { - if (combo.name == "AUDIOPROCESSING") - combo.value = 0; + this->mat = &this->object->model_model; + this->render_object = this->object->model_object; + } + else if (this->model) + { + this->mat = &this->object->combine_model; + this->render_object = this->object->combine_object; + } + else + { + this->mat = &engine->default_mat; + this->render_object = engine->default_object; } } - - // Compile shader only after all necessary combos are set. - shader->compile(); - - if (pass->effect == nullptr) + else { - // The fullscreen shader doesn't use ModelViewProjectionMatrix, - if (pass->object->fullscreen) + if (this->combine) { - model = &engine->default_model; - robject = engine->default_object; + this->mat = &this->object->combine_model; + this->render_object = this->object->combine_object; } - - // The passthrough shader uses ModelViewProjectionMatrix - // but, the texture is not scaled to a power of two. - else if (pass->object->passthrough) + else if (this->model) { - model = &pass->object->model; - robject = pass->object->gl_object; - } - // If there is no effect and combine is set, - // we are forced to use a model which will - // correctly position the object. - else if (combine) + this->mat = &this->object->model_model; + this->render_object = this->object->model_object; + } + else { - model = &pass->object->model; - if (textures[0]->is_gif) - { - robject = pass->object->gl_object_flipped; - } - else - { - robject = pass->object->gl_uv_object; - } + this->mat = &engine->default_mat; + this->render_object = engine->default_object; } + } - // The object with UV's that scale the texture - // also has the objects size on the verteices, - // so we need to use ortho for the model. - else +#if _DEBUG_ + debug("PASS (%s)", this->command == COPY ? "copy" : "draw"); + debug(" object: %s (passthrough: %i, fullscreen %i)", this->object->name.c_str(), + this->object->passthrough, this->object->fullscreen); + //debug(" visible: %i", ); + debug(" shader: %s", this->shader->origin()->name.c_str()); + debug(" target: %s", pass->target.c_str()); + debug(" textures (%i):", this->shader->origin()->texture_count); + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) + { + switch (this->texture_types[i]) { - model = &pass->object->ortho; - if (textures[0]->is_gif) + case STATIC: + if (pass->textures[i].empty() && !this->shader->textures[i].empty()) { - robject = pass->object->gl_object_flipped; + debug(" %s (DEFAULT)", this->shader->textures[i].c_str()); } else { - robject = pass->object->gl_uv_object; + debug(" %s", pass->textures[i].c_str()); } + break; + case PREVIOUS: + debug(" previous"); + break; } } - else + debug(" uniforms (%li)", this->shader->uniforms.size()); + for (Uniform *uniform : this->shader->uniforms) { - // When combine is set this is the final pass - // that needs to position the object on the screen. - // Except when fullscreen is set the object doesn't - // want to be positioned. - if (combine && !pass->object->fullscreen) - { - robject = pass->object->gl_object; - model = &pass->object->model; - } - else // Default everything. - { - robject = engine->default_object; - model = &engine->default_model; - } + debug(" %s %s %s", uniform_type_to_string(uniform->type).c_str(), + uniform->uname.c_str(), glm::to_string(*uniform->value).c_str()); } + debug(" model: %i", this->model); + debug(" combine: %i", this->combine); +#endif } -void RenderPass::draw(Engine *engine) +auto RenderPass::draw(Engine *engine) -> void { - switch (command) { - case DRAW: { - Framebuffer *_target; - - if (combine) - _target = engine->combine_buffer; - else if (target != nullptr) - _target = target; - else - _target = engine->get_framebuffer(object->get_target_buffer()); + switch (this->command) + { + case DRAW: + { + Framebuffer *adjusted_target; + if (this->combine) adjusted_target = engine->combine_buffer; + else if (this->target) adjusted_target = this->target; + else adjusted_target = engine->get_framebuffer(this->object->get_target_buffer()); - _target->buffer->bind(); + adjusted_target->buffer->bind(); - for (auto i = 0u; i < shader->texture_count; i++) + for (u32 i = 0; i < this->shader->origin()->texture_count; i++) { - if (texture_types[i] == PREVIOUS) + if (this->texture_types[i] == PREVIOUS) { - textures[i] = engine->get_framebuffer_texture(object->get_texture_buffer()); + this->textures[i] = engine->get_framebuffer_texture(this->object->get_texture_buffer()); } } - shader->bind(engine, object, model, &textures); - robject->bind(); + this->render_object->bind(); + this->shader->bind(this->render_shader, engine, this->object, this->mat, &this->textures); - if (target == nullptr) - object->swap_buffer(); + if (!this->target) this->object->swap_buffers(); - if (combine) - { - Render::blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Render::color_mask(1.f, 1.f, 1.f, 0.f); - } - else - { - Render::blend_func(GL_ONE, GL_ZERO); - Render::color_mask(1.f, 1.f, 1.f, 1.f); - } + if (this->combine) Render::blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else Render::blend_func(GL_ONE, GL_ZERO); + + Render::color_mask(1.f, 1.f, 1.f, this->combine ? 0.f : 1.f); - engine->view.default_viewport(_target->buffer->width, _target->buffer->height); + engine->view.default_viewport( + adjusted_target->buffer->width, adjusted_target->buffer->height); - robject->draw(); + this->render_object->draw(); #if FRAME_STEP - _target->buffer->blit(0, context()->width, context()->height); + adjusted_target->buffer->blit(0, context()->width, context()->height); context()->swap_buffers(); std::this_thread::sleep_for(std::chrono::seconds(1)); #endif - break; } case COPY: - source->buffer->blit(target->buffer->id, source->buffer->width, source->buffer->height); + this->source->buffer->blit(this->target->buffer->id, this->source->buffer->width, this->source->buffer->height); break; } } RenderPass::~RenderPass() { - if (shader != nullptr) - delete shader; + if (this->shader) delete this->shader; } diff --git a/src/objects/pass.h b/src/objects/pass.h index 97348a3..a2b3124 100644 --- a/src/objects/pass.h +++ b/src/objects/pass.h @@ -8,25 +8,10 @@ namespace Mauri { -enum PassStage -{ - EFFECT0_PASS, - EFFECT1_PASS, - MATERIAL_PASS, - MATERIAL_ONLY_PASS -}; - -enum PassCommand -{ - DRAW, - COPY -}; +enum PassStage { EFFECT0_PASS, EFFECT1_PASS, MATERIAL_PASS, MATERIAL_ONLY_PASS }; +enum PassCommand { DRAW, COPY }; -enum TextureType -{ - STATIC, - PREVIOUS -}; +enum TextureType { STATIC, PREVIOUS }; struct UniformOverride { @@ -34,18 +19,17 @@ struct UniformOverride std::string name; }; -class RenderPass; - class Pass { public: Pass() {} - Pass(Parser &p, json &root, PassStage stage); - + Pass(Parser &pr, const json &root, PassStage stage); ~Pass(); + bool intermediate; + PassCommand command = DRAW; - + std::string target; std::string source; @@ -54,16 +38,16 @@ class Pass std::string shader; - std::array textures = {""}; - std::array binds = {""}; + std::array binds = { }; + std::array textures = { }; + bool model; bool combine; - bool intermidiate; std::vector uniforms; std::vector combos; - void load(Engine *engine, PassStage stage, Pass *pass); + auto load(Engine *engine, PassStage stage, Pass *pass) -> void; private: Material *material = nullptr; @@ -75,14 +59,18 @@ class RenderPass RenderPass(Engine *engine, Pass *pass); ~RenderPass(); + bool model; bool combine; Shader *shader = nullptr; - std::array textures = {0}; - std::array texture_types = {STATIC}; + mat4x4 *mat; + RenderObject *render_object; + + std::array textures = { }; + std::array texture_types = { }; - void draw(Engine *engine); + auto draw(Engine *engine) -> void; private: PassCommand command; @@ -92,8 +80,7 @@ class RenderPass Framebuffer *target; Framebuffer *source; - mat4x4 *model; - RenderObject *robject; + RenderShader *render_shader; }; } // namespace Mauri diff --git a/src/objects/scene.cc b/src/objects/scene.cc index 8331b15..06412f5 100644 --- a/src/objects/scene.cc +++ b/src/objects/scene.cc @@ -2,77 +2,78 @@ using namespace Mauri; -Scene::Scene(Parser &p, const std::string &path) +Scene::Scene(const std::string &path) { - auto asset = asset_manager()->get_file(path, NONE); - if (asset->type == ASSET_VOID) - { - log_error("%s", "failed to open scene.json"); - return; - } - - auto root = json::parse(asset->as_string()); + Asset *asset = asset_manager()->get_file(path); + if (asset->type == ASSET_VOID) return; + + const json &root = json::parse(asset->as_string()); + if (root.is_discarded()) return; - auto camera = root["camera"]; - p.get_value(camera, "center", &cam.center, vec3(0.f)); - p.get_value(camera, "eye", &cam.eye, vec3(0.f)); - p.get_value(camera, "up", &cam.up, vec3(0.f)); + Parser &pr = this->parser; - auto general = root["general"]; - p.get_value(general, "clearenabled", &clear_enabled, true); - p.get_value(general, "clearcolor", &clear_color, vec4(1.f)); - p.get_value(general, "nearz", &nearz, 0.f); - p.get_value(general, "farz", &farz, 1.f); + const json &camera = root["camera"]; + pr.map_value(camera, "center", &this->cam.center, vec3(0.f)); + pr.map_value(camera, "eye", &this->cam.eye, vec3(0.f)); + pr.map_value(camera, "up", &this->cam.up, vec3(0.f)); - auto ortho = general["orthogonalprojection"]; - p.get_value(ortho, "width", &width, 0.f); - p.get_value(ortho, "height", &height, 0.f); + const json &general = root["general"]; + pr.map_value(general, "clearenabled", &this->clear_enabled, true); + pr.map_value(general, "clearcolor", &this->clear_color, vec4(1.f)); + pr.map_value(general, "nearz", &this->nearz, -1.f); + pr.map_value(general, "farz", &this->farz, 1.f); - for (auto &object : root["objects"]) + const json &ortho = general["orthogonalprojection"]; + pr.map_value(ortho, "width", &this->width, 0); + pr.map_value(ortho, "height", &this->height, 0); + + for (const json &object : root["objects"]) { - if (object["image"].is_null()) - continue; - objects.emplace_back(new Object(p, object)); + if (!object.contains("image") || object["image"].is_null()) continue; + //if (object["name"] == "ICUE") continue; + this->objects.emplace_back(new Object(pr, object)); } } -Object *Scene::get_object_by_id(u32 id) +auto Scene::get_object_by_id(u32 id) -> Object * { - for (auto &object : objects) + for (Object *object : this->objects) { - if (object->id == id) - { - return object; - } + if (object->id == id) return object; } - return nullptr; } -void Scene::load(Engine *engine) +auto Scene::load(Engine *engine) -> void { - engine->view.width = width; - engine->view.height = height; + /* + if (this->objects.size() > 0) + { + for (auto it = this->objects.begin(); it != this->objects.end();) + { + if (!(*it)->visible) it = this->objects.erase(it); + else it++; + } + } + */ - for (auto &object : objects) + for (Object *object : this->objects) { object->load(engine); } } -void Scene::draw(Engine *engine) +auto Scene::draw(Engine *engine) -> void { - for (auto &object : objects) + for (Object *object : this->objects) { - if (object->loaded_as_dep) - continue; - object->draw(engine); + if (!object->loaded_as_dep) object->draw(engine); } } Scene::~Scene() { - for (auto &object : objects) + for (Object *object : this->objects) { delete object; } diff --git a/src/objects/scene.h b/src/objects/scene.h index 672df96..7b3d07a 100644 --- a/src/objects/scene.h +++ b/src/objects/scene.h @@ -6,25 +6,25 @@ namespace Mauri { -struct Camera { +struct Camera +{ vec3 center; vec3 eye; vec3 up; - f32 farz; - f32 nearz; - f32 fov; }; class Scene { public: - Scene(Parser &p, const std::string &path); + Scene(const std::string &path); ~Scene(); + Parser parser; + Camera cam; - f32 width; - f32 height; + s32 width; + s32 height; f32 nearz; f32 farz; @@ -32,10 +32,10 @@ class Scene bool clear_enabled; vec4 clear_color; - void load(Engine *engine); - void draw(Engine *engine); + auto get_object_by_id(u32 id) -> Object *; - Object *get_object_by_id(u32 id); + auto load(Engine *engine) -> void; + auto draw(Engine *engine) -> void; std::vector objects; }; diff --git a/src/shader.cc b/src/shader.cc index 45c3e3e..0a2d568 100644 --- a/src/shader.cc +++ b/src/shader.cc @@ -1,335 +1,446 @@ #include -#include +#include "json.h" #include "shader.h" -#include "objects/object.h" +#include "objects/scene.h" -static const char *gl_compat = \ - "#define highp\n" - "#define mediump\n" - "#define lowp\n" - "#define mul(x, y) (y * x)\n" +static const char *compat = \ +// "#define mul(x, y) ((y) * (x))\n" "#define max(x, y) max(y, x)\n" "#define frac fract\n" - "#define CAST2(x) (vec2(x))\n" - "#define CAST3(x) (vec3(x))\n" - "#define CAST4(x) (vec4(x))\n" - "#define CAST3X3(x) (mat3(x))\n" - "#define saturate(x) (clamp(x, 0.0, 1.0))\n" + "#define log10 log\n" + "#define CAST2(x) vec2(x)\n" + "#define CAST3(x) vec3(x)\n" + "#define CAST4(x) vec4(x)\n" + "#define CAST3X3(x) mat3(x)\n" + "#define saturate(x) clamp(x, 0.0, 1.0)\n" "#define lerp mix\n" "#define texSample2D texture2D\n" "#define texSample2DLod texture2DLod\n" "#define texture2DLod texture2D\n" "#define atan2 atan\n" + "#define fmod(x, y) ((x)-(y)*trunc((x)/(y)))\n" "#define ddx dFdx\n" "#define ddy(x) dFdy(-(x))\n" "#define GLSL 1\n"; -static const char *version = "#version 130\n"; +static const char *mul = "#define mul(x, y) ((y) * (x))\n"; +//static const char *mul = ""; -//static const std::regex mod_regex("(\\(.*\\)\\s*)?(\\w*\\s*)%\\s*(\\w*)"); -//static const std::regex uint_regex("uint\\s*=(.*)"); +static const char *version = "#version 330\n"; + +namespace Mauri +{ + +static std::unordered_map shader_map; +static std::unordered_map render_shader_map; + +auto get_shader(const std::string &name) -> Shader * +{ + if (shader_map.contains(name)) + { + return new Shader(shader_map[name]); + } + Shader *shader = new Shader(name); + shader_map[name] = shader; + return shader; +} + +} // namespace Mauri using namespace Mauri; -void Shader::build_shader_source(Parser *p, const std::string &isource, std::string &res, bool root) +Shader::Shader(const std::string &name) + : name(name) { - auto stream = std::istringstream(isource); + Asset *vs_asset = asset_manager()->get_file(name, VERTEX_SHADER); + Asset *fs_asset = asset_manager()->get_file(name, FRAGMENT_SHADER); + this->build_shader_source(vs_asset->as_string(), this->vs_source); + this->build_shader_source(fs_asset->as_string(), this->fs_source); +} - if (root) res += gl_compat; +Shader::Shader(Shader *origin) +{ + for (Uniform *uniform : origin->uniforms) + { + Uniform *copy = new Uniform(); + copy->name = uniform->name; + copy->uname = uniform->uname; + copy->type = uniform->type; + copy->default_value = uniform->default_value; + this->uniforms.push_back(copy); + } + this->origin_ = origin; +} - for (std::string line; std::getline(stream, line);) +static auto simple_audio_bars_hack(std::string_view &line) -> void +{ + if (line == " uint barFreq1 = frequency % RESOLUTION;") + { + line = " uint barFreq1 = uint(mod(frequency, RESOLUTION));"; + } + else if (line == " uint barFreq2 = (barFreq1 + 1) % RESOLUTION;") { - auto cut_line = false; - auto prepend_line = false; + line = " uint barFreq2 = uint(mod((barFreq1 + 1u), RESOLUTION));"; + } + else if (line == " int bar = step(shapeCoord.y, 0.5 * lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1L, barVolume2L, smoothstep(0, 1, fract(frequency)))));") + { + line = " float bar = step(shapeCoord.y, 0.5 * lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1L, barVolume2L, smoothstep(0, 1, fract(frequency)))));"; + } + else if (line == " int bar = step(1 - shapeCoord.y, lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1, barVolume2, smoothstep(0, 1, fract(frequency)))));") + { + line = " float bar = step(1 - shapeCoord.y, lerp(g_BarBounds.x, g_BarBounds.y, lerp(barVolume1, barVolume2, smoothstep(0, 1, fract(frequency)))));"; + } + else if (line == " int bar = step(1 - shapeCoord.y, barHeight);") + { + line = " float bar = step(1 - shapeCoord.y, barHeight);"; + } + else if (line == " int barLeft = step(shapeCoord.y, barHeightLeft);") + { + line = " float barLeft = step(shapeCoord.y, barHeightLeft);"; + } + else if (line == " int barRight = step(1 - shapeCoord.y, barHeightRight);") + { + line = " float barRight = step(1 - shapeCoord.y, barHeightRight);"; + } +} - if (line.compare(0, 8, "#include") == 0) - { - auto open = line.find('"') + 1; - auto include_name = line.substr(open, line.rfind('"') - open); +auto Shader::build_shader_source(const std::string_view &isource, std::stringstream &out) -> void +{ + Combo current_preprocessor = Combo("", 0); - auto asset = asset_manager()->get_file(include_name, SHADER); + std::stringstream prefix; - std::string include_source; - build_shader_source(p, asset->as_string(), include_source, false); + for (const auto &range : std::ranges::split_view(isource, std::string_view("\r\n"))) + { + std::string_view line = std::string_view(range.data(), range.size()); + + simple_audio_bars_hack(line); - res += include_source; + bool cut_line = false; + bool prepend_line = false; + + if (line.compare(0, 8, "#include") == 0) + { + size_t open = line.find('"') + 1; + std::string_view include_name = line.substr(open, line.rfind('"') - open); + + Asset *asset = asset_manager()->get_file(std::string(include_name), SHADER); + if (asset->type != ASSET_VOID) + { + std::stringstream include_source; + this->build_shader_source(asset->as_string(), include_source); + out << include_source.rdbuf(); + } cut_line = true; } - else if (line.compare(0, 7, "uniform") == 0) + else if (line.compare(0, 3, "#if") == 0 && (line.compare(0, 4, "#ifd") != 0 && line.compare(0, 4, "#ifn") != 0)) { - auto type_string = line.substr(line.find(' ') + 1); - - auto uniform = new Uniform(); - - if (type_string.compare(0, 5, "float") == 0) - uniform->type = TYPE_FLOAT; - else if (type_string.compare(0, 4, "vec4") == 0) - uniform->type = TYPE_VEC4; - else if (type_string.compare(0, 4, "vec3") == 0) - uniform->type = TYPE_VEC3; - else if (type_string.compare(0, 4, "vec2") == 0) - uniform->type = TYPE_VEC2; - else if (type_string.compare(0, 4, "mat4") == 0) - uniform->type = TYPE_MAT4; - else if (type_string.compare(0, 9, "sampler2D") == 0) + std::string_view name_start = line.substr(line.find(' ') + 1); + size_t name_end = name_start.find(' '); + if (name_end != std::string::npos) { - uniform->type = TYPE_SAMPLER2D; - uniform->default_value[0] = texture_count++; - prepend_line = true; + current_preprocessor.name = name_start.substr(0, name_end); + //std::string_view value_start = name_start.substr(name_end + 4); + //std::string_view value = value_start.substr(0); } + else + { + current_preprocessor.name = name_start.substr(0); + } + } + else if (line.compare(0, 5, "#else") == 0) + { + // Change value. + } + else if (line.compare(0, 6, "#endif") == 0) + { + current_preprocessor.name = ""; + } + else if (line.compare(0, 7, "uniform") == 0) + { + Uniform *uniform = new Uniform(); - auto open = type_string.find(' ') + 1; - - uniform->uname = type_string.substr(open, type_string.find(';') - open); + std::string_view type_string = line.substr(line.find(' ') + 1); + uniform->type = uniform_type_from_string(type_string); - auto combo_start = line.find("// "); + size_t open; - if (combo_start != std::string::npos) + if (uniform->type == TYPE_INVALID) { - auto combo_string = line.substr(combo_start + 2); - - json root = json::parse(combo_string); + open = type_string.find(' '); + uniform->precision = type_string.substr(0, open); + type_string = type_string.substr(open + 1); + uniform->type = uniform_type_from_string(type_string); + } - // If material is present, it's always used to reference this - // uniform for setting its values. If material is not present, - // label is used. - auto label = root["label"]; - auto material = root["material"]; + open = type_string.find(' ') + 1; + uniform->uname = type_string.substr(open, type_string.find(';') - open); - if (!material.is_null()) - uniform->name = material; - else if (!label.is_null()) - uniform->name = label; + if (uniform->type == TYPE_SAMPLER2D) + { + this->texture_count++; + s32 index = std::stoi(uniform->uname.substr(9)); + uniform->default_value[0] = index; + prepend_line = true; + } - switch (uniform->type) + size_t combo_start = line.find("// "); + if (combo_start != std::string::npos) + { + std::string_view combo_string = line.substr(combo_start + 2); + const json &root = json::parse(combo_string, nullptr, false); + if (!root.is_discarded()) { - case TYPE_SAMPLER2D: { - auto _default = root["default"]; - - if (!_default.is_null()) - textures[(s32)uniform->default_value[0]] = _default; - - auto combo = root["combo"]; - - if (!combo.is_null()) - texture_combos[(s32)uniform->default_value[0]] = combo; - - break; + if (root.contains("material")) + { + uniform->name = root["material"]; + } + else if (root.contains("label")) + { + uniform->name = root["label"]; + } + + switch (uniform->type) + { + case TYPE_SAMPLER2D: + { + if (root.contains("combo")) + { + this->texture_combos[(s32)uniform->default_value[0]] = root["combo"]; + } + if (root.contains("default")) + { + this->textures[(s32)uniform->default_value[0]] = root["default"]; + } + break; + } + case TYPE_FLOAT: + case TYPE_VEC4: + case TYPE_VEC3: + case TYPE_VEC2: + Parser::parse_value(root, "default", &uniform->default_value, vec4(0.f)); + break; + case TYPE_MAT4: + case TYPE_INVALID: + default: + break; + } } - case TYPE_FLOAT: - case TYPE_VEC4: - case TYPE_VEC3: - case TYPE_VEC2: - p->get_value(root, "default", &uniform->default_value, vec4(0.f)); - break; - case TYPE_MAT4: - case TYPE_INVALID: - uniform->default_value = vec4(0.f); + } + + this->uniforms.push_back(uniform); + } + else if (line.compare(0, 7, "varying") == 0 && name == "effects/foliagesway") // HACK + { + std::string_view type_string = line.substr(line.find(' ') + 1); + size_t open = type_string.find(' ') + 1; + std::string_view variable_name = type_string.substr(open, type_string.find(';') - open); + bool duplicate = false; + for (const Variable &variable : this->variables) + { + if (variable.name == variable_name) + { + duplicate = true; break; } } - - uniform->value = &uniform->default_value; - - uniforms.emplace_back(uniform); + if (!duplicate) + { + this->variables.emplace_back(Variable{std::string(variable_name), uniform_type_from_string(type_string)}); + } + cut_line = true; } - else if (line.compare(0, 9, "// [COMBO") == 0) + else if (line.compare(0, 10, "// [COMBO]") == 0) { - auto open = line.find('{'); - auto combo = line.substr(open, line.length() - open); - - auto root = json::parse(combo); - - if (!root["default"].is_null()) + size_t open = line.find('{'); + std::string_view combo = line.substr(open, line.length() - open); + const json &root = json::parse(combo); + if (!root.is_discarded()) { - combos.emplace_back(Combo { root["combo"], root["default"] }); + if (root.contains("default")) + { + this->combos.emplace_back(Combo(root["combo"], root["default"])); + } + else + { + this->combos.emplace_back(Combo(root["combo"], 0)); + } } + cut_line = true; } if (!cut_line) { - //line = std::regex_replace(line, mod_regex, "mod($1$2, $3)"); - //line = std::regex_replace(line, mod_regex, "uint(uint($1$2) % $3)"); - //line = std::regex_replace(line, uint_regex, "int int($1)"); - if (prepend_line) - { - res.insert(0, line + "\n"); - } - else - { - res += line + "\n"; - } + if (prepend_line) prefix << line << "\n"; + else out << line << "\n"; } } + + prefix << out.rdbuf(); + out = std::move(prefix); } -std::string Combo::to_string() +auto Combo::to_string() const -> const std::string { - transform(name.begin(), name.end(), name.begin(), ::toupper); - - std::stringstream _combo; - _combo << "#define " << name << " " << value << "\n"; - - return _combo.str(); + return str_format("#define %s %i\n", this->name.c_str(), this->value); } -void Shader::compile() +auto Variable::to_string() const -> const std::string { - auto _vs_source = vs_source; - auto _fs_source = fs_source; - - for (auto &combo : combos) - { - _vs_source.insert(0, combo.to_string()); - _fs_source.insert(0, combo.to_string()); - } - - _vs_source.insert(0, version); - _fs_source.insert(0, version); - - rshader = new RenderShader(_vs_source, _fs_source); + return str_format("varying %s %s;\n", uniform_type_to_string(this->type).c_str(), this->name.c_str()); } -Shader::Shader(Parser *p, const std::string &name) - : name(name) +auto Shader::compile(const std::vector &combo_overrides) -> RenderShader * { - auto vs_asset = asset_manager()->get_file(name, VERTEX_SHADER); - auto fs_asset = asset_manager()->get_file(name, FRAGMENT_SHADER); + std::vector adjusted_combos = this->origin()->combos; - if (vs_asset->type != ASSET_VOID) + for (const Combo &combo : combo_overrides) { - build_shader_source(p, vs_asset->as_string(), vs_source, true); - } - else - { - log_error("failed to load shader file (%s)", name.c_str()); - return; + bool add = true; + for (Combo &shader_combo : adjusted_combos) + { + if (combo.name == shader_combo.name) + { + shader_combo.value = combo.value; + add = false; + break; + } + } + if (add) adjusted_combos.push_back(combo); } - if (fs_asset->type != ASSET_VOID) + std::stringstream combos_str; + + for (const Combo &combo : adjusted_combos) { - build_shader_source(p, fs_asset->as_string(), fs_source, true); + combos_str << combo.to_string(); } - else + + u64 hash = std::hash{}(this->origin()->name + combos_str.str()); + + if (!render_shader_map.contains(hash)) { - log_error("failed to load shader file (%s)", name.c_str()); - return; + std::stringstream preamble; + + preamble << version; + preamble << compat; + + preamble << combos_str.str(); + + for (const Variable &variable : this->origin()->variables) + { + preamble << variable.to_string(); + } + + std::string vs_source = preamble.str() + mul + this->origin()->vs_source.str(); + std::string fs_source = preamble.str() + this->origin()->fs_source.str(); + + render_shader_map[hash] = new RenderShader(vs_source, fs_source); } + + return render_shader_map[hash]; } -void Shader::bind(Engine *engine, Object *object, mat4x4 *model, std::array *textures) +auto Shader::bind(RenderShader *shader, Engine *engine, Object *object, mat4x4 *model, + const std::array *textures) -> void { - rshader->bind(); + shader->bind(); - for (auto &uniform : uniforms) + for (const Uniform *uniform : this->uniforms) { - if (uniform->uname.find('[') != std::string::npos) - continue; -#if UNIFORM_DEBUG - std::cout << uniform->uname << "(" << uniform->type << ")" << " " - << (*uniform->value)[0] << " " << (*uniform->value)[1] << " " - << (*uniform->value)[2] << " " << (*uniform->value)[3] << "\n"; -#endif - //if (uniform->value != nullptr && uniform->type != TYPE_MAT4) - if (uniform->value != nullptr) - rshader->set_uniform(uniform->uname, uniform->type, glm::value_ptr(*uniform->value)); + if (uniform->type != TYPE_MAT4 && uniform->type != TYPE_MAT3 && + uniform->uname.find('[') == std::string::npos) + { + shader->set_uniform(uniform->uname, uniform->type, glm::value_ptr(*uniform->value)); + } } - if (textures != nullptr) + if (textures) { - for (auto i = 0u; i < texture_count; i++) + for (u32 i = 0; i < this->origin()->texture_count; i++) { - auto texture = (*textures)[i]; - - if (texture == nullptr) - continue; + Texture *texture = (*textures)[i]; + if (!texture) continue; texture->update(engine->time); - - auto index = std::to_string(i); - - auto texture_resolution = "g_Texture" + index + "Resolution"; - auto texture_translation = "g_Texture" + index + "Translation"; - auto texture_rotation = "g_Texture" + index + "Rotation"; - - /* - glm::vec4 resolution = { - object->size[0], - object->size[1], - texture->texture_resolution[2], - texture->texture_resolution[3] - }; - rshader->set_uniform(texture_resolution, TYPE_VEC4, glm::value_ptr(resolution)); - */ - rshader->set_uniform(texture_resolution, TYPE_VEC4, glm::value_ptr(texture->texture_resolution)); - rshader->set_uniform(texture_translation, TYPE_VEC2, glm::value_ptr(texture->texture_translation)); - rshader->set_uniform(texture_rotation, TYPE_VEC4, glm::value_ptr(texture->texture_rotation)); - texture->bind(i); + + std::string index = std::to_string(i); + std::string texture_resolution = "g_Texture" + index + "Resolution"; + std::string texture_translation = "g_Texture" + index + "Translation"; + std::string texture_rotation = "g_Texture" + index + "Rotation"; + shader->set_uniform(texture_resolution, TYPE_VEC4, glm::value_ptr(*texture->texture_resolution)); + shader->set_uniform(texture_translation, TYPE_VEC2, glm::value_ptr(texture->texture_translation)); + shader->set_uniform(texture_rotation, TYPE_VEC4, glm::value_ptr(texture->texture_rotation)); } } f32 adjusted_time = engine->time * engine->rate; - rshader->set_uniform("g_Time", TYPE_FLOAT, &adjusted_time); + shader->set_uniform("g_Time", TYPE_FLOAT, &adjusted_time); - rshader->set_uniform("g_TexelSize", TYPE_VEC2, glm::value_ptr(engine->view.texel_size)); - rshader->set_uniform("g_TexelSizeHalf", TYPE_VEC2, glm::value_ptr(engine->view.texel_half_size)); + shader->set_uniform("g_TexelSize", TYPE_VEC2, glm::value_ptr(engine->view.texel_size)); + shader->set_uniform("g_TexelSizeHalf", TYPE_VEC2, glm::value_ptr(engine->view.texel_half_size)); - if (object) { - glm::vec2 cursor_pos = engine->view.cursor_pos; - rshader->set_uniform("g_PointerPosition", TYPE_VEC2, glm::value_ptr(cursor_pos)); - } else { - rshader->set_uniform("g_PointerPosition", TYPE_VEC2, glm::value_ptr(engine->view.cursor_pos)); + if (model) + { + mat4x4 inverse = glm::inverse(*model); + shader->set_uniform("g_ModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(*model)); + shader->set_uniform("g_ModelViewProjectionMatrixInverse", TYPE_MAT4, glm::value_ptr(inverse)); + shader->set_uniform("g_EffectModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(engine->default_mat)); } - mat4x4 _model, _inverse; - if (!model) - _model = mat4x4(1.f); - else - _model = *model; - - _inverse = glm::inverse(_model); - - rshader->set_uniform("g_ModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(_model)); - rshader->set_uniform("g_ModelViewProjectionMatrixInverse", TYPE_MAT4, glm::value_ptr(_inverse)); - - if (object != nullptr) + if (object) { - rshader->set_uniform("g_Alpha", TYPE_FLOAT, &object->alpha); - rshader->set_uniform("g_Color", TYPE_VEC3, glm::value_ptr(object->color)); + vec2 cursor_pos = engine->view.cursor_pos; + if (object->passthrough) + { + cursor_pos[0] -= object->size[0] / 2.f; + cursor_pos[1] -= object->size[1] / 2.f; + } + cursor_pos[0] /= engine->view.width; + cursor_pos[1] /= engine->view.height; + shader->set_uniform("g_PointerPosition", TYPE_VEC2, glm::value_ptr(cursor_pos)); + shader->set_uniform("g_Alpha", TYPE_FLOAT, &object->alpha); + shader->set_uniform("g_Color", TYPE_VEC3, glm::value_ptr(object->color)); + shader->set_uniform("g_Color4", TYPE_VEC4, glm::value_ptr(object->color4)); } #ifdef BUILD_AUDIO if (engine->audio_enabled) { - g_mutex_lock(&engine->visualizer->mutex); + engine->vis->update(); + + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Right"), 64, engine->vis->bars_right.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Left"), 64, engine->vis->bars_left.data()); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum64Left"), 64, &engine->visualizer->bars_64_left[0]); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum64Right"), 64, &engine->visualizer->bars_64_right[0]); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Right"), 32, engine->vis->bars_32_right.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Left"), 32, engine->vis->bars_32_left.data()); + + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Right"), 16, engine->vis->bars_16_right.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Left"), 16, engine->vis->bars_16_left.data()); + } + else + { +#else + static std::array empty_bars = { }; - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum32Left"), 32, &engine->visualizer->bars_32_left[0]); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum32Right"), 32, &engine->visualizer->bars_32_right[0]); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Left"), 64, empty_bars.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum64Right"), 64, empty_bars.data()); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum16Left"), 16, &engine->visualizer->bars_16_left[0]); - glUniform1fv(rshader->uniform_loc("g_AudioSpectrum16Right"), 16, &engine->visualizer->bars_16_right[0]); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Left"), 32, empty_bars.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum32Right"), 32, empty_bars.data()); - g_mutex_unlock(&engine->visualizer->mutex); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Left"), 16, empty_bars.data()); + glUniform1fv(shader->get_uniform_location("g_AudioSpectrum16Right"), 16, empty_bars.data()); +#endif +#ifdef BUILD_AUDIO } #endif } Shader::~Shader() { - if (rshader != nullptr) - { - delete rshader; - } - - for (auto &uniform : uniforms) - { - delete uniform; - } } diff --git a/src/shader.h b/src/shader.h index 593ffa2..1ce4209 100644 --- a/src/shader.h +++ b/src/shader.h @@ -1,7 +1,6 @@ #ifndef _SHADER_H #define _SHADER_H -#include "json.h" #include "texture.h" #define MAX_TEXTURES 8 @@ -11,18 +10,35 @@ namespace Mauri struct Uniform { + Uniform() { this->value = &this->default_value; } + std::string name; std::string uname; + std::string precision = ""; + UniformType type; - vec4 *value = nullptr; + vec4 default_value = vec4(0.f); + vec4 *value; }; struct Combo { + Combo(const std::string &name, s32 value) + : name(name), value(value) + { + str_to_upper(this->name); + } std::string name; s32 value; - std::string to_string(); + auto to_string() const -> const std::string; +}; + +struct Variable +{ + std::string name; + UniformType type; + auto to_string() const -> const std::string; }; class Engine; @@ -31,31 +47,40 @@ class Object; class Shader { public: - Shader(Parser *p, const std::string &name); + Shader(const std::string &name); + Shader(Shader *origin); ~Shader(); std::string name; - std::vector uniforms; std::vector combos; + std::vector uniforms; - std::array textures = {""}; - std::array texture_combos = {""}; + std::array textures = { }; + std::array texture_combos = { }; u32 texture_count = 0; - void bind(Engine *engine, Object *object, mat4x4 *model, std::array *textures); - void compile(); + std::stringstream vs_source; + std::stringstream fs_source; + + std::vector variables; - private: - std::string vs_source; - std::string fs_source; + auto origin() -> Shader * { return this->origin_ ? this->origin_ : this; } - RenderShader *rshader = nullptr; + auto compile(const std::vector &combo_overrides) -> RenderShader *; - void build_shader_source(Parser *p, const std::string &isource, std::string &res, bool root); + auto bind(RenderShader *shader, Engine *engine, Object *object, mat4x4 *model, + const std::array *textures) -> void; + +private: + Shader *origin_ = nullptr; + + auto build_shader_source(const std::string_view &isource, std::stringstream &out) -> void; }; +extern auto get_shader(const std::string &name) -> Shader *; + } // namespace Mauri #endif // _SHADER_H diff --git a/src/texture.cc b/src/texture.cc index 0cc2699..21dfd90 100644 --- a/src/texture.cc +++ b/src/texture.cc @@ -1,11 +1,14 @@ -#define STB_IMAGE_IMPLEMENTATION -#include - #define STB_IMAGE_WRITE_IMPLEMENTATION +#define STBI_NO_FAILURE_STRINGS +#define STB_IMAGE_STATIC #include -#include +#define STB_IMAGE_IMPLEMENTATION +#define STBI_NO_STDIO +#include + #include +#include #include "texture.h" @@ -13,81 +16,67 @@ using namespace Mauri; Texture::Texture(Asset *asset) { - hash = asset->hash; + this->wrapped = false; - //auto type = asset->reads(9); - //auto itype = asset->reads(9); + //std::string_view type = asset->reads(9); + //std::string_view itype = asset->reads(9); asset->seek(18); - auto format = (TextureFormat)asset->read(); - - flags.i = asset->read(); - - is_gif = flags.map.IS_GIF == 1; + TextureFormat format = (TextureFormat)asset->read(); - auto texture_width = asset->read(); - auto texture_height = asset->read(); + this->flags.i = asset->read(); - width = asset->read(); - height = asset->read(); + //u32 texture_width = asset->read(); + //u32 texture_height = asset->read(); + asset->seek(8); - texture_resolution[0] = texture_width; - texture_resolution[1] = texture_height; - texture_resolution[2] = width; - texture_resolution[3] = height; + this->width = asset->read(); + this->height = asset->read(); asset->seek(4); - auto container = asset->reads(9); + std::string_view container = asset->reads(9); TextureVersion version; + bool has_container = false; - auto iformat = -1; - - auto image_count = asset->read(); + u32 image_count = asset->read(); if (container.compare(0, 8, "TEXB0003") == 0) { + has_container = asset->read() != -1; version = TEXB0003; - iformat = asset->read(); } else if (container.compare(0, 8, "TEXB0002") == 0) { version = TEXB0002; } - else if (container.compare(0, 8, "TEXB0001") == 0) - { - version = TEXB0001; - } + //else if (container.compare(0, 8, "TEXB0001") == 0) else { - version = UNKNOWN; - log_error("unknown texture version (%s)", asset->path.c_str()); + version = TEXB0001; } - textures.resize(image_count); + this->textures.resize(image_count); + this->texture_resolutions.resize(image_count); - for (auto s = 0u; s < image_count; s++) + for (u32 s = 0; s < image_count; s++) { - auto mm_count = asset->read(); - - textures[s] = new RenderTexture(flags.map.NO_INTERPOLATION, flags.map.CLAMP_UV, true, mm_count); + u32 mm_count = asset->read(); - texture = textures[s]; + bool clamp = this->flags.m.CLAMP_UV == 1; + bool no_interp = this->flags.m.NO_INTERPOLATION == 1; + RenderTexture *texture = new RenderTexture(no_interp, clamp, true, mm_count); + this->textures[s] = texture; + this->texture_resolutions[s] = vec4{0.f, 0.f, this->width, this->height}; - for (auto i = 0u; i < mm_count; i++) + for (u32 i = 0; i < mm_count; i++) { - auto mwidth = asset->read(); - auto mheight = asset->read(); - - if (i == 0) - { - texture->width = mwidth; - texture->height = mheight; - } + u32 mm_width = asset->read(); + u32 mm_height = asset->read(); - auto lz4_compressed = false; - auto decompressed_byte_count = 0; + bool lz4_compressed = false; + u32 decompressed_byte_count = 0; if (version == TEXB0002 || version == TEXB0003) { @@ -95,97 +84,131 @@ Texture::Texture(Asset *asset) decompressed_byte_count = asset->read(); } - auto byte_count = asset->read(); + u32 byte_count = asset->read(); + if (!lz4_compressed) decompressed_byte_count = byte_count; - if (!lz4_compressed) - decompressed_byte_count = byte_count; - - auto buffer = asset->ptr(); + byte *buffer = asset->ptr(); if (lz4_compressed) { - auto decompressed = new char[decompressed_byte_count]; + char *decompressed = new char[decompressed_byte_count]; LZ4_decompress_safe((char *)buffer, decompressed, byte_count, decompressed_byte_count); buffer = (byte *)decompressed; } - if (iformat == -1) + if (!has_container) { + assert(buffer); switch (format) { - case RGBA8888: { + case RGBA8888: texture->format = GL_RGBA; - texture->upload(buffer, mwidth, mheight, i); + texture->upload(buffer, mm_width, mm_height, i, false); break; - } - case DXT5: { - auto decompressed = new u32[decompressed_byte_count]; - BlockDecompressImageDXT5(mwidth, mheight, buffer, decompressed); + case DXT5: + { + u32 *decompressed = new u32[decompressed_byte_count]; + BlockDecompressImageDXT5(mm_width, mm_height, buffer, decompressed); texture->format = GL_RGBA; - texture->upload((byte *)decompressed, mwidth, mheight, i, true); + assert(decompressed); + texture->upload((byte *)decompressed, mm_width, mm_height, i, true); delete[] decompressed; break; } - case DXT1: { - auto decompressed = new u32[decompressed_byte_count * 2]; - BlockDecompressImageDXT1(mwidth, mheight, buffer, decompressed); + case DXT1: + { + u32 *decompressed = new u32[decompressed_byte_count * 2]; + BlockDecompressImageDXT1(mm_width, mm_height, buffer, decompressed); texture->format = GL_RGBA; - texture->upload((byte *)decompressed, mwidth, mheight, i, true); + assert(decompressed); + texture->upload((byte *)decompressed, mm_width, mm_height, i, true); delete[] decompressed; break; } case DXT3: - log_warn("%s", "DXT3 Texture"); + warn("DXT3 texture"); break; case RG88: texture->format = GL_RG; - texture->upload(buffer, mwidth, mheight, i); + texture->upload(buffer, mm_width, mm_height, i); break; case R8: texture->format = GL_RED; - texture->upload(buffer, mwidth, mheight, i); + texture->upload(buffer, mm_width, mm_height, i); break; default: - log_warn("%s", "Uknown Texture"); + warn("uknown texture format"); break; } + if (i == 0) + { + this->textures[s]->width = mm_width; + this->textures[s]->height = mm_height; + } } else { s32 w, h, channels; - byte *data = stbi_load_from_memory(buffer, byte_count, &w, &h, &channels, 4); - texture->format = GL_RGBA; - texture->upload(data, mwidth, mheight, i); - free(data); + byte *data = stbi_load_from_memory(buffer, byte_count, &w, &h, &channels, 0); + assert(data); + switch (channels) + { + case 1: + texture->format = GL_RED; + break; + case 2: + texture->format = GL_RG; + break; + case 3: + texture->format = GL_RGB; + break; + case 4: + default: + texture->format = GL_RGBA; + break; + } + texture->upload(data, w, h, i); + stbi_image_free(data); + if (i == 0) + { + this->textures[s]->width = w; + this->textures[s]->height = h; + } } - if (lz4_compressed) - delete[] buffer; + if (lz4_compressed) delete[] buffer; asset->seek(byte_count); } + + this->texture_resolutions[s][0] = this->textures[s]->width; + this->texture_resolutions[s][1] = this->textures[s]->height; } - texture = textures.front(); + this->texture = this->textures[0]; + this->texture_resolution = &this->texture_resolutions[0]; - if (is_gif) + if (this->is_gif()) { - auto gif_container = asset->reads(9); - - auto frame_count = asset->read(); + std::string_view gif_container = asset->reads(9); + u32 gif_frame_count = asset->read(); - if (gif_container.compare(0, 8, "TEXS0002") == 0) {} + if (gif_container.compare(0, 8, "TEXS0002") == 0) + { + //this->flags.m.IS_GIF = 0; + } else if (gif_container.compare(0, 8, "TEXS0003") == 0) { - gif_width = asset->read(); - gif_height = asset->read(); + this->gif_width = asset->read(); + this->gif_height = asset->read(); } - for (auto i = 0u; i < frame_count; i++) + this->frames.resize(gif_frame_count); + + for (u32 i = 0; i < gif_frame_count; i++) { - // See TextureFrame - frames.push_back(TextureFrame { + this->frames[i] = TextureFrame{ asset->read(), asset->read(), asset->read(), @@ -194,59 +217,70 @@ Texture::Texture(Asset *asset) asset->read(), asset->read(), asset->read(), - }); + }; } } asset->reset(); } -void Texture::save_to_file(const std::string &filename) +Texture::Texture(RenderFramebuffer *buffer) { - stbi_write_png(filename.c_str(), texture->width, texture->height, - 4, texture->pixels.data(), 4 * texture->width); + this->wrapped = true; + this->texture = buffer->texture; + this->width = buffer->width; + this->height = buffer->height; + this->texture->width = buffer->width; + this->texture->height = buffer->height; + this->texture_resolutions.resize(1); + this->texture_resolutions[0][0] = buffer->width; + this->texture_resolutions[0][1] = buffer->height; + this->texture_resolutions[0][2] = buffer->width; + this->texture_resolutions[0][3] = buffer->height; + this->texture_resolution = &this->texture_resolutions[0]; } -void Texture::update(f32 time) +auto Texture::update(f32 time) -> void { - if (wrapped || !is_gif) - return; - - if (time >= gif_update) - { - auto frame = &frames[gif_frame_index]; + if (!this->is_gif() || time < this->gif_update) return; - texture = textures[frame->id]; + TextureFrame *frame = &this->frames[this->gif_index]; - // Scale texture cordinates to size of one frame. - texture_rotation[0] = frame->width / texture->width; - texture_rotation[1] = 0.0; - texture_rotation[2] = 0.0; - texture_rotation[3] = frame->height / texture->height; + this->texture = this->textures[frame->id]; + this->texture_resolution = &this->texture_resolutions[frame->id]; - // Offset texture cordinates to the current frame - // in the spritesheet. - texture_translation[0] = frame->x / texture->width; - texture_translation[1] = frame->y / texture->height; + // Scale texture cordinates to size of one frame. + this->texture_rotation[0] = frame->width / (f32)this->texture->width; + //this->texture_rotation[1] = 0.f; + //this->texture_rotation[2] = 0.f; + this->texture_rotation[1] = frame->unknown0 / (f32)this->texture->width; + this->texture_rotation[2] = frame->unknown1 / (f32)this->texture->height; + this->texture_rotation[3] = frame->height / (f32)this->texture->height; - gif_frame_index++; + // Offset texture cordinates to the current frame + // in the spritesheet. + this->texture_translation[0] = frame->x / (f32)this->texture->width; + this->texture_translation[1] = frame->y / (f32)this->texture->height; - if (gif_frame_index >= frames.size()) - gif_frame_index = 0; + this->gif_index = (this->gif_index + 1) % this->frames.size(); + this->gif_update = time + frame->frame_time; +} - gif_update = time + frame->frame_time; - } +auto Texture::bind(s32 index) -> void +{ + this->texture->bind(index); } -Texture::~Texture() +auto Texture::save_to_file(const std::string &filename) -> void { - if (!wrapped) + for (u32 i = 0; i < this->textures.size(); i++) { - delete texture; + stbi_write_png((filename + std::to_string(i)).c_str(), this->textures[i]->width, this->textures[i]->height, + 4, this->textures[i]->pixels.data(), 4 * this->textures[i]->width); } } -void Texture::bind(s32 index) +Texture::~Texture() { - texture->bind(index); + if (!this->wrapped) delete this->texture; } diff --git a/src/texture.h b/src/texture.h index cf6491d..c552037 100644 --- a/src/texture.h +++ b/src/texture.h @@ -2,12 +2,13 @@ #define _TEXTURE_H #include "gl.h" -#include "util.h" #include "assets.h" namespace Mauri { +enum TextureVersion { TEXB0001, TEXB0002, TEXB0003 }; + enum TextureFormat { RGBA8888 = 0, @@ -18,14 +19,6 @@ enum TextureFormat R8 = 9 }; -enum TextureVersion -{ - UNKNOWN, - TEXB0001, - TEXB0002, - TEXB0003 -}; - struct TextureFlags { bool NO_INTERPOLATION : 1; @@ -50,45 +43,40 @@ class Texture { public: Texture(Asset *asset); - Texture() {} - + Texture(RenderFramebuffer *buffer); ~Texture(); - s32 width; - s32 height; + bool wrapped; - union { - u32 i; - TextureFlags map; - } flags; + RenderTexture *texture; - u64 hash; + u32 width; + u32 height; - bool wrapped = false; + union { u32 i; TextureFlags m; } flags = { 0 }; - bool is_gif; + auto is_gif() const -> bool + { + return this->flags.m.IS_GIF == 1; + } u32 gif_width; u32 gif_height; + u32 gif_index = 0; + f32 gif_update = 0.f; - u32 gif_frame_index = 0; + vec4 *texture_resolution; + vec2 texture_translation = vec2(0.f); + vec4 texture_rotation = vec4(0.f); - f32 gif_update = 0; - - std::vector frames; - - vec4 texture_resolution; - vec2 texture_translation; - vec4 texture_rotation; - - RenderTexture *texture; + auto bind(s32 index) -> void; + auto update(f32 time) -> void; + auto save_to_file(const std::string &filename) -> void; + private: std::vector textures; - - void bind(s32 index); - void update(f32 time); - void save_pixels(byte *buffer, s32 mwidth, s32 mheight); - void save_to_file(const std::string &filename); + std::vector texture_resolutions; + std::vector frames; }; } // namespace Mauri diff --git a/src/util.cc b/src/util.cc index a9d7664..8a02572 100644 --- a/src/util.cc +++ b/src/util.cc @@ -3,14 +3,14 @@ namespace Mauri { -s32 next_power_of_two(s32 n) +auto str_to_lower(std::string &s) -> void { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - return ++n; -}; + transform(s.begin(), s.end(), s.begin(), ::tolower); +} + +auto str_to_upper(std::string &s) -> void +{ + transform(s.begin(), s.end(), s.begin(), ::toupper); +} } // namespace Mauri diff --git a/src/util.h b/src/util.h index 3b88745..c0c4084 100644 --- a/src/util.h +++ b/src/util.h @@ -2,14 +2,17 @@ #define _UTIL_H #include -#include + #include +#include #include +#include +#include #include "log.h" -using namespace nlohmann; using namespace glm; +using namespace nlohmann; #define FRAME_STEP 0 #define UNIFORM_DEBUG 0 @@ -35,7 +38,21 @@ typedef unsigned char byte; namespace Mauri { -extern s32 next_power_of_two(s32 n); + +template +static auto str_format(const std::string &format, Args ... args) -> std::string +{ + s32 size_s = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; + if (size_s <= 0) { return ""; } + auto size = static_cast(size_s); + std::unique_ptr buf(new char[size]); + std::snprintf(buf.get(), size, format.c_str(), args ...); + return std::string(buf.get(), buf.get() + size - 1); } +auto str_to_lower(std::string &s) -> void; +auto str_to_upper(std::string &s) -> void; + +} // namespace Mauri + #endif // _UTIL_H diff --git a/src/vis.cc b/src/vis.cc new file mode 100644 index 0000000..7b52808 --- /dev/null +++ b/src/vis.cc @@ -0,0 +1,286 @@ +#include + +#include "vis.h" + +using namespace Mauri; + +static const f32 gausian_array[] = {0.242f, 0.399f, 0.242f}; + +auto Visualizer::calc_cutoff() -> void +{ + f64 freqconst = std::log10(freq_cap_low / (f64)freq_cap_high) / ((1.0 / (BAR_COUNT + 1.0)) - 1.0); + + for (s32 i = 0; i <= BAR_COUNT; i++) + { + freqconst_per_bin[i] = freq_cap_high * std::pow(10.0, + (freqconst * -1) + (((i + 1.0) / (BAR_COUNT + 1.0)) * freqconst)); + + f64 freq = freqconst_per_bin[i] / (sample_rate / 2.0); + + cutoff_low[i] = std::floor(freq * (sample_count / 4.0)); + + if (i == 0) continue; + + if (cutoff_low[i] <= cutoff_low[i - 1]) + { + cutoff_low[i] = cutoff_low[i - 1] + 1; + } + + cutoff_high[i - 1] = cutoff_low[i - 1]; + } +} + +auto Visualizer::gaussian(f32 *bars) -> void +{ + for (s32 i = 1; i < BAR_COUNT - 1; i++) + { + bars[i] = bars[i - 1] * gausian_array[0] + bars[i] * gausian_array[1] + bars[i + 1] * gausian_array[2]; + } +} + +auto Visualizer::monstercat_smoothing(f32 *bars) -> void +{ + std::array smoothing_factors; + + for (s32 i = 0; i < BAR_COUNT; i++) + { + smoothing_factors[i] = std::pow(1.5, i); + } + + for (s32 i = 1; i < BAR_COUNT; i++) + { + for (s32 j = 0; j < BAR_COUNT; j++) + { + if (i == j) continue; + f32 weighted = bars[i] / smoothing_factors[std::abs(i - j)]; + if (bars[j] < weighted) + { + bars[j] = weighted; + } + } + } +} + +auto Visualizer::update_bars(f32 *bars, f32 *bars_32, f32 *bars_16, f32 *bars_falloff, fftw_complex *out) -> void +{ + for (s32 i = 0; i < BAR_COUNT; i++) + { + f64 magnitude = 0.0; + + s32 low = floor(cutoff_low[i]); + s32 high = floor(cutoff_high[i]); + + for (s32 s = low; s <= high && s < (sample_count / 2); ++s) + { + magnitude += std::sqrt((out[s][0] * out[s][0]) + (out[s][1] * out[s][1])); + } + + f32 *bar; + + if (i < BAR_COUNT / 2) bar = &bars[((BAR_COUNT / 2) - 1) - i]; + else bar = &bars[i]; + + *bar = magnitude / (cutoff_high[i] - cutoff_low[i] + 1.f); + *bar *= std::log2(2 + i) * (100.0 / BAR_COUNT); + *bar = std::pow(*bar, 0.5); + + f32 falloff_value = std::min(bars_falloff[i] * 0.945f, bars_falloff[i] - 1.f); + + bars_falloff[i] = std::max(falloff_value, *bar); + } + + monstercat_smoothing(bars_falloff); + + for (s32 i = 0; i < BAR_COUNT; i++) + { + bars[i] = bars_falloff[i] / 2500.f; + } + + const s32 scale_32 = BAR_COUNT / 32; + + for (s32 i = 0; i < 32; i++) + { + s32 s = i * scale_32; + bars_32[i] = 0.f; + for (s32 j = 0; j < scale_32; j++) + { + bars_32[i] += bars[s + j]; + } + bars_32[i] /= (f32)scale_32; + } + + const s32 scale_16 = BAR_COUNT / 16; + + for (s32 i = 0; i < 16; i++) + { + s32 s = i * scale_16; + bars_16[i] = 0.f; + for (s32 j = 0; j < scale_16; j++) + { + bars_16[i] += bars[s + j]; + } + bars_16[i] /= (f32)scale_16; + } +} + +auto Visualizer::update() -> void +{ + if (this->sample_processed) return; + + g_mutex_lock(&this->mutex); + + if (!this->sample) return; + + GstBuffer *buffer = gst_sample_get_buffer(sample); + + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_READ); + + Sample *samples = (Sample *)(info.data); + + for (s32 i = 0; i < sample_count; i++) + { + in_left[i] = (f64)samples[i].left; + in_right[i] = (f64)samples[i].right; + } + + fftw_execute(this->plan_left); + fftw_execute(this->plan_right); + + update_bars(this->bars_left.data(), this->bars_32_left.data(), + this->bars_16_left.data(), this->bars_left_falloff.data(), this->out_left); + update_bars(this->bars_right.data(), this->bars_32_right.data(), + this->bars_16_right.data(), this->bars_right_falloff.data(), this->out_right); + + gst_buffer_unmap(buffer, &info); + + this->sample_processed = true; + + g_mutex_unlock(&this->mutex); +} + +auto Visualizer::update_sample() -> void +{ + GstSample *sample; + g_signal_emit_by_name(this->appsink, "pull-sample", &sample); + + if (this->sample) + { + gst_sample_unref(this->sample); + } + else + { + GstBuffer *buffer = gst_sample_get_buffer(sample); + + GstCaps *caps = gst_sample_get_caps(sample); + GstStructure *structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "rate", &this->sample_rate); + + gst_caps_unref(caps); + + this->sample_count = gst_buffer_get_size(buffer) / sizeof(Sample); + + this->in_left = (f64 *)fftw_malloc(sizeof(f64) * this->sample_count); + this->in_right = (f64 *)fftw_malloc(sizeof(f64) * this->sample_count); + + this->out_left = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * this->sample_count); + this->out_right = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * this->sample_count); + + memset(this->out_left, 0, sizeof(fftw_complex) * this->sample_count); + memset(this->out_right, 0, sizeof(fftw_complex) * this->sample_count); + + this->calc_cutoff(); + + this->plan_left = fftw_plan_dft_r2c_1d(this->sample_count, this->in_left, this->out_left, FFTW_ESTIMATE); + this->plan_right = fftw_plan_dft_r2c_1d(this->sample_count, this->in_right, this->out_right, FFTW_ESTIMATE); + } + + this->sample = sample; + this->sample_processed = false; +} + +static auto new_sample_callback(GstElement *appsink, gpointer *data) -> GstFlowReturn +{ + Visualizer *vis = (Visualizer *)data; + g_mutex_lock(&vis->mutex); + vis->update_sample(); + g_mutex_unlock(&vis->mutex); + return GST_FLOW_OK; +} + +static auto main_thread(void *data) -> void * +{ + Visualizer *vis = (Visualizer *)data; + + GstMessage *msg = gst_bus_timed_pop_filtered(vis->bus, + GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); + + if (!msg) return 0; + + switch (msg->type) + { + case GST_MESSAGE_ERROR: + error("gstreamer pipeline died, audio won't work"); + break; + default: + break; + } + + gst_message_unref(msg); + + return 0; +} + +auto Visualizer::run() -> bool +{ + g_mutex_init(&this->mutex); + + this->pipeline = gst_pipeline_new(nullptr); + + this->pulsesrc = gst_element_factory_make("pulsesrc", nullptr); + this->appsink = gst_element_factory_make("appsink", nullptr); + + if (this->pulsesrc == nullptr || this->appsink == nullptr) + { + error("failed to create gstreamer pipeline"); + return false; + } + + g_object_set(this->appsink, "emit-signals", TRUE, nullptr); + + gst_bin_add_many(GST_BIN(this->pipeline), this->pulsesrc, this->appsink, nullptr); + gst_element_link(this->pulsesrc, this->appsink); + + gst_element_set_state(this->pipeline, GST_STATE_PLAYING); + + this->bus = gst_pipeline_get_bus(GST_PIPELINE(this->pipeline)); + + this->signal_handler = g_signal_connect(this->appsink, "new-sample", + G_CALLBACK(new_sample_callback), this); + + this->thread = g_thread_new(nullptr, main_thread, this); + + return true; +} + +Visualizer::~Visualizer() +{ + if (this->in_left) fftw_free(this->in_left); + if (this->in_right) fftw_free(this->in_right); + if (this->out_left) fftw_free(this->out_left); + if (this->out_right) fftw_free(this->out_right); + + g_mutex_clear(&this->mutex); + + /* + gst_bus_post(this->bus, gst_message_new_eos(GST_OBJECT(this->pipeline))); + g_thread_join(this->thread); + gst_object_unref(this->bus); + + //gst_element_set_state(this->pipeline, GST_STATE_NULL); + //gst_element_get_state(this->pipeline, nullptr, nullptr, GST_CLOCK_TIME_NONE); + //gst_object_unref(this->pipeline); + + if (this->sample) gst_sample_unref(this->sample); + */ +} diff --git a/src/vis.h b/src/vis.h new file mode 100644 index 0000000..65261c7 --- /dev/null +++ b/src/vis.h @@ -0,0 +1,87 @@ +#ifndef _VISUALIZER_H +#define _VISUALIZER_H + +#include +#include +#include + +#include "util.h" + +#define BAR_COUNT 64 + +namespace Mauri +{ + +struct Sample +{ + s16 left; + s16 right; +}; + +class Visualizer +{ + public: + Visualizer() {} + ~Visualizer(); + + std::array bars_16_left; + std::array bars_16_right; + + std::array bars_32_left; + std::array bars_32_right; + + std::array bars_left = { }; + std::array bars_right = { }; + + std::array bars_left_falloff = { }; + std::array bars_right_falloff = { }; + + GMutex mutex; + + GstBus *bus; + GstElement *pipeline; + + auto update() -> void; + auto update_sample() -> void; + + auto run() -> bool; + + private: + GstElement *pulsesrc; + GstElement *appsink; + + GThread *thread; + + gulong signal_handler; + + GstSample *sample = nullptr; + std::atomic sample_processed = false; + + f64 *in_left = nullptr; + f64 *in_right = nullptr; + + fftw_plan plan_left; + fftw_plan plan_right; + + fftw_complex *out_left = nullptr; + fftw_complex *out_right = nullptr; + + std::array cutoff_low = { }; + std::array cutoff_high = { }; + std::array freqconst_per_bin = { }; + + u32 freq_cap_low = 20; + u32 freq_cap_high = 25000; + + s32 sample_rate; + s32 sample_count; + + auto calc_cutoff() -> void; + auto gaussian(f32 *bars) -> void; + auto monstercat_smoothing(f32 *bars) -> void; + auto update_bars(f32 *bars, f32 *bars_32, f32 *bars_16, f32 *bars_falloff, fftw_complex *out) -> void; +}; + +} // namespace Mauri + +#endif // _VISUALIZER_H diff --git a/src/visualizer.cc b/src/visualizer.cc deleted file mode 100644 index 8ef34d0..0000000 --- a/src/visualizer.cc +++ /dev/null @@ -1,303 +0,0 @@ -#include - -#include "assets.h" -#include "visualizer.h" - -using namespace Mauri; - -static const float gausian_array[] = {.242, .399, .242}; - -void Visualizer::calc_cutoff() -{ - f64 freqconst = std::log10(low_freq_cap / (f64)high_freq_cap) / - ((1.0 / (BAR_COUNT + 1.0)) - 1.0); - - for (auto i = 0; i <= BAR_COUNT; ++i) - { - freqconst_per_bin[i] = high_freq_cap * std::pow(10.0, (freqconst * -1) + - (((i + 1.0) / (BAR_COUNT + 1.0)) * freqconst)); - - f64 freq = freqconst_per_bin[i] / (sample_rate / 2.0); - - low_cutoff[i] = std::floor(freq * (sample_count / 4.0)); - - if (i <= 0) continue; - - if (low_cutoff[i] <= low_cutoff[i - 1]) - low_cutoff[i] = low_cutoff[i - 1] + 1; - - high_cutoff[i - 1] = low_cutoff[i - 1]; - } -} - -void Visualizer::gaussian_blur(bool right) -{ - f32 *bars = (right) ? (f32 *)bars_64_right : (f32 *)bars_64_left; - - for (auto i = 1; i < BAR_COUNT - 1; i++) - { - bars[i] = bars[i - 1] * gausian_array[0] + - bars[i] * gausian_array[1] + bars[i + 1] * gausian_array[2]; - } -} - -void Visualizer::monstercat_smoothing(bool right) -{ - f32 *bars = (right) ? (f32 *)bars_64_right_falloff : (f32 *)bars_64_left_falloff; - - std::array smoothing_factors; - - for (auto i = 0; i < BAR_COUNT; ++i) - { - smoothing_factors[i] = std::pow(1.5, i); - } - - for (auto i = 1; i < BAR_COUNT; ++i) - { - for (auto j = 0; j < BAR_COUNT; ++j) - { - if (i == j) continue; - - const auto weighted_value = bars[i] / smoothing_factors[(s32)(std::abs(i - j))]; - - if (bars[j] < weighted_value) - { - bars[j] = weighted_value; - } - } - } -} - -void Visualizer::update_bars(bool right) -{ - f32 *bars_64, *bars_32, *bars_16, *bars_falloff; - fftw_complex *out; - - if (right) - { - bars_64 = &bars_64_right[0]; - bars_32 = &bars_32_right[0]; - bars_16 = &bars_16_right[0]; - bars_falloff = &bars_64_right_falloff[0]; - out = &out_right[0]; - } - else - { - bars_64 = &bars_64_left[0]; - bars_32 = &bars_32_left[0]; - bars_16 = &bars_16_left[0]; - bars_falloff = &bars_64_left_falloff[0]; - out = &out_left[0]; - } - - for (auto i = 0; i < BAR_COUNT; i++) - { - f64 magnitude = 0.0; - - for (auto s = (s32)low_cutoff[i]; s <= high_cutoff[i] && s < (sample_count / 2); ++s) - { - magnitude += std::sqrt((out[s][0] * out[s][0]) + (out[s][1] * out[s][1])); - } - - f32 *bar; - - if (i < BAR_COUNT / 2) - { - bar = &bars_64[((BAR_COUNT / 2) - 1) - i]; - } - else - { - bar = &bars_64[i]; - } - - *bar = magnitude / (high_cutoff[i] - low_cutoff[i] + 1); - - *bar *= std::log2(2 + i) * (100.0 / BAR_COUNT); - *bar = std::pow(*bar, 0.5); - - auto falloff_value = std::min( - bars_falloff[i] * 0.85, - bars_falloff[i] - 1.0); - - bars_falloff[i] = std::max((f32)falloff_value, *bar); - } - - //monstercat_smoothing(right); - - for (auto i = 0; i < BAR_COUNT; i++) - { - bars_64[i] = bars_falloff[i] / 5500; - } - - for (auto i = 0; i < BAR_COUNT / 2; i++) - { - s32 index = i * 2; - - bars_32[i] = 0; - bars_32[i] += bars_64[index]; - bars_32[i] += bars_64[index + 1]; - - bars_32[i] /= 2.0; - } - - for (auto i = 0; i < BAR_COUNT / 4; i++) - { - s32 index = i * 4; - - bars_16[i] = 0; - bars_16[i] += bars_64[index]; - bars_16[i] += bars_64[index + 1]; - bars_16[i] += bars_64[index + 2]; - bars_16[i] += bars_64[index + 3]; - - bars_16[i] /= 4.0; - } -} - -void Visualizer::update_sample() -{ - GstSample *sample; - - g_signal_emit_by_name(appsink, "pull-sample", &sample); - - GstBuffer *buffer = gst_sample_get_buffer(sample); - - GstMapInfo info; - gst_buffer_map(buffer, &info, GST_MAP_READ); - - Sample *samples = (Sample *)(info.data); - - g_mutex_lock(&mutex); - - for (auto i = 0; i < sample_count; i++) - { - in_left[i] = (f64)samples[i].left; - in_right[i] = (f64)samples[i].right; - } - - fftw_execute(p_left); - fftw_execute(p_right); - - gst_sample_unref(sample); - - g_mutex_unlock(&mutex); -} - -void Visualizer::update() -{ - g_mutex_lock(&mutex); - - update_bars(true); - update_bars(false); - - g_mutex_unlock(&mutex); -} - -static GstFlowReturn new_sample_callback(GstElement *appsink, gpointer *udata) -{ - Visualizer *v = (Visualizer *)udata; - - v->update_sample(); - v->update(); - - return GST_FLOW_OK; -} - -static void *main_thread(void *data) -{ - Visualizer *v = (Visualizer *)data; - - GstMessage *msg = gst_bus_timed_pop_filtered(v->bus, GST_CLOCK_TIME_NONE, - (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); - - switch (msg->type) - { - case GST_MESSAGE_ERROR: - log_error("%s", "gstreamer pipeline died, audio won't work"); - break; - default: - break; - } - - return 0; -} - -Visualizer::Visualizer() -{ - g_mutex_init(&mutex); - - GstElement *pulsesrc; - - pipeline = gst_pipeline_new(NULL); - - pulsesrc = gst_element_factory_make("pulsesrc", NULL); - appsink = gst_element_factory_make("appsink", NULL); - - if (!pulsesrc || !appsink) - { - log_error("%s", "failed to create pipeline"); - return; - } - - g_object_set(appsink, "emit-signals", TRUE, NULL); - - g_object_set(pulsesrc, "device", "0", NULL); - - gst_bin_add_many(GST_BIN(pipeline), pulsesrc, appsink, NULL); - gst_element_link(pulsesrc, appsink); - - gst_element_set_state(pipeline, GST_STATE_PLAYING); - - bus = gst_element_get_bus(pipeline); - - GstSample *sample; - - g_signal_emit_by_name(appsink, "pull-sample", &sample); - - GstBuffer *buffer = gst_sample_get_buffer(sample); - - GstCaps *caps = gst_sample_get_caps(sample); - GstStructure *structure = gst_caps_get_structure(caps, 0); - - gst_structure_get_int(structure, "rate", &sample_rate); - - gst_caps_unref(caps); - - sample_count = gst_buffer_get_size(buffer) / sizeof(Sample); - - in_left = (f64 *)fftw_malloc(sizeof(f64) * sample_count); - in_right = (f64 *)fftw_malloc(sizeof(f64) * sample_count); - - out_left = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * sample_count); - out_right = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * sample_count); - - memset(out_left, 0, sizeof(fftw_complex) * sample_count); - memset(out_right, 0, sizeof(fftw_complex) * sample_count); - - calc_cutoff(); - - p_left = fftw_plan_dft_r2c_1d(sample_count, in_left, out_left, FFTW_ESTIMATE); - p_right = fftw_plan_dft_r2c_1d(sample_count, in_right, out_right, FFTW_ESTIMATE); - - g_signal_connect(appsink, "new-sample", G_CALLBACK(new_sample_callback), this); - - thread = g_thread_new(NULL, main_thread, this); -} - -Visualizer::~Visualizer() -{ - fftw_free(in_left); - fftw_free(in_right); - - fftw_free(out_left); - fftw_free(out_right); - - gst_bus_post(bus, gst_message_new_eos(GST_OBJECT(pipeline))); - - g_thread_join(thread); - - gst_element_set_state(pipeline, GST_STATE_NULL); - gst_object_unref(pipeline); - - g_mutex_clear(&mutex); -} diff --git a/src/visualizer.h b/src/visualizer.h deleted file mode 100644 index 2094d8a..0000000 --- a/src/visualizer.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _VISUALIZER_H -#define _VISUALIZER_H - -#include -#include - -#include "util.h" - -#define BAR_COUNT 64 - -namespace Mauri -{ - -struct Sample -{ - s16 left; - s16 right; -}; - -class Visualizer -{ -public: - Visualizer(); - - ~Visualizer(); - - f32 bars_16_left[16]; - f32 bars_16_right[16]; - - f32 bars_32_left[32]; - f32 bars_32_right[32]; - - f32 bars_64_left[BAR_COUNT]; - f32 bars_64_right[BAR_COUNT]; - - f32 bars_64_left_falloff[BAR_COUNT] = {0}; - f32 bars_64_right_falloff[BAR_COUNT] = {0}; - - GstElement *pipeline; - GstBus *bus; - - GMutex mutex; - GThread *thread; - - void update(); - void update_sample(); - -private: - GstElement *appsink; - - f64 *in_left; - f64 *in_right; - - fftw_complex *out_left; - fftw_complex *out_right; - - fftw_plan p_left; - fftw_plan p_right; - - f32 low_cutoff[BAR_COUNT + 1] = {0}; - f32 high_cutoff[BAR_COUNT + 1] = {0}; - - f32 freqconst_per_bin[BAR_COUNT + 1] = {0}; - - u32 high_freq_cap = 25000; - u32 low_freq_cap = 20; - - u32 sample_count; - s32 sample_rate; - - void calc_cutoff(); - void gaussian_blur(bool right); - void update_bars(bool right); - void monstercat_smoothing(bool right); -}; - -} // namespace Mauri - -#endif // _VISUALIZER_H -- cgit v1.2.3-101-g0448