diff options
Diffstat (limited to 'src')
51 files changed, 3261 insertions, 2686 deletions
diff --git a/src/assets.c b/src/assets.c deleted file mode 100644 index 1e18eb9..0000000 --- a/src/assets.c +++ /dev/null @@ -1,181 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> - -#include "log.h" -#include "assets.h" -#include "moro_str.h" -#include "moro_vec.h" -#include "util.h" - -mri_asset_manager asset_manager; - -static mri_asset asset_void = { - .type = ASSET_VOID, - .size = 0, - .pos = 0, - .data = NULL, - .hash = 0 -}; - -static mri_asset asset_from_slice(mstr *path, unsigned char* begin, unsigned char *end) { - return (mri_asset) { - .type = ASSET_SLICE, - .size = end - begin, - .pos = 0, - .data = begin, - .hash = mstr_hash(path) - }; -} - -static void asset_from_path(mstr *path, mri_asset *asset) { - char *path_c = mstr_to_cstr(path); - - FILE *asset_file = fopen(path_c, "rb"); - if (asset_file == NULL) { - *asset = asset_void; - free(path_c); - return; - } - - free(path_c); - - asset->type = ASSET_FILE; - asset->pos = 0; - asset->hash = mstr_hash(path); - - fseek(asset_file, 0, SEEK_END); - - asset->size = ftell(asset_file); - asset->data = (unsigned char *)malloc(asset->size); - - rewind(asset_file); - - size_t res = fread(asset->data, 1, asset->size, asset_file); - - fclose(asset_file); - - if (res != asset->size) { - log_error("failed to read file (%.*s)", MSTR_PRINTF(path)); - free(asset->data); - return; - } -} - -static void mri_asset_manager_init() { - memset(&asset_manager.pkg, 0, sizeof(mri_asset)); - mvec_init(asset_manager.files); -} - -bool mri_asset_manager_load_pkg(mstr *path) { - mri_asset_manager_init(); - - asset_from_path(path, &asset_manager.pkg); - - if (asset_manager.pkg.type == ASSET_VOID) { - log_error("failed to load pkg file: (%.*s)", MSTR_PRINTF(path)); - return false; - } - - mstr signature = mri_asset_str(&asset_manager.pkg, - mri_asset_value(&asset_manager.pkg, int)); - - if (!mstr_eqn(&signature, mstr_c("PKGV"), 4)) { - log_error("invalid pkg file: (%.*s)", MSTR_PRINTF(path)); - return false; - } - - int count = mri_asset_value(&asset_manager.pkg, int); - - mri_pkg_file *pkg_files = (mri_pkg_file *)malloc(sizeof(mri_pkg_file) * count); - - for (int i = 0; i < count; i++) { - pkg_files[i] = (mri_pkg_file) { - .path = mri_asset_str(&asset_manager.pkg, mri_asset_value(&asset_manager.pkg, int)), - .length = mri_asset_value(&asset_manager.pkg, unsigned int), - .offset = mri_asset_value(&asset_manager.pkg, unsigned int) - }; - } - - for (int i = 0; i < count; i++) { - mri_pkg_file *file = &pkg_files[i]; - - mri_asset asset = asset_from_slice(&file->path, - mri_asset_ptr(&asset_manager.pkg) + file->offset, - mri_asset_ptr(&asset_manager.pkg) + file->offset + file->length); - - log_debug("loaded file (%.*s)", MSTR_PRINTF(&file->path)); - - mvec_push(asset_manager.files, asset); - } - - log_info("successfully loaded pkg file (%.*s)", MSTR_PRINTF(path)); - - free(pkg_files); - - return true; -} - -mri_asset *mri_asset_manager_get(mstr *part, mri_file_type_hint hint) { - mstr *path = mstr_clone(part); - - switch (hint) { - case TEXTURE: - mstr_prepend(path, mstr_c("materials/")); - mstr_cat(path, mstr_c(".tex")); - break; - case SHADER: - mstr_prepend(path, mstr_c("shaders/")); - break; - case FRAGMENT_SHADER: - mstr_prepend(path, mstr_c("shaders/")); - mstr_cat(path, mstr_c(".frag")); - break; - case VERTEX_SHADER: - mstr_prepend(path, mstr_c("shaders/")); - mstr_cat(path, mstr_c(".vert")); - break; - case NONE: - break; - } - - unsigned long path_hash = mstr_hash(path); - - // true if this asset has already been loaded - bool asset_loaded = false; - - mri_asset *asset = NULL; - mvec_loop_ptr(asset_manager.files, asset, i) { - if (asset->hash == path_hash) { - asset_loaded = true; - break; - } - } - - if (!asset_loaded) { - mstr_prepend(path, mstr_c("assets/")); - asset = mvec_next(asset_manager.files); - asset_from_path(path, asset); - if (asset->type == ASSET_VOID) { - asset = &mvec_pop(asset_manager.files); - } - } - - mstr_free(path); - - return asset; -} - -void mri_asset_unload(mri_asset *asset) { - if (asset->type == ASSET_FILE) { - free(asset->data); - } -} - -void mri_asset_manager_unload() { - mri_asset *asset; - mvec_loop_ptr(asset_manager.files, asset, i) { - mri_asset_unload(asset); - } - mvec_free(asset_manager.files); - mri_asset_unload(&asset_manager.pkg); -} diff --git a/src/assets.cc b/src/assets.cc new file mode 100644 index 0000000..6b34c03 --- /dev/null +++ b/src/assets.cc @@ -0,0 +1,206 @@ +#include "assets.h" +#include "log.h" +#include "util.h" + +#include <array> +#include <fstream> +#include <iostream> + +using namespace Mauri; + +Asset::Asset() +{ + this->type = ASSET_VOID; + this->size = 0; + this->pos = 0; + this->data = nullptr; + this->hash = 0; +} + +static Asset asset_void = Asset(); + +Asset::Asset(std::string path, byte *begin, byte *end) +{ + this->type = ASSET_SLICE; + this->size = end - begin; + this->pos = 0; + this->data = begin; + this->hash = std::hash<std::string>{}(path); +} + +Asset::Asset(std::string path) +{ + //FILE *file = fopen(path.c_str(), "rb"); + + //if (file == NULL) + // return; + + //this->type = ASSET_FILE; + //this->pos = 0; + //this->hash = std::hash<std::string>{}(path); + + //fseek(file, 0, SEEK_END); + + //this->size = ftell(file); + //this->data = (unsigned char *)malloc(this->size); + + //rewind(file); + + //size_t res = fread(this->data, 1, this->size, file); + + //fclose(file); + + //if (res != this->size) { + // log_error("failed to read file (%s)", path.c_str()); + // free(this->data); + // return; + //} + + std::ifstream file(path, std::ios::binary | std::ios::ate); + + if (file.bad() || file.fail()) + { + this->type = ASSET_VOID; + file.close(); + return; + } + + this->size = file.tellg(); + this->data = new byte[this->size]; + + file.seekg(0, std::ios::beg); + if (!file.read(reinterpret_cast<char *>(this->data), this->size)) + { + this->type = ASSET_VOID; + file.close(); + return; + } + + file.close(); + + this->pos = 0; + this->type = ASSET_FILE; + + this->hash = std::hash<std::string>{}(path); +} + +Asset::~Asset() +{ + if (this->type == ASSET_FILE) + { + delete[] this->data; + } +} + +bool AssetManager::load_package(std::string path) +{ + this->pkg = new Asset(path); + + if (this->pkg->type == ASSET_VOID) + { + log_error("failed to load pkg file: (%s)", path.c_str()); + return false; + }; + + auto signature = this->pkg->reads(this->pkg->read<u32>()); + + if (signature.compare(0, 4, "PKGV") != 0) + { + log_error("invalid pkg file (%s)", path.c_str()); + return false; + } + + auto count = this->pkg->read<u32>(); + + std::vector<PkgFile> pkg_files; + pkg_files.reserve(count); + + for (auto i = 0; i < count; i++) + { + pkg_files.emplace_back((PkgFile) { + this->pkg->reads(this->pkg->read<u32>()), + this->pkg->read<u32>(), + this->pkg->read<u32>() + }); + } + + for (auto file : pkg_files) + { + this->files.push_back( + new Asset(std::string(file.path), + this->pkg->ptr() + file.offset, + this->pkg->ptr() + file.offset + file.length)); + + log_debug("loaded file (%s)", std::string(file.path).c_str()); + } + + return true; +} + +Asset *AssetManager::get_file(std::string path, FileTypeHint hint) +{ + switch (hint) + { + case FileTypeHint::TEXTURE: + path.insert(0, "materials/"); + path.append(".tex"); + break; + case FileTypeHint::SHADER: + path.insert(0, "shaders/"); + break; + case FileTypeHint::FRAGMENT_SHADER: + path.insert(0, "shaders/"); + path.append(".frag"); + break; + case FileTypeHint::VERTEX_SHADER: + path.insert(0, "shaders/"); + path.append(".vert"); + break; + case NONE: + default: + break; + } + + u64 hash = std::hash<std::string>{}(path); + + for (auto &file : this->files) + { + if (file->hash == hash) + { + return file; + } + } + + path.insert(0, "assets/"); + + Asset *asset = new Asset(path); + + if (asset->type == ASSET_VOID) + { + delete asset; + return &asset_void; + } + + this->files.push_back(std::move(asset)); + + return this->files.back(); +} + +AssetManager::~AssetManager() +{ + for (auto &file : this->files) + { + delete file; + } + delete this->pkg; +} + +namespace Mauri +{ +static AssetManager manager; + +AssetManager *asset_manager() +{ + return &manager; +} +} // namespace Mauri diff --git a/src/assets.h b/src/assets.h index 5fbc995..57fde5a 100644 --- a/src/assets.h +++ b/src/assets.h @@ -1,76 +1,115 @@ #ifndef _ASSET_H #define _ASSET_H -#include <stdbool.h> -#include <moro.h> +#include <cstring> +#include <iostream> +#include <sstream> +#include <vector> +#include <jsoncpp/json/json.h> #include "util.h" -typedef enum { - ASSET_FILE, - ASSET_SLICE, - ASSET_VOID -} mri_asset_type; +namespace Mauri +{ +enum AssetType +{ + ASSET_FILE, + ASSET_SLICE, + ASSET_VOID +}; -typedef enum { - TEXTURE, - SHADER, - FRAGMENT_SHADER, - VERTEX_SHADER, - NONE -} mri_file_type_hint; +enum FileTypeHint +{ + TEXTURE, + SHADER, + FRAGMENT_SHADER, + VERTEX_SHADER, + NONE +}; -typedef struct { - // If this is ASSET_FILE, the data should be - // free'd along with this asset. - mri_asset_type type; +struct Asset +{ + Asset(); + Asset(std::string path, byte *begin, byte *end); + Asset(std::string path); - unsigned int size; - unsigned int pos; - unsigned char *data; + ~Asset(); - unsigned long hash; -} mri_asset; + // If this is ASSET_FILE, the data should be + // free'd along with this asset. + AssetType type; -typedef struct { - mstr path; - unsigned int offset; - unsigned int length; -} mri_pkg_file; + u32 size; + u32 pos; + byte *data; -typedef struct { - // scene.pkg of the current wallpaper - mri_asset pkg; + u64 hash; - mvec(mri_asset) files; -} mri_asset_manager; + std::string_view str; -// not thread-safe -extern mri_asset_manager asset_manager; + void seek(s32 n) + { + this->pos += n; + } -#define mri_asset_ptr(asset) &((asset)->data[(asset)->pos]) -#define mri_asset_seek(asset, n) (asset)->pos += n + byte *ptr() const + { + return &this->data[this->pos]; + } -#define mri_asset_value(asset, type) ({ \ - type val = *((type *)(mri_asset_ptr(asset))); \ - mri_asset_seek((asset), sizeof(type)); \ - val; \ -}) + template <typename T> + T read() + { + T val = *((T *)this->ptr()); + this->seek(sizeof(T)); + return val; + } -#define mri_asset_str(a, n) ({ \ - size_t _n = n; \ - mstr *s = mstr_w(mri_asset_ptr(a), _n); \ - mri_asset_seek(a, _n); \ - (*s); \ -}) + std::string_view reads(u32 n) + { + std::string_view s((char *)this->ptr(), n); + this->seek(n); + return s; + } + + std::string as_string() + { + return std::string((char *)this->ptr(), this->size); + } -#define mri_asset_as_str(a) mstr_w(mri_asset_ptr(a), (a)->size) + Json::Value as_json() + { + Json::Value root; + std::stringstream(std::string((char *)this->ptr(), this->size)) >> root; + return root; + } +}; -bool mri_asset_manager_load_pkg(mstr *path); +struct PkgFile +{ + std::string_view path; + u32 offset; + u32 length; +}; -void mri_asset_unload(mri_asset *asset); -void mri_asset_manager_unload(); +class AssetManager +{ + public: + AssetManager() {}; + ~AssetManager(); -mri_asset *mri_asset_manager_get(mstr *path, mri_file_type_hint hint); + bool load_package(std::string path); + + Asset *get_file(std::string path, FileTypeHint hint); + + private: + Asset *pkg; + + std::vector<Asset *> files; +}; + +extern AssetManager *asset_manager(); + +} // namespace Mauri #endif // _ASSET_H diff --git a/src/context.c b/src/context.c deleted file mode 100644 index 292061d..0000000 --- a/src/context.c +++ /dev/null @@ -1,27 +0,0 @@ -#include <glad/gl.h> - -#include "log.h" -#include "context.h" - -void print_gl_info() { - log_info("OpenGL loaded"); - log_info("Vendor: %s", glGetString(GL_VENDOR)); - log_info("Renderer: %s", glGetString(GL_RENDERER)); - log_info("Version: %s", glGetString(GL_VERSION)); -} - -GLenum gl_check_error() { - GLenum error_code; - while ((error_code = glGetError()) != GL_NO_ERROR) { - const char *error; - switch (error_code) { - case GL_INVALID_ENUM: error = "INVALID_ENUM"; break; - case GL_INVALID_VALUE: error = "INVALID_VALUE"; break; - case GL_INVALID_OPERATION: error = "INVALID_OPERATION"; break; - case GL_OUT_OF_MEMORY: error = "OUT_OF_MEMORY"; break; - case GL_INVALID_FRAMEBUFFER_OPERATION: error = "INVALID_FRAMEBUFFER_OPERATION"; break; - } - log_debug("%s", error); - } - return error_code; -} diff --git a/src/context.cc b/src/context.cc new file mode 100644 index 0000000..6de03a9 --- /dev/null +++ b/src/context.cc @@ -0,0 +1,41 @@ +#include "context.h" +#include "log.h" + +using namespace Mauri; + +void Context::print_gl_info() +{ + log_info("OpenGL loaded"); + log_info("Vendor: %s", glGetString(GL_VENDOR)); + log_info("Renderer: %s", glGetString(GL_RENDERER)); + log_info("Version: %s", glGetString(GL_VERSION)); +} + +GLenum Context::check_error() +{ + GLenum error_code; + while ((error_code = glGetError()) != GL_NO_ERROR) + { + std::string error; + switch (error_code) + { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + log_error("%s", error.c_str()); + } + return error_code; +} diff --git a/src/context.h b/src/context.h index a6e4dc0..a4f2342 100644 --- a/src/context.h +++ b/src/context.h @@ -1,32 +1,41 @@ #ifndef _CONTEXT_H #define _CONTEXT_H -#include <stdbool.h> #include <glad/gl.h> +#include <iostream> #define GL_VERSION_MAJOR 3 #define GL_VERSION_MINOR 3 -typedef struct mri_context mri_context; +namespace Mauri +{ -struct mri_context { - float width, height; +class Context +{ + public: + Context(int width, int height, std::string name, bool wallpaper) {} - bool (*create_window)(mri_context *, float, float, const char *, bool); - void (*resize_window)(mri_context *, float, float); + virtual ~Context() {} - void (*close_window)(mri_context *); - bool (*should_close)(mri_context *); + int width, height; - void (*swap_buffers)(mri_context *); + virtual void resize_window(int width, int height) = 0; - void (*process_input)(mri_context *); - void (*cursor_pos)(mri_context *, float *, float *); + virtual void close_window() = 0; + virtual bool should_close() const = 0; - double (*current_time)(mri_context *); + virtual void swap_buffers() = 0; + + virtual void process_input() = 0; + virtual void cursor_pos(float *x, float *y) = 0; + + virtual double current_time() = 0; + + protected: + GLenum check_error(); + void print_gl_info(); }; -void print_gl_info(); -GLenum gl_check_error(); +} // namespace Mauri #endif // _CONTEXT_H diff --git a/src/context_glfw.c b/src/context_glfw.c deleted file mode 100644 index d40b595..0000000 --- a/src/context_glfw.c +++ /dev/null @@ -1,105 +0,0 @@ -#include <stdlib.h> -#include <glad/gl.h> - -#include "log.h" -#include "context_glfw.h" -#include "context.h" - -static bool create_window(mri_context *c, float width, float height, const char *name, bool wallpaper) { - if (!glfwInit()) { - log_error("%s", "failed to init GLFW"); - return false; - } - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION_MAJOR); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION_MINOR); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); - - glfwWindowHint(GLFW_SAMPLES, 2); - - c->width = width; - c->height = height; - - GLFWwindow *window = glfwCreateWindow(width, height, name, NULL, NULL); - - ((mri_glfw_context *)c)->window = window; - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) { - log_info("%s", "couldn't load opengl"); - return false; - } - - glEnable(GL_BLEND); - glEnable(GL_MULTISAMPLE); - - glDisable(GL_CULL_FACE); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - print_gl_info(); - - return true; -} - -static void close_window(mri_context *c) { - glfwSetWindowShouldClose(((mri_glfw_context *)c)->window, GLFW_TRUE); -} - -static void resize_window(mri_context *c, float width, float height) { - c->width = width; - c->height = height; - glfwSetWindowSize(((mri_glfw_context *)c)->window, width, height); -} - -static bool should_close(mri_context *c) { - return glfwWindowShouldClose(((mri_glfw_context *)c)->window); -} - -static void cursor_pos(mri_context *c, float *x, float *y) { - double _x, _y; - glfwGetCursorPos(((mri_glfw_context *)c)->window, &_x, &_y); - *x = (float)_x; - *y = (float)_y; -} - -static void swap_buffers(mri_context *c) { - glfwSwapBuffers(((mri_glfw_context *)c)->window); -} - -static void process_input(mri_context *c) { - glfwPollEvents(); -} - -static double current_time(mri_context *c) { - return glfwGetTime(); -} - -mri_context *mri_glfw_context_new() { - mri_glfw_context *c = (mri_glfw_context *)malloc(sizeof(mri_glfw_context)); - mri_glfw_context _c = { - ._c = { - .create_window = &create_window, - .resize_window = &resize_window, - .close_window = &close_window, - .should_close = &should_close, - .swap_buffers = &swap_buffers, - .process_input = &process_input, - .cursor_pos = &cursor_pos, - .current_time = ¤t_time - }, - .window = NULL - }; - memcpy(c, &_c, sizeof(mri_glfw_context)); - return (mri_context *)(c); -} - -void mri_glfw_context_unload(mri_context *c) { - glfwDestroyWindow(((mri_glfw_context *)c)->window); - glfwTerminate(); - free((mri_glfw_context *)c); -} diff --git a/src/context_glfw.cc b/src/context_glfw.cc new file mode 100644 index 0000000..2900f15 --- /dev/null +++ b/src/context_glfw.cc @@ -0,0 +1,90 @@ +#include "context_glfw.h" +#include "context.h" +#include "log.h" +#include "src/objects/pass.h" +#include <GLFW/glfw3.h> + +using namespace Mauri; + +void ContextGLFW::close_window() +{ + glfwSetWindowShouldClose(this->window, GLFW_TRUE); +} + +void ContextGLFW::resize_window(int width, int height) +{ + this->width = width; + this->height = height; + glfwSetWindowSize(this->window, width, height); +} + +bool ContextGLFW::should_close() const +{ + return glfwWindowShouldClose(this->window); +} + +void ContextGLFW::cursor_pos(f32 *x, f32 *y) +{ + f64 _x, _y; + glfwGetCursorPos(this->window, &_x, &_y); + *x = (f32)_x; + *y = (f32)_y; +} + +void ContextGLFW::swap_buffers() +{ + glfwSwapBuffers(this->window); +} + +void ContextGLFW::process_input() +{ + glfwPollEvents(); +} + +double ContextGLFW::current_time() +{ + return glfwGetTime(); +} + +ContextGLFW::ContextGLFW(int width, int height, std::string name, bool wallpaper) + : Context(width, height, name, wallpaper) +{ + this->width = width; + this->height = height; + + if (!glfwInit()) + { + log_print("err", "failed to init glfw"); + return; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSION_MAJOR); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSION_MINOR); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); + + glfwWindowHint(GLFW_SAMPLES, 4); + + this->window = glfwCreateWindow(width, height, name.c_str(), nullptr, nullptr); + + glfwMakeContextCurrent(window); + + if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) + { + log_print("err", "could not load opengl"); + return; + } + + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_MULTISAMPLE); + + print_gl_info(); +} + +ContextGLFW::~ContextGLFW() +{ + glfwDestroyWindow(this->window); + glfwTerminate(); +} diff --git a/src/context_glfw.h b/src/context_glfw.h index f68b284..30a60f2 100644 --- a/src/context_glfw.h +++ b/src/context_glfw.h @@ -1,17 +1,37 @@ -#ifndef _CONTEXT_GLFW_H -#define _CONTEXT_GLFW_H +#ifndef _CONTEXT_GLFW_HPP +#define _CONTEXT_GLFW_HPP #define GLFW_INCLUDE_NONE #include <GLFW/glfw3.h> #include "context.h" +#include "util.h" -typedef struct { - mri_context _c; - GLFWwindow *window; -} mri_glfw_context; +namespace Mauri +{ -mri_context *mri_glfw_context_new(); -void mri_glfw_context_unload(mri_context *c); +class ContextGLFW : public Context +{ + public: + ContextGLFW(int width, int height, std::string name, bool wallpaper); + ~ContextGLFW(); -#endif // _CONTEXT_GLFW_H + void close_window(); + void resize_window(int width, int height); + + bool should_close() const; + void process_input(); + + void cursor_pos(float *x, float *y); + + void swap_buffers(); + + double current_time(); + + private: + GLFWwindow *window; +}; + +} // namespace Mauri + +#endif // _CONTEXT_GLFW_HPP diff --git a/src/context_x11.cc b/src/context_x11.cc new file mode 100644 index 0000000..898aa86 --- /dev/null +++ b/src/context_x11.cc @@ -0,0 +1,290 @@ +#include "context_x11.h" + +#include "context.h" +#include "log.h" + +using namespace Mauri; + +static Atom ATOM_WM_PROTOCOLS, ATOM_WM_DELETE_WINDOW, ATOM__NET_ACTIVE_WINDOW; + +void ContextX11::disable_input() +{ + XWMHints wmHint; + + wmHint.flags = InputHint | StateHint; + wmHint.input = false; + wmHint.initial_state = NormalState; + + XSetWMProperties(x11_d, window, NULL, NULL, NULL, + 0, NULL, &wmHint, NULL); +} + +ContextX11::ContextX11(int width, int height, std::string name, bool wallpaper) + : Context(width, height, name, wallpaper) +{ + this->width = width; + this->height = height; + + x11_d = XOpenDisplay(NULL); + + ATOM_WM_DELETE_WINDOW = XInternAtom(x11_d, "WM_DELETE_WINDOW", true); + + if (!x11_d) + { + log_error("%s", "could not open display"); + return; + } + + int screen = XDefaultScreen(x11_d); + Window root = RootWindow(x11_d, screen); + + static int 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, + None + }; + + GLint context_attrs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, GL_VERSION_MAJOR, + GLX_CONTEXT_MINOR_VERSION_ARB, GL_VERSION_MINOR, + None + }; + + // https://github.com/jarcode-foss/glava/blob/master/glava/glx_wcb.c#L383 + int fb_sz, best = -1, samp = -1; + + GLXFBConfig *fbc = glXChooseFBConfig(x11_d, screen, gl_attrs, &fb_sz); + if (!fbc) + return; + + for (int t = 0; t < fb_sz; ++t) + { + XVisualInfo* xvi = glXGetVisualFromFBConfig(x11_d, fbc[t]); + if (xvi) + { + int 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); + } + } + + if (best == -1) + { + log_error("%s", "XRender could not find suitable format"); + return; + } + + GLXFBConfig config = fbc[best]; + XFree(fbc); + + XVisualInfo *vi = glXGetVisualFromFBConfig(x11_d, config); + + Colormap cmap = XCreateColormap(x11_d, root, vi->visual, AllocNone); + + XSetWindowAttributes swa; + + swa.colormap = cmap; + 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); + + XFree(vi); + + //disable_input(); + + XStoreName(x11_d, window, name.c_str()); + + 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 (!gladLoadGL((GLADloadfunc)glXGetProcAddressARB)) + { + log_error("%s", "failed to load glad"); + return; + } + + if (wallpaper) + { + //Atom desktop = XInternAtom(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); + + Atom desktop_type = XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE_DESKTOP", false); + + XChangeProperty(x11_d, window, XInternAtom(x11_d, "_NET_WM_WINDOW_TYPE", false), + XA_ATOM, 32, PropModeReplace, (unsigned char*)&desktop_type, 1); + + //XEvent event; + //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.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[2] = 0; //unused. + //event.xclient.data.l[3] = 0; + //event.xclient.data.l[4] = 0; + + //XSendEvent(x11_d, root, false, + // SubstructureRedirectMask|SubstructureNotifyMask, &event); + + //event.xclient.data.l[1] = XInternAtom(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); + } + + XSetWMProtocols(x11_d, window, &ATOM_WM_DELETE_WINDOW, 1); + + XMoveWindow(x11_d, window, 1080, 0); + + XMapWindow(x11_d, window); + XFlush(x11_d); + + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glEnable(GL_MULTISAMPLE); + + print_gl_info(); + + start = system_clock::now(); +} + +void ContextX11::resize_window(int w, int h) +{ + width = w; + height = h; +} + +void ContextX11::swap_buffers() +{ + glXSwapBuffers(x11_d, window); +} + +void ContextX11::raise() { + XClientMessageEvent ev = { + .type = ClientMessage, + .serial = 0, + .send_event = true, + .display = x11_d, + .window = window, + .message_type = ATOM__NET_ACTIVE_WINDOW, + .format = 32, + .data = { .l = { + 1, /* source indication -- `1` when coming from an application */ + 0, /* timestamp -- `0` to (attempt to) ignore */ + (long)window /* requestor's currently active window -- `0` for none */ + } + } + }; + /* Send the client message as defined by EWMH standards (usually works) */ + XSendEvent(x11_d, DefaultRootWindow(x11_d), false, StructureNotifyMask, (XEvent*) &ev); + /* Raise the client in the X11 stacking order (sometimes works, can be blocked by the WM) */ + XRaiseWindow(x11_d, window); + XFlush(x11_d); +} + +void ContextX11::set_clickthrough() +{ + Window win = window, root = DefaultRootWindow(x11_d); + while (win != None) + { + Region region; + if ((region = XCreateRegion())) + { + XShapeCombineRegion(x11_d, window, ShapeInput, 0, 0, region, ShapeSet); + XDestroyRegion(region); + } + + Window parent, *children = NULL; + unsigned int num_children; + + if (XQueryTree(x11_d, win, &root, &parent, &children, &num_children)) + { + if (children) + XFree(children); + } + + win = (parent == root ? None : parent); + } + + XFlush(x11_d); +} + +void ContextX11::close_window() +{ + this->_should_close = true; + +} + +void ContextX11::process_input() +{ + XEvent xev; + + while (XPending(x11_d) > 0) + { + XNextEvent(x11_d, &xev); + + switch (xev.type) + { + case ClientMessage: + if (xev.xclient.message_type == ATOM_WM_PROTOCOLS + && xev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW) + { + _should_close = true; + } + break; + case MapNotify: // make window not clickable + //set_clickthrough(); + XFlush(x11_d); + break; + } + } +} + +ContextX11::~ContextX11() +{ + glXMakeCurrent(x11_d, None, NULL); + glXDestroyContext(x11_d, glc); + XDestroyWindow(x11_d, window); + XCloseDisplay(x11_d); +} diff --git a/src/context_x11.h b/src/context_x11.h new file mode 100644 index 0000000..c0fd015 --- /dev/null +++ b/src/context_x11.h @@ -0,0 +1,86 @@ +#ifndef _CONTEXT_X11_H +#define _CONTEXT_X11_H + +#include "context.h" + +#include <chrono> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/extensions/Xrender.h> +#include <X11/extensions/shape.h> + +#include <glad/gl.h> + +#include <GL/glx.h> +#include <GL/glu.h> + +#define XWIN_ALL_DESKTOPS 0xFFFFFFFF + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + + +typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const int *); + +using std::chrono::time_point; +using std::chrono::system_clock; + +namespace Mauri { + +class ContextX11 : public Context { +public: + ContextX11(int width, int height, std::string name, bool wallpaper); + ~ContextX11(); + + bool create_window(int width, int height, std::string name, bool wallpaper); + void close_window(); + + void resize_window(int width, int height); + + bool should_close() const + { + return _should_close; + } + + void swap_buffers(); + void process_input(); + + void cursor_pos(float *x, float *y) + { + *x = 0.f; + *y = 0.f; + } + + double current_time() + { + std::chrono::duration<double> diff = (system_clock::now() - start); + return diff.count() * 0.5; + } + +private: + bool _should_close = false; + + time_point<system_clock> 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(); + + Window window; + Display *x11_d; + + GLXContext glc; +}; + +} // namespace Mauri + +#endif // _CONTEXT_X11_H diff --git a/src/engine.c b/src/engine.c deleted file mode 100644 index f8cb450..0000000 --- a/src/engine.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "engine.h" - -#include "moro_vec.h" -#include "objects/object.h" -#include "objects/scene.h" -#include "src/context.h" -#include "src/objects/types.h" -#include "src/shader.h" -#include "src/texture.h" - -#include <unistd.h> -#include <cglm/cglm.h> - -void mri_engine_load(mri_engine *engine, mri_scene *scene) { - engine->scene = scene; - - mvec_init(engine->texture_cache); - mvec_init(engine->framebuffers); - - mvec_reserve(engine->texture_cache, 100); - mvec_reserve(engine->framebuffers, mri_scene_framebuffer_count(scene) + 1); - - engine->width = scene->width; - engine->height = scene->height; - - engine->texel_size[0] = 1.f / engine->width; - engine->texel_size[1] = 1.f / engine->height; - - engine->texel_half_size[0] = .5f / engine->width; - engine->texel_half_size[1] = .5f / engine->height; - - engine->combine_buffer = mri_engine_get_or_new_framebuffer(engine, - mstr_c("_rt_FullFrameBuffer"), engine->context->width, engine->context->height, 1.f); - - create_gl_object(&engine->default_gl_object, DEFAULT, 0, 0); -} - -mri_framebuffer *mri_engine_get_framebuffer(mri_engine *engine, mstr *name) { - mri_framebuffer *existing_buffer; - mvec_loop_ptr(engine->framebuffers, existing_buffer, i) { - if (mstr_eq(existing_buffer->name, name)) { - return existing_buffer; - } - } - return NULL; -} - -mri_framebuffer *mri_engine_get_or_new_framebuffer(mri_engine *engine, mstr *name, - float width, float height, float scale) { - - mri_framebuffer *buffer = mri_engine_get_framebuffer(engine, name); - - if (!buffer) { - buffer = mvec_next(engine->framebuffers); - mri_framebuffer_create(buffer, name, width, height, scale); - mri_framebuffer_load(buffer); - } - - return buffer; -} - -void mri_engine_update(mri_engine *engine) { - engine->context->process_input(engine->context); - engine->time = engine->context->current_time(engine->context); -} - -void mri_engine_draw(mri_engine *engine) { - mri_framebuffer_bind(engine->combine_buffer); - glClearBufferfv(GL_COLOR, 0, engine->scene->clear_color); - - mri_scene_draw(engine, engine->scene); - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, engine->combine_buffer->buffer.id); - - // Drawing past the dimensions of the read buffer is an error. - glBlitFramebuffer(0, engine->context->height, engine->context->width, 0, 0, 0, - engine->context->width, engine->context->height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); -} - -void mri_engine_run(mri_engine *engine) { - const float TARGET_FPS = 60.f; - - float time = engine->context->current_time(engine->context); - - while (!engine->context->should_close(engine->context)) { - mri_engine_update(engine); - mri_engine_draw(engine); - - while (engine->context->current_time(engine->context) < time + 1.f / TARGET_FPS) { - usleep(2000); // 2ms - } - - time = engine->context->current_time(engine->context); - - engine->context->swap_buffers(engine->context); - } -} diff --git a/src/engine.cc b/src/engine.cc new file mode 100644 index 0000000..fc76bdd --- /dev/null +++ b/src/engine.cc @@ -0,0 +1,138 @@ +#include "engine.h" +#include "context.h" +#include "shader.h" +#include "src/gl.h" +#include "texture.h" + +#include "objects/object.h" +#include "objects/scene.h" + +#include <glm/ext.hpp> +#include <glm/fwd.hpp> +#include <thread> + +using namespace Mauri; + +void View::center_scale(float w1, float h1) +{ + auto w0 = this->width; + auto h0 = this->height; + + auto w_scale = w1 / w0; + auto h_scale = h1 / h0; + + auto scale = (w_scale > h_scale) ? w_scale : h_scale; + + auto width = w0 * scale; + auto height = h0 * scale; + + auto wpad = (width - w1) / -2.f; + auto hpad = (height - h1) / -2.f; + + glViewport(wpad, hpad, width, height); +} + +void View::default_viewport(float w0, float h0) +{ + glViewport(0, 0, w0, h0); +} + +Engine::Engine(Scene *scene, Context *context) +{ + this->scene = scene; + this->context = context; + + this->view.width = scene->width; + this->view.height = scene->height; + + this->view.texel_size[0] = 1.f / scene->width; + this->view.texel_size[1] = 1.f / scene->height; + + this->view.texel_half_size[0] = .5f / scene->width; + this->view.texel_half_size[1] = .5f / scene->height; + + this->texture_cache.reserve(100); + + auto combine = std::string("_rt_FullFrameBuffer"); + + this->new_framebuffer(combine, this->context->width, this->context->height, 1.f); + this->combine_buffer = this->get_framebuffer(combine); + + this->default_object = new RenderObject(DEFAULT); + this->identity = mat4x4(1.f); + + this->scene->load(this); +} + +Framebuffer *Engine::get_framebuffer(std::string name) +{ + return this->framebuffers[name]; +} + +Texture *Engine::get_framebuffer_texture(std::string name) +{ + return this->framebuffers[name]->texture; +} + +void Engine::new_framebuffer(std::string name, f32 width, f32 height, f32 scale) +{ + this->framebuffers[name] = new Framebuffer(width, height, scale); +} + +void Engine::update() +{ + this->context->process_input(); + this->time = this->context->current_time(); + + for (auto &object: this->scene->objects) + { + object->update(this); + } +} + +void Engine::draw() +{ + for (auto& buffer : this->framebuffers) + { + if (buffer.second != nullptr) + buffer.second->buffer->clear((vec4){0.f, 0.f, 0.f, 0.f}); + } + + if (this->scene->clear_enabled) + this->combine_buffer->buffer->clear(this->scene->clear_color); + + this->scene->draw(this); + + this->combine_buffer->buffer->blit(this->context->width, this->context->height); +} + +void Engine::run() +{ + const f32 TARGET_FPS = 60.f; + + f32 time = this->context->current_time(); + + while (!this->context->should_close()) + { + this->update(); + this->draw(); + + while (this->context->current_time() < time + 1.f / TARGET_FPS) + { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + + time = this->context->current_time(); + + this->context->swap_buffers(); + } +} + +Engine::~Engine() +{ + for (auto &buffer : this->framebuffers) + { + delete buffer.second; + } + delete this->default_object; +} diff --git a/src/engine.h b/src/engine.h index 1b5026a..c663e2d 100644 --- a/src/engine.h +++ b/src/engine.h @@ -1,49 +1,75 @@ #ifndef _ENGINE_H #define _ENGINE_H -#include <cglm/cglm.h> +#include <glm/glm.hpp> #include "context.h" -#include "texture.h" -#include "shader.h" #include "gl.h" +#include "shader.h" +#include "texture.h" -#include "objects/types.h" -#include "objects/object.h" - -struct mri_engine { - mri_context *context; - mri_scene *scene; +using namespace glm; - float width; - float height; +static const std::string COMBINE_BUFFER = "_rt_FullFrameBuffer"; - float time; +namespace Mauri +{ - vec2 texel_size; - vec2 texel_half_size; +class Scene; +class Object; - gl_object default_gl_object; +struct View +{ + float width; + float height; - mri_framebuffer *combine_buffer; + float shake_y = 0.f; + float shake_x = 0.f; - mvec(mri_framebuffer) framebuffers; + bool shake_y_switch = false; + bool shake_x_switch = false; - mvec(mri_texture) texture_cache; + vec2 texel_size; + vec2 texel_half_size; - mri_object *current_object; + void center_scale(float w0, float h0); + void default_viewport(float w0, float h0); }; -void mri_engine_load(mri_engine *engine, mri_scene *scene); +class Engine +{ + public: + Engine(Scene *scene, Context *context); + ~Engine(); + + View view; + + Context *context; + + float time; -mri_framebuffer *mri_engine_get_framebuffer(mri_engine *engine, mstr *name); -mri_framebuffer *mri_engine_get_or_new_framebuffer(mri_engine *engine, - mstr *name, float width, float height, float scale); + RenderObject *default_object; + mat4x4 identity; -void mri_engine_next_pass(mri_engine *engine); -void mri_engine_next_effect(mri_engine *engine); -void mri_engine_next_object(mri_engine *engine, mri_object *object); + std::vector<Texture> texture_cache; + + std::unordered_map<std::string, Framebuffer *> framebuffers; + + Framebuffer *combine_buffer; + + void new_framebuffer(std::string name, float width, float height, float scale); + + Framebuffer *get_framebuffer(std::string name); + Texture *get_framebuffer_texture(std::string name); + + void run(); + void draw(); + void update(); + + private: + Scene *scene; +}; -void mri_engine_run(mri_engine *engine); +} // namespace Mauri #endif // _ENGINE_H diff --git a/src/gl.c b/src/gl.c deleted file mode 100644 index b40880a..0000000 --- a/src/gl.c +++ /dev/null @@ -1,238 +0,0 @@ -#include <glad/gl.h> -#include <stddef.h> - -#include <moro.h> - -#include "cglm/vec4.h" -#include "gl.h" -#include "src/log.h" - -void bind_gl_texture(gl_texture *tex, unsigned int index) { - glActiveTexture(GL_TEXTURE0 + index); - glBindTexture(GL_TEXTURE_2D, tex->id); -} - -void create_gl_texture(gl_texture *tex, bool interp, bool clamp_uv, int mm_count) { - glGenTextures(1, &tex->id); - - bind_gl_texture(tex, 0); - - if (interp) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (clamp_uv) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mm_count - 1); -} - -void upload_gl_texture(gl_texture *tex, unsigned char *data, int width, int height, int level, - unsigned int format) { - bind_gl_texture(tex, 0); - - int _width = m_npof2(width); - int _height = m_npof2(height); - - glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, _width, _height, 0, - format, GL_UNSIGNED_BYTE, NULL); - - glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, - format, GL_UNSIGNED_BYTE, data); -} - -void destroy_gl_texture(gl_texture *tex) { - glDeleteTextures(1, &tex->id); -} - -void bind_gl_framebuffer(gl_framebuffer *buffer) { - glBindFramebuffer(GL_FRAMEBUFFER, buffer->id); -} - -void create_gl_framebuffer(gl_framebuffer *buffer, float width, float height) { - glGenFramebuffers(1, &buffer->id); - - glGenTextures(1, &buffer->texture.id); - glBindTexture(GL_TEXTURE_2D, buffer->texture.id); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindFramebuffer(GL_FRAMEBUFFER, buffer->id); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - buffer->texture.id, 0); - - glDrawBuffers(1, (GLenum[]){GL_COLOR_ATTACHMENT0}); - - glClearBufferfv(GL_COLOR, 0, (vec4){0.f, 0.f, 0.f, 0.f}); -} - -void destroy_gl_framebuffer(gl_framebuffer *buffer) { - glDeleteFramebuffers(1, &buffer->id); -} - -static void set_gl_object(gl_object *obj, float *vertices, int vertex_count) { - glGenVertexArrays(1, &obj->vao); - glBindVertexArray(obj->vao); - - glGenBuffers(1, &obj->vbo); - glBindBuffer(GL_ARRAY_BUFFER, obj->vbo); - - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)12); - - glBufferData(GL_ARRAY_BUFFER, vertex_count, vertices, GL_STATIC_DRAW); -} - -void create_gl_object(gl_object *obj, gl_obj_type type, float width, float height) { - switch (type) { - case DEFAULT: { - float vertices[] = { - -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, 0.f, 1.f, - 1.f, 1.f, 0.f, 1.f, 1.f, - 1.f, -1.f, 0.f, 1.f, 0.f - }; - - set_gl_object(obj, vertices, sizeof(vertices)); - break; - } - case 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. - float x_scale = width / (float)m_npof2(width); - float y_scale = height / (float)m_npof2(height); - - float 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 - }; - - set_gl_object(obj, vertices, sizeof(vertices)); - break; - } - case OBJECT: { - float 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 - }; - - set_gl_object(obj, vertices, sizeof(vertices)); - break; - } - } -} - -void bind_gl_object(gl_object *obj) { - glBindVertexArray(obj->vao); - glBindBuffer(GL_ARRAY_BUFFER, obj->vbo); -} - -static bool compile_check_error(unsigned int shader, char *source, char *error) { - glShaderSource(shader, 1, (const char * const*)&source, 0); - glCompileShader(shader); - - int status; - - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - - if (status == GL_FALSE) { - glGetShaderInfoLog(shader, 256, NULL, error); - glDeleteShader(shader); - - return false; - } - - return true; -} - -void create_gl_shader(gl_shader *shader, char *vs_source, char *fs_source) { - shader->program = glCreateProgram(); - - char error[256]; - - unsigned int vs_shader = glCreateShader(GL_VERTEX_SHADER); - if (!compile_check_error(vs_shader, vs_source, error)) { - log_error("failed to compile vertex shader (%s)", error); - return; - } - - unsigned int fs_shader = glCreateShader(GL_FRAGMENT_SHADER); - if (!compile_check_error(fs_shader, fs_source, error)) { - log_error("failed to compile fragment shader (%s)", error); - return; - } - - glAttachShader(shader->program, vs_shader); - glAttachShader(shader->program, fs_shader); - - glLinkProgram(shader->program); -} - -void bind_gl_shader(gl_shader *shader) { - glUseProgram(shader->program); -} - -void destroy_gl_shader(gl_shader *shader) { - glDeleteProgram(shader->program); -} - -void set_gl_uniform(unsigned int loc, uniform_type type, float *value) { - switch (type) { - case TYPE_FLOAT: - glUniform1f(loc, *value); - break; - case TYPE_SAMPLER2D: - glUniform1i(loc, (int)*value); - break; - case TYPE_VEC2: - glUniform2fv(loc, 1, value); - break; - case TYPE_VEC3: - glUniform3fv(loc, 1, value); - break; - case TYPE_VEC4: - glUniform4fv(loc, 1, value); - break; - case TYPE_MAT4: - glUniformMatrix4fv(loc, 1, GL_FALSE, value); - break; - } -} - -unsigned int get_uniform_loc(const char *name, gl_shader *shader) { - return glGetUniformLocation(shader->program, name); -} - -void set_gl_uniform_named(const char *name, gl_shader *shader, uniform_type type, - float *value) { - set_gl_uniform(get_uniform_loc(name, shader), type, value); -} diff --git a/src/gl.cc b/src/gl.cc new file mode 100644 index 0000000..9da7974 --- /dev/null +++ b/src/gl.cc @@ -0,0 +1,368 @@ +#include <glm/glm.hpp> +#include <glm/gtc/type_ptr.hpp> + +#include <glad/gl.h> + +#include "gl.h" +#include "log.h" +#include "util.h" + +using namespace Mauri; + +void RenderObject::set_data(f32 *vertices, s32 vertex_count) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glGenVertexArrays(1, &this->vao); + glBindVertexArray(this->vao); + + glGenBuffers(1, &this->vbo); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + 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); +} + +void RenderObject::bind() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glBindVertexArray(this->vao); + glBindBuffer(GL_ARRAY_BUFFER, this->vbo); +} + +RenderObject::RenderObject(RenderObjType type, f32 width, f32 height) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + switch (type) + { + case RenderObjType::DEFAULT: { + f32 vertices[] = { + -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, 0.f, 1.f, + 1.f, 1.f, 0.f, 1.f, 1.f, + 1.f, -1.f, 0.f, 1.f, 0.f + }; + this->set_data(vertices, sizeof(vertices)); + 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); + + 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 + }; + this->set_data(vertices, sizeof(vertices)); + break; + } + case RenderObjType::OBJECT: { + 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 + }; + this->set_data(vertices, sizeof(vertices)); + break; + } + } +} + +RenderObject::~RenderObject() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glDeleteVertexArrays(1, &this->vao); + glDeleteBuffers(1, &this->vbo); +} + +bool RenderShader::compile(u32 program, const char *source, char *error) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glShaderSource(program, 1, &source, 0); + glCompileShader(program); + + s32 status; + + glGetShaderiv(program, GL_COMPILE_STATUS, &status); + + if (status == GL_FALSE) + { + glGetShaderInfoLog(program, 256, NULL, error); + glDeleteShader(program); + return false; + } + + return true; +} + +RenderShader::RenderShader(std::string vs_source, std::string fs_source) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + this->uniform_locations.clear(); + this->program = glCreateProgram(); + + char error[256]; + + u32 vs_shader = glCreateShader(GL_VERTEX_SHADER); + if (!this->compile(vs_shader, vs_source.c_str(), error)) + { + log_error("failed to compile vertex shader (%s)", error); + return; + } + + u32 fs_shader = glCreateShader(GL_FRAGMENT_SHADER); + if (!this->compile(fs_shader, fs_source.c_str(), error)) + { + log_error("failed to compile fragment shader (%s)", error); + return; + } + + glAttachShader(this->program, vs_shader); + glAttachShader(this->program, fs_shader); + + glLinkProgram(this->program); +} + +void RenderShader::bind() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glUseProgram(this->program); +} + +RenderShader::~RenderShader() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glDeleteShader(this->program); +} + +s32 RenderShader::uniform_loc(std::string &name) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + auto loc = this->uniform_locations.find(name); + + if (loc == this->uniform_locations.end()) + { + this->uniform_locations[name] = glGetUniformLocation(this->program, name.c_str()); + return this->uniform_locations[name]; + } + else + return loc->second; +} + +void RenderShader::set_uniform(std::string name, UniformType type, f32 *value) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + auto loc = this->uniform_loc(name); + + switch (type) + { + case TYPE_FLOAT: + glUniform1f(loc, *value); + break; + case TYPE_SAMPLER2D: + glUniform1i(loc, (s32)*value); + break; + case TYPE_VEC2: + glUniform2fv(loc, 1, value); + break; + case TYPE_VEC3: + glUniform3fv(loc, 1, value); + break; + case TYPE_VEC4: + glUniform4fv(loc, 1, value); + break; + case TYPE_MAT4: + glUniformMatrix4fv(loc, 1, GL_FALSE, value); + break; + case TYPE_INVALID: + break; + } +} + +void RenderTexture::bind(s32 index) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, this->id); +} + +RenderTexture::RenderTexture(bool interp, bool clamp_uv, s32 mm_count) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glGenTextures(1, &this->id); + + glBindTexture(GL_TEXTURE_2D, this->id); + + if (interp) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + else + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (clamp_uv) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mm_count - 1); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void RenderTexture::upload(byte *data, s32 width, s32 height, s32 level, u32 format, bool scale, bool dxt) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + this->bind(0); + + if (scale) + { + s32 _width = next_power_of_two(width); + s32 _height = next_power_of_two(height); + + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, _width, _height, 0, + format, GL_UNSIGNED_BYTE, nullptr); + + if (dxt) + { + glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, + format, GL_UNSIGNED_INT_8_8_8_8, data); + } + else + { + glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, width, height, + format, GL_UNSIGNED_BYTE, data); + } + } + else + { + glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, width, height, 0, + format, GL_UNSIGNED_BYTE, data); + } +} + +RenderTexture::~RenderTexture() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glDeleteTextures(1, &this->id); +} + +RenderFramebuffer::RenderFramebuffer(f32 width, f32 height, f32 scale) +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + this->width = width / scale; + this->height = height / scale; + + this->texture = new RenderTexture(false, false, 1); + + glGenFramebuffers(1, &this->id); + + this->texture->upload(nullptr, this->width, this->height, 0, GL_RGBA, false); + + glBindFramebuffer(GL_FRAMEBUFFER, this->id); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, this->texture->id, 0); + + GLenum attatchment[] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, attatchment); +} + +void RenderFramebuffer::bind() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + glBindFramebuffer(GL_FRAMEBUFFER, this->id); +} + +void RenderFramebuffer::blit(f32 width, f32 height) +{ + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, this->id); + glBlitFramebuffer(0, this->height, this->width, 0, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); +} + +void RenderFramebuffer::clear(vec4 color) +{ + glBindFramebuffer(GL_FRAMEBUFFER, this->id); + glClearBufferfv(GL_COLOR, 0, glm::value_ptr(color)); +} + +RenderFramebuffer::~RenderFramebuffer() +{ +#ifdef __DEBUG__ + if (!gl_initialized) return; +#endif + + delete this->texture; + glDeleteFramebuffers(1, &this->id); +} + @@ -1,61 +1,103 @@ #ifndef _GL_H #define _GL_H -#include <stdbool.h> -#include <cglm/cglm.h> +#include <iostream> +#include <unordered_map> -typedef enum { - DEFAULT, - UV_SCALE, - OBJECT -} gl_obj_type; +#include <glm/glm.hpp> -typedef enum { - TYPE_FLOAT, - TYPE_VEC4, - TYPE_VEC3, - TYPE_VEC2, - TYPE_MAT4, - TYPE_SAMPLER2D -} uniform_type; +#include "util.h" -typedef struct { - unsigned int vao; - unsigned int vbo; -} gl_object; +using namespace glm; -typedef struct { - unsigned int program; -} gl_shader; +namespace Mauri +{ -typedef struct { - unsigned int id; -} gl_texture; +#ifdef __DEBUG__ +static bool gl_initialized = false; +#endif -typedef struct { - unsigned int id; - gl_texture texture; -} gl_framebuffer; +enum RenderObjType +{ + DEFAULT, + UV_SCALE, + OBJECT +}; -void bind_gl_texture(gl_texture *tex, unsigned int index); -void create_gl_texture(gl_texture *tex, bool interp, bool clamp_uv, int mm_count); -void upload_gl_texture(gl_texture *tex, unsigned char *data, int width, int height, int level, - unsigned int format); -void destroy_gl_texture(gl_texture *tex); +enum UniformType +{ + TYPE_FLOAT, + TYPE_VEC4, + TYPE_VEC3, + TYPE_VEC2, + TYPE_MAT4, + TYPE_SAMPLER2D, + TYPE_INVALID +}; -void bind_gl_framebuffer(gl_framebuffer *buffer); -void create_gl_framebuffer(gl_framebuffer *buffer, float width, float height); -void destroy_gl_framebuffer(gl_framebuffer *buffer); +class RenderObject +{ + public: + RenderObject(RenderObjType type, f32 width = 0.f, f32 height = 0.f); + ~RenderObject(); -void create_gl_object(gl_object *obj, gl_obj_type type, float width, float height); -void bind_gl_object(gl_object *obj); + void bind(); + void set_data(float *vertices, s32 vertex_count); -void create_gl_shader(gl_shader *shader, char *vs_source, char *fs_source); -void bind_gl_shader(gl_shader *shader); -void destroy_gl_shader(gl_shader *shader); + private: + u32 vao; + u32 vbo; +}; -void set_gl_uniform(unsigned int loc, uniform_type type, float *value); -unsigned int get_uniform_loc(const char *name, gl_shader *shader); -void set_gl_uniform_named(const char *name, gl_shader *shader, uniform_type type, float *value); +class RenderShader +{ + public: + RenderShader(std::string vs_source, std::string fs_source); + ~RenderShader(); + + bool compile(u32 program, const char *source, char *error); + + void set_uniform(std::string name, UniformType type, f32 *value); + s32 uniform_loc(std::string &name); + + void bind(); + + private: + u32 program = 0; + + std::unordered_map<std::string, s32> uniform_locations; +}; + +class RenderTexture +{ + public: + RenderTexture(bool interp, bool clamp_uv, s32 mm_count); + ~RenderTexture(); + + u32 id = 0; + + void bind(int index); + void upload(byte *data, s32 width, s32 height, s32 level, u32 format, bool scale, bool dxt = false); +}; + +class RenderFramebuffer +{ + public: + RenderFramebuffer(f32 width, f32 height, f32 scale); + ~RenderFramebuffer(); + + u32 id = 0; + + f32 width; + f32 height; + + RenderTexture *texture = nullptr; + + void bind(); + void blit(f32 width, f32 height); + void clear(vec4 color); +}; + +} // namespace Mauri #endif // _GL_H diff --git a/src/json.c b/src/json.c deleted file mode 100644 index ddebcc9..0000000 --- a/src/json.c +++ /dev/null @@ -1,84 +0,0 @@ -#include <cglm/cglm.h> -#include <jansson.h> - -#include "json.h" -#include "log.h" - -json_t *json_load_mstr(mstr *s) { - json_error_t error; - json_t *root = json_loadb(s->ptr, s->len, 0, &error); - - if (!root) { - log_error("err: failed to parse json on line %d: %s", error.line, error.text); - return NULL; - } - - return root; -} - -static json_t *json_value_user(json_t *value) { - if (json_is_object(value)) { - return json_object_get(value, "value"); - } - return value; -} - -void json_get_bool(bool *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_boolean(value)) { - *result = json_boolean_value(value); - } else { - *result = false; - //log_debug("using default value for (%s)", name); - } -} - -void json_get_int(int *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_integer(value)) { - *result = json_integer_value(value); - } else { - *result = 0; - //log_debug("using default value for (%s)", name); - } -} - -void json_get_float(float *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_number(value)) { - *result = json_number_value(value); - } else { - *result = 0.f; - //log_debug("using default value for (%s)", name); - } -} - -void json_get_vec2(vec2 *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_string(value)) { - sscanf_vec2(json_string_value(value), *result); - } else { - memset(*result, 0, sizeof(vec2)); - //log_debug("using default value for (%s)", name); - } -} - -void json_get_vec3(vec3 *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_string(value)) { - sscanf_vec3(json_string_value(value), *result); - } else { - memset(*result, 0, sizeof(vec3)); - //log_debug("using default value for (%s)", name); - } -} - -void json_get_vec4(vec4 *result, json_t *root, const char *name) { - json_t *value = json_value_user(json_object_get(root, name)); - if (json_is_string(value)) { - sscanf_vec4(json_string_value(value), *result); - } else { - memset(*result, 0, sizeof(vec4)); - //log_debug("using default value for (%s)", name); - } -} diff --git a/src/json.cc b/src/json.cc new file mode 100644 index 0000000..2be9a0e --- /dev/null +++ b/src/json.cc @@ -0,0 +1,21 @@ +#include "json.h" + +namespace Mauri { + +Json::Value json_user_value(Json::Value value) +{ + if (value.isObject()) { + return value["value"]; + } + return value; +} + +void json_number(Json::Value value, float *res) +{ + if (value.isNumeric()) + *res = value.asFloat(); + else if (value.isString()) + sscanf_vec4(value.asCString(), res); +} + +} @@ -1,24 +1,17 @@ #ifndef _JSON_H #define _JSON_H -#include <cglm/cglm.h> -#include <stdbool.h> -#include <moro_str.h> +#include <jsoncpp/json/json.h> -json_t *json_load_mstr(mstr *s); - -#define json_array(value, array) \ - for (size_t i = 0; i < json_array_size(array) && (value = json_array_get(array, i), 1); i++) +namespace Mauri { #define sscanf_vec4(s, v) sscanf(s, "%f %f %f %f", &(v)[0], &(v)[1], &(v)[2], &(v)[3]) #define sscanf_vec3(s, v) sscanf(s, "%f %f %f", &(v)[0], &(v)[1], &(v)[2]) #define sscanf_vec2(s, v) sscanf(s, "%f %f", &(v)[0], &(v)[1]) - -void json_get_bool(bool *result, json_t *root, const char *name); -void json_get_int(int *result, json_t *root, const char *name); -void json_get_float(float *result, json_t *root, const char *name); -void json_get_vec2(vec2 *result, json_t *root, const char *name); -void json_get_vec3(vec3 *result, json_t *root, const char *name); -void json_get_vec4(vec4 *result, json_t *root, const char *name); + +extern Json::Value json_user_value(Json::Value value); +extern void json_number(Json::Value value, float *res); + +} // namespace Mauri #endif // _JSON_H @@ -8,16 +8,15 @@ #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__); \ - } \ - while(0) +#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_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__) #ifdef _DEBUG_ diff --git a/src/mauri.c b/src/mauri.c deleted file mode 100644 index 75d9d47..0000000 --- a/src/mauri.c +++ /dev/null @@ -1,42 +0,0 @@ -#include <stdio.h> -#include <moro_str.h> - -#include "assets.h" -#include "context.h" -#include "engine.h" -#include "context_glfw.h" -#include "objects/scene.h" - -int main(int argc, char* argv[]) { - mri_context *context = mri_glfw_context_new(); - context->create_window(context, 1920, 1080, "mauri", false); - - if (argc == 2) { - if (!mri_asset_manager_load_pkg(mstr_c(argv[1]))) { - return 1; - } - - mri_scene scene = {0}; - mri_scene_parse(&scene); - - //context->resize_window(context, scene.width, scene.height); - - mri_engine engine = {0}; - - engine.context = context; - - mri_engine_load(&engine, &scene); - - mri_scene_load(&engine, &scene); - - mri_engine_run(&engine); - - mri_scene_unload(&scene); - mri_asset_manager_unload(); - } - - context->close_window(context); - mri_glfw_context_unload(context); - - return 0; -} diff --git a/src/mauri.cc b/src/mauri.cc new file mode 100644 index 0000000..f6f6703 --- /dev/null +++ b/src/mauri.cc @@ -0,0 +1,54 @@ +#include "assets.h" +#include "engine.h" +#include "gl.h" +#include "util.h" + +#include "context.h" +#include "context_glfw.h" +#include "context_x11.h" + +#include "objects/scene.h" + +#include <iostream> + +using namespace Mauri; + +static std::string scene_path = std::string("scene.json"); + +s32 main(s32 argc, char *argv[]) +{ + srand(time(NULL)); + +#ifdef __DEBUG__ + gl_initialized = true; +#endif + + //Context *context = new ContextX11(2560, 1440, "mauri", true); + Context *context = new ContextGLFW(720, 1280, "mauri", false); + //Context *context = new ContextGLFW(1920, 1080, "mauri", false); + + if (argc == 2) + { + if (!asset_manager()->load_package(std::string(argv[1]))) + { + delete context; + return 1; + } + + Scene *scene = new Scene(scene_path); + + //context->resize_window(scene->width, scene->height); + + Engine engine(scene, context); + + engine.run(); + + delete scene; + } + + context->close_window(); + + delete context; + + return 0; +} diff --git a/src/objects/effect.c b/src/objects/effect.c deleted file mode 100644 index d5fb747..0000000 --- a/src/objects/effect.c +++ /dev/null @@ -1,133 +0,0 @@ -#include "effect.h" - -#include "../engine.h" -#include "../json.h" -#include "../log.h" - -#include "cglm/mat4.h" -#include "moro_str.h" -#include "moro_vec.h" -#include "src/objects/pass.h" -#include "src/objects/types.h" -#include <jansson.h> - -void mri_effect_parse(mri_effect *effect, json_t *root) { - mvec_init(effect->passes); - mvec_init(effect->passes0); - mvec_init(effect->passes1); - - json_get_int(&effect->id, root, "id"); - - json_get_bool(&effect->visible, root, "visible"); - - json_t *pass0; - json_t *passes0 = json_object_get(root, "passes"); - json_array(pass0, passes0) { - mri_pass_parse(mvec_next(effect->passes0), pass0, EFFECT0_PASS); - } - - json_t *file = json_object_get(root, "file"); - if (json_is_string(file)) { - mri_asset *effect_asset = mri_asset_manager_get(mstr_c(json_string_value(file)), NONE); - - if (effect_asset->type == ASSET_VOID) - return; - - json_t *_root = json_load_mstr(mri_asset_as_str(effect_asset)); - - effect->name = mstr_from(json_string_value(json_object_get(_root, "name"))); - - json_t *pass1; - json_t *passes1 = json_object_get(_root, "passes"); - json_array(pass1, passes1) { - mri_pass_parse(mvec_next(effect->passes1), pass1, EFFECT1_PASS); - } - - mvec_init(effect->fbos); - - json_t *fbo; - json_t *fbos = json_object_get(_root, "fbos"); - json_array(fbo, fbos) { - mri_effect_buffer buffer = { - .name = mstr_from(json_string_value(json_object_get(fbo, "name"))), - .scale = json_number_value(json_object_get(fbo, "scale")) - }; - mvec_push(effect->fbos, buffer); - } - } -} - -mri_render_pass *mri_effect_next_pass(mri_effect *effect) { - mri_render_pass *pass = mvec_next(effect->passes); - mvec_init(pass->uniforms); - mvec_init(pass->combos); - return pass; -} - -void mri_effect_load(mri_engine *engine, mri_effect *effect, bool last_effect) { - mri_effect_buffer *buffer; - mvec_loop_ptr(effect->fbos, buffer, i) { - mri_object *object = engine->current_object; - - mstr *effect_buffer_id = mstr_sprintf("%i_%i", effect->id, object->id); - mstr_cat(buffer->name, effect_buffer_id); - mstr_free(effect_buffer_id); - - mri_engine_get_or_new_framebuffer(engine, buffer->name, - object->size[0], object->size[1], buffer->scale); - } - - for (unsigned int i = 0; i < mvec_size(effect->passes0); i++) { - mri_render_pass *pass = mri_effect_next_pass(effect); - - pass->object = engine->current_object; - pass->effect = effect; - - pass->combine = (i == mvec_size(effect->passes0) - 1) && last_effect; - - if (pass->combine) { - pass->gl_object = &engine->current_object->gl_object; - glm_mat4_copy(engine->current_object->model, pass->model); - } else { - pass->gl_object = &engine->default_gl_object; - glm_mat4_identity(pass->model); - } - - mri_pass_load(engine, mvec_at(effect->passes0, i), EFFECT0_PASS, pass); - mri_pass_load(engine, mvec_at(effect->passes1, i), EFFECT1_PASS, pass); - } -} - -void mri_effect_draw(mri_engine *engine, mri_effect *effect) { - mri_render_pass *pass; - mvec_loop_ptr(effect->passes, pass, i) { - //log_debug("effect (%.*s): %.*s %i", pass->object->name->len, pass->object->name->ptr, - // effect->name->len, effect->name->ptr, i + 1); - mri_render_pass_draw(engine, pass); - } -} - -void mri_effect_unload(mri_effect *effect) { - mstr_free(effect->name); - - mri_pass *pass; - - mvec_loop_ptr(effect->passes0, pass, i) { - mri_pass_unload(pass); - } - - mvec_loop_ptr(effect->passes1, pass, i) { - mri_pass_unload(pass); - } - - mvec_free(effect->passes0); - mvec_free(effect->passes1); - - mri_effect_buffer *buffer; - mvec_loop_ptr(effect->fbos, buffer, i) { - if (buffer->name) - mstr_free(buffer->name); - } - - mvec_free(effect->fbos); -} diff --git a/src/objects/effect.cc b/src/objects/effect.cc new file mode 100644 index 0000000..7f22ef0 --- /dev/null +++ b/src/objects/effect.cc @@ -0,0 +1,90 @@ +#include "../assets.h" +#include "../engine.h" +#include "../json.h" +#include "../log.h" + +#include "effect.h" +#include "fmt/core.h" +#include "object.h" + +#include "objects/pass.h" + +#include <jsoncpp/json/reader.h> + +#include <fmt/format.h> +#include <glm/fwd.hpp> + +using namespace Mauri; + +Effect::Effect(Json::Value root) +{ + this->id = root["id"].asInt(); + + this->visible = json_user_value(root.get("visible", true)).asBool(); + + for (auto &pass0 : root["passes"]) + { + this->passes0.emplace_back(new Pass(pass0, EFFECT0_PASS)); + } + + auto asset = asset_manager()->get_file(root["file"].asString(), NONE); + + if (asset->type == ASSET_VOID) + return; + + Json::Value file = asset->as_json(); + + this->name = file["name"].asString(); + + for (auto &pass1 : file["passes"]) + { + this->passes1.emplace_back(new Pass(pass1, EFFECT1_PASS)); + } + + for (auto &fbo : file["fbos"]) + { + this->fbos.emplace_back((EffectBuffer) { fbo["name"].asString(), fbo["scale"].asFloat() }); + } +} + +void Effect::load(Engine *engine, Object *object, bool last_effect) +{ + if (!this->visible) + return; + + this->buffer_id = fmt::format("{}_{}", this->id, object->id); + + for (auto &fbo : this->fbos) + { + fbo.name.append(this->buffer_id); + engine->new_framebuffer(fbo.name, object->size[0], object->size[1], fbo.scale); + } + + for (int i = 0; i < this->passes0.size(); i++) + { + Pass *pass = new Pass(); + + pass->combine = (i == (this->passes0.size() - 1)) && last_effect; + + pass->object = object; + pass->effect = this; + + this->passes0[i]->load(EFFECT0_PASS, pass); + this->passes1[i]->load(EFFECT1_PASS, pass); + + object->passes.push_back(pass); + } +} + +Effect::~Effect() +{ + for (auto &pass0 : this->passes0) + { + delete pass0; + } + + for (auto &pass1 : this->passes1) + { + delete pass1; + } +} diff --git a/src/objects/effect.h b/src/objects/effect.h index 4d31081..9edeea7 100644 --- a/src/objects/effect.h +++ b/src/objects/effect.h @@ -1,32 +1,43 @@ #ifndef _EFFECT_H #define _EFFECT_H -#include <moro.h> +#include <vector> -#include "types.h" -#include "pass.h" +#include "engine.h" -typedef struct { - mstr *name; - float scale; -} mri_effect_buffer; +namespace Mauri +{ -struct mri_effect { - mstr *name; - int id; +class Pass; + +struct EffectBuffer +{ + std::string name; + float scale; +}; + +class Effect +{ + public: + Effect(Json::Value root); + ~Effect(); + + u32 id; + std::string name; + + std::string buffer_id; + + void load(Engine *engine, Object *object, bool last_effect); - bool visible; + private: + bool visible; - mvec(mri_pass) passes0; - mvec(mri_pass) passes1; - mvec(mri_render_pass) passes; + std::vector<Pass *> passes0; + std::vector<Pass *> passes1; - mvec(mri_effect_buffer) fbos; + std::vector<EffectBuffer> fbos; }; -void mri_effect_parse(mri_effect *effect, json_t *root); -void mri_effect_load(mri_engine *engine, mri_effect *effect, bool last_effect); -void mri_effect_draw(mri_engine *engine, mri_effect *effect); -void mri_effect_unload(mri_effect *effect); +} // namespace Mauri #endif // _EFFECT_H diff --git a/src/objects/material.c b/src/objects/material.c deleted file mode 100644 index 75422d1..0000000 --- a/src/objects/material.c +++ /dev/null @@ -1,87 +0,0 @@ -#include "material.h" -#include "cglm/mat4.h" -#include "pass.h" - -#include <moro_str.h> - -#include "../log.h" -#include "../json.h" -#include "../engine.h" -#include "src/objects/types.h" - -void mri_material_parse(mri_material *material, json_t *value) { - mvec_init(material->passes); - mvec_init(material->passes0); - - if (json_is_string(value)) { - mri_asset *material_asset = mri_asset_manager_get(mstr_c(json_string_value(value)), NONE); - - if (material_asset->type == ASSET_VOID) - return; - - json_t *root = json_load_mstr(mri_asset_as_str(material_asset)); - - json_t *pass; - json_t *passes = json_object_get(root, "passes"); - json_array(pass, passes) { - mri_pass_parse(mvec_next(material->passes0), pass, MATERIAL_PASS); - } - - json_decref(root); - } -} - -mri_render_pass *mri_material_next_pass(mri_material *material) { - mri_render_pass *pass = mvec_next(material->passes); - mvec_init(pass->uniforms); - mvec_init(pass->combos); - return pass; -} - -void mri_material_load(mri_engine *engine, mri_material *material, mri_material_type t, - mri_render_pass *pass, bool no_effects) { - mri_pass *pass0; - mvec_loop_ptr(material->passes0, pass0, i) { - switch (t) { - case MATERIAL_EFFECT: - mri_pass_load(engine, pass0, MATERIAL_PASS, pass); - break; - case MATERIAL_MODEL: - pass = mri_material_next_pass(material); - - pass->object = engine->current_object; - pass->combine = no_effects; - - if (engine->current_object->passthrough || no_effects) { - glm_mat4_copy(engine->current_object->model, pass->model); - } else { - glm_mat4_copy(engine->current_object->ortho, pass->model); - } - - if (engine->current_object->passthrough) { - pass->gl_object = &engine->current_object->gl_object; - } else { - pass->gl_object = &engine->current_object->gl_uv_object; - } - - mri_pass_load(engine, pass0, MATERIAL_ONLY_PASS, pass); - break; - } - } -} - -void mri_material_draw(mri_engine *engine, mri_material *material) { - mri_render_pass *pass; - mvec_loop_ptr(material->passes, pass, i) { - //log_debug("background (%.*s): %i", pass->object->name->len, pass->object->name->ptr, i); - mri_render_pass_draw(engine, pass); - } -} - -void mri_material_unload(mri_material *material) { - mri_pass *pass0; - mvec_loop_ptr(material->passes0, pass0, i) { - mri_pass_unload(pass0); - } - mvec_free(material->passes); -} diff --git a/src/objects/material.cc b/src/objects/material.cc new file mode 100644 index 0000000..4c61cbc --- /dev/null +++ b/src/objects/material.cc @@ -0,0 +1,64 @@ +#include <jsoncpp/json/json.h> + +#include "assets.h" +#include "engine.h" +#include "json.h" +#include "log.h" + +#include "objects/material.h" +#include "objects/object.h" +#include "objects/pass.h" + +using namespace Mauri; + +Material::Material(std::string path) +{ + auto asset = asset_manager()->get_file(path, NONE); + + if (asset->type == ASSET_VOID) + { + log_error("failed to load material.json (%s)", path.c_str()); + return; + } + + Json::Value root = asset->as_json(); + + for (auto &pass : root["passes"]) + { + this->passes0.push_back(new Pass(pass, MATERIAL_PASS)); + } +} + +void Material::load(Object *object, MaterialType type, Pass *pass, bool no_effects) +{ + for (auto &pass0 : this->passes0) + { + switch (type) + { + case MATERIAL_EFFECT: + pass0->load(MATERIAL_PASS, pass); + break; + case MATERIAL_MODEL: + Pass *pass = new Pass(); + + pass->combine = no_effects; + + pass->object = object; + pass->effect = nullptr; + + pass0->load(MATERIAL_ONLY_PASS, pass); + + object->passes.push_back(pass); + + break; + } + } +} + +Material::~Material() +{ + for (auto &pass0 : this->passes0) + { + delete pass0; + } +} diff --git a/src/objects/material.h b/src/objects/material.h index e8bc13d..31b3be0 100644 --- a/src/objects/material.h +++ b/src/objects/material.h @@ -1,25 +1,32 @@ #ifndef _MATERIAL_H #define _MATERIAL_H -#include <jansson.h> +#include <iostream> -#include "types.h" -#include "pass.h" +#include "engine.h" -typedef enum { - MATERIAL_MODEL, - MATERIAL_EFFECT -} mri_material_type; +namespace Mauri +{ -struct mri_material { - mvec(mri_pass) passes0; - mvec(mri_render_pass) passes; +class Pass; + +enum MaterialType +{ + MATERIAL_MODEL, + MATERIAL_EFFECT +}; + +class Material +{ + public: + Material(std::string path); + ~Material(); + + void load(Object *object, MaterialType type, Pass *pass, bool no_effects); + + std::vector<Pass *> passes0; }; -void mri_material_parse(mri_material *material, json_t *value); -void mri_material_load(mri_engine *engine, mri_material *material, mri_material_type t, mri_render_pass *pass, - bool no_effects); -void mri_material_draw(mri_engine *engine, mri_material *material); -void mri_material_unload(mri_material *material); +} // namespace Mauri #endif // MATERIAL_H diff --git a/src/objects/model.c b/src/objects/model.c deleted file mode 100644 index 45317f2..0000000 --- a/src/objects/model.c +++ /dev/null @@ -1,43 +0,0 @@ -#include <jansson.h> -#include <moro_str.h> - -#include "../json.h" -#include "../assets.h" -#include "../log.h" -#include "../engine.h" - -#include "model.h" -#include "src/objects/types.h" - -void mri_model_parse(mri_model *model, json_t *value) { - if (json_is_string(value)) { - mri_asset *model_asset = mri_asset_manager_get(mstr_c(json_string_value(value)), NONE); - - if (model_asset->type == ASSET_VOID) - return; - - json_t *root = json_load_mstr(mri_asset_as_str(model_asset)); - - json_get_bool(&model->autosize, root, "autosize"); - json_get_bool(&model->fullscreen, root, "fullscreen"); - json_get_bool(&model->passthrough, root, "passthrough"); - - mri_material_parse(&model->material, json_object_get(root, "material")); - - json_decref(root); - } else { - memset(model, 0, sizeof(mri_model)); - } -} - -void mri_model_load(mri_engine *engine, mri_model *model, bool no_effects) { - mri_material_load(engine, &model->material, MATERIAL_MODEL, NULL, no_effects); -} - -void mri_model_draw(mri_engine *engine, mri_model *model) { - mri_material_draw(engine, &model->material); -} - -void mri_model_unload(mri_model *model) { - mri_material_unload(&model->material); -} diff --git a/src/objects/model.cc b/src/objects/model.cc new file mode 100644 index 0000000..59c1cb3 --- /dev/null +++ b/src/objects/model.cc @@ -0,0 +1,40 @@ +#include <jsoncpp/json/json.h> + +#include "assets.h" +#include "json.h" +#include "log.h" + +#include "objects/material.h" +#include "objects/model.h" + +using namespace Mauri; + +Model::Model(std::string path) +{ + auto asset = asset_manager()->get_file(path, NONE); + + if (asset->type == ASSET_VOID) + { + log_error("failed to load model.json (%s)", path.c_str()); + return; + } + + Json::Value root = asset->as_json(); + + this->autosize = root.get("autosize", true).asBool(); + this->fullscreen = root.get("fullscreen", false).asBool(); + this->passthrough = root.get("passthrough", false).asBool(); + + this->material = new Material(root["material"].asString()); +} + +void Model::load(Object *object, bool no_effects) +{ + this->material->load(object, MATERIAL_MODEL, NULL, no_effects); +} + +Model::~Model() +{ + if (this->material != nullptr) + delete this->material; +} diff --git a/src/objects/model.h b/src/objects/model.h index 9daed21..e25a592 100644 --- a/src/objects/model.h +++ b/src/objects/model.h @@ -1,22 +1,31 @@ #ifndef _MODEL_H #define _MODEL_H -#include <jansson.h> +#include <iostream> -#include "types.h" -#include "pass.h" -#include "material.h" +#include "engine.h" -struct mri_model { - bool autosize; - bool fullscreen; - bool passthrough; - mri_material material; +#include "objects/material.h" + +namespace Mauri +{ + +class Model +{ + public: + Model(std::string path); + ~Model(); + + bool autosize; + bool fullscreen; + bool passthrough; + + void load(Object *object, bool no_effects); + + private: + Material *material = nullptr; }; -void mri_model_parse(mri_model *model, json_t *value); -void mri_model_load(mri_engine *engine, mri_model *model, bool no_effects); -void mri_model_draw(mri_engine *engine, mri_model *model); -void mri_model_unload(mri_model *model); +} // namespace Mauri #endif // _MODEL_H diff --git a/src/objects/object.c b/src/objects/object.c index 431852e..69c5411 100644 --- a/src/objects/object.c +++ b/src/objects/object.c @@ -1,174 +1 @@ -#include <jansson.h> -#include <cglm/cglm.h> - -#include "moro_str.h" -#include "src/shader.h" -#include "src/texture.h" - -#include "types.h" -#include "object.h" -#include "model.h" -#include "scene.h" - -#include "../log.h" -#include "../gl.h" -#include "../engine.h" -#include "../json.h" - -void mri_object_parse(mri_object *object, json_t *root) { - json_get_int(&object->id, root, "id"); - - object->name = mstr_from(json_string_value(json_object_get(root, "name"))); - - mvec_init(object->deps); - - json_t *dep; - json_t *deps = json_object_get(root, "dependencies"); - json_array(dep, deps) { - int value = json_integer_value(dep); - mvec_push(object->deps, value); - } - - json_get_float(&object->alpha, root, "alpha"); - json_get_vec3(&object->angles, root, "angles"); - json_get_vec3(&object->color, root, "color"); - json_get_vec2(&object->size, root, "size"); - json_get_vec3(&object->scale, root, "scale"); - json_get_vec3(&object->origin, root, "origin"); - object->origin[2] = 0.f; - - json_get_int(&object->color_blend_mode, root, "colorblendmode"); - json_get_bool(&object->copy_background, root, "copybackground"); - - json_get_bool(&object->visible, root, "visible"); - - json_t *config = json_object_get(root, "config"); - if (json_is_object(config)) { - json_get_bool(&object->passthrough, config, "passthrough"); - } - - mri_model_parse(&object->image, json_object_get(root, "image")); - - mvec_init(object->effects); - - json_t *effect; - json_t *effects = json_object_get(root, "effects"); - json_array(effect, effects) { - mri_effect_parse(mvec_next(object->effects), effect); - } -} - -void mri_object_get_model(mri_object *object, mat4 model, float width, float height) { - glm_mat4_identity(model); - - glm_translate_to(model, (vec3){-width, -height, 0.f}, model); - - vec3 origin; - glm_vec3_scale(object->origin, 2.f, origin); - glm_translate_to(model, origin, model); - - glm_rotate(model, object->angles[0], (vec3){1.f, 0.f, 0.f}); - glm_rotate(model, object->angles[1], (vec3){0.f, 1.f, 0.f}); - glm_rotate(model, object->angles[2], (vec3){0.f, 0.f, 1.f}); - - glm_scale(model, object->scale); - - mat4 ortho; - glm_ortho(-width, width, height, -height, 0.f, 1.f, ortho); - glm_mat4_mul(ortho, model, model); -} - -void mri_object_load(mri_engine *engine, mri_object *object) { - if (object->loaded) return; - - engine->current_object = object; - - float width = object->size[0]; - float height = object->size[1]; - - mstr *abuffer = mstr_sprintf("_rt_imageLayerComposite_%i_a", object->id); - mstr *bbuffer = mstr_sprintf("_rt_imageLayerComposite_%i_b", object->id); - - object->abuffer = mri_engine_get_or_new_framebuffer(engine, - abuffer, width, height, 1.f); - - object->bbuffer = mri_engine_get_or_new_framebuffer(engine, - bbuffer, width, height, 1.f); - - mstr_free(abuffer); - mstr_free(bbuffer); - - object->buffer_switch = false; - - mri_object_get_model(object, engine->current_object->model, - engine->width, engine->height); - - glm_ortho(-width, width, -height, height, 0.f, 1.f, - engine->current_object->ortho); - - create_gl_object(&engine->current_object->gl_uv_object, - UV_SCALE, width, height); - - create_gl_object(&engine->current_object->gl_object, - OBJECT, width, height); - - mri_model_load(engine, &object->image, mvec_size(object->effects) == 0); - - if (object->image.fullscreen) { - object->fullscreen = true; - object->size[0] = engine->width; - object->size[1] = engine->height; - object->origin[0] = object->size[0] / 2.f; - object->origin[1] = object->size[1] / 2.f; - } - - if (object->image.passthrough) { - object->passthrough = true; - } - - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, i) { - mri_effect_load(engine, effect, i == (mvec_size(object->effects) - 1)); - } - - object->loaded = true; -} - -void mri_object_switch_buffer(mri_object *object) { - object->buffer_switch = !object->buffer_switch; -} - -mri_framebuffer *mri_object_get_target_buffer(mri_object *object) { - return (object->buffer_switch) ? object->abuffer : object->bbuffer; -} - -mri_texture *mri_object_get_texture_buffer(mri_object *object) { - return &((object->buffer_switch) ? object->bbuffer : object->abuffer)->texture; -} - -void mri_object_draw(mri_engine *engine, mri_object *object) { - mri_model_draw(engine, &object->image); - - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, i) { - mri_effect_draw(engine, effect); - } -} - -void mri_object_unload(mri_object *object) { - mstr_free(object->name); - - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, i) { - mri_effect_unload(effect); - } - - mvec_free(object->effects); - mvec_free(object->deps); - - mri_framebuffer_unload(object->abuffer); - mri_framebuffer_unload(object->bbuffer); - - mri_model_unload(&object->image); -} - +//#include "../engine.h" diff --git a/src/objects/object.cc b/src/objects/object.cc new file mode 100644 index 0000000..a356637 --- /dev/null +++ b/src/objects/object.cc @@ -0,0 +1,174 @@ +#include <glm/fwd.hpp> +#include <glm/trigonometric.hpp> +#include <jsoncpp/json/json.h> + +#include <glm/ext/matrix_transform.hpp> +#include <glm/glm.hpp> +#include <glm/gtc/type_ptr.hpp> + +#include <fmt/core.h> + +#include "../gl.h" +#include "../json.h" +#include "../log.h" + +#include "objects/effect.h" +#include "objects/object.h" +#include "objects/scene.h" +#include "objects/pass.h" + +using namespace Mauri; + +Object::Object(Json::Value root) +{ + this->id = root["id"].asInt(); + this->name = root["name"].asString(); + + for (auto &id : root["dependencies"]) + this->deps.push_back(id.asInt()); + + sscanf_vec3(json_user_value(root.get("angles", "")).asCString(), this->angles); + sscanf_vec3(json_user_value(root.get("colors", "")).asCString(), this->color); + sscanf_vec2(json_user_value(root.get("size", "")).asCString(), this->size); + sscanf_vec3(json_user_value(root.get("scale", "")).asCString(), this->scale); + sscanf_vec3(json_user_value(root.get("origin", "")).asCString(), this->origin); + + this->color_blend_mode = (BlendMode)root.get("colorblendmode", 0).asInt(); + + this->visible = json_user_value(root.get("visible", true)).asBool(); + + auto _image = root["image"]; + + if (!_image.isNull()) + this->image = new Model(_image.asString()); + + auto config = root["config"]; + + if (!config.isNull()) + this->passthrough = config.get("passthrough", false).asBool(); + + for (auto &effect : root["effects"]) + { + this->effects.emplace_back(new Effect(effect)); + } +} + +void Object::get_model(f32 width, f32 height, bool flip) +{ + this->model = mat4x4(1.f); + + this->model = glm::translate(this->model, (vec3){-width, -height, 0.f}); + this->model = glm::translate(this->model, this->origin * 2.f); + + this->model = glm::rotate(this->model, this->angles[0], (vec3){1.f, 0.f, 0.f}); + this->model = glm::rotate(this->model, this->angles[1], (vec3){0.f, 1.f, 0.f}); + this->model = glm::rotate(this->model, this->angles[2], (vec3){0.f, 0.f, 1.f}); + + this->model = glm::scale(this->model, this->scale); + + mat4x4 _ortho = glm::ortho(-width, width, height, -height, 0.f, 1.f); + + if (flip) + { + this->model = glm::scale(this->model, (vec3){1.f, -1.f, 1.f}); + } + + this->model = _ortho * this->model; +} + +void Object::load(Engine *engine) +{ + if (this->loaded || !this->visible) + return; + + f32 width = this->size[0]; + f32 height = this->size[1]; + + this->abuffer = fmt::format("_rt_imageLayerComposite_{}_a", this->id); + this->bbuffer = fmt::format("_rt_imageLayerComposite_{}_b", this->id); + + engine->new_framebuffer(this->abuffer, width, height, 1.f); + engine->new_framebuffer(this->bbuffer, width, height, 1.f); + + this->get_model(engine->view.width, engine->view.height, this->effects.size() == 0); + + this->ortho = glm::ortho(-width, width, -height, height, 0.f, 1.f); + + this->gl_object = new RenderObject(OBJECT, width, height); + this->gl_uv_object = new RenderObject(UV_SCALE, width, height); + + if (this->image) + { + this->image->load(this, this->effects.size() == 0); + + if (this->image->fullscreen) + { + this->fullscreen = true; + this->size[0] = engine->view.width; + this->size[1] = engine->view.height; + this->origin[0] = this->size[0] / 2.f; + this->origin[1] = this->size[1] / 2.f; + } + + if (this->image->passthrough) + this->passthrough = true; + } + + for (int i = 0; i < this->effects.size(); i++) + { + this->effects[i]->load(engine, this, i == (this->effects.size() - 1)); + } + + this->loaded = true; +} + +void Object::update(Engine *engine) +{ + //if (engine->time > this->shake_tick) + //{ + //} +} + +void Object::draw(Engine *engine) +{ + if (!this->visible) + return; + + for (auto &pass : this->passes) + { + pass->draw(engine); + } +} + +std::string Object::get_target_buffer() const +{ + if (this->buffer_switch) + return this->abuffer; + else + return this->bbuffer; +} + +std::string Object::get_texture_buffer() const +{ + if (this->buffer_switch) + return this->bbuffer; + else + return this->abuffer; +} + +Object::~Object() +{ + if (this->image != nullptr) + delete this->image; + + if (this->gl_object != nullptr) + delete this->gl_object; + + if (this->gl_uv_object != nullptr) + delete this->gl_uv_object; + + for (auto &effect : this->effects) + { + delete effect; + } +} diff --git a/src/objects/object.h b/src/objects/object.h index fbaae60..fe44e03 100644 --- a/src/objects/object.h +++ b/src/objects/object.h @@ -1,76 +1,90 @@ #ifndef _OBJECT_H #define _OBJECT_H -#include <moro.h> -#include <jansson.h> -#include <stdbool.h> -#include <cglm/cglm.h> +#include <glm/glm.hpp> +#include <jsoncpp/json/json.h> + +#include "../engine.h" -#include "src/texture.h" -#include "types.h" -#include "model.h" -#include "pass.h" #include "effect.h" +#include "model.h" -#include "../gl.h" +using namespace glm; -typedef enum { - CENTER, -} mri_object_alignment; +namespace Mauri +{ -struct mri_object { - int id; - mstr *name; +enum ObjectAlignment +{ + CENTER, +}; - mvec(int) deps; +enum BlendMode +{ + TRANSLUCENT = 0, + NORMAL = 1 +}; - mri_object_alignment alignment; +class Object +{ + public: + Object(Json::Value root); + ~Object(); - float alpha; + u32 id; + std::string name; - vec3 angles; - vec3 color; + std::vector<int> deps; - int color_blend_mode; + bool passthrough = false; + bool fullscreen = false; - bool copy_background; + vec2 size; - mri_model image; + void get_model(f32 width, f32 height, bool flip); - vec2 size; - vec3 origin; - vec3 scale; + mat4x4 ortho; + mat4x4 model; - bool visible; + RenderObject *gl_uv_object = nullptr; + RenderObject *gl_object = nullptr; - bool passthrough; - bool fullscreen; + BlendMode color_blend_mode; - gl_object gl_uv_object; - gl_object gl_object; - - mat4 ortho; - mat4 model; + std::string get_target_buffer() const; + std::string get_texture_buffer() const; - mri_framebuffer *abuffer; - mri_framebuffer *bbuffer; + void swap_buffer() { + this->buffer_switch = !this->buffer_switch; + } - bool buffer_switch; + std::vector<Pass *> passes; - bool loaded; + void load(Engine *engine); + void update(Engine *engine); + void draw(Engine *engine); - mvec(mri_effect) effects; -}; + private: + bool visible; -void mri_object_get_model(mri_object *object, mat4 model, float width, float height); + Model *image = nullptr; -void mri_object_switch_buffer(mri_object *object); -mri_framebuffer *mri_object_get_target_buffer(mri_object *object); -mri_texture *mri_object_get_texture_buffer(mri_object *object); + vec3 angles; + vec3 color; + + vec3 origin; + vec3 scale; + + std::string abuffer; + std::string bbuffer; + + bool buffer_switch = false; + + bool loaded = false; + + std::vector<Effect *> effects; +}; -void mri_object_parse(mri_object *object, json_t *root); -void mri_object_load(mri_engine *engine, mri_object *object); -void mri_object_draw(mri_engine *engine, mri_object *object); -void mri_object_unload(mri_object *object); +} // namespace Mauri #endif // _OBJECT_H diff --git a/src/objects/pass.c b/src/objects/pass.c deleted file mode 100644 index df3b70d..0000000 --- a/src/objects/pass.c +++ /dev/null @@ -1,405 +0,0 @@ -#include <jansson.h> -#include <unistd.h> - -#include "moro_str.h" -#include "moro_vec.h" -#include "pass.h" -#include "material.h" -#include "model.h" -#include "object.h" -#include "scene.h" - -#include "../json.h" -#include "../texture.h" -#include "../engine.h" -#include "../assets.h" -#include "../gl.h" -#include "../objects/types.h" -#include "../shader.h" - -void mri_pass_parse(mri_pass *pass, json_t *root, mri_pass_stage stage) { - memset(pass, 0, sizeof(mri_pass)); - - switch (stage) { - case EFFECT0_PASS: { - mvec_init(pass->uniforms); - - json_t *uniform; - json_t *uniforms = json_object_get(root, "constantshadervalues"); - - void *iter = json_object_iter(uniforms); - - while (iter) { - uniform = json_object_iter_value(iter); - - mri_uniform u = { - .name = mstr_from(json_object_iter_key(iter)) - }; - - if (json_is_object(uniform)) { - uniform = json_object_get(uniform, "value"); - } - - if (json_is_number(uniform)) { - u.value[0] = json_number_value(uniform); - } else if (json_is_string(uniform)) { - sscanf_vec4(json_string_value(uniform), u.value); - } - - mvec_push(pass->uniforms, u); - - iter = json_object_iter_next(uniforms, iter); - } - - break; - } - case EFFECT1_PASS: { - json_t *material = json_object_get(root, "material"); - if (json_is_string(material)) { - pass->material = (mri_material *)calloc(1, sizeof(mri_material)); - mri_material_parse(pass->material, material); - } - - json_t *target = json_object_get(root, "target"); - if (json_is_string(target)) { - pass->target = mstr_from(json_string_value(target)); - } - - json_t *bind; - json_t *binds = json_object_get(root, "bind"); - json_array(bind, binds) { - int index = json_integer_value(json_object_get(bind, "index")); - pass->binds[index] = mstr_from(json_string_value(json_object_get(bind, "name"))); - } - - break; - } - case MATERIAL_ONLY_PASS: - case MATERIAL_PASS: { - json_t *shader = json_object_get(root, "shader"); - if (json_is_string(shader)) { - pass->shader = (mri_shader *)calloc(1, sizeof(mri_shader)); - mri_shader_load(pass->shader, mstr_c(json_string_value(shader))); - } - - json_t *blend = json_object_get(root, "blending"); - if (json_is_string(blend)) { - if (mstr_eq(mstr_c(json_string_value(blend)), mstr_c("normal"))) { - pass->blend = NORMAL; - } else if (mstr_eq(mstr_c(json_string_value(blend)), mstr_c("translucent"))) { - pass->blend = TRANSLUCENT; - } - } - - break; - } - } - - json_t *texture; - json_t *textures = json_object_get(root, "textures"); - json_array(texture, textures) { - if (json_is_string(texture)) { - pass->textures[i] = mstr_from(json_string_value(texture)); - } - } - - mvec_init(pass->combos); - - json_t *combo; - json_t *combos = json_object_get(root, "combos"); - - void *iter = json_object_iter(combos); - - while (iter) { - combo = json_object_iter_value(iter); - - mri_combo c = { - .name = mstr_from(json_object_iter_key(iter)) - }; - - if (json_is_integer(combo)) { - c.value = json_integer_value(combo); - } - - mvec_push(pass->combos, c); - - iter = json_object_iter_next(combo, iter); - } -} - -void mri_pass_load(mri_engine *engine, mri_pass *pass, mri_pass_stage stage, mri_render_pass *rpass) { - mri_object *object = engine->current_object; - - for (int i = 0; i < 7; i++) { - if (pass->textures[i]) { - mri_asset *texture_asset = mri_asset_manager_get( - pass->textures[i], TEXTURE); - if (texture_asset->type != ASSET_VOID) { - rpass->textures[i] = mri_texture_load(engine, texture_asset); - } else { - mri_framebuffer *buffer = mri_engine_get_framebuffer( - engine, pass->textures[i]); - if (buffer) { - rpass->textures[i] = &buffer->texture; - } - } - } - } - - mri_combo *combo; - mvec_loop_ptr(pass->combos, combo, i) { - mvec_push(rpass->combos, *combo); - } - - switch (stage) { - case EFFECT0_PASS: { - mri_uniform *uniform; - mvec_loop_ptr(pass->uniforms, uniform, i) { - mvec_push(rpass->uniforms, *uniform); - } - break; - } - case EFFECT1_PASS: - // If there is an explicit target framebuffer, set it. - if (!pass->target) { - if (rpass->combine) { - rpass->target = engine->combine_buffer; - } else { - // If no target was specified pick the next buffer in the - // alternating AB buffers. - rpass->target = mri_object_get_target_buffer(object); - } - rpass->textures[0] = mri_object_get_texture_buffer(object); - } else { - mstr *effect_buffer_id = mstr_sprintf("%i_%i", rpass->effect->id, rpass->object->id); - - mstr_cat(pass->target, effect_buffer_id); - mstr_free(effect_buffer_id); - - rpass->target = mri_engine_get_framebuffer(engine, pass->target); - } - - for (int i = 0; i < 7; i++) { - if (!pass->binds[i]) continue; - // If there are explicit binds given for this pass, set them. - if (mstr_eq(pass->binds[i], mstr_c("previous"))) { - rpass->textures[i] = mri_object_get_texture_buffer(object); - } else { - mstr *effect_buffer_id = mstr_sprintf("%i_%i", rpass->effect->id, rpass->object->id); - - mstr_cat(pass->binds[i], effect_buffer_id); - mstr_free(effect_buffer_id); - - rpass->textures[i] = &mri_engine_get_framebuffer(engine, pass->binds[i])->texture; - } - } - - // If we selected one of the AB buffers, switch the flag _after_ - // setting binds to keep the ordering correct. - if (!pass->target) mri_object_switch_buffer(object); - - // EFFECT1 passes has the material with the final draw passes. - mri_material_load(engine, pass->material, MATERIAL_EFFECT, rpass, false); - break; - case MATERIAL_ONLY_PASS: - // There is no EFFECT1 pass before this to set the target, so set it here. - if (rpass->combine) { - rpass->target = engine->combine_buffer; - } else { - rpass->target = mri_object_get_target_buffer(object); - mri_object_switch_buffer(object); - } - rpass->combine_blend = pass->blend; - __attribute__ ((fallthrough)); - case MATERIAL_PASS: { - rpass->shader = *pass->shader; - - mvec_clone(rpass->shader.uniforms, pass->shader->uniforms); - mvec_clone(rpass->shader.combos, pass->shader->combos); - - for (int i = 0; i < 7; i++) { - // As the last step for shaders, set any unset texture slots - // with the default textures from the shader is there is any. - if (!rpass->textures[i] && rpass->shader.textures[i]) { - mri_asset *texture_asset = mri_asset_manager_get( - rpass->shader.textures[i], TEXTURE); - if (texture_asset->type != ASSET_VOID) { - rpass->textures[i] = mri_texture_load(engine, texture_asset); - } - } - - // Set TEX0FORMAT in-case extra steps are needed when sampling, - // see "common_fragment.h". - if (rpass->textures[i] && i == 0) { - mri_combo c = { - .name = mstr_from("TEX0FORMAT"), - .value = rpass->textures[i]->format - }; - mvec_push(rpass->shader.combos, c); - } - } - - mri_uniform *pass_uniform, *shader_uniform; - mvec_loop_ptr(rpass->shader.uniforms, shader_uniform, i) { - // ex: g_Texture2Resolution (vec4) - // ^ index - if (mstr_eqn(shader_uniform->uname, mstr_c("g_Texture"), 9) && - mstr_cmp(shader_uniform->uname, mstr_c("Resolution"), 10, 10) == 0) { - int index = shader_uniform->uname->ptr[9] - '0'; - if (rpass->textures[index]) { - memcpy(shader_uniform->value, - rpass->textures[index]->texture_resolution, sizeof(vec4)); - } - } - } - - // Handle uniform value overrides. - mvec_loop_ptr(rpass->uniforms, pass_uniform, i) - mvec_loop_ptr(rpass->shader.uniforms, shader_uniform, s) { - if (!shader_uniform->name) continue; - if (mstr_eq(pass_uniform->name, shader_uniform->name)) { - memcpy(shader_uniform->value, pass_uniform->value, sizeof(vec4)); - break; - } - } - - mri_combo *pass_combo, *shader_combo; - mvec_loop_ptr(rpass->shader.combos, shader_combo, s) { - if (mstr_eq(shader_combo->name, mstr_c("AUDIOPROCESSING"))) { - shader_combo->value = 0; - break; - } - } - - // Handle combo value overrides. - mvec_loop_ptr(rpass->combos, pass_combo, i) { - bool override = false; - mvec_loop_ptr(rpass->shader.combos, shader_combo, s) { - if (mstr_eq(pass_combo->name, shader_combo->name)) { - shader_combo->value = pass_combo->value; - override = true; - break; - } - } - if (!override) { - mvec_push(rpass->shader.combos, *pass_combo); - } - } - - // compile the shader with the new combo values. - mri_shader_compile(&rpass->shader); - - break; - } - } -} - -static void viewport_center_scale(float w0, float h0, float w1, float h1) { - float w_scale = w1 / w0; - float h_scale = h1 / h0; - - float scale = (w_scale > h_scale) ? w_scale : h_scale; - - float width = w0 * scale; - float height = h0 * scale; - - float wpad = (width - w1) / -2.f; - float hpad = (height - h1) / -2.f; - - glViewport(wpad, hpad, width, height); -} - -static void viewport_default(float w0, float h0) { - glViewport(0, 0, w0, h0); -} - -void mri_render_pass_draw(mri_engine *engine, mri_render_pass *pass) { - mri_framebuffer_bind(pass->target); - mri_shader_bind(engine, &pass->shader, pass->model); - - bind_gl_object(pass->gl_object); - - for (int i = 0; i < pass->shader.texture_count; i++) { - if (pass->textures[i]) mri_texture_bind(pass->textures[i], i); - } - - mri_pass_blend_mode blend; - - if (pass->combine) { - blend = pass->combine_blend; - } else { - blend = NORMAL; - } - - switch (blend) { - case NORMAL: - glBlendFunc(GL_ONE, GL_ZERO); - glColorMask(1.f, 1.f, 1.f, 1.f); - break; - case TRANSLUCENT: - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColorMask(1.f, 1.f, 1.f, 0.f); - break; - } - - if (pass->combine) { - viewport_center_scale(engine->width, engine->height, engine->context->width, engine->context->height); - } else { - viewport_default(pass->target->width, pass->target->height); - } - - if (pass->object->visible) - glDrawArrays(GL_TRIANGLES, 0, 6); - - for (int i = 0; i < pass->shader.texture_count; i++) { - glActiveTexture(GL_TEXTURE0 + i); - glBindTexture(GL_TEXTURE_2D, 0); - } - -#if 0 - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->target->buffer.id); - - glBlitFramebuffer(0, engine->context->height, engine->context->width, 0, 0, 0, - engine->context->width, engine->context->height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - - engine->context->swap_buffers(engine->context); - - usleep(1400000); -#endif -} - -void mri_pass_unload(mri_pass *pass) { - // TODO render pass unloading - - for (int i = 0; i < 7; i++) { - if (pass->textures[i]) mstr_free(pass->textures[i]); - } - - mri_uniform *uniform; - mvec_loop_ptr(pass->uniforms, uniform, i) { - mstr_free(uniform->name); - } - - mri_combo *combo; - mvec_loop_ptr(pass->combos, combo, i) { - mstr_free(combo->name); - } - - mvec_free(pass->uniforms); - mvec_free(pass->combos); - - if (pass->shader) { - mri_shader_unload(pass->shader); - free(pass->shader); - } - - if (pass->material) { - mri_material_unload(pass->material); - free(pass->material); - } - - if (pass->target) - mstr_free(pass->target); -} diff --git a/src/objects/pass.cc b/src/objects/pass.cc new file mode 100644 index 0000000..7825602 --- /dev/null +++ b/src/objects/pass.cc @@ -0,0 +1,346 @@ +#include <fmt/core.h> +#include <glm/fwd.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <thread> + +#include "assets.h" +#include "engine.h" +#include "gl.h" +#include "json.h" +#include "shader.h" +#include "json.h" +#include "texture.h" + +#include "objects/pass.h" +#include "objects/material.h" +#include "objects/model.h" +#include "objects/object.h" +#include "objects/scene.h" + +using namespace Mauri; + +Pass::Pass(Json::Value root, PassStage stage) +{ + switch (stage) + { + case EFFECT0_PASS: { + auto uniforms = root["constantshadervalues"]; + + for (auto &name : uniforms.getMemberNames()) + { + auto uniform = Uniform(); + + uniform.name = name; + + json_number(json_user_value(uniforms[name]), + glm::value_ptr(uniform.value)); + + this->uniforms.push_back(uniform); + } + + break; + } + case EFFECT1_PASS: { + auto material = root["material"]; + + if (material.isString()) + this->material = new Material(material.asString()); + + this->target = root.get("target", "previous").asString(); + + for (auto &bind : root["bind"]) + { + auto index = bind["index"].asInt(); + this->binds[index] = bind["name"].asString(); + } + + break; + } + case MATERIAL_ONLY_PASS: + this->target = "previous"; + [[fallthrough]]; + case MATERIAL_PASS: { + this->shader = root["shader"].asString(); + + auto blend = root.get("blending", "normal").asString(); + + if (blend == "normal") + this->blend = NORMAL; + else if (blend == "translucent") + this->blend = TRANSLUCENT; + + break; + } + } + + auto _textures = root["textures"]; + + for (int i = 0; i < textures.size(); i++) + { + auto texture = _textures[i]; + if (texture.isString()) + this->textures[i] = texture.asString(); + } + + auto _combos = root["combos"]; + + for (auto &name : _combos.getMemberNames()) + { + this->combos.emplace_back((Combo){name, _combos[name].asInt()}); + } +} + +void Pass::load(PassStage stage, Pass *pass) +{ + for (int i = 0; i < 8; i++) + { + if (this->textures[i].empty()) + continue; + + pass->textures[i] = this->textures[i]; + } + + for (auto &combo : this->combos) + { + pass->combos.push_back(combo); + } + + switch (stage) + { + case EFFECT0_PASS: + for (auto &uniform : this->uniforms) + { + pass->uniforms.push_back(uniform); + } + break; + case EFFECT1_PASS: + pass->target = this->target; + + if (pass->target == "previous") + { + if (pass->combine) + pass->target = COMBINE_BUFFER; + else + pass->target = pass->object->get_target_buffer(); + + pass->textures[0] = pass->object->get_texture_buffer(); + } + else + pass->target.append(pass->effect->buffer_id); + + for (int i = 0; i < 8; i++) + { + if (this->binds[i].empty()) + continue; + + if (this->binds[i] == "previous") + pass->textures[i] = pass->object->get_texture_buffer(); + else + pass->textures[i] = this->binds[i] + pass->effect->buffer_id; + } + + if (this->target == "previous") + pass->object->swap_buffer(); + + if (this->material != nullptr) + this->material->load(pass->object, MATERIAL_EFFECT, pass, false); + + break; + case MATERIAL_ONLY_PASS: + if (pass->combine) + pass->target = COMBINE_BUFFER; + else + { + pass->target = pass->object->get_target_buffer(); + pass->object->swap_buffer(); + } + [[fallthrough]]; + case MATERIAL_PASS: + pass->shader = this->shader; + break; + } +} + +void Pass::generate_pass(Engine *engine) +{ + this->rpass = new RenderPass(); + + this->rpass->combine = this->combine; + this->rpass->combine_blend = this->object->color_blend_mode; + + this->rpass->shader = new Shader(this->shader); + + auto rshader = this->rpass->shader; + + for (int i = 0; i < 8; i++) + { + if (this->textures[i].empty() && !rshader->textures[i].empty()) + this->textures[i] = rshader->textures[i]; + + if (this->textures[i].empty()) + continue; + + auto asset = asset_manager()->get_file(this->textures[i], TEXTURE); + + if (asset->type != ASSET_VOID) + this->rpass->textures[i] = new Texture(asset); + else + this->rpass->textures[i] = engine->get_framebuffer_texture(this->textures[i]); + } + + for (auto &uniform : rshader->uniforms) + { + // ex: g_Texture2Resolution (vec4) + // ^ index + if (uniform.uname.compare(0, 9, "g_Texture") == 0 && + uniform.uname.compare(10, 10, "Resolution") == 0) + { + auto index = uniform.uname[9] - '0'; + if (this->rpass->textures[index] != nullptr) + uniform.value = this->rpass->textures[index]->texture_resolution; + } + } + + for (auto &pass_uniform : this->uniforms) + { + for (auto &shader_uniform : rshader->uniforms) + { + if (shader_uniform.name.empty()) + continue; + if (pass_uniform.name == shader_uniform.name) + { + shader_uniform.value = pass_uniform.value; + break; + } + } + } + + for (auto &combo : rshader->combos) + { + if (combo.name == "AUDIOPROCESSING") + { + combo.value = 0; + break; + } + } + + for (auto &pass_combo : this->combos) + { + auto add = true; + for (auto &shader_combo : rshader->combos) + { + if (pass_combo.name == shader_combo.name) + { + shader_combo.value = pass_combo.value; + add = false; + break; + } + } + if (add) + rshader->combos.push_back(pass_combo); + } + + this->rpass->shader->compile(); + + this->rpass->target = engine->get_framebuffer(this->target); + + if (this->effect == nullptr) + { + if (this->object->passthrough || this->combine) + this->rpass->model = &this->object->model; + else + this->rpass->model = &this->object->ortho; + + if (this->object->passthrough) + this->rpass->robject = this->object->gl_object; + else + this->rpass->robject = this->object->gl_uv_object; + } + else + { + if (this->combine) + { + this->rpass->robject = this->object->gl_object; + this->rpass->model = &this->object->model; + } + else + { + this->rpass->robject = engine->default_object; + this->rpass->model = &engine->identity; + } + } +} + +void Pass::draw(Engine *engine) +{ + if (this->rpass != nullptr) + this->rpass->draw(engine); +} + +Pass::~Pass() +{ + if (this->material != nullptr) + delete this->material; + + if (this->rpass != nullptr) + delete this->rpass; +} + +void RenderPass::draw(Engine *engine) +{ + this->target->buffer->bind(); + this->shader->bind(engine, this->model); + + this->robject->bind(); + + for (int i = 0; i < this->shader->texture_count; i++) + { + if (this->textures[i] != nullptr) + this->textures[i]->bind(i); + } + + BlendMode blend; + + if (this->combine) + blend = this->combine_blend; + else + blend = NORMAL; + + switch (blend) + { + case NORMAL: + glBlendFunc(GL_ONE, GL_ZERO); + glColorMask(1.f, 1.f, 1.f, 1.f); + break; + case TRANSLUCENT: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(1.f, 1.f, 1.f, 0.f); + break; + } + + if (this->combine) + engine->view.center_scale(engine->context->width, engine->context->height); + else + engine->view.default_viewport(this->target->buffer->width, this->target->buffer->height); + + glDrawArrays(GL_TRIANGLES, 0, 6); + +#if 0 + engine->combine_buffer->buffer->blit(this->context->width, this->context->height); + engine->context->swap_buffers(); + std::this_thread::sleep_for(std::chrono::seconds(1)); +#endif +} + +RenderPass::~RenderPass() +{ + if (this->shader != nullptr) + delete this->shader; + + for (int i = 0; i < 8; i++) + { + if (this->textures[i] != nullptr && !this->textures[i]->wrapped) + delete this->textures[i]; + } +} + diff --git a/src/objects/pass.h b/src/objects/pass.h index 59dcdba..f7d5129 100644 --- a/src/objects/pass.h +++ b/src/objects/pass.h @@ -1,79 +1,86 @@ #ifndef _PASS_H #define _PASS_H -#include <jansson.h> +#include <glm/glm.hpp> +#include <jsoncpp/json/json.h> -#include "src/gl.h" -#include "types.h" +#include "context.h" +#include "engine.h" +#include "gl.h" +#include "shader.h" +#include "texture.h" -#include "../context.h" -#include "../shader.h" -#include "../texture.h" +#include "objects/object.h" -typedef enum { - EFFECT0_PASS, - EFFECT1_PASS, - MATERIAL_PASS, - MATERIAL_ONLY_PASS -} mri_pass_stage; +namespace Mauri +{ -typedef enum { - BACKGROUND, - BACKGROUND_ONLY, - TRANSFORM, - COMBINE, - PASS, -} mri_render_pass_stage; +enum PassStage +{ + EFFECT0_PASS, + EFFECT1_PASS, + MATERIAL_PASS, + MATERIAL_ONLY_PASS +}; -typedef enum { - TRANSLUCENT, - NORMAL -} mri_pass_blend_mode; +struct RenderPass; -struct mri_pass { - int id; +class Pass +{ + public: + Pass() {} + Pass(Json::Value root, PassStage stage); - mri_material *material; + ~Pass(); - mri_shader *shader; + std::string target; - mstr *textures[7]; + Object *object; + Effect *effect; - mstr *target; - mstr *binds[7]; + bool combine; - mri_pass_blend_mode blend; + void load(PassStage stage, Pass *pass); + void draw(Engine *engine); - mvec(mri_uniform) uniforms; - mvec(mri_combo) combos; -}; + void generate_pass(Engine *engine); -struct mri_render_pass { - mri_shader shader; + private: + std::string shader; - mri_object *object; - mri_effect *effect; + Material *material = nullptr; - mat4 model; - gl_object *gl_object; + std::array<std::string, 8> textures; + std::array<std::string, 8> binds; - mri_texture *textures[7]; - mri_framebuffer *target; + BlendMode blend; - bool combine; + std::vector<Uniform> uniforms; + std::vector<Combo> combos; - mri_pass_blend_mode combine_blend; + RenderPass *rpass = nullptr; +}; - mri_render_pass_stage stage; +struct RenderPass +{ + RenderPass() {}; + ~RenderPass(); - mvec(mri_uniform) uniforms; - mvec(mri_combo) combos; -}; + Shader *shader; + Framebuffer *target; + + mat4x4 *model; + RenderObject *robject; -void mri_pass_parse(mri_pass *pass, json_t *root, mri_pass_stage stage); -void mri_pass_load(mri_engine *engine, mri_pass *pass, mri_pass_stage stage, mri_render_pass *rpass); -void mri_pass_unload(mri_pass *pass); + bool combine; + + BlendMode combine_blend; + + std::array<Texture *, 8> textures = {0}; + + void draw(Engine *engine); +}; -void mri_render_pass_draw(mri_engine *engine, mri_render_pass *pass); +} // namespace Mauri #endif // _PASS_H diff --git a/src/objects/scene.c b/src/objects/scene.c deleted file mode 100644 index 3bcd6ac..0000000 --- a/src/objects/scene.c +++ /dev/null @@ -1,104 +0,0 @@ -#include <jansson.h> -#include <moro_vec.h> - -#include "scene.h" -#include "object.h" - -#include "../json.h" -#include "../log.h" -#include "../engine.h" -#include "src/objects/types.h" - -void mri_scene_parse(mri_scene *scene) { - mri_asset *scene_asset = mri_asset_manager_get(mstr_c("scene.json"), NONE); - - if (scene_asset->type == ASSET_VOID) { - log_error("err: failed to open scene.json"); - return; - } - - json_t *root = json_load_mstr(mri_asset_as_str(scene_asset)); - - json_t *camera = json_object_get(root, "camera"); - json_t *general = json_object_get(root, "general"); - json_t *ortho = json_object_get(general, "orthogonalprojection"); - - json_get_vec3(&scene->camera.center, camera, "center"); - json_get_vec3(&scene->camera.eye, camera, "eye"); - json_get_vec3(&scene->camera.up, camera, "up"); - - json_get_bool(&scene->clear_enabled, general, "clearenabled"); - - json_get_vec3((vec3 *)&scene->clear_color, general, "clearcolor"); - scene->clear_color[3] = 1.f; - - json_get_vec3(&scene->ambient_color, general, "ambientcolor"); - json_get_vec3(&scene->skylight_color, general, "skylightcolor"); - - json_get_float(&scene->width, ortho, "width"); - json_get_float(&scene->height, ortho, "height"); - - mvec_init(scene->objects); - - json_t *object; - json_t *objects = json_object_get(root, "objects"); - json_array(object, objects) { - mri_object_parse(mvec_next(scene->objects), object); - } - - json_decref(root); -} - -int mri_scene_framebuffer_count(mri_scene *scene) { - int total = 0; - - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - mri_effect *effect; - mvec_loop_ptr(object->effects, effect, s) { - mri_pass *pass; - mvec_loop_ptr(effect->passes1, pass, j) { - if (pass->target) { - total++; - } - } - } - total += 2; - } - - return total; -} - -void mri_scene_load(mri_engine *engine, mri_scene *scene) { - engine->width = scene->width; - engine->height = scene->height; - - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - int dep; - mvec_loop(object->deps, dep, s) { - mri_object *dep_object; - mvec_loop_ptr(scene->objects, dep_object, j) { - if (dep_object->id == dep) { - mri_object_load(engine, dep_object); - } - } - } - mri_object_load(engine, object); - } -} - -void mri_scene_draw(mri_engine *engine, mri_scene *scene) { - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - mri_object_draw(engine, object); - } -} - -void mri_scene_unload(mri_scene *scene) { - mri_object *object; - mvec_loop_ptr(scene->objects, object, i) { - mri_object_unload(object); - } - mvec_free(scene->objects); -} diff --git a/src/objects/scene.cc b/src/objects/scene.cc new file mode 100644 index 0000000..5c00147 --- /dev/null +++ b/src/objects/scene.cc @@ -0,0 +1,120 @@ +#include <istream> +#include <jsoncpp/json/json.h> +#include <sstream> + +#include "assets.h" +#include "json.h" +#include "log.h" + +#include "objects/scene.h" +#include "objects/pass.h" + +using namespace Mauri; + +Scene::Scene(std::string path) +{ + auto asset = asset_manager()->get_file(path, NONE); + + if (asset->type == ASSET_VOID) + { + log_error("err: failed to open scene.json"); + return; + } + + Json::Value root = asset->as_json(); + + auto camera = root["camera"]; + auto general = root["general"]; + auto ortho = general["orthogonalprojection"]; + + sscanf_vec3(json_user_value(camera.get("center", "")).asCString(), this->camera.center); + sscanf_vec3(json_user_value(camera.get("eye", "")).asCString(), this->camera.eye); + sscanf_vec3(json_user_value(camera.get("up", "")).asCString(), this->camera.up); + + this->clear_enabled = general.get("clearenabled", false).asBool(); + + sscanf_vec3(json_user_value(general.get("clearcolor", "")).asCString(), this->clear_color); + this->clear_color[3] = 1.f; + + this->width = json_user_value(ortho.get("width", 0.f)).asFloat(); + this->height = json_user_value(ortho.get("height", 0.f)).asFloat(); + + for (auto &object : root["objects"]) + { + this->objects.emplace_back(new Object(object)); + } +} + +Object *Scene::get_object_by_id(u32 id) +{ + for (auto &object : this->objects) + { + if (object->id == id) + { + return object; + } + } + + return nullptr; +} + +void Scene::load(Engine *engine) +{ + engine->view.width = this->width; + engine->view.height = this->height; + + for (auto &object : this->objects) + { + for (auto &dep : object->deps) + { + auto dep_object = this->get_object_by_id(dep); + if (dep_object) + dep_object->load(engine); + } + + object->load(engine); + } + + for (auto &object : this->objects) + { + for (auto &pass : object->passes) + pass->generate_pass(engine); + } +} + +void Scene::draw(Engine *engine) +{ + for (auto &object : this->objects) + { + object->draw(engine); + } +} + +Scene::~Scene() +{ + for (auto &object : this->objects) + { + delete object; + } +} + +s32 Scene::estimated_buffer_count() +{ + return 0; +// s32 total = 0; +// +// for (auto &object : this->objects) +// { +// for (auto &effect : object->effects) +// { +// for (auto &pass1 : effect->passes1) +// { +// if (!pass1->target.empty()) +// total++; +// } +// } +// total += 2; +// } +// +// return total; +} diff --git a/src/objects/scene.h b/src/objects/scene.h index 9514580..47aa19a 100644 --- a/src/objects/scene.h +++ b/src/objects/scene.h @@ -1,47 +1,54 @@ #ifndef _SCENE_H #define _SCENE_H -#include <cglm/cglm.h> -#include <moro_vec.h> +#include <glm/glm.hpp> -#include "types.h" -#include "object.h" +#include <vector> -#include "../assets.h" +#include "assets.h" +#include "engine.h" -struct mri_scene { - float width; - float height; +#include "objects/object.h" - struct { - vec3 center; - vec3 eye; - vec3 up; +using namespace glm; - float farz; - float nearz; - float fov; - } camera; +namespace Mauri +{ - bool clear_enabled; +class Scene +{ + public: + Scene(std::string path); + ~Scene(); - vec4 clear_color; - vec3 ambient_color; - vec3 skylight_color; + f32 width; + f32 height; - bool bloom; + bool clear_enabled; + vec4 clear_color; - float bloom_strength; - float bloom_threshold; + void load(Engine *engine); + void draw(Engine *engine); - mvec(mri_object) objects; -}; + s32 estimated_buffer_count(); + + Object *get_object_by_id(u32 id); + + std::vector<Object *> objects; -void mri_scene_parse(mri_scene *scene); -void mri_scene_load(mri_engine *engine, mri_scene *scene); -void mri_scene_draw(mri_engine *engine, mri_scene *scene); -void mri_scene_unload(mri_scene *scene); + private: + struct + { + vec3 center; + vec3 eye; + vec3 up; + + float farz; + float nearz; + float fov; + } camera; +}; -int mri_scene_framebuffer_count(mri_scene *scene); +} // namespace Mauri #endif // _SCENE_H diff --git a/src/objects/types.h b/src/objects/types.h deleted file mode 100644 index 76fd8eb..0000000 --- a/src/objects/types.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _TYPES_H -#define _TYPES_H - -typedef struct mri_scene mri_scene; -typedef struct mri_object mri_object; -typedef struct mri_model mri_model; -typedef struct mri_material mri_material; -typedef struct mri_pass mri_pass; -typedef struct mri_effect mri_effect; - -typedef struct mri_render_pass mri_render_pass; - -typedef struct mri_engine mri_engine; - -#endif // _TYPES_H diff --git a/src/shader.c b/src/shader.c deleted file mode 100644 index a89eebc..0000000 --- a/src/shader.c +++ /dev/null @@ -1,306 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <jansson.h> -#include <glad/gl.h> - -#include <moro.h> - -#include "moro_str.h" -#include "shader.h" -#include "assets.h" -#include "json.h" -#include "log.h" -#include "gl.h" -#include "engine.h" - -#include "objects/object.h" - -static const char *gl_compat = - "#define highp\n" - "#define mediump\n" - "#define lowp\n" - "#define mul(x, y) (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 texSample2D texture2D\n" - "#define texSample2DLod texture2DLod\n" - "#define texture2DLod texture2D\n" - "#define atan2 atan\n" - "#define ddx dFdx\n" - "#define ddy(x) dFdy(-(x))\n" - "#define GLSL 1\n"; - -static const char *version = "#version 120\n"; - -static mstr *build_shader_source(mri_shader *shader, mstr *isource, bool root) { - mstr *source = mstr_from("\0"); - - // Append compatablity macros once per shader - if (root) mstr_cat(source, mstr_c(gl_compat)); - - mstr line = {0}; - - while (mstr_get_line(isource, &line)) { - bool cut_line = false; - - if (mstr_eqn(&line, mstr_c("#include"), 8)) { - mstr inc_name = mstr_substr(&line, mstr_find(&line, '"') + 1, mstr_rfind(&line, '"')); - - mri_asset *inc_asset = mri_asset_manager_get(&inc_name, SHADER); - mstr *inc = build_shader_source(shader, mri_asset_as_str(inc_asset), false); - - mstr_cat(source, inc); - mstr_free(inc); - - cut_line = true; - } else if (mstr_eqn(&line, mstr_c("uniform"), 7)) { - mri_uniform *u = mvec_next(shader->uniforms); - - mstr type = mstr_substr(&line, mstr_find(&line, ' ') + 1, mstr_len(&line)); - - if (mstr_eqn(&type, mstr_c("float"), 5)) u->type = TYPE_FLOAT; - else if (mstr_eqn(&type, mstr_c("vec4"), 4)) u->type = TYPE_VEC4; - else if (mstr_eqn(&type, mstr_c("vec3"), 4)) u->type = TYPE_VEC3; - else if (mstr_eqn(&type, mstr_c("vec2"), 4)) u->type = TYPE_VEC2; - else if (mstr_eqn(&type, mstr_c("mat4"), 4)) u->type = TYPE_MAT4; - else if (mstr_eqn(&type, mstr_c("sampler2D"), 9)) { - u->type = TYPE_SAMPLER2D; - u->value[0] = (float)shader->texture_count++; - } - - u->uname = mstr_substr_copy(&type, mstr_find(&type, ' ') + 1, mstr_find(&type, ';')); - - ptrdiff_t combo_start = mstr_find(&line, '/'); - - if (combo_start >= 0) { - mstr combo_str = mstr_substr(&line, combo_start + 2, mstr_len(&line)); - - json_t *root = json_load_mstr(&combo_str); - - if (!root) { - log_error("err: failed to load the combo for (%.*s)", MSTR_PRINTF(u->uname)); - continue; - } - - // If material is present, it's always used to reference this - // uniform for setting its values. If material is not present, - // label is used. - json_t *label = json_object_get(root, "label"); - json_t *material = json_object_get(root, "material"); - - if (json_is_string(material)) { - u->name = mstr_clone(mstr_c(json_string_value(material))); - } else if (json_is_string(label)) { - u->name = mstr_clone(mstr_c(json_string_value(label))); - } - - switch (u->type) { - case TYPE_SAMPLER2D: { - int index = (int)u->value[0]; - - json_t *texture = json_object_get(root, "default"); - if (json_is_string(texture)) { - shader->textures[index] = mstr_from(json_string_value(texture)); - } - - // NOTE: I assume the only case where this is wrong would - // be if "combo" is present and not "default". - json_t *combo = json_object_get(root, "combo"); - if (json_is_string(combo)) { - mri_combo c = { - .name = mstr_from(json_string_value(combo)), - .value = 1 - }; - mvec_push(shader->combos, c); - } - - break; - } - case TYPE_FLOAT: - json_get_float(&u->value[0], root, "default"); - break; - case TYPE_VEC4: - json_get_vec4(&u->value, root, "default"); - break; - case TYPE_VEC3: - json_get_vec3((vec3 *)&u->value, root, "default"); - break; - case TYPE_VEC2: - json_get_vec2((vec2 *)&u->value, root, "default"); - break; - case TYPE_MAT4: - default: - memset(&u->value, 0, sizeof(vec4)); - break; - } - - json_decref(root); - } - } else if (mstr_eqn(&line, mstr_c("// [COMBO]"), 10)) { - mstr combo = mstr_substr(&line, mstr_find(&line, '{'), mstr_len(&line)); - - json_t *root = json_load_mstr(&combo); - - if (!root) { - log_error("%s", "err: failed to load [COMBO]"); - continue; - } - - json_t *_combo = json_object_get(root, "combo"); - json_t *_value = json_object_get(root, "default"); - - if (json_is_string(_combo) && json_is_integer(_value)) { - mri_combo c = { - .name = mstr_from(json_string_value(_combo)), - .value = json_integer_value(_value) - }; - - mvec_push(shader->combos, c); - } - - json_decref(root); - } - - if (!cut_line) { - mstr_cat(source, &line); - mstr_cat(source, mstr_c("\n")); - } - } - - return source; -} - -static mstr *mri_combo_to_str(mri_combo *combo) { - return mstr_sprintf("#define %.*s %i\n", (int)combo->name->len, - combo->name->ptr, combo->value); -} - -void mri_shader_compile(mri_shader *shader) { - mstr *vs_source = mstr_clone(shader->vs_source); - mstr *fs_source = mstr_clone(shader->fs_source); - - mri_combo *combo; - mvec_loop_ptr(shader->combos, combo, i) { - mstr *combo_str = mri_combo_to_str(combo); - mstr_prepend(vs_source, combo_str); - mstr_prepend(fs_source, combo_str); - mstr_free(combo_str); - } - - mstr_prepend(vs_source, mstr_c(version)); - mstr_prepend(fs_source, mstr_c(version)); - - char *vs_source_c = mstr_to_cstr(vs_source); - char *fs_source_c = mstr_to_cstr(fs_source); - - create_gl_shader(&shader->gl_shader, vs_source_c, fs_source_c); - - free(vs_source_c); - free(fs_source_c); - - mstr_free(vs_source); - mstr_free(fs_source); -} - -void mri_shader_load(mri_shader *shader, mstr *name) { - mvec_init(shader->uniforms); - mvec_init(shader->combos); - - mri_asset *vs_asset = mri_asset_manager_get(name, VERTEX_SHADER); - - if (vs_asset->type != ASSET_VOID) { - shader->vs_source = build_shader_source(shader, mri_asset_as_str(vs_asset), true); - } else { - log_error("failed to load shader file (%.*s.vert)", MSTR_PRINTF(name)); - } - - mri_asset *fs_asset = mri_asset_manager_get(name, FRAGMENT_SHADER); - - if (fs_asset->type != ASSET_VOID) { - shader->fs_source = build_shader_source(shader, mri_asset_as_str(fs_asset), true); - } else { - log_error("failed to load shader file (%.*s.frag)", MSTR_PRINTF(name)); - } -} - -void mri_uniform_set(mri_shader *shader, mri_uniform *uniform) { - if (!uniform->loc) { - char *name = mstr_to_cstr(uniform->uname); - uniform->loc = get_uniform_loc(name, &shader->gl_shader); - free(name); - } - - set_gl_uniform(uniform->loc, uniform->type, uniform->value); -} - -void mri_shader_bind(mri_engine *engine, mri_shader *shader, mat4 mvp) { - bind_gl_shader(&shader->gl_shader); - - mri_uniform *uniform; - mvec_loop_ptr(shader->uniforms, uniform, i) { - if (mstr_find(uniform->uname, '[') >= 0) - continue; - mri_uniform_set(shader, uniform); - //printf("%.*s %f %f %f %f %i\n", uniform->uname->len, uniform->uname->ptr, - // uniform->value[0], uniform->value[1], uniform->value[2], uniform->value[3], uniform->type); - } - - set_gl_uniform_named("g_Time", &shader->gl_shader, TYPE_FLOAT, &engine->time); - - set_gl_uniform_named("g_TexelSize", &shader->gl_shader, TYPE_VEC2, engine->texel_size); - set_gl_uniform_named("g_TexelSizeHalf", &shader->gl_shader, TYPE_VEC2, - engine->texel_half_size); - - mat4 _mvp, _inverse; - - if (!mvp) - glm_mat4_identity(_mvp); - else - glm_mat4_copy(mvp, _mvp); - - glm_mat4_inv(_mvp, _inverse); - - set_gl_uniform_named("g_ModelViewProjectionMatrix", - &shader->gl_shader, TYPE_MAT4, (float *)_mvp); - - set_gl_uniform_named("g_ModelViewProjectionMatrixInverse", - &shader->gl_shader, TYPE_MAT4, (float *)_inverse); -} - -void mri_shader_unload(mri_shader *shader) { - for (int i = 0; i < 7; i++) { - if (shader->textures[i]) mstr_free(shader->textures[i]); - } - - if (shader->vs_source) - mstr_free(shader->vs_source); - if (shader->fs_source) - mstr_free(shader->fs_source); - - mri_uniform *uniform; - mvec_loop_ptr(shader->uniforms, uniform, i) { - if (uniform->uname) - mstr_free(uniform->uname); - if (uniform->name) - mstr_free(uniform->name); - } - - mri_combo *combo; - mvec_loop_ptr(shader->combos, combo, i) { - if (combo->name) - mstr_free(combo->name); - } - - mvec_free(shader->uniforms); - mvec_free(shader->combos); - - //destroy_gl_shader(&shader->gl_shader); -} - - - diff --git a/src/shader.cc b/src/shader.cc new file mode 100644 index 0000000..6a27954 --- /dev/null +++ b/src/shader.cc @@ -0,0 +1,249 @@ +#include <glad/gl.h> +#include <glm/ext.hpp> +#include <glm/fwd.hpp> +#include <glm/matrix.hpp> +#include <jsoncpp/json/json.h> + +#include <fmt/core.h> + +#include "assets.h" +#include "gl.h" +#include "json.h" +#include "log.h" +#include "shader.h" + +#include "objects/object.h" + +static const char *gl_compat = "#define highp\n" + "#define mediump\n" + "#define lowp\n" + "#define mul(x, y) (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 texSample2D texture2D\n" + "#define texSample2DLod texture2DLod\n" + "#define texture2DLod texture2D\n" + "#define atan2 atan\n" + "#define ddx dFdx\n" + "#define ddy(x) dFdy(-(x))\n" + "#define GLSL 1\n"; + +static const char *version = "#version 120\n"; + +using namespace Mauri; + +std::string Shader::build_shader_source(std::string isource, bool root) +{ + std::stringstream stream(isource), source; + + if (root) source << gl_compat; + + for (std::string line; std::getline(stream, line);) + { + auto cut_line = false; + + if (line.compare(0, 8, "#include") == 0) + { + auto open = line.find('"') + 1; + auto include_name = line.substr(open, line.rfind('"') - open); + + auto asset = asset_manager()->get_file(include_name, SHADER); + auto include_source = this->build_shader_source(asset->as_string(), false); + + source << include_source; + + cut_line = true; + } + else if (line.compare(0, 7, "uniform") == 0) + { + auto type_string = line.substr(line.find(' ') + 1); + + auto type = TYPE_INVALID; + auto value = vec4(0.f); + + if (type_string.compare(0, 5, "float") == 0) + type = TYPE_FLOAT; + else if (type_string.compare(0, 4, "vec4") == 0) + type = TYPE_VEC4; + else if (type_string.compare(0, 4, "vec3") == 0) + type = TYPE_VEC3; + else if (type_string.compare(0, 4, "vec2") == 0) + type = TYPE_VEC2; + else if (type_string.compare(0, 4, "mat4") == 0) + type = TYPE_MAT4; + else if (type_string.compare(0, 9, "sampler2D") == 0) + { + type = TYPE_SAMPLER2D; + value[0] = this->texture_count++; + } + + auto open = type_string.find(' ') + 1; + auto uname = type_string.substr(open, type_string.find(';') - open); + + auto name = std::string(""); + + auto combo_start = line.find("// "); + + if (combo_start != std::string::npos) + { + auto combo_string = line.substr(combo_start + 2); + + Json::Value root; + + std::stringstream(combo_string) >> root; + + // 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"]; + + if (!material.isNull()) + name = material.asString(); + else if (!label.isNull()) + name = label.asString(); + + switch (type) + { + case TYPE_SAMPLER2D: { + auto index = (int)value[0]; + + auto _default = root["default"]; + + if (!_default.isNull()) + this->textures[index] = _default.asString(); + + auto combo = root["combo"]; + + if (!combo.isNull()) + this->combos.emplace_back((Combo) { .name = combo.asString(), .value = 1 }); + + break; + } + case TYPE_FLOAT: { + auto _value = root["default"]; + if (_value.isString()) + sscanf_vec2(_value.asCString(), value); + else if (_value.isNumeric()) + value[0] = _value.asFloat(); + break; + } + case TYPE_VEC4: + sscanf_vec4(root.get("default", "").asCString(), value); + break; + case TYPE_VEC3: + sscanf_vec3(root.get("default", "").asCString(), value); + break; + case TYPE_VEC2: + sscanf_vec2(root.get("default", "").asCString(), value); + break; + case TYPE_MAT4: + case TYPE_INVALID: + value = glm::vec4(0.f); + break; + } + } + + this->uniforms.emplace_back((Uniform){type, name, uname, value}); + } + else if (line.compare(0, 10, "// [COMBO]") == 0) + { + auto open = line.find('{'); + auto combo = line.substr(open, line.length() - open); + + Json::Value root; + + std::stringstream(combo) >> root; + + auto _combo = root["combo"].asString(); + auto _value = root["default"].asInt(); + + this->combos.emplace_back((Combo) { _combo, _value }); + } + + if (!cut_line) source << line << "\n"; + } + + return source.str(); +} + +std::string Combo::to_string() +{ + return fmt::format("#define {} {}\n", this->name, this->value); +} + +void Shader::compile() +{ + auto vs_source = this->vs_source; + auto fs_source = this->fs_source; + + for (auto &combo : this->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); + + this->rshader = new RenderShader(vs_source, fs_source); +} + +Shader::Shader(std::string name) +{ + auto vs_asset = asset_manager()->get_file(name, VERTEX_SHADER); + auto fs_asset = asset_manager()->get_file(name, FRAGMENT_SHADER); + + if (vs_asset->type != ASSET_VOID) + this->vs_source = this->build_shader_source(vs_asset->as_string(), true); + else + log_error("failed to load shader file (%s)", name.c_str()); + + if (fs_asset->type != ASSET_VOID) + this->fs_source = this->build_shader_source(fs_asset->as_string(), true); + else + log_error("failed to load shader file (%s)", name.c_str()); +} + +void Shader::bind(Engine *engine, mat4x4 *model) +{ + this->rshader->bind(); + + + for (auto &uniform : this->uniforms) + { + if (uniform.uname.find('[') != std::string::npos) + continue; + //std::cout << uniform.uname << " " << uniform.value[0] << "\n"; + this->rshader->set_uniform(uniform.uname, uniform.type, glm::value_ptr(uniform.value)); + } + + this->rshader->set_uniform("g_Time", TYPE_FLOAT, &engine->time); + + this->rshader->set_uniform("g_TexelSize", TYPE_VEC2, glm::value_ptr(engine->view.texel_size)); + this->rshader->set_uniform("g_TexelSizeHalf", TYPE_VEC2, glm::value_ptr(engine->view.texel_half_size)); + + mat4x4 _model, _inverse; + + if (!model) + _model = mat4x4(1.f); + else + _model = *model; + + _inverse = glm::inverse(_model); + + this->rshader->set_uniform("g_ModelViewProjectionMatrix", TYPE_MAT4, glm::value_ptr(_model)); + this->rshader->set_uniform("g_ModelViewProjectionMatrixInverse", TYPE_MAT4, glm::value_ptr(_inverse)); +} + +Shader::~Shader() +{ + if (this->rshader) + { + delete this->rshader; + } +} diff --git a/src/shader.h b/src/shader.h index 30c9e6c..bd4a80c 100644 --- a/src/shader.h +++ b/src/shader.h @@ -1,44 +1,60 @@ #ifndef _SHADER_H #define _SHADER_H -#include <moro_str.h> +#include <glm/glm.hpp> -#include "objects/types.h" -#include "texture.h" #include "gl.h" +#include "texture.h" + +using namespace glm; + +namespace Mauri +{ + +struct Uniform +{ + UniformType type; + + std::string name; + std::string uname; + + vec4 value; +}; + +struct Combo +{ + std::string name; + int value; + std::string to_string(); +}; + +class Engine; + +class Shader +{ + public: + Shader(std::string name); + ~Shader(); -typedef struct mri_engine mri_engine; + std::vector<Uniform> uniforms; + std::vector<Combo> combos; -typedef struct { - uniform_type type; - mstr *name; - mstr *uname; - vec4 value; - unsigned int loc; -} mri_uniform; + std::array<std::string, 8> textures; -typedef struct { - mstr *name; - int value; -} mri_combo; + int texture_count = 0; -typedef struct { - mstr *vs_source; - mstr *fs_source; - - gl_shader gl_shader; + void bind(Engine *engine, mat4x4 *model); + void compile(); - int texture_count; + private: + std::string vs_source; + std::string fs_source; - mstr *textures[7]; + RenderShader *rshader = nullptr; - mvec(mri_combo) combos; - mvec(mri_uniform) uniforms; -} mri_shader; + std::string build_shader_source(std::string isource, bool root); +}; -void mri_shader_load(mri_shader *shader, mstr *name); -void mri_shader_compile(mri_shader *shader); -void mri_shader_bind(mri_engine *engine, mri_shader *shader, mat4 mvp); -void mri_shader_unload(mri_shader *shader); +} // namespace Mauri #endif // _SHADER_H diff --git a/src/texture.c b/src/texture.c deleted file mode 100644 index 42f0830..0000000 --- a/src/texture.c +++ /dev/null @@ -1,199 +0,0 @@ -#define STB_IMAGE_IMPLEMENTATION -#include <stb_image.h> -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include <stb_image_write.h> - -#include <lz4.h> -#include <moro.h> -#include <glad/gl.h> - -#include "util.h" -#include "texture.h" -#include "assets.h" -#include "gl.h" -#include "log.h" -#include "engine.h" - -static unsigned char *load_image_rgba(unsigned char *data, int len) { - int w, h, channels; - return stbi_load_from_memory(data, len, &w, &h, &channels, 4); -} - -//static uint32_t rgba_to_brga(uint32_t x) { -// return ((x) & 0xff00ff00) | -// ((x >> 16) & 0xFF) | -// ((x << 16) & 0xFF0000); -//} -// -//static uint32_t argb_to_brga(uint32_t x) { -// return ((x & 0xFF000000) >> 24) | //______AA -// ((x & 0x00FF0000) >> 8) | //____RR__ -// ((x & 0x0000FF00) << 8) | //__GG____ -// ((x & 0x000000FF) << 24); -//} - -mri_texture *mri_texture_load(mri_engine *engine, mri_asset *asset) { - mri_texture *previous_texture; - mvec_loop_ptr(engine->texture_cache, previous_texture, i) { - if (previous_texture->hash == asset->hash) { - return previous_texture; - } - } - - mri_texture *t = mvec_next(engine->texture_cache); - - t->hash = asset->hash; - - //mstr type = mri_asset_str(asset, 9); - //mstr itype = mri_asset_str(asset, 9); - mri_asset_seek(asset, 18); - - t->format = (mri_texture_format)mri_asset_value(asset, unsigned int); - - t->flags.integer = mri_asset_value(asset, unsigned int); - - t->texture_width = mri_asset_value(asset, unsigned int); - t->texture_height = mri_asset_value(asset, unsigned int); - - t->width = mri_asset_value(asset, unsigned int); - t->height = mri_asset_value(asset, unsigned int); - - t->texture_resolution[0] = t->texture_width; - t->texture_resolution[1] = t->texture_height; - t->texture_resolution[2] = t->width; - t->texture_resolution[3] = t->height; - - mri_asset_seek(asset, 4); - - mstr container = mri_asset_str(asset, 9); - - int iformat = -1; - - if (mstr_eqn(&container, mstr_c("TEXB0003"), 8)) { - t->version = TEXB0003; - mri_asset_seek(asset, 4); - iformat = mri_asset_value(asset, unsigned int); - } else if (mstr_eqn(&container, mstr_c("TEXB0002"), 8)) { - t->version = TEXB0002; - mri_asset_seek(asset, 4); - } else if (mstr_eqn(&container, mstr_c("TEXB0001"), 8)) { - t->version = TEXB0001; - mri_asset_seek(asset, 4); - } else { - log_error("%s", "unknown texture version"); - } - - int mm_count = mri_asset_value(asset, int); - - create_gl_texture(&t->texture, t->flags.map.NO_INTERPOLATION, - t->flags.map.CLAMP_UV, mm_count); - - for (int i = 0; i < mm_count; i++) { - unsigned int mwidth = mri_asset_value(asset, unsigned int); - unsigned int mheight = mri_asset_value(asset, unsigned int); - - bool lz4_compressed = false; - unsigned int decompressed_byte_count = 0; - - if (t->version == TEXB0002 || t->version == TEXB0003) { - lz4_compressed = mri_asset_value(asset, unsigned int) == 1; - decompressed_byte_count = mri_asset_value(asset, unsigned int); - } - - unsigned int byte_count = mri_asset_value(asset, unsigned int); - - if (!lz4_compressed) { - decompressed_byte_count = byte_count; - } - - unsigned char *buffer = mri_asset_ptr(asset); - - if (lz4_compressed) { - char *decompressed = (char *)malloc(decompressed_byte_count); - LZ4_decompress_safe((char *)buffer, decompressed, byte_count, decompressed_byte_count); - buffer = (unsigned char *)decompressed; - } - - if (iformat == -1) { - switch (t->format) { - case RGBA8888: { - upload_gl_texture(&t->texture, buffer, mwidth, mheight, i, GL_RGBA); - break; - } - case DXT5: - log_warn("%s", "DXT5 Texture"); - break; - case DXT1: - log_warn("%s", "DXT1 Texture"); - break; - case DXT3: - log_warn("%s", "DXT3 Texture"); - break; - case RG88: - upload_gl_texture(&t->texture, buffer, mwidth, mheight, i, GL_RG); - log_warn("%s", "RG88 Texture"); - break; - case R8: - upload_gl_texture(&t->texture, buffer, mwidth, mheight, i, GL_RED); - log_warn("%s", "RG8 Texture"); - break; - default: - log_warn("%s", "Uknown Texture"); - break; - } - } else { - unsigned char *data = load_image_rgba(buffer, decompressed_byte_count); - upload_gl_texture(&t->texture, data, mwidth, mheight, i, GL_RGBA); - free(data); - } - - if (lz4_compressed) { - free(buffer); - } - - mri_asset_seek(asset, byte_count); - } - - asset->pos = 0; - - return t; -} - -void mri_texture_unload(mri_texture *t) { - destroy_gl_texture(&t->texture); - free(t); -} - -void mri_texture_bind(mri_texture *t, int index) { - bind_gl_texture(&t->texture, index); -} - -void mri_framebuffer_create(mri_framebuffer *buffer, mstr *name, float width, float height, float scale) { - buffer->name = mstr_clone(name); - - buffer->width = width / scale; - buffer->height = height / scale; - - buffer->texture.texture_resolution[0] = buffer->width; - buffer->texture.texture_resolution[1] = buffer->height; - buffer->texture.texture_resolution[2] = buffer->width; - buffer->texture.texture_resolution[3] = buffer->height; -} - -void mri_framebuffer_load(mri_framebuffer *buffer) { - create_gl_framebuffer(&buffer->buffer, buffer->width, buffer->height); - buffer->texture.texture.id = buffer->buffer.texture.id; -} - -void mri_framebuffer_unload(mri_framebuffer *buffer) { - mstr_free(buffer->name); - destroy_gl_framebuffer(&buffer->buffer); - //free(buffer); -} - -void mri_framebuffer_bind(mri_framebuffer *buffer) { - bind_gl_framebuffer(&buffer->buffer); -} - - - diff --git a/src/texture.cc b/src/texture.cc new file mode 100644 index 0000000..c47352b --- /dev/null +++ b/src/texture.cc @@ -0,0 +1,201 @@ +#define STB_IMAGE_IMPLEMENTATION +extern "C" +{ +#include <stb_image.h> +} + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include <stb_image_write.h> + +#include <s3tc.h> + +#include <glad/gl.h> +#include <lz4.h> + +#include "assets.h" +#include "engine.h" +#include "gl.h" +#include "log.h" +#include "texture.h" +#include "util.h" + +using namespace Mauri; + +static byte *load_image_rgba(byte *data, s32 len) +{ + s32 w, h, channels; + return stbi_load_from_memory(data, len, &w, &h, &channels, 4); +} + +Texture::Texture(Asset *asset) +{ + this->hash = asset->hash; + + // auto type = asset->read_str(9); + // auto itype = asset->read_str(9); + + asset->seek(18); + + this->format = (TextureFormat)asset->read<u32>(); + + this->flags.integer = asset->read<u32>(); + + this->texture_width = asset->read<u32>(); + this->texture_height = asset->read<u32>(); + + this->width = asset->read<u32>(); + this->height = asset->read<u32>(); + + this->texture_resolution[0] = this->texture_width; + this->texture_resolution[1] = this->texture_height; + this->texture_resolution[2] = this->width; + this->texture_resolution[3] = this->height; + + asset->seek(4); + + auto container = asset->reads(9); + + auto iformat = -1; + + if (container.compare(0, 8, "TEXB0003") == 0) + { + this->version = TEXB0003; + asset->seek(4); + iformat = asset->read<u32>(); + } + else if (container.compare(0, 8, "TEXB0002") == 0) + { + this->version = TEXB0002; + asset->seek(4); + } + else if (container.compare(0, 8, "TEXB0001") == 0) + { + this->version = TEXB0001; + asset->seek(4); + } + else + { + log_error("%s", "unknown texture version"); + } + + auto mm_count = asset->read<u32>(); + + this->texture = new RenderTexture(this->flags.map.NO_INTERPOLATION, this->flags.map.CLAMP_UV, mm_count); + + for (int i = 0; i < mm_count; i++) + { + auto mwidth = asset->read<u32>(); + auto mheight = asset->read<u32>(); + + auto lz4_compressed = false; + auto decompressed_byte_count = 0; + + if (this->version == TEXB0002 || this->version == TEXB0003) + { + lz4_compressed = asset->read<u32>() == 1; + decompressed_byte_count = asset->read<u32>(); + } + + auto byte_count = asset->read<u32>(); + + if (!lz4_compressed) + decompressed_byte_count = byte_count; + + auto buffer = asset->ptr(); + + if (lz4_compressed) + { + auto decompressed = new char[decompressed_byte_count]; + LZ4_decompress_safe(reinterpret_cast<char *>(buffer), decompressed, + byte_count, decompressed_byte_count); + buffer = reinterpret_cast<byte *>(decompressed); + } + + if (iformat == -1) + { + switch (this->format) + { + case RGBA8888: + this->texture->upload(buffer, mwidth, mheight, i, GL_RGBA, true); + break; + case DXT5: { + auto decompressed = new u32[decompressed_byte_count]; + BlockDecompressImageDXT5(mwidth, mheight, buffer, decompressed); + this->texture->upload(reinterpret_cast<byte *>(decompressed), + mwidth, mheight, i, GL_RGBA, true, true); + delete[] decompressed; + log_warn("%s", "DXT5 Texture"); + break; + } + case DXT1: { + auto decompressed = new u32[decompressed_byte_count * 2]; + BlockDecompressImageDXT1(mwidth, mheight, buffer, decompressed); + this->texture->upload(reinterpret_cast<byte *>(decompressed), + mwidth, mheight, i, GL_RGBA, true, true); + delete[] decompressed; + log_warn("%s", "DXT1 Texture"); + break; + } + case DXT3: + log_warn("%s", "DXT3 Texture"); + break; + case RG88: + this->texture->upload(buffer, mwidth, mheight, i, GL_RG, true); + log_warn("%s", "RG88 Texture"); + break; + case R8: + this->texture->upload(buffer, mwidth, mheight, i, GL_RED, true); + log_warn("%s", "RG8 Texture"); + break; + default: + log_warn("%s", "Uknown Texture"); + break; + } + } + else + { + auto data = load_image_rgba(buffer, decompressed_byte_count); + this->texture->upload(data, mwidth, mheight, i, GL_RGBA, true); + free(data); + } + + if (lz4_compressed) + delete[] buffer; + + asset->seek(byte_count); + } + + asset->pos = 0; +} + +Texture::~Texture() +{ + if (!this->wrapped) + delete this->texture; +} + +void Texture::bind(s32 index) +{ + this->texture->bind(index); +} + +Framebuffer::Framebuffer(f32 width, f32 height, f32 scale) +{ + this->buffer = new RenderFramebuffer(width, height, scale); + + this->texture = new Texture(); + this->texture->wrapped = true; + + this->texture->texture = this->buffer->texture; + + this->texture->texture_resolution[0] = this->buffer->width; + this->texture->texture_resolution[1] = this->buffer->height; + this->texture->texture_resolution[2] = this->buffer->width; + this->texture->texture_resolution[3] = this->buffer->height; +} + +Framebuffer::~Framebuffer() +{ + delete this->texture; + delete this->buffer; +} diff --git a/src/texture.h b/src/texture.h index 316d7fd..d70b423 100644 --- a/src/texture.h +++ b/src/texture.h @@ -1,72 +1,89 @@ #ifndef _TEXTURE_H #define _TEXTURE_H -#include <stdbool.h> -#include <cglm/cglm.h> +#include <glm/glm.hpp> -#include "gl.h" #include "assets.h" +#include "gl.h" +#include "util.h" + +using namespace glm; + +namespace Mauri +{ +enum TextureFormat +{ + RGBA8888 = 0, + DXT5 = 4, + DXT3 = 6, + DXT1 = 7, + RG88 = 8, + R8 = 9 +}; + +enum TextureVersion +{ + TEXB0001, + TEXB0002, + TEXB0003 +}; -#include "objects/types.h" +struct TextureFlags +{ + bool NO_INTERPOLATION : 1; + bool CLAMP_UV : 1; + bool IS_GIF : 1; + u32 UNKNWON : 29; +}; -typedef enum { - RGBA8888 = 0, - DXT5 = 4, - DXT3 = 6, - DXT1 = 7, - RG88 = 8, - R8 = 9 -} mri_texture_format; +class Texture +{ + public: + Texture(Asset *asset); + Texture() + { + } -typedef enum { - TEXB0001, - TEXB0002, - TEXB0003 -} mri_texture_version; + ~Texture(); -typedef struct { - bool NO_INTERPOLATION:1; - bool CLAMP_UV:1; - bool IS_GIF:1; - unsigned int UNKNWON: 29; -} mri_texture_flags; + s32 width; + s32 height; -typedef struct { - int width, height; - int texture_width, texture_height; + bool wrapped = false; - unsigned long hash; + vec4 texture_resolution; - gl_texture texture; + RenderTexture *texture; - vec4 texture_resolution; + TextureFormat format; - union { - mri_texture_flags map; - unsigned int integer; - } flags; + void load(Asset *asset); + void bind(s32 index); - mri_texture_format format; - mri_texture_version version; -} mri_texture; + private: + s32 texture_width; + s32 texture_height; -typedef struct { - mstr *name; + u64 hash; - float width, height; + union { + TextureFlags map; + unsigned int integer; + } flags; - gl_framebuffer buffer; + TextureVersion version; +}; - mri_texture texture; -} mri_framebuffer; +class Framebuffer +{ + public: + Framebuffer(f32 width, f32 height, f32 scale); + ~Framebuffer(); -mri_texture *mri_texture_load(mri_engine *engine, mri_asset *asset); -void mri_texture_unload(mri_texture *texture); -void mri_texture_bind(mri_texture *t, int index); + Texture *texture; + RenderFramebuffer *buffer; +}; -void mri_framebuffer_create(mri_framebuffer *buffer, mstr *name, float width, float height, float scale); -void mri_framebuffer_load(mri_framebuffer *buffer); -void mri_framebuffer_unload(mri_framebuffer *buffer); -void mri_framebuffer_bind(mri_framebuffer *buffer); +} // namespace Mauri #endif // _TEXTURE_H diff --git a/src/util.c b/src/util.c deleted file mode 100644 index f71761b..0000000 --- a/src/util.c +++ /dev/null @@ -1,17 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stddef.h> - -#include "util.h" - -// http://www.cse.yorku.ca/~oz/hash.html -unsigned long djb2_hash(unsigned char *str) { - return 0; - unsigned long hash = 5381; - int c; - - while ((c = *str++)) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - - return hash; -} diff --git a/src/util.cc b/src/util.cc new file mode 100644 index 0000000..ff341ef --- /dev/null +++ b/src/util.cc @@ -0,0 +1,20 @@ +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +namespace Mauri +{ + +int next_power_of_two(int n) +{ + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + return ++n; +}; + +} // namespace Mauri @@ -1,6 +1,25 @@ #ifndef _UTIL_H #define _UTIL_H -unsigned long djb2_hash(unsigned char *str); +#include <stdint.h> + +typedef uint32_t u32; +typedef int32_t s32; + +typedef uint64_t u64; +typedef int64_t s64; + +typedef uint8_t u8; +typedef int8_t s8; + +typedef float f32; +typedef double f64; + +typedef unsigned char byte; + +namespace Mauri +{ +extern s32 next_power_of_two(s32 n); +} #endif // _UTIL_H |