├── .gitignore ├── lib ├── win │ └── libamogpu.a └── linux │ └── libamogpu.a ├── test ├── build │ ├── SDL2.dll │ ├── glew32.dll │ ├── testfun.exe │ ├── libstdc++-6.dll │ ├── jogodaforcagpu.exe │ ├── libgcc_s_seh-1.dll │ ├── libwinpthread-1.dll │ └── data │ │ ├── fonts │ │ ├── Impacted.ttf │ │ ├── impact.ttf │ │ └── unicode.impact.ttf │ │ └── fx │ │ ├── fx_shape.fsh │ │ └── fx_shape.vsh ├── libs │ ├── libglew32.a │ └── libfreetype.a ├── src │ ├── render.hpp │ ├── keyboard.hpp │ ├── render.cpp │ ├── keyboard.cpp │ └── main.cpp └── CMakeLists.txt ├── splash ├── splash-texture.png ├── splash-shape-builder.png ├── splash-font-rendering.png └── splash-multi-instances.png ├── include └── amogpu │ ├── amogpu.hpp │ ├── shape_builder.hpp │ ├── font_renderer.hpp │ ├── gpu_handler.hpp │ └── core.hpp ├── CMakeLists.txt ├── src ├── shape_builder.cpp ├── core.cpp ├── font_renderer.cpp └── gpu_handler.cpp └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /cmake-build-debug/ 2 | /test/cmake-build-debug/ 3 | /releases/ 4 | /.idea/ -------------------------------------------------------------------------------- /lib/win/libamogpu.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/lib/win/libamogpu.a -------------------------------------------------------------------------------- /test/build/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/SDL2.dll -------------------------------------------------------------------------------- /lib/linux/libamogpu.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/lib/linux/libamogpu.a -------------------------------------------------------------------------------- /test/build/glew32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/glew32.dll -------------------------------------------------------------------------------- /test/libs/libglew32.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/libs/libglew32.a -------------------------------------------------------------------------------- /test/build/testfun.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/testfun.exe -------------------------------------------------------------------------------- /test/libs/libfreetype.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/libs/libfreetype.a -------------------------------------------------------------------------------- /splash/splash-texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/splash/splash-texture.png -------------------------------------------------------------------------------- /test/build/libstdc++-6.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/libstdc++-6.dll -------------------------------------------------------------------------------- /test/build/jogodaforcagpu.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/jogodaforcagpu.exe -------------------------------------------------------------------------------- /test/build/libgcc_s_seh-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/libgcc_s_seh-1.dll -------------------------------------------------------------------------------- /splash/splash-shape-builder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/splash/splash-shape-builder.png -------------------------------------------------------------------------------- /test/build/libwinpthread-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/libwinpthread-1.dll -------------------------------------------------------------------------------- /splash/splash-font-rendering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/splash/splash-font-rendering.png -------------------------------------------------------------------------------- /splash/splash-multi-instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/splash/splash-multi-instances.png -------------------------------------------------------------------------------- /test/build/data/fonts/Impacted.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/data/fonts/Impacted.ttf -------------------------------------------------------------------------------- /test/build/data/fonts/impact.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/data/fonts/impact.ttf -------------------------------------------------------------------------------- /test/build/data/fonts/unicode.impact.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vokegpu/amogpu/HEAD/test/build/data/fonts/unicode.impact.ttf -------------------------------------------------------------------------------- /include/amogpu/amogpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef AMOGPU_H 3 | #define AMOGPU_H 4 | 5 | #include "font_renderer.hpp" 6 | #include "gpu_handler.hpp" 7 | #include "shape_builder.hpp" 8 | 9 | #endif -------------------------------------------------------------------------------- /test/src/render.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef RENDER_H 3 | #define RENDER_H 4 | 5 | #include 6 | 7 | namespace draw { 8 | extern dynamic_batching batch; 9 | extern font_renderer font; 10 | extern bool refresh; 11 | 12 | void rectangle(float x, float y, float w, float h, const amogpu::vec4f &color); 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /test/build/data/fx/fx_shape.fsh: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | uniform vec4 u_vec_color; 4 | uniform sampler2D u_sampler_texture_slot; 5 | uniform bool u_bool_texture_active; 6 | 7 | in vec2 varying_attrib_tex_coords; 8 | 9 | void main() { 10 | vec4 frag_color = u_vec_color; 11 | 12 | if (u_bool_texture_active) { 13 | frag_color = texture(u_sampler_texture_slot, varying_attrib_tex_coords); 14 | frag_color = vec4(frag_color.xyz - ((1.0 - u_vec_color.xyz) - 1.0), frag_color.w * u_vec_color.w); 15 | } 16 | 17 | gl_FragColor = frag_color; 18 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(amogpu) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | if(WIN32) 7 | set(LIBRARY_OUTPUT_PATH ../lib/win/) 8 | else() 9 | set(LIBRARY_OUTPUT_PATH ../lib/linux/) 10 | endif() 11 | 12 | file(GLOB_RECURSE HEADER_FILES "include/*.hpp") 13 | file(GLOB_RECURSE SRC_FILES "src/*.cpp") 14 | 15 | include_directories(include) 16 | add_library(amogpu STATIC "${SRC_FILES}") 17 | add_compile_options(-O3) 18 | 19 | set_target_properties(amogpu PROPERTIES VERSION 1.0 SOVERSION 1) 20 | set_target_properties(amogpu PROPERTIES PUBLIC_HEADER "${HEADER_FILES}") 21 | target_include_directories(amogpu PUBLIC .) -------------------------------------------------------------------------------- /test/src/keyboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef KEYBOARD_H 3 | #define KEYBOARD_H 4 | 5 | #include "render.hpp" 6 | #include 7 | #include 8 | 9 | class keyboard { 10 | protected: 11 | bool should_accept_input; 12 | 13 | std::vector key_char_list; 14 | std::vector key_list; 15 | 16 | char* char_highlight; 17 | float offset; 18 | public: 19 | amogpu::rect rect; 20 | 21 | void set_size(float width, float height); 22 | void set_pos(float x, float y); 23 | 24 | void init(); 25 | void calculate_scale(); 26 | void on_event(SDL_Event &sdl_event); 27 | void on_draw_reload(); 28 | }; 29 | 30 | #endif -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(testfun) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | set(EXECUTABLE_OUTPUT_PATH ../build) 6 | 7 | get_filename_component(BACKWARD_DIR ../ ABSOLUTE) 8 | file(GLOB_RECURSE DYNAMIC_BATCHING_STATIC_LIB "${BACKWARD_DIR}/lib/libamogpu.a") 9 | file(GLOB_RECURSE SRC_FILES "src/*.cpp") 10 | 11 | # The dynamic batching library. 12 | include_directories("${BACKWARD_DIR}/include") 13 | include_directories(src) 14 | message("-- Creating executable!") 15 | 16 | add_executable(testfun ${SRC_FILES}) 17 | target_link_libraries(testfun "${DYNAMIC_BATCHING_STATIC_LIB}" mingw32 SDL2main SDL2 opengl32 glew32 freetype) -------------------------------------------------------------------------------- /test/build/data/fx/fx_shape.vsh: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | layout (location = 0) in vec2 attrib_vertexes; 4 | layout (location = 1) in vec2 attrib_tex_coords; 5 | 6 | uniform mat4 u_mat_projection; 7 | uniform vec4 u_vec_rect; 8 | uniform float u_float_zdepth; 9 | 10 | out vec2 varying_attrib_tex_coords; 11 | 12 | void main() { 13 | if (u_vec_rect.z != 0 || u_vec_rect.w != 0) { 14 | gl_Position = u_mat_projection * vec4((attrib_vertexes * u_vec_rect.zw) + u_vec_rect.xy, (u_float_zdepth / 100000), 1.0f); 15 | } else { 16 | gl_Position = u_mat_projection * vec4(attrib_vertexes + u_vec_rect.xy, (u_float_zdepth / 100000), 1.0f); 17 | } 18 | 19 | varying_attrib_tex_coords = attrib_tex_coords; 20 | } -------------------------------------------------------------------------------- /include/amogpu/shape_builder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef SHAPE_BUILDER_H 3 | #define SHAPE_BUILDER_H 4 | 5 | #include 6 | #include "core.hpp" 7 | 8 | /** 9 | * Buiild static geometry shape, fast and immediate. 10 | **/ 11 | class shape_builder { 12 | protected: 13 | static amogpu::gpu_gl_program fx_shape; 14 | 15 | static GLuint vertex_array; 16 | static GLuint vertex_buffer; 17 | 18 | amogpu::shape enum_flag_format; 19 | amogpu::gpu_data concurrent_gpu_data; 20 | public: 21 | float depth {}; 22 | 23 | /* 24 | * Init shape builder. 25 | */ 26 | static void init(); 27 | 28 | /* 29 | * Delete buffers. 30 | */ 31 | static void free_buffers(); 32 | 33 | /* 34 | * Invoke GPU data. 35 | */ 36 | void invoke(); 37 | 38 | /* 39 | * Revoke GPU data. 40 | */ 41 | void revoke(); 42 | 43 | /* 44 | * Build a shape. 45 | */ 46 | void build(const amogpu::shape &format, const amogpu::vec4f &color, GLuint texture = 0); 47 | 48 | /* 49 | * Set texture uv. 50 | */ 51 | void modal(float x, float y, float w, float h); 52 | 53 | /* 54 | * Draw the shape. 55 | */ 56 | void draw(float x, float y, float w, float h); 57 | }; 58 | 59 | #endif -------------------------------------------------------------------------------- /test/src/render.cpp: -------------------------------------------------------------------------------- 1 | #include "render.hpp" 2 | 3 | dynamic_batching draw::batch; 4 | font_renderer draw::font; 5 | bool draw::refresh = true; 6 | 7 | void draw::rectangle(float x, float y, float w, float h, const amogpu::vec4f &color) { 8 | // Call an instance of the global batch. 9 | draw::batch.instance(x, y); 10 | draw::batch.modal(w, h); 11 | 12 | // We need to reset the coords for the geometry mesh. 13 | x = 0.0f; 14 | y = 0.0f; 15 | w = 1.0f; 16 | h = 1.0f; 17 | 18 | // Fill with the color. 19 | draw::batch.fill(color); 20 | 21 | // First triangle part of rect. 22 | draw::batch.vertex(x, y); 23 | draw::batch.vertex(x, y + h); 24 | draw::batch.vertex(x + w, y + h); 25 | 26 | // Second triangle part of rect. 27 | draw::batch.vertex(x + w, y + h); 28 | draw::batch.vertex(x + w, y); 29 | draw::batch.vertex(x, y); 30 | 31 | // We do not need to set texture coords so set everything to 0. 32 | draw::batch.coords(0.0f, 0.0f); 33 | draw::batch.coords(0.0f, 0.0f); 34 | draw::batch.coords(0.0f, 0.0f); 35 | draw::batch.coords(0.0f, 0.0f); 36 | draw::batch.coords(0.0f, 0.0f); 37 | draw::batch.coords(0.0f, 0.0f); 38 | 39 | // End the instance. 40 | draw::batch.next(); 41 | } -------------------------------------------------------------------------------- /include/amogpu/font_renderer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include FT_FREETYPE_H 5 | 6 | #ifndef FONT_RENDERER_H 7 | #define FONT_RENDERER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include "amogpu/gpu_handler.hpp" 13 | 14 | /** 15 | * The amogpu font renderer, draw strings into a space. 16 | **/ 17 | class font_renderer { 18 | protected: 19 | static FT_Library ft_library; 20 | 21 | FT_Face ft_face; 22 | FT_GlyphSlot ft_glyph_slot; 23 | FT_Bool use_kerneking; 24 | FT_UInt previous; 25 | FT_Vector_ previous_char_vec; 26 | 27 | uint32_t texture_width; 28 | uint32_t texture_height; 29 | 30 | std::string current_font_path; 31 | uint8_t current_font_size; 32 | 33 | dynamic_batching* binded_batch; 34 | uint8_t batch_mode = 0; 35 | 36 | amogpu::font_char allocated_font_char[256]; 37 | public: 38 | /* 39 | * The texture bitmap. 40 | */ 41 | GLuint texture_bitmap; 42 | 43 | /* 44 | * Get the current binded batch. 45 | */ 46 | dynamic_batching* batch(); 47 | 48 | /* 49 | * Init Freetype library. 50 | */ 51 | static void init(); 52 | 53 | /* 54 | * End the Freetype library. 55 | */ 56 | static void end_ft_library(); 57 | 58 | /* 59 | * Load ttf font and size. 60 | */ 61 | void load(const std::string &font_path, uint8_t font_size); 62 | 63 | /* 64 | * Specify what batch the font renderer will use. 65 | */ 66 | void from(dynamic_batching *concurrent_batch); 67 | 68 | /* 69 | * Specify what batch the font renderer will use. 70 | */ 71 | void from(uint8_t mode); 72 | 73 | /* 74 | * Get input text width. 75 | */ 76 | float get_text_width(const std::string &text); 77 | 78 | /* 79 | * Get current font height. 80 | */ 81 | float get_text_height(); 82 | 83 | /* 84 | * Send data for GPU to display text into screen space. 85 | */ 86 | void render(const std::string &text, float x, float y, const amogpu::vec4f &color); 87 | }; 88 | 89 | #endif -------------------------------------------------------------------------------- /include/amogpu/gpu_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef GPU_HANDLER_H 3 | #define GPU_HANDLER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "amogpu/core.hpp" 10 | 11 | /** 12 | * Batch but dynamic, high performance. 13 | **/ 14 | struct dynamic_batching { 15 | protected: 16 | std::vector concurrent_allocated_textures {}; 17 | std::vector concurrent_allocated_vertices {}; 18 | std::vector concurrent_allocated_texture_coords {}; 19 | 20 | uint32_t sizeof_allocated_gpu_data {}; 21 | uint32_t sizeof_previous_allocated_gpu_data {}; 22 | 23 | uint32_t sizeof_allocated_vertices {}; 24 | uint32_t sizeof_instanced_allocated_vertices {}; 25 | 26 | std::vector allocated_data {}; 27 | std::vector allocated_data_copy {}; 28 | 29 | bool should_alloc_new_buffers {}; 30 | bool should_not_create_buffers {}; 31 | 32 | GLuint vertex_arr_object {}; 33 | GLuint vbo_vertices {}; 34 | GLuint vbo_texture_coords {}; 35 | bool frustum_depth {}; 36 | float depth {}; 37 | 38 | static amogpu::gpu_gl_program fx_shape; 39 | public: 40 | void set_depth(float depth_test); 41 | float get_depth(); 42 | 43 | void set_frustum_depth(bool depth_test); 44 | bool get_frustum_depth(); 45 | 46 | /* 47 | * The current invoked batch. 48 | */ 49 | static dynamic_batching* invoked; 50 | 51 | /* 52 | * Init the dynamic batching. 53 | */ 54 | static void init(); 55 | 56 | /* 57 | * Update global matrices. 58 | */ 59 | static void matrix(); 60 | 61 | /* 62 | * Invoke GPU. 63 | */ 64 | void invoke(); 65 | 66 | /* 67 | * Create a sub-section into GPU section. 68 | */ 69 | void instance(float x, float y, int32_t factor = -1); 70 | 71 | /* 72 | * For a simple shapes. 73 | */ 74 | void modal(float w, float h); 75 | 76 | /* 77 | * For complex shapes. 78 | */ 79 | void factor(int32_t factor); 80 | 81 | /* 82 | * Fill with RGBA color normalised. 83 | */ 84 | void fill(const amogpu::vec4f &color); 85 | 86 | /* 87 | * Fill with RGBA color normalised. 88 | */ 89 | void fill(float r, float g, float b, float a = 1.0f); 90 | 91 | /* 92 | * Add one vertex. 93 | */ 94 | void vertex(float x, float y); 95 | 96 | /* 97 | * Bind a texture. 98 | */ 99 | void bind(GLuint texture); 100 | 101 | /* 102 | * Add one uv coordinates for texture, if there is not any binded texture just put 0.0f at uv(s) parameters. 103 | */ 104 | void coords(float u, float v); 105 | 106 | /* 107 | * End the sub-segment. 108 | */ 109 | void next(); 110 | 111 | /* 112 | * End GPU communication. 113 | */ 114 | void revoke(); 115 | 116 | /* 117 | * Draw the current batch. 118 | */ 119 | void draw(); 120 | 121 | /* 122 | * Delete the buffers of batch. 123 | */ 124 | void free_buffers(); 125 | }; 126 | 127 | #endif -------------------------------------------------------------------------------- /test/src/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "keyboard.hpp" 2 | 3 | void keyboard::set_size(float width, float height) { 4 | if (this->rect.w != width || this->rect.h != height) { 5 | this->rect.w = width; 6 | this->rect.h = height; 7 | } 8 | } 9 | 10 | void keyboard::set_pos(float x, float y) { 11 | if (this->rect.x != x || this->rect.y != y) { 12 | this->rect.x = x; 13 | this->rect.y = y; 14 | draw::refresh = true; 15 | } 16 | } 17 | 18 | void keyboard::init() { 19 | this->key_char_list = {"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "A", "S", "D", "F", "G", "H", "J", "K", "L", "Cc", "Z", "X", "C", "V", "B", "N", "M", " ", " ", " "}; 20 | this->calculate_scale(); 21 | } 22 | 23 | void keyboard::on_event(SDL_Event &sdl_event) { 24 | switch (sdl_event.type) { 25 | case SDL_MOUSEMOTION: { 26 | float mx = static_cast(sdl_event.motion.x); 27 | float my = static_cast(sdl_event.motion.y); 28 | 29 | char* concurrent_highlight = nullptr; 30 | 31 | if (this->rect.aabb_collide_with_point(mx, my)) { 32 | for (uint8_t i = 0; i < this->key_list.size(); i++) { 33 | amogpu::rect &rekts = this->key_list.at(i); 34 | char* key = const_cast(this->key_char_list.at(i)); 35 | 36 | // Add the rects. 37 | rekts += this->rect; 38 | 39 | if (rekts.aabb_collide_with_point(mx, my)) { 40 | concurrent_highlight = key; 41 | } 42 | 43 | // Subtract to old pos. 44 | rekts -= this->rect; 45 | } 46 | } 47 | 48 | if (this->char_highlight != concurrent_highlight) { 49 | this->char_highlight = concurrent_highlight; 50 | draw::refresh = true; 51 | } 52 | 53 | break; 54 | } 55 | } 56 | } 57 | 58 | void keyboard::calculate_scale() { 59 | float x = 0; 60 | float y = 0; 61 | 62 | float text_height = draw::font.get_text_height(); 63 | float square_width = text_height + (text_height / 6); 64 | 65 | this->key_list.clear(); 66 | this->offset = 1.0f; 67 | 68 | uint8_t step = 0; 69 | uint8_t steps_going_on = 0; 70 | float subset = 0; 71 | 72 | // NOTE: I created a formula to add opossite offset propety of keyboards. 73 | // (key_width / (10 - step * 2)) * step 74 | 75 | float w = square_width + this->offset; 76 | float h = text_height + this->offset; 77 | 78 | this->rect.w = 0; 79 | this->rect.h = 0; 80 | 81 | for (uint8_t i = 0; i < this->key_char_list.size(); i++) { 82 | ++steps_going_on; 83 | 84 | if ((steps_going_on > 10 && step == 0 || steps_going_on > 9 && step == 1)) { 85 | step++; 86 | steps_going_on = 0; 87 | subset = (square_width / (10 - step * 2)) * step; 88 | 89 | y += h; 90 | x = subset; 91 | } else if (steps_going_on > 7 && step == 3) { 92 | break; 93 | } 94 | 95 | amogpu::rect rekt; 96 | 97 | rekt.x = x; 98 | rekt.y = y; 99 | rekt.w = square_width; 100 | rekt.h = text_height; 101 | 102 | x += w; 103 | this->key_list.push_back(rekt); 104 | 105 | if (x + w > this->rect.w) { 106 | this->rect.w = x + w; 107 | } 108 | 109 | if (y + h > this->rect.h) { 110 | this->rect.h = y + h; 111 | } 112 | } 113 | 114 | draw::refresh = true; 115 | } 116 | 117 | void keyboard::on_draw_reload() { 118 | amogpu::vec4f color(1.0f, 1.0f, 1.0f, 0.9f); 119 | amogpu::vec4f color_highlight(200.0f / 255, 86.0f / 255, 121.0f / 255, 0.5f); 120 | amogpu::vec4f color_str(0.0f, 0.0f, 0.0f, 1.0f); 121 | 122 | for (uint8_t i = 0; i < this->key_list.size(); i++) { 123 | amogpu::rect rekt = this->key_list.at(i); 124 | const char* key = this->key_char_list.at(i); 125 | 126 | draw::rectangle(this->rect.x + rekt.x, this->rect.y + rekt.y, rekt.w, rekt.h, key == this->char_highlight ? color_highlight : color); 127 | draw::font.render(std::string(key), this->rect.x + rekt.x + (rekt.w / 9), this->rect.y + rekt.y + (rekt.h / 9), color_str); 128 | } 129 | } -------------------------------------------------------------------------------- /include/amogpu/core.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef CORE_H 3 | #define CORE_H 4 | 5 | #include 6 | #include 7 | #define VERSION 1_0_3 8 | 9 | /** 10 | * @author Rina 11 | * @since 28/07/22 at 15:07pm 12 | * 13 | * THE AMOGPU LIBRARY. 14 | * ALL CREDITS RESERVED TO RINA (aka MrsRina). 15 | * FREE COMERCIAL USE, YOU NEED USE THE PUBLIC LICENSE. 16 | **/ 17 | namespace amogpu { 18 | extern uint8_t dynamic; 19 | extern uint8_t invoked; 20 | 21 | extern float matrix_viewport[4]; 22 | extern float matrix_projection_ortho[16]; 23 | extern std::string gl_version; 24 | 25 | /* 26 | * Shape type to shpae builder. 27 | */ 28 | enum shape { 29 | RECT, CIRCLE 30 | }; 31 | 32 | /* 33 | * Store font data. 34 | */ 35 | struct font_char { 36 | float x = 0; 37 | float texture_x = 0; 38 | 39 | float w = 0; 40 | float h = 0; 41 | 42 | float top = 0; 43 | float left = 0; 44 | }; 45 | 46 | /** 47 | * Mini-programs on GPU. 48 | **/ 49 | struct gpu_gl_program { 50 | GLuint program = 0; 51 | bool validation = false; 52 | 53 | void use(); 54 | void end(); 55 | 56 | /* Start of uniform setters. */ 57 | void setb(const std::string &uniform_name, bool val); 58 | void seti(const std::string &uniform_name, int32_t val); 59 | void setf(const std::string &uniform_name, float val); 60 | void set2f(const std::string &uniform_name, const float* val); 61 | void set4f(const std::string &uniform_name, const float* val); 62 | void setm4f(const std::string &uniform_name, const float* val); 63 | void set3f(const std::string &uniform_name, const float* val); 64 | void setm3f(const std::string &uniform_name, const float* val); 65 | /* End of uniform setters. */ 66 | }; 67 | 68 | /** 69 | * Store GPU data. 70 | **/ 71 | struct gpu_data { 72 | uint32_t factor; 73 | float color[4]; 74 | float rect[4]; 75 | 76 | GLint begin = 0; 77 | GLint end = 0; 78 | 79 | uint8_t texture_slot = 0; 80 | GLuint texture = 0; 81 | }; 82 | 83 | /* 84 | * Rectangle. 85 | */ 86 | struct rect { 87 | float x = 0.0f; 88 | float y = 0.0f; 89 | float w = 0.0f; 90 | float h = 0.0f; 91 | 92 | /* 93 | * Detect if the rect is colliding with a point in space. 94 | */ 95 | bool aabb_collide_with_point(float x, float y); 96 | 97 | void operator += (amogpu::rect &rect) { 98 | this->x += rect.x; 99 | this->y += rect.y; 100 | } 101 | 102 | void operator -= (amogpu::rect &rect) { 103 | this->x -= rect.x; 104 | this->y -= rect.y; 105 | } 106 | }; 107 | 108 | /* 109 | * Vector. 110 | */ 111 | struct vec4f { 112 | float x = 0.0f; 113 | float y = 0.0f; 114 | float z = 0.0f; 115 | float w = 0.0f; 116 | 117 | vec4f(float x, float y, float z, float w); 118 | }; 119 | 120 | /* 121 | * DEPRECATED 122 | * Global settings of a clock. 123 | */ 124 | struct clock { 125 | static float dt; 126 | static uint32_t fps; 127 | }; 128 | 129 | /* 130 | * Init amogpu library. 131 | */ 132 | void init(); 133 | 134 | /* 135 | * Quit amogpu library. 136 | */ 137 | void quit(); 138 | 139 | /* 140 | * Update matrices. 141 | */ 142 | void matrix(); 143 | 144 | /* 145 | * Send output log. 146 | */ 147 | void log(const std::string &input_str); 148 | 149 | /* 150 | * Open and get string data output from file. 151 | */ 152 | bool read_file(std::string &input_str, const std::string &path); 153 | 154 | /* 155 | * Pass to matrix the current window viewport. 156 | */ 157 | void viewport(float* mat); 158 | 159 | /* 160 | * Pass to matrix the projection view ortho 2D. 161 | */ 162 | void projection_view_ortho(float* mat, float left, float right, float bottom, float top); 163 | 164 | /* 165 | * Compile shader. 166 | */ 167 | bool compile_shader(GLuint &shader, GLuint mode, const char* src); 168 | 169 | /* 170 | * Create mini-program into GPU. 171 | */ 172 | bool create_program(gpu_gl_program &program, const char* vsh_path, const char* fsh_path); 173 | 174 | /* 175 | * Create mini-program into GPU without load a file. 176 | */ 177 | bool create_program_from_src(gpu_gl_program &program, const char* vsh_src, const char* fsh_src); 178 | } 179 | 180 | 181 | #endif -------------------------------------------------------------------------------- /src/shape_builder.cpp: -------------------------------------------------------------------------------- 1 | #include "amogpu/shape_builder.hpp" 2 | 3 | amogpu::gpu_gl_program shape_builder::fx_shape; 4 | 5 | GLuint shape_builder::vertex_array = 0; 6 | GLuint shape_builder::vertex_buffer = 0; 7 | 8 | void shape_builder::init() { 9 | const char* vsh_src = "#version 330 core\n" 10 | "layout (location = 0) in vec2 attrib_vertexes;\n" 11 | "out vec2 varying_attrib_vertexes;\n" 12 | "uniform vec4 u_vec_rect;\n" 13 | "uniform mat4 u_mat_projection;\n" 14 | "uniform float u_float_depth;\n" 15 | "void main() {\n" 16 | "gl_Position = u_mat_projection * vec4((attrib_vertexes * u_vec_rect.zw) + u_vec_rect.xy, u_float_depth, 1.0);\n" 17 | "varying_attrib_vertexes = attrib_vertexes;\n" 18 | "}\n"; 19 | 20 | const char* fsh_src = "#version 330 core\n" 21 | "in vec2 varying_attrib_vertexes;\n" 22 | "out vec4 frag_color;" 23 | "uniform vec4 u_vec_color;\n" 24 | "uniform bool u_bool_texture;\n" 25 | "uniform sampler2D u_sampler_texture_slot;\n" 26 | "uniform vec4 u_vec_uv_rect;\n" 27 | "uniform bool u_bool_circle;\n" 28 | "void main() {\n" 29 | "vec4 color = u_vec_color;\n" 30 | "if (u_bool_texture) {\n" 31 | "color = texture(u_sampler_texture_slot, (varying_attrib_vertexes * u_vec_uv_rect.zw) + u_vec_uv_rect.xy);\n" 32 | "color = u_vec_color * color;\n" 33 | "}\n" 34 | "frag_color = color;\n" 35 | "}\n"; 36 | 37 | amogpu::create_program_from_src(shape_builder::fx_shape, vsh_src, fsh_src); 38 | 39 | const float mesh[12] = { 40 | 0.0f, 0.0f, 41 | 1.0f, 0.0f, 42 | 1.0f, 1.0f, 43 | 1.0f, 1.0f, 44 | 0.0f, 1.0f, 45 | 0.0f, 0.0f 46 | }; 47 | 48 | // Create the buffers for rendering. 49 | glGenBuffers(1, &shape_builder::vertex_buffer); 50 | glGenVertexArrays(1, &shape_builder::vertex_array); 51 | glBindVertexArray(shape_builder::vertex_array); 52 | 53 | // 1 buffer is ok, do not need use another buffers to do rendering. 54 | glBindBuffer(GL_ARRAY_BUFFER, shape_builder::vertex_buffer); 55 | glEnableVertexAttribArray(0); 56 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 12, mesh, GL_STATIC_DRAW); 57 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*) 0); 58 | glBindBuffer(GL_ARRAY_BUFFER, 0); 59 | glBindVertexArray(0); 60 | 61 | amogpu::log("Shape builder fx compiled."); 62 | } 63 | 64 | void shape_builder::free_buffers() { 65 | glDeleteBuffers(1, &shape_builder::vertex_buffer); 66 | } 67 | 68 | void shape_builder::build(const amogpu::shape &format, const amogpu::vec4f &color, GLuint texture) { 69 | this->enum_flag_format = format; 70 | shape_builder::fx_shape.setb("u_bool_circle", enum_flag_format == amogpu::shape::CIRCLE); 71 | 72 | // Set the vertices amount (two triangles). 73 | this->concurrent_gpu_data.begin = 0; 74 | this->concurrent_gpu_data.end = 6; 75 | 76 | // Pass RGB normalised values to gpu data. 77 | this->concurrent_gpu_data.color[0] = color.x; 78 | this->concurrent_gpu_data.color[1] = color.y; 79 | this->concurrent_gpu_data.color[2] = color.z; 80 | this->concurrent_gpu_data.color[3] = color.w; 81 | 82 | // Set texture settings and slot. 83 | this->concurrent_gpu_data.texture = texture; 84 | this->concurrent_gpu_data.texture_slot = 0; 85 | 86 | // Pass texture values to the shader. 87 | shape_builder::fx_shape.setb("u_bool_texture", this->concurrent_gpu_data.texture != 0); 88 | shape_builder::fx_shape.set4f("u_vec_color", this->concurrent_gpu_data.color); 89 | 90 | if (this->concurrent_gpu_data.texture != 0) { 91 | glActiveTexture(GL_TEXTURE0 + this->concurrent_gpu_data.texture_slot); 92 | glBindTexture(GL_TEXTURE_2D, this->concurrent_gpu_data.texture); 93 | shape_builder::fx_shape.seti("u_sampler_texture_slot", this->concurrent_gpu_data.texture_slot); 94 | } 95 | } 96 | 97 | void shape_builder::modal(float x, float y, float w, float h) { 98 | if (this->concurrent_gpu_data.texture == 0) { 99 | return; 100 | } 101 | 102 | this->concurrent_gpu_data.rect[0] = x; 103 | this->concurrent_gpu_data.rect[1] = y; 104 | this->concurrent_gpu_data.rect[2] = w; 105 | this->concurrent_gpu_data.rect[3] = h; 106 | 107 | shape_builder::fx_shape.set4f("u_vec_uv_rect", this->concurrent_gpu_data.rect); 108 | } 109 | 110 | void shape_builder::draw(float x, float y, float w, float h) { 111 | this->concurrent_gpu_data.rect[0] = x; 112 | this->concurrent_gpu_data.rect[1] = y; 113 | this->concurrent_gpu_data.rect[2] = w; 114 | this->concurrent_gpu_data.rect[3] = h; 115 | 116 | shape_builder::fx_shape.set4f("u_vec_rect", this->concurrent_gpu_data.rect); 117 | shape_builder::fx_shape.setf("u_float_depth", this->depth); 118 | 119 | // Bind buffer and draw it. 120 | glDrawArrays(GL_TRIANGLES, this->concurrent_gpu_data.begin, this->concurrent_gpu_data.end); 121 | 122 | // Bind off buffer and texture. 123 | glBindTexture(GL_TEXTURE_2D, 0); 124 | } 125 | 126 | void shape_builder::invoke() { 127 | shape_builder::fx_shape.use(); 128 | shape_builder::fx_shape.setm4f("u_mat_projection", amogpu::matrix_projection_ortho); 129 | 130 | // Enable the VAO. 131 | glBindVertexArray(shape_builder::vertex_array); 132 | } 133 | 134 | void shape_builder::revoke() { 135 | shape_builder::fx_shape.end(); 136 | } -------------------------------------------------------------------------------- /test/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "keyboard.hpp" 5 | 6 | SDL_Window* sdl_win; 7 | keyboard keyklass; 8 | bool running = true; 9 | dynamic_batching batch; 10 | 11 | static float px {}; 12 | static float py {}; 13 | 14 | static float nx {}; 15 | static float ny {}; 16 | 17 | void update_window_viewport() { 18 | int32_t w, h; 19 | SDL_GetWindowSize(sdl_win, &w, &h); 20 | 21 | float width = static_cast(w); 22 | float height = static_cast(h); 23 | 24 | // Set the viewport of window. 25 | glViewport(0.0f, 0.0f, width, height); 26 | amogpu::log("Window viewport update (" + std::to_string(width) + ", " + std::to_string(height) + ")"); 27 | 28 | // Also update the overlay stuff here, im coding in sublime so... it is hard to refactor every time. 29 | keyklass.set_pos((width / 2) - (keyklass.rect.w / 2), (height / 2) + (keyklass.rect.h / 4)); 30 | } 31 | 32 | void on_poll_event(SDL_Event &sdl_event) { 33 | switch (sdl_event.type) { 34 | case SDL_QUIT: { 35 | running = false; 36 | break; 37 | } 38 | 39 | case SDL_WINDOWEVENT: { 40 | switch (sdl_event.window.event) { 41 | case SDL_WINDOWEVENT_SIZE_CHANGED: { 42 | update_window_viewport(); 43 | break; 44 | } 45 | } 46 | } 47 | 48 | case SDL_MOUSEBUTTONDOWN: { 49 | nx = static_cast(sdl_event.button.x); 50 | ny = static_cast(sdl_event.button.y); 51 | break; 52 | } 53 | } 54 | 55 | keyklass.on_event(sdl_event); 56 | } 57 | 58 | void on_update() { 59 | 60 | } 61 | 62 | void on_render() { 63 | amogpu::matrix(); 64 | 65 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 66 | glClearColor(.5f, .5f, 1.0f, 1.0f); 67 | 68 | if (draw::refresh) { 69 | draw::batch.invoke(); 70 | draw::font.render("hi the fps: " + std::to_string(amogpu::clock::fps), 10, 10, amogpu::vec4f(0.0f, 0.0f, 1.0f, 0.5f)); 71 | draw::batch.revoke(); 72 | } 73 | 74 | // Draw the batch. 75 | draw::batch.draw(); 76 | } 77 | 78 | int main(int argv, char** argc) { 79 | amogpu::log("The Jogo da Forca x GPU Edition ..."); 80 | amogpu::log("Creating window"); 81 | 82 | SDL_Init(SDL_INIT_EVERYTHING); 83 | 84 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 85 | SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); 86 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 87 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); 88 | 89 | sdl_win = SDL_CreateWindow("The Jogo da Forca x GPU Edition", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); 90 | SDL_GLContext sdl_gl_context = SDL_GL_CreateContext(sdl_win); 91 | 92 | glewExperimental = false; 93 | glewInit(); 94 | 95 | glDisable(GL_DEPTH_TEST); 96 | amogpu::log("Window and OpenGL context created!"); 97 | 98 | uint32_t cpu_fps = 60; 99 | uint32_t ticked_frames = 0; 100 | uint64_t delta_fps = 0; 101 | 102 | uint64_t elapsed_ticks = 0; 103 | uint64_t ticks_going_on = 0; 104 | uint64_t current_ticks = 0; 105 | uint64_t interval = 1000 / (uint64_t) cpu_fps; 106 | 107 | bool no_vsync = true; 108 | SDL_GL_SetSwapInterval(!no_vsync); // v-sync 109 | 110 | amogpu::gl_version = "#version 450 core"; 111 | amogpu::log("Initinalising buffers!"); 112 | amogpu::init(); 113 | 114 | draw::font.load("data/fonts/impact.ttf", 30); 115 | 116 | keyklass.init(); 117 | keyklass.calculate_scale(); 118 | 119 | draw::refresh = true; 120 | update_window_viewport(); 121 | 122 | shape_builder shape {}; 123 | 124 | batch.invoke(); 125 | batch.instance(200, 200); 126 | batch.fill(1.0f, 1.0f, 1.0f, 1.0f); // white; 127 | 128 | float x = 0; 129 | float y = 0; 130 | 131 | float w = 30; 132 | float h = 30; 133 | 134 | for (uint8_t i = 0; i < 5; i++) { 135 | batch.vertex(x, y); 136 | batch.vertex(x, y + h); 137 | batch.vertex(x + w, y + h); 138 | batch.vertex(x + w, y + h); 139 | batch.vertex(x + w, y); 140 | batch.vertex(x, y); 141 | 142 | batch.coords(0.0f, 0.0f); 143 | batch.coords(0.0f, 0.0f); 144 | batch.coords(0.0f, 0.0f); 145 | batch.coords(0.0f, 0.0f); 146 | batch.coords(0.0f, 0.0f); 147 | batch.coords(0.0f, 0.0f); 148 | 149 | x += w + 5; 150 | } 151 | 152 | batch.factor(x / 5); // why x / 5? we flag it as a difference. 153 | x = 0; 154 | y = 0; 155 | 156 | w = 1.0f; 157 | h = 1.0f; 158 | 159 | batch.next(); 160 | batch.instance(500, 50); 161 | batch.bind(draw::font.texture_bitmap); 162 | batch.fill(1.0f, 1.0f, 1.0f, 1.0f); 163 | batch.modal(300, 300); 164 | batch.vertex(x, y); 165 | batch.vertex(x, y + h); 166 | batch.vertex(x + w, y + h); 167 | batch.vertex(x + w, y + h); 168 | batch.vertex(x + w, y); 169 | batch.vertex(x, y); 170 | 171 | x = 0.922495f; 172 | w = 0.008192f; 173 | y = 0.000000f; 174 | h = 0.678571f; 175 | 176 | batch.coords(x, y); 177 | batch.coords(x, y + h); 178 | batch.coords(x + w, y + h); 179 | batch.coords(x + w, y + h); 180 | batch.coords(x + w, y); 181 | batch.coords(x, y); 182 | batch.next(); 183 | 184 | batch.revoke(); 185 | batch.set_frustum_depth(true); 186 | 187 | SDL_Event sdl_event; 188 | while (running) { 189 | current_ticks = SDL_GetTicks64(); 190 | ticks_going_on = current_ticks - elapsed_ticks; 191 | 192 | if (ticks_going_on > interval || no_vsync) { 193 | elapsed_ticks = current_ticks; 194 | delta_fps += ticks_going_on; 195 | 196 | // Set the DT based on current ticks (interval ms int divided by 100... 16 int -> 0.16f); 197 | amogpu::clock::dt = static_cast(current_ticks) / 100.0f; 198 | 199 | // Flag and set the current frame rate based on CPU-ticks. 200 | if (delta_fps > 1000) { 201 | amogpu::clock::fps = ticked_frames; 202 | ticked_frames = 0; 203 | delta_fps = 0; 204 | draw::refresh = true; 205 | } 206 | 207 | // Input etc. 208 | while (SDL_PollEvent(&sdl_event)) { 209 | on_poll_event(sdl_event); 210 | } 211 | 212 | // Update and render section. 213 | on_update(); 214 | on_render(); 215 | 216 | //px = px + (nx - px) * amogpu::clock::dt; 217 | //py = py + (ny - py) * amogpu::clock::dt; 218 | 219 | 220 | draw::batch.draw(); 221 | 222 | // Count ticked frames. 223 | ticked_frames++; 224 | 225 | // Swap bufferws (front to back and back to front) 226 | SDL_GL_SwapWindow(sdl_win); 227 | } 228 | } 229 | 230 | amogpu::log("Game shutdown complete!"); 231 | return 1; 232 | } 233 | -------------------------------------------------------------------------------- /src/core.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "amogpu/gpu_handler.hpp" 3 | #include "amogpu/font_renderer.hpp" 4 | #include "amogpu/shape_builder.hpp" 5 | 6 | uint8_t amogpu::dynamic {1}; 7 | uint8_t amogpu::invoked {2}; 8 | 9 | float amogpu::matrix_viewport[4] {}; 10 | float amogpu::matrix_projection_ortho[16] {}; 11 | std::string amogpu::gl_version {"#version 330 core"}; 12 | 13 | void amogpu::gpu_gl_program::use() { 14 | glUseProgram(this->program); 15 | } 16 | 17 | void amogpu::gpu_gl_program::end() { 18 | glUseProgram(0); 19 | } 20 | 21 | void amogpu::gpu_gl_program::setb(const std::string &uniform_name, bool val) { 22 | glUniform1i(glGetUniformLocation(this->program, uniform_name.c_str()), (int32_t) val); 23 | } 24 | 25 | void amogpu::gpu_gl_program::seti(const std::string &uniform_name, int32_t val) { 26 | glUniform1i(glGetUniformLocation(this->program, uniform_name.c_str()), val); 27 | } 28 | 29 | void amogpu::gpu_gl_program::setf(const std::string &uniform_name, float val) { 30 | glUniform1f(glGetUniformLocation(this->program, uniform_name.c_str()), val); 31 | } 32 | 33 | void amogpu::gpu_gl_program::set2f(const std::string &uniform_name, const float* val) { 34 | glUniform2fv(glGetUniformLocation(this->program, uniform_name.c_str()), GL_TRUE, val); 35 | } 36 | 37 | void amogpu::gpu_gl_program::set4f(const std::string &uniform_name, const float* val) { 38 | glUniform4fv(glGetUniformLocation(this->program, uniform_name.c_str()), GL_TRUE, val); 39 | } 40 | 41 | void amogpu::gpu_gl_program::setm4f(const std::string &uniform_name, const float* val) { 42 | glUniformMatrix4fv(glGetUniformLocation(this->program, uniform_name.c_str()), 1, GL_FALSE, val); 43 | } 44 | 45 | void amogpu::gpu_gl_program::setm3f(const std::string &uniform_name, const float* val) { 46 | glUniformMatrix3fv(glGetUniformLocation(this->program, uniform_name.c_str()), 1, GL_FALSE, val); 47 | } 48 | 49 | void amogpu::gpu_gl_program::set3f(const std::string &uniform_name, const float* val) { 50 | glUniform3fv(glGetUniformLocation(this->program, uniform_name.c_str()), GL_TRUE, val); 51 | } 52 | 53 | void amogpu::init() { 54 | dynamic_batching::init(); 55 | font_renderer::init(); 56 | shape_builder::init(); 57 | 58 | glEnable(GL_BLEND); 59 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 60 | } 61 | 62 | void amogpu::quit() { 63 | font_renderer::end_ft_library(); 64 | shape_builder::free_buffers(); 65 | } 66 | 67 | void amogpu::matrix() { 68 | dynamic_batching::matrix(); 69 | 70 | // Calculate the matrixes. 71 | amogpu::viewport(amogpu::matrix_viewport); 72 | amogpu::projection_view_ortho(amogpu::matrix_projection_ortho, 0.0f, amogpu::matrix_viewport[2], amogpu::matrix_viewport[3], 0.0f); 73 | } 74 | 75 | bool amogpu::rect::aabb_collide_with_point(float x, float y) { 76 | return x > this->x && x < this->x + this->w && y > this->y && y < this->y + this->h; 77 | } 78 | 79 | void amogpu::log(const std::string &input_str) { 80 | std::cout << ("[AMOGPU] " + input_str).c_str() << std::endl; 81 | } 82 | 83 | bool amogpu::read_file(std::string &input_str, const std::string &path) { 84 | std::ifstream ifs(path.c_str()); 85 | 86 | if (ifs.is_open()) { 87 | std::string string_buffer_builder = ""; 88 | 89 | while (getline(ifs, string_buffer_builder)) { 90 | input_str += "\n" + string_buffer_builder; 91 | } 92 | 93 | ifs.close(); 94 | return true; 95 | } else { 96 | amogpu::log("Could not open file '" + path + "'."); 97 | } 98 | 99 | return false; 100 | } 101 | 102 | bool amogpu::compile_shader(GLuint &shader, GLuint mode, const char* src) { 103 | shader = glCreateShader(mode); 104 | 105 | glShaderSource(shader, 1, &src, NULL); 106 | glCompileShader(shader); 107 | 108 | GLint compile_status = GL_TRUE; 109 | glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); 110 | 111 | if (!compile_status) { 112 | char log[256]; 113 | glGetShaderInfoLog(shader, 256, NULL, log); 114 | amogpu::log(log); 115 | } 116 | 117 | return compile_status; 118 | } 119 | 120 | bool amogpu::create_program(gpu_gl_program &program, const char* vsh_path, const char* fsh_path) { 121 | std::string vsh_src; 122 | std::string fsh_src; 123 | 124 | GLuint vsh; 125 | GLuint fsh; 126 | 127 | bool flag = true; 128 | 129 | flag = amogpu::read_file(vsh_src, vsh_path) && amogpu::read_file(fsh_src, fsh_path); 130 | flag = flag && amogpu::create_program_from_src(program, vsh_src.c_str(), fsh_src.c_str()); 131 | 132 | if (program.validation) { 133 | amogpu::log("'" + std::string(vsh_path) + "' & '" + std::string(fsh_path) + "' shaders compiled."); 134 | } 135 | 136 | return program.validation; 137 | } 138 | 139 | bool amogpu::create_program_from_src(gpu_gl_program &program, const char* vsh_src, const char* fsh_src) { 140 | GLuint vsh; 141 | GLuint fsh; 142 | 143 | bool flag = amogpu::compile_shader(vsh, GL_VERTEX_SHADER, vsh_src) && amogpu::compile_shader(fsh, GL_FRAGMENT_SHADER, fsh_src); 144 | 145 | if (flag) { 146 | program.program = glCreateProgram(); 147 | 148 | glAttachShader(program.program, vsh); 149 | glAttachShader(program.program, fsh); 150 | glLinkProgram(program.program); 151 | 152 | GLint link_status = GL_FALSE; 153 | glGetProgramiv(program.program, GL_LINK_STATUS, &link_status); 154 | 155 | if (!link_status) { 156 | char log[256]; 157 | glGetProgramInfoLog(program.program, 256, NULL, log); 158 | } else { 159 | amogpu::log("Program linked."); 160 | } 161 | 162 | program.validation = link_status; 163 | 164 | glDeleteShader(vsh); 165 | glDeleteShader(fsh); 166 | } 167 | 168 | return program.validation; 169 | } 170 | 171 | void amogpu::viewport(float* mat) { 172 | glGetFloatv(GL_VIEWPORT, mat); 173 | } 174 | 175 | void amogpu::projection_view_ortho(float* mat, float left, float right, float bottom, float top) { 176 | const float znear = -1.0f; 177 | const float zfar = 1.0f; 178 | const float zinv = 1.0f / (zfar - znear); 179 | const float yinv = 1.0f / (top - bottom); 180 | const float xinv = 1.0f / (right - left); 181 | 182 | mat[0] = (2.0f * xinv); 183 | mat[1] = 0.0f; 184 | mat[2] = 0.0f; 185 | mat[3] = 0.0f; 186 | 187 | mat[4] = 0.0f; 188 | mat[5] = 2.0f * yinv; 189 | mat[6] = 0.0f; 190 | mat[7] = 0.0f; 191 | 192 | mat[8] = 0.0f; 193 | mat[9] = 0.0f; 194 | mat[10] = -2.0f * zinv; 195 | mat[11] = 0.0f; 196 | 197 | mat[12] = (-(right + left) * xinv); 198 | mat[13] = (-(top + bottom) * yinv); 199 | mat[14] = (-(zfar + znear) * zinv); 200 | mat[15] = 1.0f; 201 | } 202 | 203 | amogpu::vec4f::vec4f(float x, float y, float z, float w) { 204 | this->x = x; 205 | this->y = y; 206 | this->z = z; 207 | this->w = w; 208 | } -------------------------------------------------------------------------------- /src/font_renderer.cpp: -------------------------------------------------------------------------------- 1 | #include "amogpu/amogpu.hpp" 2 | 3 | FT_Library font_renderer::ft_library; 4 | 5 | dynamic_batching* font_renderer::batch() { 6 | return this->batch_mode == amogpu::invoked ? dynamic_batching::invoked : this->binded_batch; 7 | } 8 | 9 | void font_renderer::init() { 10 | FT_Init_FreeType(&font_renderer::ft_library); 11 | } 12 | 13 | void font_renderer::end_ft_library() { 14 | FT_Done_FreeType(font_renderer::ft_library); 15 | } 16 | 17 | void font_renderer::load(const std::string &font_path, uint8_t font_size) { 18 | if (this->batch_mode == 0) { 19 | this->from(amogpu::invoked); 20 | } 21 | 22 | if (this->current_font_path == font_path && this->current_font_size == font_size) { 23 | return; 24 | } 25 | 26 | if (this->current_font_path != font_path && FT_New_Face(font_renderer::ft_library, font_path.c_str(), 0, &this->ft_face)) { 27 | amogpu::log("Could not load font or invalid path."); 28 | return; 29 | } 30 | 31 | this->current_font_path = font_path; 32 | this->current_font_size = font_size; 33 | 34 | this->texture_width = 0; 35 | this->texture_height = 0; 36 | 37 | FT_Set_Pixel_Sizes(this->ft_face, 0, font_size); 38 | this->use_kerneking = FT_HAS_KERNING(this->ft_face); 39 | this->ft_glyph_slot = this->ft_face->glyph; 40 | 41 | for (uint8_t i = 0; i < 128; i++) { 42 | if (FT_Load_Char(this->ft_face, i, FT_LOAD_RENDER)) { 43 | continue; 44 | } 45 | 46 | this->texture_width += this->ft_glyph_slot->bitmap.width; 47 | this->texture_height = std::max(this->texture_height, this->ft_glyph_slot->bitmap.rows); 48 | } 49 | 50 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 51 | 52 | if (this->texture_bitmap == 0) { 53 | glGenTextures(1, &this->texture_bitmap); 54 | } else { 55 | glDeleteTextures(1, &this->texture_bitmap); 56 | glGenTextures(1, &this->texture_bitmap); 57 | } 58 | 59 | glBindTexture(GL_TEXTURE_2D, this->texture_bitmap); 60 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, (int32_t) this->texture_width, (int32_t) this->texture_height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr); 61 | 62 | float offset = 0.0f; 63 | 64 | for (uint8_t i = 0; i < 128; i++) { 65 | if (FT_Load_Char(this->ft_face, i, FT_LOAD_RENDER)) { 66 | continue; 67 | } 68 | 69 | amogpu::font_char &f_char = this->allocated_font_char[i]; 70 | 71 | f_char.x = offset / static_cast(this->texture_width); 72 | f_char.w = static_cast(this->ft_glyph_slot->bitmap.width); 73 | f_char.h = static_cast(this->ft_glyph_slot->bitmap.rows); 74 | 75 | f_char.left = static_cast(this->ft_glyph_slot->bitmap_left); 76 | f_char.top = static_cast(this->ft_glyph_slot->bitmap_top); 77 | f_char.texture_x = static_cast(this->ft_glyph_slot->advance.x >> 6); 78 | 79 | glTexSubImage2D(GL_TEXTURE_2D, 0, static_cast(offset), 0, f_char.w, f_char.h, GL_RED, GL_UNSIGNED_BYTE, this->ft_glyph_slot->bitmap.buffer); 80 | offset += f_char.w; 81 | } 82 | 83 | GLint data[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED}; 84 | 85 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 86 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 87 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 88 | glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 89 | glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, data); 90 | 91 | glBindTexture(GL_TEXTURE_2D, 0); 92 | 93 | amogpu::log("Font loaded, created bitmap!"); 94 | } 95 | 96 | void font_renderer::from(dynamic_batching* concurrent_batch) { 97 | this->binded_batch = concurrent_batch; 98 | this->batch_mode = amogpu::dynamic; 99 | } 100 | 101 | void font_renderer::from(uint8_t mode) { 102 | this->batch_mode = mode; 103 | } 104 | 105 | float font_renderer::get_text_width(const std::string &text) { 106 | FT_Vector vec; 107 | this->previous = 0; 108 | 109 | float start_x = 0.5f; 110 | float render_x = 0.0f; 111 | float text_width = 0.0f; 112 | 113 | amogpu::font_char f_char; 114 | 115 | for (const char* i = text.c_str(); *i; i++) { 116 | if (this->use_kerneking && this->previous && *i) { 117 | FT_Get_Kerning(this->ft_face, this->previous, *i, 0, &vec); 118 | start_x += static_cast(vec.x >> 6); 119 | } 120 | 121 | f_char = this->allocated_font_char[*i]; 122 | 123 | render_x = start_x + f_char.left; 124 | start_x += f_char.texture_x; 125 | 126 | this->previous = *i; 127 | text_width = render_x + f_char.w; 128 | } 129 | 130 | return static_cast(text_width); 131 | } 132 | 133 | float font_renderer::get_text_height() { 134 | float h = static_cast(this->texture_height); 135 | return h + (h / 8); 136 | } 137 | 138 | void font_renderer::render(const std::string &text, float x, float y, const amogpu::vec4f &color) { 139 | const char* char_str = text.c_str(); 140 | const int32_t str_len = strlen(char_str); 141 | 142 | render_x = 0, render_y = 0, render_w = 0, render_h = 0; 143 | float texture_x = 0, texture_y = 0, texture_w = 0, texture_h = 0; 144 | float impl = (static_cast(this->texture_height) / 8); 145 | int32_t diff = 1; 146 | 147 | x = static_cast(static_cast(x)); 148 | y = static_cast(static_cast(y - (impl / 2))); 149 | 150 | // Call GPU instance. 151 | dynamic_batching* batch = this->batch(); 152 | 153 | batch->instance(x, y); 154 | batch->fill(color); 155 | batch->bind(this->texture_bitmap); 156 | 157 | // Reset to mesh geometry. 158 | x = 0; 159 | y = 0; 160 | 161 | this->previous = 0; 162 | amogpu::font_char f_char; 163 | 164 | for (const char* i = char_str; *i; i++) { 165 | if (this->use_kerneking && this->previous && *i) { 166 | FT_Get_Kerning(this->ft_face, this->previous, *i, 0, &this->previous_char_vec); 167 | x += static_cast(this->previous_char_vec.x >> 6); 168 | } 169 | 170 | f_char = this->allocated_font_char[*i]; 171 | 172 | render_x = x + f_char.left; 173 | render_y = y + (static_cast(this->texture_height) - f_char.top); 174 | 175 | render_w = f_char.w; 176 | render_h = f_char.h; 177 | 178 | texture_x = f_char.x; 179 | texture_w = render_w / static_cast(this->texture_width); 180 | texture_h = render_h / static_cast(this->texture_height); 181 | diff += static_cast(texture_x); 182 | 183 | // Draw a quad. 184 | batch->vertex(render_x, render_y); 185 | batch->vertex(render_x, render_y + render_h); 186 | batch->vertex(render_x + render_w, render_y + render_h); 187 | batch->vertex(render_x + render_w, render_y + render_h); 188 | batch->vertex(render_x + render_w, render_y); 189 | batch->vertex(render_x, render_y); 190 | 191 | // Also set the modal rect texture. 192 | batch->coords(texture_x, texture_y); 193 | batch->coords(texture_x, texture_y + texture_h); 194 | batch->coords(texture_x + texture_w, texture_y + texture_h); 195 | batch->coords(texture_x + texture_w, texture_y + texture_h); 196 | batch->coords(texture_x + texture_w, texture_y); 197 | batch->coords(texture_x, texture_y); 198 | 199 | x += f_char.texture_x; 200 | this->previous = *i; 201 | } 202 | 203 | batch->factor(str_len + diff); 204 | batch->next(); 205 | } 206 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amogpu 2 | 3 | The reason to this readme is written in another language and not english is because there is not good materials to learn OpenGL in portuguese (Brasil). 4 | 5 | -- 6 | 7 | Entenda, a GPU é muito importante para nós programdores e programadoras, sem ela aplicações que nós usamos não teria o minimo de performance e design bonito! 8 | infelizmente poucas pessoas se interessam por este incrível lado do hardware! 9 | Essa biblioteca mostra como podemos utilizar corretamente funções do OpenGL moderno. 10 | 11 | Tessellator, batch. shape builder e outros conceitos estão inseridos na amogpu, aqui está um simples resumo do que são: 12 | 13 | - O que é tessellator? 14 | Do mesmo modo que traçamos linhas para formar um tecido, em computação trassamos linhas por vértices, quando tratamos de elementos UI que elaboram uma GUI, é preciso manipular cada evento de cada elemento UI sincronizado com o desenho, para isso preciamos passar para a GPU vértices e as posições na tela, entretanto não dá pra só atualizar a todo tick e passar vértices a todo tick e a todo momento. 15 | 16 | - O que é batch? 17 | Batch é salvar em forma de lote e utilizar depois, diferente de você enviar vértices todo instante (tick), podemos armazenar as vértices em lote e depois renderizar, e mais, salvar as posições, cor ou textura para renderizar sem a necessidade de mandar todas as vértices denovo para a GPU, com isso é possível ter uma performance muito maior se comparada com outros métodos. 18 | 19 | - O que é shape builder? 20 | Se você quiser renderizar formas geométricas rapidamente, mas ela não é eficiênte para UI complexas, o nome builder por que ela constrói um shape e destrói logo em seguida. 21 | 22 | --- 23 | 24 | # Get Start 25 | 26 | Essa biblioteca foi compilado pra Windows-x86_x64, se você quiser contribuir com uma versão compilada em outro sistema fique a vontade. 27 | Primeiramente baixe o repositório e copie a pasta `include/` & `lib/` dentro do MinGW, pronto! Agora você deve linkar: 28 | 29 | `target_link_libraries( amogpu)` 30 | `g++ ... -lamogpu` 31 | 32 | Inicialmente inclua `amogpu/amogpu` e chame a função `amogpu::init`, deste jeitinho ó: 33 | ```c++ 34 | #include 35 | 36 | // ... 37 | amogpu::init(); // Não é pra ocorrer nenhum erro, caso sim reporte. 38 | 39 | /** 40 | * Mainloop. 41 | **/ 42 | while (true) { 43 | // ... 44 | // Você deve chamar essa função apenas 1 vez antes de desenhar qualquer coisa. 45 | // Ela serve pra atualizar as matrizes de posição da camêra. 46 | amogpu::matrix(); 47 | 48 | // ... 49 | ``` 50 | 51 | # Shape Builder 52 | 53 | Shape builder é uma feature para desenhar circulos e retangulos de forma rápida e eficiente, mas ela não serve pra contextos aonde você precisa de performance e controle. 54 | ```c++ 55 | shape_builder shape; 56 | 57 | /* 58 | * Main loop. 59 | */ 60 | while (true) { 61 | // Diferente de dynamic batching, você não precisa controlar invoke e revoke calls. 62 | shape.invoke(); 63 | 64 | // Se você quiser criar um circulo use amogpu::shape::CIRCLE, se não use amogpu::shape::RECT 65 | shape.build(amogpu::shape::RECT, amogpu::vec4f(1.0f, 1.0f, 1.0f, 0.5f), 0); // o ultimo argumento é a texture id. 66 | shape.modal(0.0f, 0.0f, 1.0f, 1.0f); // se não tiver uma textura embutida no shape não é preciso chamar esse metódo! 67 | shape.draw(20, 20, 200, 200); // (x, y, width, height), se você estiver renderizando um circle então deixe width e height iguais. 68 | 69 | // Pronto é só isso. 70 | // Você pode chamar quantas vezes quiser. 71 | 72 | shape.build(amogpu::shape::RECT, amogpu::vec4f(1.0f, 1.0f, 1.0f, 1.0f)); 73 | shape.draw(20, 60, 200, 200); 74 | 75 | shape.build(amogpu::shape::RECT, amogpu::vec4f(1.0f, 1.0f, 1.0f, 1.0f), texture_cool_cat_id); 76 | shape.modal(0.0f, 0.0f, 1.0f, 1.0f); 77 | shape.draw(0, 60, 200, 200); 78 | 79 | // Prontinho. 80 | shape.revoke(); 81 | } 82 | ``` 83 | 84 | A imagem a baixo é ilustrativa, se trata de outro código usando a feature shape_builder. 85 | ![Alt text](splash/splash-shape-builder.png?raw=true) 86 | 87 | # Dynamic Batching 88 | 89 | O funcionamento é simples: 90 | ```c++ 91 | /** 92 | * Mainloop. 93 | **/ 94 | while (true) { 95 | // ... 96 | // Qualquer lugar após glClear & glClearColor. 97 | batch1.draw(); 98 | batch3.draw(); 99 | batch2.draw(); 100 | 101 | // ... 102 | // Final do loop. 103 | } 104 | ``` 105 | 106 | Detalhes: 107 | ```c++ 108 | #include // ou só #include 109 | 110 | // ... 111 | dynamic_batching batch; 112 | 113 | // Em 1 tick ou em poucos ticks aonde queremos mudar alguma coisa. 114 | batch.invoke(); // Chamamos a GPU. 115 | // Enviamos dados para a GPU. 116 | batch.revoke(); // Finalizamos o segmento de desenhar da GPU. 117 | 118 | // Em um loop com contexto OpenGL. 119 | while (true) { 120 | batch.draw(); 121 | } 122 | ``` 123 | 124 | Dentro do segmento de desenhar podemos renderizar muitos rects (dá pra facilmente renderizar circulos mas nesse caso é feito no fragment shader): 125 | ```c++ 126 | batch.invoke(); // Iniciamos esse segmento. 127 | 128 | x = 0; 129 | y = 0; 130 | w = 1.0f; 131 | h = 1.0f; 132 | 133 | batch.instance(20, 50); // Instancia contém 3 argumentos, 2 primeiros são [x, y] e o ultimo é o factor, mas não precisa especificar agora. 134 | batch.fill(1.0f, 1.0f, 1.0f, 1.0f); // definimos a cor em RGBA normalisados (1.0 - 0.0). 135 | batch.bind(f_renderer.texture_bitmap); // texture é um uint que guarda dados de uma textura. 136 | 137 | // Como isso é um shape simples e não um grande desenho (e.g tile map, wireframe) você não precisa especificar um fator unico. 138 | batch.modal(300, 300); // isso serve para definir o tamanho, mas isso só funciona pra shape simples. 139 | 140 | // Mesh de vértices. 141 | batch.vertex(x, y); 142 | batch.vertex(x, y + h); 143 | batch.vertex(x + w, y + h); 144 | batch.vertex(x + w, y + h); 145 | batch.vertex(x + w, y); 146 | batch.vertex(x, y); 147 | 148 | // Vamos desenhar a letra V. 149 | x = 0.922495f; 150 | w = 0.008192f; 151 | y = 0.000000f; 152 | h = 0.678571f; 153 | 154 | // Se temos 6 vértices então devemos aplicar 6 vezes coordenadas uv. 155 | // Como é texturizado devemos pasar as coordenadas normalisadas, se não tiver textura inclusa é só passar 0.0f 6 vezes. 156 | batch.coords(x, y); 157 | batch.coords(x, y + h); 158 | batch.coords(x + w, y + h); 159 | batch.coords(x + w, y + h); 160 | batch.coords(x + w, y); 161 | batch.coords(x, y); 162 | 163 | batch.next(); // Se você quiser desenhar 30 rects é só pegar esse sub-segmento de (instance() - next()) calls e replicar. 164 | 165 | // e.g 166 | // você colocou todo o código acima dentro de uma função ou metódo com parametros para apenas a posição. 167 | // então você pode invocar muitas vezes. 168 | push_rect(20, 50); 169 | push_rect(90, 80); 170 | push_rect(700, 250); 171 | 172 | batch.revoke(); // Finalizamos esse segmento. 173 | ``` 174 | ![Alt text](splash/splash-texture.png?raw=true) 175 | 176 | Se você quiser ver um exemplo real recomendo olhar a pasta `test/` do projeto, no `main.cpp` você pode ver como usar as features `dynamic_batching` e `font_renderer` de forma otimizada. 177 | 178 | Aqui irei explicar como usar o `dynamic_batching` com multiplas instâncias. 179 | `Observação: Multiplas instâncias não tem nada haver com instanced rendering, amogpu ainda não tem essa feature.` 180 | 181 | ```c++ 182 | // ... 183 | // Entretanto se você querer desenhar multiplos shapes na tela em uma unica instância, você tem que especificar o tamanho na hora de enviar as vértices. 184 | // e.g você dá 18 vértices e 18 coordenadas em uma unica instância, mas o tamanho de cada shape é unico, então você altera a posição 185 | // dá vértice e o tamanho de cada mesh, então ai que entra o factor, você tem que colocar no paramêtro alguma coisa que vá fazer sentido 186 | // para o dynamic batch alterar o buffer. 187 | // ... 188 | 189 | batch.instance(20, 20); 190 | batch.fill(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); // white; 191 | 192 | float x = 0; 193 | float y = 0; 194 | 195 | float w = 30; 196 | float h = 30; 197 | 198 | for (uint8_t i = 0; i < 5; i++) { 199 | batch.vertex(x, y); 200 | batch.vertex(x, y + h); 201 | batch.vertex(x + w, y + h); 202 | batch.vertex(x + w, y + h); 203 | batch.vertex(x + w, y); 204 | batch.vertex(x, y); 205 | 206 | batch.coords(0.0f, 0.0f); 207 | batch.coords(0.0f, 0.0f); 208 | batch.coords(0.0f, 0.0f); 209 | batch.coords(0.0f, 0.0f); 210 | batch.coords(0.0f, 0.0f); 211 | batch.coords(0.0f, 0.0f); 212 | 213 | x += w + 5; 214 | } 215 | 216 | batch.factor(x / 5); // Por que x / 5? Se o tamanho de algum shape mudar, então o buffer precisa mudar. 217 | batch.next(); 218 | ``` 219 | ![Alt text](/splash/splash-multi-instances.png?raw=true) 220 | 221 | --- 222 | # Font Renderer 223 | 224 | ```c++ 225 | #include 226 | 227 | dynamic_batching batch; 228 | font_renderer f_renderer; 229 | 230 | // Se você quiser alterar o tamanho ou mudar de fonte é só rodar esse método. 231 | f_renderer.load("path/to/font.ttf", 18); 232 | f_renderer.from(&batch); // isso vai dizer pro font renderer qual dynamic batch usar. 233 | 234 | // Se você quiser deixar automatico, é só fazer. 235 | f_renderer.from(amogpu::invoked); 236 | 237 | batch.invoke(); 238 | f_renderer.render("hi sou linwda", 10, 10, amogpu::vec4(1.0f, 1.0f, 1.0f, 1.0f)); 239 | batch.revoke(); 240 | 241 | // Ai no loop você dá draw. 242 | while (true) { 243 | batch.draw(); 244 | } 245 | 246 | // Você também pode fazer combinações. 247 | // e.g 248 | dynamic_batch batch2; 249 | f_renderer.from(&batch); 250 | 251 | // Você pode chamar o batch aplicado pelo método batch. 252 | f_renderer.batch()->invoke(); 253 | f_renderer.render("vwc é linda(o)", 10, 10 + 1 + f_renderer.get_text_height(), amogpu::vec4(1.0f, 1.0f, 1.0f, 1.0f)); 254 | f_renderer.batch()->revoke(); 255 | 256 | // Ai no loop você faz. 257 | while (true) { 258 | f_renderer.batch()->draw(); 259 | } 260 | ``` 261 | ![Alt text](splash/splash-font-rendering.png?raw=true) 262 | -------------------------------------------------------------------------------- /src/gpu_handler.cpp: -------------------------------------------------------------------------------- 1 | #include "amogpu/amogpu.hpp" 2 | #include "amogpu/gpu_handler.hpp" 3 | 4 | float amogpu::clock::dt = 0.0f; 5 | uint32_t amogpu::clock::fps = 0; 6 | 7 | amogpu::gpu_gl_program dynamic_batching::fx_shape; 8 | dynamic_batching* dynamic_batching::invoked = nullptr; 9 | 10 | void dynamic_batching::init() { 11 | std::string vsh_src = amogpu::gl_version + "\n" 12 | "layout (location = 0) in vec2 attrib_vertexes;\n" 13 | "layout (location = 1) in vec2 attrib_tex_coords;\n" 14 | "\n" 15 | "uniform mat4 u_mat_projection;\n" 16 | "uniform vec4 u_vec_rect;\n" 17 | "uniform float u_float_zdepth;\n" 18 | "\n" 19 | "out vec2 varying_attrib_tex_coords;\n" 20 | "\n" 21 | "void main() {\n" 22 | "\tif (u_vec_rect.z != 0 || u_vec_rect.w != 0) {\n" 23 | "\t\tgl_Position = u_mat_projection * vec4((attrib_vertexes * u_vec_rect.zw) + u_vec_rect.xy, u_float_zdepth, 1.0f);\n" 24 | "\t} else {\n" 25 | "\t\tgl_Position = u_mat_projection * vec4(attrib_vertexes + u_vec_rect.xy, u_float_zdepth, 1.0f);\n" 26 | "\t}\n" 27 | "\n" 28 | "\tvarying_attrib_tex_coords = attrib_tex_coords;\n" 29 | "}"; 30 | 31 | std::string fsh_src = amogpu::gl_version + "\n" 32 | "out vec4 out_frag_color;\n" 33 | "uniform vec4 u_vec_color;\n" 34 | "uniform sampler2D u_sampler_texture_slot;\n" 35 | "uniform bool u_bool_texture_active;\n" 36 | "\n" 37 | "in vec2 varying_attrib_tex_coords;\n" 38 | "\n" 39 | "void main() {\n" 40 | "\tvec4 frag_color = u_vec_color;\n" 41 | "\n" 42 | "\tif (u_bool_texture_active) {\n" 43 | "\t\tfrag_color = texture(u_sampler_texture_slot, varying_attrib_tex_coords);\n" 44 | "\t\tfrag_color = vec4(frag_color.xyz - ((1.0 - u_vec_color.xyz) - 1.0), frag_color.w * u_vec_color.w);\n" 45 | "\t}\n" 46 | "\n" 47 | "\tout_frag_color = frag_color;\n" 48 | "}"; 49 | 50 | amogpu::create_program_from_src(dynamic_batching::fx_shape, vsh_src.c_str(), fsh_src.c_str()); 51 | } 52 | 53 | void dynamic_batching::matrix() { 54 | } 55 | 56 | void dynamic_batching::invoke() { 57 | this->sizeof_allocated_gpu_data = 0; 58 | this->sizeof_allocated_vertices = 0; 59 | 60 | if (this->allocated_data.size() == this->sizeof_allocated_gpu_data) { 61 | this->allocated_data.emplace_back(); 62 | } 63 | 64 | this->allocated_data_copy.clear(); 65 | dynamic_batching::invoked = this; 66 | } 67 | 68 | void dynamic_batching::instance(float x, float y, int32_t factor) { 69 | amogpu::gpu_data &data = this->allocated_data[this->sizeof_allocated_gpu_data]; 70 | 71 | data.begin = this->sizeof_allocated_vertices; 72 | data.texture = 0; 73 | data.texture_slot = 0; 74 | 75 | data.rect[0] = x; 76 | data.rect[1] = y; 77 | 78 | if (factor != -1) { 79 | data.rect[2] = 0; 80 | data.rect[3] = 0; 81 | } 82 | 83 | this->factor(factor); 84 | this->sizeof_instanced_allocated_vertices = 0; 85 | } 86 | 87 | void dynamic_batching::modal(float w, float h) { 88 | amogpu::gpu_data &data = this->allocated_data[this->sizeof_allocated_gpu_data]; 89 | 90 | data.rect[2] = w; 91 | data.rect[3] = h; 92 | } 93 | 94 | void dynamic_batching::factor(int32_t factor) { 95 | amogpu::gpu_data &data = this->allocated_data[this->sizeof_allocated_gpu_data]; 96 | 97 | data.rect[2] = 0; 98 | data.rect[3] = 0; 99 | 100 | if (factor != -1 && data.factor != factor) { 101 | this->should_alloc_new_buffers = true; 102 | } 103 | } 104 | 105 | void dynamic_batching::fill(const amogpu::vec4f &color) { 106 | this->fill(color.x, color.y, color.z, color.w); 107 | } 108 | 109 | void dynamic_batching::fill(float r, float g, float b, float a) { 110 | amogpu::gpu_data &data = this->allocated_data[this->sizeof_allocated_gpu_data]; 111 | 112 | // RGBA - normalised value. 113 | data.color[0] = r; 114 | data.color[1] = g; 115 | data.color[2] = b; 116 | data.color[3] = a; 117 | } 118 | 119 | void dynamic_batching::vertex(float x, float y) { 120 | this->concurrent_allocated_vertices.push_back(x); 121 | this->concurrent_allocated_vertices.push_back(y); 122 | this->sizeof_instanced_allocated_vertices++; 123 | } 124 | 125 | void dynamic_batching::bind(GLuint texture) { 126 | amogpu::gpu_data &data = this->allocated_data[this->sizeof_allocated_gpu_data]; 127 | 128 | // Why alloc more textures? there is no reason for that. 129 | for (size_t i {}; i < this->concurrent_allocated_textures.size(); i++) { 130 | GLuint allocated_texture = this->concurrent_allocated_textures.at(i); 131 | 132 | if (allocated_texture == texture) { 133 | data.texture = texture; 134 | data.texture_slot = i; 135 | 136 | break; 137 | } 138 | } 139 | 140 | if (data.texture == 0 && data.texture_slot == 0) { 141 | // Set the slot and push new texture to memory. 142 | data.texture = texture; 143 | data.texture_slot = (uint8_t) this->concurrent_allocated_textures.size(); 144 | this->concurrent_allocated_textures.push_back(texture); 145 | } 146 | } 147 | 148 | void dynamic_batching::coords(float u, float v) { 149 | this->concurrent_allocated_texture_coords.push_back(u); 150 | this->concurrent_allocated_texture_coords.push_back(v); 151 | } 152 | 153 | void dynamic_batching::next() { 154 | if (this->allocated_data.size() == this->sizeof_allocated_gpu_data) { 155 | this->allocated_data.emplace_back(); 156 | } 157 | 158 | this->allocated_data[this->sizeof_allocated_gpu_data].end = static_cast(this->sizeof_instanced_allocated_vertices); 159 | this->sizeof_allocated_vertices += this->sizeof_instanced_allocated_vertices; 160 | this->allocated_data_copy.push_back(this->allocated_data[this->sizeof_allocated_gpu_data]); 161 | this->sizeof_allocated_gpu_data++; 162 | } 163 | 164 | void dynamic_batching::revoke() { 165 | if (!this->should_not_create_buffers) { 166 | glGenVertexArrays(1, &this->vertex_arr_object); 167 | glGenBuffers(1, &this->vbo_vertices); 168 | glGenBuffers(1, &this->vbo_texture_coords); 169 | this->should_not_create_buffers = true; 170 | } 171 | 172 | this->allocated_data = this->allocated_data_copy; 173 | this->allocated_data_copy.clear(); 174 | 175 | if ((this->sizeof_previous_allocated_gpu_data != this->sizeof_allocated_gpu_data) || this->should_alloc_new_buffers) { 176 | this->sizeof_previous_allocated_gpu_data = this->sizeof_allocated_gpu_data; 177 | this->should_alloc_new_buffers = false; 178 | 179 | glBindVertexArray(this->vertex_arr_object); 180 | 181 | glEnableVertexAttribArray(0); // Location 0; the vertexes data. 182 | glBindBuffer(GL_ARRAY_BUFFER, this->vbo_vertices); 183 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * this->concurrent_allocated_vertices.size(), &this->concurrent_allocated_vertices[0], GL_STATIC_DRAW); 184 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*) 0); 185 | 186 | glEnableVertexAttribArray(1); // Location 1; the coordinates texture data. 187 | glBindBuffer(GL_ARRAY_BUFFER, this->vbo_texture_coords); 188 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * this->concurrent_allocated_texture_coords.size(), &this->concurrent_allocated_texture_coords[0], GL_STATIC_DRAW); 189 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*) 0); 190 | 191 | glBindVertexArray(0); 192 | } 193 | 194 | this->concurrent_allocated_vertices.clear(); 195 | this->concurrent_allocated_textures.clear(); 196 | this->concurrent_allocated_texture_coords.clear(); 197 | } 198 | 199 | void dynamic_batching::draw() { 200 | dynamic_batching::fx_shape.use(); 201 | dynamic_batching::fx_shape.setm4f("u_mat_projection", amogpu::matrix_projection_ortho); 202 | 203 | bool flag {}; 204 | bool texture_enabled {}; 205 | glBindVertexArray(this->vertex_arr_object); 206 | 207 | if (!this->frustum_depth) { 208 | glDisable(GL_DEPTH_TEST); 209 | } 210 | 211 | float depth_calc {this->depth}; 212 | for (amogpu::gpu_data &data : this->allocated_data) { 213 | flag = data.texture != 0; 214 | 215 | dynamic_batching::fx_shape.set4f("u_vec_rect", data.rect); 216 | dynamic_batching::fx_shape.set4f("u_vec_color", data.color); 217 | dynamic_batching::fx_shape.setb("u_bool_texture_active", flag); 218 | dynamic_batching::fx_shape.setf("u_float_zdepth", depth_calc); 219 | 220 | if (flag) { 221 | glActiveTexture(GL_TEXTURE0 + data.texture_slot); 222 | glBindTexture(GL_TEXTURE_2D, data.texture); 223 | 224 | dynamic_batching::fx_shape.seti("u_sampler_texture_slot", data.texture_slot); 225 | texture_enabled = true; 226 | } 227 | 228 | glDrawArrays(GL_TRIANGLES, data.begin, data.end); 229 | 230 | if (texture_enabled && !flag) { 231 | glBindTexture(GL_TEXTURE_2D, 0); 232 | } 233 | 234 | depth_calc += 0.001f; 235 | } 236 | 237 | if (!this->frustum_depth) { 238 | glEnable(GL_DEPTH_TEST); 239 | } 240 | 241 | // Plus the concurrent allocated gpu data to global value of depth space. 242 | dynamic_batching::fx_shape.end(); 243 | 244 | glBindVertexArray(0); 245 | } 246 | 247 | void dynamic_batching::free_buffers() { 248 | glDeleteBuffers(1, &this->vbo_vertices); 249 | glDeleteBuffers(1, &this->vbo_texture_coords); 250 | } 251 | 252 | void dynamic_batching::set_frustum_depth(bool depth_test) { 253 | this->frustum_depth = depth_test; 254 | } 255 | 256 | bool dynamic_batching::get_frustum_depth() { 257 | return this->frustum_depth; 258 | } 259 | 260 | void dynamic_batching::set_depth(float depth_test) { 261 | this->depth = depth_test; 262 | } 263 | 264 | float dynamic_batching::get_depth() { 265 | return this->depth; 266 | } 267 | --------------------------------------------------------------------------------