├── Logo32.png ├── header.png ├── Logo128.png ├── res ├── fonts │ ├── roboto_regular_sdf.png │ └── roboto_regular_sdf.fnt └── shaders │ ├── basic.glsl │ ├── box_select.glsl │ ├── gui_rect.glsl │ ├── node_shadow.glsl │ ├── text_sdf.glsl │ ├── node_connector.glsl │ ├── node.glsl │ └── link.glsl ├── src ├── platform │ ├── platform_linux.cpp │ ├── platform.hpp │ └── platform_win32.cpp ├── core │ ├── defines.hpp │ ├── logger.hpp │ └── math.hpp ├── render │ ├── shapes.hpp │ ├── shapes.cpp │ ├── text.hpp │ ├── backend.hpp │ ├── text.cpp │ └── backend.cpp ├── node │ ├── look_and_feel.hpp │ ├── nodes.hpp │ ├── node_system.hpp │ └── node_system.cpp ├── gui │ └── gui.hpp └── main.cpp ├── .gitignore ├── external ├── stb │ └── LICENCE └── glad │ ├── LICENCE │ └── include │ └── KHR │ └── khrplatform.h ├── LICENSE ├── README.md └── CMakeLists.txt /Logo32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallcluster/BasicBool/HEAD/Logo32.png -------------------------------------------------------------------------------- /header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallcluster/BasicBool/HEAD/header.png -------------------------------------------------------------------------------- /Logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallcluster/BasicBool/HEAD/Logo128.png -------------------------------------------------------------------------------- /res/fonts/roboto_regular_sdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallcluster/BasicBool/HEAD/res/fonts/roboto_regular_sdf.png -------------------------------------------------------------------------------- /src/platform/platform_linux.cpp: -------------------------------------------------------------------------------- 1 | #include "platform.hpp" 2 | 3 | // LINUX platform layer 4 | #if PLATFORM_LINUX 5 | 6 | 7 | #endif -------------------------------------------------------------------------------- /src/core/defines.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using std::string; 6 | 7 | #include "logger.hpp" 8 | 9 | #if defined(_WIN32) 10 | #define PLATFORM_WINDOWS 1 11 | #elif defined(__linux__) 12 | #define PLATFORM_LINUX 1 13 | #endif 14 | 15 | #define MULTI_CORES 0 -------------------------------------------------------------------------------- /res/shaders/basic.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec2 aPos; 4 | 5 | uniform mat4 projection; 6 | uniform mat4 view; 7 | 8 | void main(){ 9 | gl_Position = projection*view*vec4(aPos.x, aPos.y, 0.0, 1.0); 10 | } 11 | 12 | 13 | #SHADER FRAGMENT 14 | #version 330 core 15 | out vec4 FragColor; 16 | 17 | uniform vec3 color; 18 | 19 | void main(){ 20 | FragColor = vec4(color, 1.0); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | *.pdb 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Compiled Dynamic libraries 16 | *.so 17 | *.dylib 18 | *.dll 19 | 20 | # Fortran module files 21 | *.mod 22 | *.smod 23 | 24 | # Compiled Static libraries 25 | *.lai 26 | *.la 27 | *.a 28 | *.lib 29 | 30 | # Executables 31 | *.exe 32 | *.out 33 | *.app 34 | 35 | # directories 36 | build/ 37 | bin/ 38 | doc/ 39 | .vscode/ 40 | .idea/ 41 | 42 | -------------------------------------------------------------------------------- /src/render/shapes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "backend.hpp" 4 | 5 | class Shapes { 6 | protected: 7 | Shapes(Shapes &other); 8 | 9 | void operator=(const Shapes &); 10 | 11 | Shapes(); 12 | 13 | private: 14 | // WARNING : 15 | // VBO & EBO need to be stored, thanks to a bug with AMD drivers not letting OpenGL manage 16 | // the memory by it self. 17 | 18 | // QUAD 19 | VertexArray m_quadVAO; 20 | VertexBuffer m_quadVBO; 21 | ElementBuffer m_quadEBO; 22 | 23 | public: 24 | static Shapes &getInstance(); 25 | 26 | void drawQuad(); 27 | }; 28 | -------------------------------------------------------------------------------- /src/node/look_and_feel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "render/text.hpp" 4 | 5 | struct NodeStyle { 6 | vec3 nodeSelectedColor = vec3(1, 0.5, 0); 7 | vec3 trueColor = vec3(0, 1, 0); 8 | vec3 falseColor = vec3(1, 0, 0); 9 | vec3 headerColor = vec3(0, 0, 1); 10 | vec3 bodyColor = vec3(0.2); 11 | vec3 headerTextColor = vec3(1); 12 | vec3 connectorTextColor = vec3(1); 13 | float headerTextSize = 22; 14 | float connectorTextSize = 18; 15 | float connectorRadius = 8; 16 | vec2 connectorMargin = vec2(4, 4); 17 | float inOutDist = 32; 18 | float nodeRadius = 8; 19 | float shadowSize = 16; 20 | float textOutletMargin = 4; 21 | Font &font; 22 | 23 | NodeStyle(Font &font) : font(font) {}; 24 | 25 | NodeStyle() : font(Font::getDefault()) {}; 26 | }; 27 | -------------------------------------------------------------------------------- /src/render/shapes.cpp: -------------------------------------------------------------------------------- 1 | #include "shapes.hpp" 2 | 3 | // QUAD 4 | const float quadVertices[16] = { 5 | // pos // uv 6 | -0.5, -0.5, 0, 0, // top left 7 | 0.5, -0.5, 1, 0, // top right 8 | 0.5, 0.5, 1, 1, // bottom right 9 | -0.5, 0.5, 0, 1, // bottom left 10 | }; 11 | const unsigned int quadIndices[6] = {0, 1, 2, 0, 2, 3}; 12 | 13 | Shapes::Shapes() : m_quadVBO(quadVertices, sizeof(quadVertices)), m_quadEBO(quadIndices, sizeof(quadIndices)) { 14 | // QUAD 15 | VertexBufferLayout rectLayout; 16 | rectLayout.push(2); // pos 17 | rectLayout.push(2); // uv 18 | m_quadVAO.addBuffer(m_quadVBO, rectLayout); 19 | m_quadVAO.addBuffer(m_quadEBO); 20 | m_quadVAO.unbind(); 21 | } 22 | 23 | Shapes &Shapes::getInstance() { 24 | static Shapes inst; 25 | return inst; 26 | } 27 | 28 | void Shapes::drawQuad() { 29 | m_quadVAO.bind(); 30 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 31 | } -------------------------------------------------------------------------------- /res/shaders/box_select.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec4 aRect; 4 | 5 | void main(){ 6 | gl_Position = aRect; 7 | } 8 | 9 | #SHADER GEOMETRY 10 | #version 330 core 11 | layout (points) in; 12 | layout (triangle_strip, max_vertices = 4) out; 13 | 14 | uniform mat4 projection; 15 | uniform mat4 view; 16 | 17 | void main(){ 18 | vec4 rect = gl_in[0].gl_Position; 19 | mat4 transfrom = projection*view; 20 | // p1 21 | gl_Position = transfrom*vec4(rect.x, rect.y+rect.w, 0, 1); 22 | EmitVertex(); 23 | // p2 24 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y+rect.w, 0, 1); 25 | EmitVertex(); 26 | // p3 27 | gl_Position = transfrom*vec4(rect.x, rect.y, 0, 1); 28 | EmitVertex(); 29 | // p4 30 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y, 0, 1); 31 | EmitVertex(); 32 | EndPrimitive(); 33 | } 34 | 35 | 36 | #SHADER FRAGMENT 37 | #version 330 core 38 | out vec4 FragColor; 39 | 40 | uniform vec3 selectedColor; 41 | 42 | void main(){ 43 | FragColor = vec4(selectedColor, 0.25); 44 | } 45 | 46 | -------------------------------------------------------------------------------- /external/stb/LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2017 Sean Barrett 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pierre Jaffuer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/platform/platform.hpp: -------------------------------------------------------------------------------- 1 | // Abstract away OS-implementation for window & input handling. 2 | // Only one instance possible for the entire program life. 3 | 4 | #pragma once 5 | 6 | #include "core/defines.hpp" 7 | 8 | enum class MouseButton { 9 | LEFT, 10 | MIDDLE, 11 | RIGHT 12 | }; 13 | 14 | class Platform { 15 | protected: 16 | Platform(Platform &other); 17 | 18 | void operator=(const Platform &); 19 | 20 | Platform(const string &title, int width, int height); 21 | 22 | ~Platform(); 23 | 24 | public: 25 | static Platform &getInstance(const string &title, int width, int height) { 26 | static Platform inst(title, width, height); 27 | return inst; 28 | } 29 | 30 | static Platform &getInstance() { 31 | return getInstance("APP", 640, 480); 32 | } 33 | 34 | bool processEvents(); 35 | 36 | void swapBuffers(); 37 | 38 | int getWidth(); 39 | 40 | int getHeight(); 41 | 42 | int getMouseX(); 43 | 44 | int getMouseY(); 45 | 46 | int getMouseWheel(); 47 | 48 | bool isMouseDown(MouseButton button); 49 | 50 | bool isMouseUP(MouseButton button); 51 | 52 | bool isMousePressed(MouseButton button); 53 | 54 | bool isMouseReleased(MouseButton button); 55 | }; 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![](Logo32.png) BasicBool 2 | ![](header.png) 3 | 4 | ![GitHub](https://img.shields.io/github/license/smallcluster/BasicBool?logo=Github) ![](https://img.shields.io/badge/platform-win--64-blue) 5 | 6 | A Windows (Linux support not yet implemented) application written in C++ to visualize computing using boolean logic. 7 | 8 | ## Disclaimer 9 | 10 | THIS IS A TOY PROJECT to help me learn C++, CMake, OS abstraction, OpenGL and many more... 11 | It will take A LOT of time to get something usable, and even more time to have a clean architecture/coding style. 12 | 13 | ## Project Goals 14 | 15 | - no external libraries apart from GLAD for OpenGL extension loading and stb_image for image loading 16 | - modern OpenGL 17 | - cross-platform 18 | - ~~simple immediate mode GUI~~ 19 | - save system 20 | 21 | ## Inspirations 22 | 23 | - Digital-Logic-Sim from Sebastian Lague () 24 | - The Cherno Youtube channel by Yan Chernikov () 25 | - The Kohi Game Engine series by Travis Vroman () 26 | - The Godot 3.3 source code () 27 | - ThinMatrix's OpenGL 3D Game Tutorials series () 28 | -------------------------------------------------------------------------------- /src/render/text.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "backend.hpp" 4 | #include "core/math.hpp" 5 | #include 6 | #include 7 | 8 | class ParserFnt { 9 | private: 10 | std::fstream m_file; 11 | 12 | public: 13 | ParserFnt(const string &path); 14 | 15 | bool nextLine(std::pair> &line); // value> 16 | }; 17 | 18 | struct Glyph { 19 | char id; 20 | vec2 pos; 21 | vec2 dim; 22 | vec2 offset; 23 | float advance; 24 | }; 25 | 26 | struct Text { 27 | string textString; 28 | float fontSize; 29 | vec3 position; 30 | vec3 color; 31 | }; 32 | 33 | class Font { 34 | private: 35 | string m_name; 36 | Texture m_texture; 37 | float m_size; 38 | float m_lineHeight; 39 | vec4 m_padding; 40 | std::map m_glyphs; 41 | std::vector m_texts; 42 | Shader textShader; 43 | 44 | public: 45 | Font(const string &name); 46 | 47 | Texture &getTexture(); 48 | 49 | std::map &getGlyphs(); 50 | 51 | float getHeight(const string &text, float size); 52 | 53 | float getWidth(const string &text, float size); 54 | 55 | vec2 getDim(const string &text, float size); 56 | 57 | void text(const string &textString, vec2 position, float size, vec3 color); 58 | void text(const string &textString, vec3 position, float size, vec3 color); 59 | 60 | void render(const mat4 &pmat, const mat4 &view = identity<4>()); 61 | 62 | static Font &getDefault() { 63 | static Font defaultfont("roboto_regular_sdf"); 64 | return defaultfont; 65 | } 66 | }; -------------------------------------------------------------------------------- /res/shaders/gui_rect.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec4 aRect; 4 | layout (location = 1) in vec3 aColor; 5 | 6 | out vec3 vsColor; 7 | 8 | void main(){ 9 | gl_Position = aRect; 10 | vsColor = aColor; 11 | } 12 | 13 | #SHADER GEOMETRY 14 | #version 330 core 15 | layout (points) in; 16 | layout (triangle_strip, max_vertices = 4) out; 17 | 18 | uniform mat4 view; 19 | 20 | in vec3 vsColor[]; 21 | 22 | out vec2 uv; 23 | out vec2 size; 24 | out vec3 color; 25 | 26 | void main(){ 27 | color = vsColor[0]; 28 | vec4 rect = gl_in[0].gl_Position; 29 | 30 | size = vec2(rect.z, rect.w); 31 | 32 | // p1 33 | gl_Position = view*vec4(rect.x, rect.y+rect.w, 0, 1); 34 | uv = vec2(0, 1); 35 | EmitVertex(); 36 | 37 | // p2 38 | gl_Position = view*vec4(rect.x+rect.z, rect.y+rect.w, 0, 1); 39 | uv = vec2(1, 1); 40 | EmitVertex(); 41 | 42 | // p3 43 | gl_Position = view*vec4(rect.x, rect.y, 0, 1); 44 | uv = vec2(0, 0); 45 | EmitVertex(); 46 | 47 | // p4 48 | gl_Position = view*vec4(rect.x+rect.z, rect.y, 0, 1); 49 | uv = vec2(1, 0); 50 | EmitVertex(); 51 | 52 | EndPrimitive(); 53 | } 54 | 55 | 56 | #SHADER FRAGMENT 57 | #version 330 core 58 | out vec4 FragColor; 59 | 60 | in vec2 uv; 61 | in vec2 size; 62 | in vec3 color; 63 | 64 | uniform float radius; 65 | const float smoothing = 4; 66 | 67 | float rect(vec2 p, vec2 c, float w, float h, float r){ 68 | vec2 size = vec2(w-r, h-r); 69 | vec2 o = p-c; 70 | vec2 d = abs(o)-size; 71 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r; 72 | } 73 | 74 | void main(){ 75 | float width = size.x; 76 | float height = size.y; 77 | vec2 p = vec2(uv.x*width, uv.y*height); 78 | float d = rect(p, vec2(width/2.0, height/2.0), width/2.0-1.5, height/2.0-1.5, radius); 79 | vec2 duv = fwidth(uv)*smoothing; 80 | float dd = length(duv*size); 81 | float pixelDist = d * 2 / dd; 82 | float alpha = clamp(0.5 - pixelDist, 0.0, 1.0); 83 | FragColor = vec4(color, alpha); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | 3 | project(BasicBool) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS -static) 8 | 9 | # set output directories for all builds (Debug, Release, etc.) 10 | foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) 11 | string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) 12 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${PROJECT_SOURCE_DIR} ) 13 | endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) 14 | 15 | add_executable( 16 | ${PROJECT_NAME} 17 | src/main.cpp 18 | 19 | # PLATFORM 20 | src/platform/platform.hpp 21 | src/platform/platform_win32.cpp 22 | src/platform/platform_linux.cpp 23 | 24 | # CORE 25 | src/core/logger.hpp 26 | src/core/defines.hpp 27 | src/core/math.hpp 28 | 29 | # RENDER 30 | src/render/backend.hpp 31 | src/render/backend.cpp 32 | src/render/shapes.hpp 33 | src/render/shapes.cpp 34 | src/render/text.hpp 35 | src/render/text.cpp 36 | 37 | # NODE 38 | src/node/node_system.hpp 39 | src/node/node_system.cpp 40 | src/node/nodes.hpp 41 | src/node/look_and_feel.hpp 42 | 43 | # GUI 44 | src/gui/gui.hpp) 45 | 46 | target_include_directories(${PROJECT_NAME} PUBLIC src/) 47 | 48 | #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} --my-debug-flags") 49 | target_compile_definitions(${PROJECT_NAME} PUBLIC "$<$:BUILD_DEBUG>") 50 | 51 | 52 | # --- DEPS --- 53 | 54 | # OpenGL 55 | find_package(OpenGL REQUIRED) 56 | include_directories(${OPENGL_INCLUDE_DIRS}) 57 | target_link_libraries(${PROJECT_NAME} PRIVATE ${OPENGL_LIBRARIES}) 58 | 59 | # glad 60 | add_library(glad STATIC 61 | external/glad/src/glad.c 62 | external/glad/include/glad/glad.h 63 | external/glad/include/KHR/khrplatform.h) 64 | target_include_directories(glad PUBLIC external/glad/include) 65 | target_link_libraries(${PROJECT_NAME} PRIVATE glad) 66 | 67 | # OPENMP 68 | find_package(OpenMP REQUIRED) 69 | target_link_libraries(${PROJECT_NAME} PRIVATE OpenMP::OpenMP_CXX) 70 | 71 | target_include_directories(${PROJECT_NAME} PUBLIC external/stb) -------------------------------------------------------------------------------- /res/shaders/node_shadow.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec4 aRect; 4 | layout (location = 1) in float aSelected; 5 | 6 | out float vsSelected; 7 | 8 | void main(){ 9 | gl_Position = aRect; 10 | vsSelected = aSelected; 11 | } 12 | 13 | #SHADER GEOMETRY 14 | #version 330 core 15 | layout (points) in; 16 | layout (triangle_strip, max_vertices = 4) out; 17 | 18 | uniform mat4 projection; 19 | uniform mat4 view; 20 | 21 | in float vsSelected[]; 22 | 23 | out vec2 uv; 24 | out vec2 size; 25 | out float selected; 26 | 27 | void main(){ 28 | selected = vsSelected[0]; 29 | vec4 rect = gl_in[0].gl_Position; 30 | mat4 transfrom = projection*view; 31 | 32 | size = vec2(rect.z, rect.w); 33 | 34 | // p1 35 | gl_Position = transfrom*vec4(rect.x, rect.y+rect.w, 0, 1); 36 | uv = vec2(0,1); 37 | EmitVertex(); 38 | 39 | // p2 40 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y+rect.w, 0, 1); 41 | uv = vec2(1,1); 42 | EmitVertex(); 43 | 44 | // p3 45 | gl_Position = transfrom*vec4(rect.x, rect.y, 0, 1); 46 | uv = vec2(0,0); 47 | EmitVertex(); 48 | 49 | // p4 50 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y, 0, 1); 51 | uv = vec2(1,0); 52 | EmitVertex(); 53 | 54 | EndPrimitive(); 55 | } 56 | 57 | 58 | #SHADER FRAGMENT 59 | #version 330 core 60 | out vec4 FragColor; 61 | 62 | in vec2 uv; 63 | in vec2 size; 64 | in float selected; 65 | 66 | uniform float smoothing; 67 | uniform float radius; 68 | uniform vec3 selectedColor; 69 | 70 | float rect(vec2 p, vec2 c, float w, float h, float r){ 71 | vec2 size = vec2(w-r, h-r); 72 | vec2 o = p-c; 73 | vec2 d = abs(o)-size; 74 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r; 75 | } 76 | 77 | void main(){ 78 | float width = size.x; 79 | float height = size.y; 80 | 81 | // fragment pos in the rectangle 82 | vec2 p = vec2(uv.x*width, uv.y*height); 83 | 84 | // shape 85 | float d = rect(p, vec2(width/2.0, height/2.0), width/2.0-smoothing-1.5, height/2.0-smoothing-1.5, radius); 86 | 87 | // mixing coef 88 | float res = smoothstep(0., smoothing, d); 89 | 90 | // final result 91 | FragColor = mix(vec4(selectedColor*selected, 0.25), vec4(0), res); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /src/node/nodes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "node_system.hpp" 4 | 5 | class TrueNode : public Node { 6 | public: 7 | TrueNode(vec2 pos) : Node("TRUE", pos) { 8 | addOutput("out"); 9 | outputs[0]->state = true; 10 | } 11 | 12 | void update() override { 13 | } 14 | 15 | void reset() override { 16 | } 17 | }; 18 | 19 | class NotNode : public Node { 20 | public: 21 | NotNode(vec2 pos) : Node("NOT", pos) { 22 | addInput("in"); 23 | addOutput("out"); 24 | outputs[0]->state = true; 25 | } 26 | 27 | void update() override { 28 | outputs[0]->state = !(inputs[0]->state); 29 | } 30 | 31 | void reset() override { 32 | outputs[0]->state = true; 33 | inputs[0]->state = false; 34 | } 35 | }; 36 | 37 | class AndNode : public Node { 38 | public: 39 | AndNode(vec2 pos) : Node("AND", pos) { 40 | addInput("in1"); 41 | addInput("in2"); 42 | addOutput("out"); 43 | } 44 | 45 | void update() override { 46 | outputs[0]->state = inputs[0]->state && inputs[1]->state; 47 | } 48 | 49 | void reset() override { 50 | outputs[0]->state = false; 51 | inputs[0]->state = false; 52 | inputs[1]->state = false; 53 | } 54 | }; 55 | 56 | class OrNode : public Node { 57 | public: 58 | OrNode(vec2 pos) : Node("OR", pos) { 59 | addInput("in1"); 60 | addInput("in2"); 61 | addOutput("out"); 62 | } 63 | 64 | void update() override { 65 | outputs[0]->state = inputs[0]->state || inputs[1]->state; 66 | } 67 | 68 | void reset() override { 69 | outputs[0]->state = false; 70 | inputs[0]->state = false; 71 | inputs[1]->state = false; 72 | } 73 | }; 74 | 75 | class XorNode : public Node { 76 | public: 77 | XorNode(vec2 pos) : Node("XOR", pos) { 78 | addInput("in1"); 79 | addInput("in2"); 80 | addOutput("out"); 81 | } 82 | 83 | void update() override { 84 | outputs[0]->state = (inputs[0]->state && !inputs[1]->state) || (!inputs[0]->state && inputs[1]->state); 85 | } 86 | 87 | void reset() override { 88 | outputs[0]->state = false; 89 | inputs[0]->state = false; 90 | inputs[1]->state = false; 91 | } 92 | }; -------------------------------------------------------------------------------- /res/shaders/text_sdf.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec4 aRect; // (x,y,w,h) in world space 4 | layout (location = 1) in vec4 aRectUV; // (x,y,w,h) in [0,1] 5 | layout (location = 2) in vec3 aColor; 6 | layout (location = 3) in float aDepth; 7 | 8 | out vec4 rectUV; 9 | out vec3 vscolor; 10 | out float vsdepth; 11 | 12 | 13 | void main(){ 14 | gl_Position = aRect; // (x,y,w,h) 15 | rectUV = aRectUV; 16 | vscolor = aColor; 17 | vsdepth = aDepth; 18 | } 19 | 20 | 21 | #SHADER GEOMETRY 22 | #version 330 core 23 | layout (points) in; 24 | layout (triangle_strip, max_vertices = 4) out; 25 | 26 | uniform mat4 projection; 27 | uniform mat4 view; 28 | 29 | in vec3 vscolor[]; 30 | in vec4 rectUV[]; 31 | in float vsdepth[]; 32 | 33 | out vec2 uv; 34 | out vec3 color; 35 | 36 | void main(){ 37 | vec4 rect = gl_in[0].gl_Position; 38 | mat4 transfrom = projection*view; 39 | float depth = vsdepth[0]; 40 | 41 | color = vscolor[0]; 42 | 43 | // p1 44 | gl_Position = transfrom*vec4(rect.x, rect.y+rect.w, depth, 1); 45 | uv = vec2(rectUV[0].x, rectUV[0].y+rectUV[0].w); 46 | EmitVertex(); 47 | 48 | // p2 49 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y+rect.w, depth, 1); 50 | uv = vec2(rectUV[0].x+rectUV[0].z, rectUV[0].y+rectUV[0].w); 51 | EmitVertex(); 52 | 53 | // p3 54 | gl_Position = transfrom*vec4(rect.x, rect.y, depth, 1); 55 | uv = rectUV[0].xy; 56 | EmitVertex(); 57 | 58 | // p4 59 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y, depth, 1); 60 | uv = vec2(rectUV[0].x+rectUV[0].z, rectUV[0].y); 61 | EmitVertex(); 62 | 63 | 64 | EndPrimitive(); 65 | } 66 | 67 | 68 | #SHADER FRAGMENT 69 | #version 330 core 70 | out vec4 FragColor; 71 | 72 | uniform sampler2D tex; 73 | 74 | in vec2 uv; 75 | in vec3 color; 76 | 77 | const float width = 0.5; 78 | const float sdfSize = 20; 79 | 80 | // Adapted from : https://drewcassidy.me/2020/06/26/sdf-antialiasing/ 81 | void main(){ 82 | vec4 col = vec4(color, 1); 83 | float dist = (width - texture(tex, uv).a)*sdfSize; 84 | vec2 duv = fwidth(uv); 85 | float dtex = length(duv*textureSize(tex, 0)); 86 | float pixelDist = dist * 2 / dtex; 87 | col.a = clamp(0.5 - pixelDist, 0.0, 1.0); 88 | FragColor = col; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /res/shaders/node_connector.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec4 data; // (x,y,r,state) 4 | layout (location = 1) in float aDepth; 5 | 6 | out float vsdepth; 7 | 8 | void main(){ 9 | gl_Position = data; 10 | vsdepth = aDepth; 11 | } 12 | 13 | #SHADER GEOMETRY 14 | #version 330 core 15 | layout (points) in; 16 | layout (triangle_strip, max_vertices = 4) out; 17 | 18 | uniform mat4 projection; 19 | uniform mat4 view; 20 | 21 | out vec2 uv; 22 | out float radius; 23 | out float status; 24 | 25 | in float vsdepth[]; 26 | 27 | void main(){ 28 | 29 | vec4 data = gl_in[0].gl_Position; 30 | mat4 transfrom = projection*view; 31 | float depth = vsdepth[0]; 32 | 33 | radius = data.z; 34 | status = data.w; 35 | 36 | // p1 37 | gl_Position = transfrom*vec4(data.x-radius, data.y+radius, depth, 1); 38 | uv = vec2(0,1); 39 | EmitVertex(); 40 | 41 | // p2 42 | gl_Position = transfrom*vec4(data.x+radius, data.y+radius, depth, 1); 43 | uv = vec2(1,1); 44 | EmitVertex(); 45 | 46 | // p3 47 | gl_Position = transfrom*vec4(data.x-radius, data.y-radius, depth, 1); 48 | uv = vec2(0,0); 49 | EmitVertex(); 50 | 51 | // p4 52 | gl_Position = transfrom*vec4(data.x+radius, data.y-radius, depth, 1); 53 | uv = vec2(1,0); 54 | EmitVertex(); 55 | 56 | EndPrimitive(); 57 | } 58 | 59 | 60 | #SHADER FRAGMENT 61 | #version 330 core 62 | out vec4 FragColor; 63 | 64 | in vec2 uv; 65 | in float radius; 66 | in float status; 67 | 68 | uniform vec3 falseColor; 69 | uniform vec3 trueColor; 70 | 71 | const float smoothing = 4.0; 72 | 73 | 74 | float circle(vec2 p, vec2 c, float r){ 75 | vec2 d = abs(p-c); 76 | return length(max(d, 0.0)) - r; 77 | } 78 | 79 | void main(){ 80 | // fragment pos in the rectangle 81 | vec2 p = vec2(uv.x*radius*2, uv.y*radius*2); 82 | vec2 c = vec2(radius, radius); 83 | float d1 = circle(p, c, radius); 84 | float d2 = circle(p, c, 3*(radius)/4); // Inside 85 | float d = max(-d2, d1); // Ring 86 | vec2 duv = fwidth(uv)*smoothing; 87 | float dd = length(duv*radius); 88 | float pixelDist1 = d * 2 / dd; 89 | float pixelDist2 = d2 * 2 / dd; 90 | float alphaRing = clamp(0.5 - pixelDist1, 0.0, 1.0); 91 | float alphaDisk = clamp(0.5 - pixelDist2, 0.0, 1.0); 92 | vec3 diskColor = (status == 1.0 ? trueColor : falseColor); 93 | vec3 ringColor = vec3(1); 94 | vec4 col = vec4(diskColor*alphaDisk+ringColor*alphaRing, alphaRing+alphaDisk); 95 | // final result 96 | FragColor = col; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /res/shaders/node.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec4 aRect; 4 | layout (location = 1) in float aHeaderHeight; 5 | layout (location = 2) in float aSelected; 6 | layout (location = 3) in float aDepth; 7 | 8 | out float vsSelected; 9 | out float vsHeaderHeight; 10 | out float vsdepth; 11 | 12 | void main(){ 13 | gl_Position = aRect; 14 | vsHeaderHeight = aHeaderHeight; 15 | vsSelected = aSelected; 16 | vsdepth = aDepth; 17 | } 18 | 19 | #SHADER GEOMETRY 20 | #version 330 core 21 | layout (points) in; 22 | layout (triangle_strip, max_vertices = 4) out; 23 | 24 | uniform mat4 projection; 25 | uniform mat4 view; 26 | 27 | in float vsHeaderHeight[]; 28 | in float vsSelected[]; 29 | in float vsdepth[]; 30 | 31 | out vec2 uv; 32 | out vec2 size; 33 | out float headerHeight; 34 | out float selected; 35 | 36 | void main(){ 37 | selected = vsSelected[0]; 38 | vec4 rect = gl_in[0].gl_Position; 39 | mat4 transfrom = projection*view; 40 | float depth = vsdepth[0]; 41 | 42 | size = vec2(rect.z, rect.w); 43 | headerHeight = vsHeaderHeight[0]; 44 | 45 | // p1 46 | gl_Position = transfrom*vec4(rect.x, rect.y+rect.w, depth, 1); 47 | uv = vec2(0,1); 48 | EmitVertex(); 49 | 50 | // p2 51 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y+rect.w, depth, 1); 52 | uv = vec2(1,1); 53 | EmitVertex(); 54 | 55 | // p3 56 | gl_Position = transfrom*vec4(rect.x, rect.y, depth, 1); 57 | uv = vec2(0,0); 58 | EmitVertex(); 59 | 60 | // p4 61 | gl_Position = transfrom*vec4(rect.x+rect.z, rect.y, depth, 1); 62 | uv = vec2(1,0); 63 | EmitVertex(); 64 | 65 | EndPrimitive(); 66 | } 67 | 68 | 69 | #SHADER FRAGMENT 70 | #version 330 core 71 | out vec4 FragColor; 72 | 73 | in vec2 uv; 74 | in vec2 size; 75 | in float headerHeight; 76 | in float selected; 77 | 78 | uniform float radius; 79 | uniform vec3 headerColor; 80 | uniform vec3 selectedColor; 81 | uniform vec3 bodyColor; 82 | const float smoothing = 4; 83 | 84 | 85 | // SDF functions 86 | float rect(vec2 p, vec2 c, float w, float h, float r){ 87 | vec2 size = vec2(w-r, h-r); 88 | vec2 o = p-c; 89 | vec2 d = abs(o)-size; 90 | return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r; 91 | } 92 | 93 | void main(){ 94 | float width = size.x; 95 | float height = size.y; 96 | vec2 p = vec2(uv.x*width, uv.y*height); 97 | float d = rect(p, vec2(width/2.0, height/2.0), width/2.0-1.5, height/2.0-1.5, radius); 98 | vec2 duv = fwidth(uv)*smoothing; 99 | float dd = length(duv*size); 100 | float pixelDist = d * 2 / dd; 101 | float alpha = clamp(0.5 - pixelDist, 0.0, 1.0); 102 | float header = 1-step(headerHeight, p.y); 103 | vec4 col = vec4( (selected == 1.0 ? selectedColor : headerColor)*header+bodyColor*(1-header), alpha); 104 | 105 | // Used for the depth buffer 106 | if(col.a <= 0.01) 107 | discard; 108 | else 109 | FragColor = col; 110 | } 111 | 112 | -------------------------------------------------------------------------------- /external/glad/LICENCE: -------------------------------------------------------------------------------- 1 | The glad source code: 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2013-2021 David Herberth 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | 25 | The Khronos Specifications: 26 | 27 | Copyright (c) 2013-2020 The Khronos Group Inc. 28 | 29 | Licensed under the Apache License, Version 2.0 (the "License"); 30 | you may not use this file except in compliance with the License. 31 | You may obtain a copy of the License at 32 | 33 | http://www.apache.org/licenses/LICENSE-2.0 34 | 35 | Unless required by applicable law or agreed to in writing, software 36 | distributed under the License is distributed on an "AS IS" BASIS, 37 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 38 | See the License for the specific language governing permissions and 39 | limitations under the License. 40 | 41 | 42 | The EGL Specification and various headers: 43 | 44 | Copyright (c) 2007-2016 The Khronos Group Inc. 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining a 47 | copy of this software and/or associated documentation files (the 48 | "Materials"), to deal in the Materials without restriction, including 49 | without limitation the rights to use, copy, modify, merge, publish, 50 | distribute, sublicense, and/or sell copies of the Materials, and to 51 | permit persons to whom the Materials are furnished to do so, subject to 52 | the following conditions: 53 | 54 | The above copyright notice and this permission notice shall be included 55 | in all copies or substantial portions of the Materials. 56 | 57 | THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 58 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 59 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 60 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 61 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 62 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 63 | MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -------------------------------------------------------------------------------- /src/render/backend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core/defines.hpp" 4 | #include "core/logger.hpp" 5 | #include "core/math.hpp" 6 | #include 7 | #include 8 | 9 | class Shader { 10 | private: 11 | GLuint m_program; 12 | 13 | std::vector splitShaderSources(const string &glslCode); 14 | 15 | public: 16 | Shader(const string &name); 17 | 18 | void setFloat(const string &name, float value) const; 19 | 20 | void setInt(const string &name, int value) const; 21 | 22 | void setVec4(const string &name, float v0, float v1, float v2, float v3) const; 23 | 24 | void setVec2(const string &name, float v0, float v1) const; 25 | 26 | void setVec3(const string &name, float v0, float v1, float v2) const; 27 | 28 | void setVec4(const string &name, const vec4 &v) const; 29 | 30 | void setVec2(const string &name, const vec2 &v) const; 31 | 32 | void setVec3(const string &name, const vec3 &v) const; 33 | 34 | void setMat4(const string &name, const mat4 &m) const; 35 | 36 | void setMat3(const string &name, const mat3 &m) const; 37 | 38 | ~Shader(); 39 | 40 | void use(); 41 | }; 42 | 43 | class VertexBuffer { 44 | private: 45 | GLuint m_vbo; 46 | 47 | public: 48 | VertexBuffer(const void *data, unsigned int size); 49 | 50 | ~VertexBuffer(); 51 | 52 | void bind() const; 53 | 54 | void unbind() const; 55 | }; 56 | 57 | class ElementBuffer { 58 | private: 59 | GLuint m_ebo; 60 | 61 | public: 62 | ElementBuffer(const void *data, unsigned int size); 63 | 64 | ~ElementBuffer(); 65 | 66 | void bind() const; 67 | 68 | void unbind() const; 69 | }; 70 | 71 | struct VertexBufferElement { 72 | unsigned int type; 73 | unsigned int count; 74 | unsigned int normalized; 75 | 76 | static unsigned int getTypeSize(unsigned int type); 77 | }; 78 | 79 | class VertexBufferLayout { 80 | private: 81 | std::vector m_elements; 82 | unsigned int m_stride; 83 | 84 | public: 85 | VertexBufferLayout(); 86 | 87 | template 88 | void push(unsigned int count) { 89 | LOGERROR("VertexBufferLayout::push() -> unknow type"); 90 | } 91 | 92 | inline const std::vector &getElements() const; 93 | 94 | inline unsigned int getStride() const; 95 | }; 96 | 97 | template<> 98 | inline void VertexBufferLayout::push(unsigned int count) { 99 | m_elements.push_back({GL_FLOAT, count, GL_FALSE}); 100 | m_stride += VertexBufferElement::getTypeSize(GL_FLOAT) * count; 101 | } 102 | 103 | 104 | class VertexArray { 105 | private: 106 | GLuint m_vao; 107 | 108 | public: 109 | VertexArray(); 110 | 111 | ~VertexArray(); 112 | 113 | void addBuffer(const VertexBuffer &vb, const VertexBufferLayout &layout); 114 | 115 | void addBuffer(const ElementBuffer &ebo); 116 | 117 | void bind() const; 118 | 119 | void unbind() const; 120 | }; 121 | 122 | class Texture { 123 | private: 124 | unsigned int m_texture; 125 | int m_width; 126 | int m_height; 127 | 128 | public: 129 | Texture(const string &path); 130 | 131 | ~Texture(); 132 | 133 | void bind(int unit); 134 | 135 | int getWidth(); 136 | 137 | int getHeight(); 138 | }; -------------------------------------------------------------------------------- /res/shaders/link.glsl: -------------------------------------------------------------------------------- 1 | #SHADER VERTEX 2 | #version 330 core 3 | layout (location = 0) in vec2 aPos; 4 | layout (location = 1) in float aT; 5 | layout (location = 2) in float aStartTrue; 6 | 7 | out float vsT; 8 | out float vsStartTrue; 9 | 10 | void main(){ 11 | gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 12 | vsT = aT; 13 | vsStartTrue = aStartTrue; 14 | } 15 | 16 | #SHADER GEOMETRY 17 | #version 330 core 18 | #define RES 32 19 | #define VERTICES 64 20 | layout (lines) in; 21 | layout (triangle_strip, max_vertices = VERTICES) out; 22 | 23 | uniform mat4 projection; 24 | uniform mat4 view; 25 | uniform float width; 26 | 27 | in float vsT[]; 28 | in float vsStartTrue[]; 29 | 30 | out vec2 uv; 31 | out float t; 32 | out float startTrue; 33 | 34 | 35 | const float step = 1.0/(RES-1); 36 | const float smooting = 0.5; 37 | 38 | vec2 bezier(float t, vec2 start, vec2 end, vec2 control1, vec2 control2){ 39 | return (1.0-t)*(1.0-t)*(1.0-t)*start + 3*(1.0-t)*(1.0-t)*t*control1 + 3*(1.0-t)*t*t*control2 + t*t*t*end; 40 | } 41 | 42 | void main(){ 43 | float radius = width/2.0+smooting; 44 | vec2 start = vec2(gl_in[0].gl_Position.x, gl_in[0].gl_Position.y); 45 | vec2 end = vec2(gl_in[1].gl_Position.x, gl_in[1].gl_Position.y); 46 | 47 | vec2 dir = end-start; 48 | vec2 control1 = start + vec2(abs(dir.x)/2.0, 0); 49 | vec2 control2 = end - vec2(abs(dir.x)/2.0, 0); 50 | 51 | vec2 ortho = normalize(vec2(0, abs(dir.x))); 52 | 53 | mat4 transfrom = projection*view; 54 | 55 | t = vsT[0]; 56 | startTrue = vsStartTrue[0]; 57 | 58 | // first vert 59 | gl_Position = transfrom*vec4(start + ortho*radius, 0, 1); 60 | uv = vec2(0,0); 61 | EmitVertex(); 62 | gl_Position = transfrom*vec4(start - ortho*radius, 0, 1); 63 | uv = vec2(0,1); 64 | EmitVertex(); 65 | 66 | // inside vert 67 | for(int i=0; i < RES-2; i++){ 68 | vec2 p1 = bezier(i*step, start, end, control1, control2); 69 | vec2 p2 = bezier((i+1)*step, start, end, control1, control2); 70 | vec2 p3 = bezier((i+2)*step, start, end, control1, control2); 71 | 72 | vec2 d1 = p2-p1; 73 | vec2 d2 = p3-p2; 74 | vec2 o1 = normalize(vec2(-d1.y, d1.x)); 75 | vec2 o2 = normalize(vec2(-d2.y, d2.x)); 76 | vec2 v = normalize(o1+o2); 77 | 78 | gl_Position = transfrom*vec4(p2 + v*radius, 0, 1); 79 | uv = vec2((i+1)*step, 0); 80 | EmitVertex(); 81 | gl_Position = transfrom*vec4(p2 - v*radius, 0, 1); 82 | uv = vec2((i+1)*step, 1); 83 | EmitVertex(); 84 | } 85 | 86 | // last vert 87 | gl_Position = transfrom*vec4(end + ortho*radius, 0, 1); 88 | uv = vec2(1,0); 89 | EmitVertex(); 90 | gl_Position = transfrom*vec4(end - ortho*radius, 0, 1); 91 | uv = vec2(1,1); 92 | EmitVertex(); 93 | 94 | EndPrimitive(); 95 | } 96 | 97 | #SHADER FRAGMENT 98 | #version 330 core 99 | 100 | out vec4 FragColor; 101 | in vec2 uv; 102 | in float t; 103 | in float startTrue; 104 | 105 | uniform vec3 trueColor; 106 | uniform vec3 falseColor; 107 | uniform float width; 108 | 109 | const float smooting = 0.5; 110 | 111 | void main(){ 112 | 113 | float d = (width/2.0-smooting-abs(uv.y*width-width/2.0)); 114 | float dw = fwidth(d); 115 | float pixelDist = d / dw; 116 | float alpha = 1-clamp(0.5 - pixelDist, 0.0, 1.0); 117 | 118 | vec3 startColor = mix(falseColor, trueColor, startTrue); 119 | vec3 endColor = mix(trueColor, falseColor, startTrue); 120 | 121 | vec3 color = mix(startColor, endColor, step(t, uv.x)); 122 | 123 | FragColor = vec4(color, alpha); 124 | } 125 | -------------------------------------------------------------------------------- /src/node/node_system.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core/defines.hpp" 4 | #include "core/math.hpp" 5 | #include "look_and_feel.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | class Node; 11 | 12 | struct Link; 13 | 14 | struct Connector { 15 | Node *parent; 16 | std::vector links; 17 | bool state = false; 18 | string name; 19 | vec2 pos; // relative to parent node 20 | vec2 textPos; // relative to parent node 21 | bool isInput = false; 22 | 23 | Connector(string name, Node *parent, bool isInput); 24 | }; 25 | 26 | // in -> out 27 | struct Link { 28 | Connector *input; 29 | Connector *output; 30 | float t = 0.5f; 31 | bool state = false; 32 | bool nextState = false; 33 | 34 | Link(Connector *in, Connector *out); 35 | 36 | ~Link(); 37 | 38 | void fetchNextState(); 39 | 40 | void propagate(); 41 | 42 | void reset(); 43 | }; 44 | 45 | class Node { 46 | public: 47 | std::vector inputs; 48 | std::vector outputs; 49 | bool selected = false; 50 | string name; 51 | vec2 pos; 52 | vec2 textPos; // relative to it's pos 53 | vec2 headerSize; 54 | vec2 size; 55 | 56 | Node(string name, vec2 pos); 57 | 58 | ~Node(); 59 | 60 | bool addOutput(string name); 61 | 62 | bool addInput(string name); 63 | 64 | Connector *getInput(string name); 65 | 66 | Connector *getOutput(string name); 67 | 68 | virtual void update() = 0; 69 | 70 | virtual void reset() = 0; 71 | }; 72 | 73 | 74 | class NodeManager { 75 | public: 76 | NodeManager(); 77 | 78 | ~NodeManager(); 79 | 80 | void addNode(Node *node); 81 | 82 | void addLink(Link *link); 83 | 84 | void render(const mat4 &pmat, const mat4 &view, const vec2 &camstart, const vec2 &camend); 85 | 86 | void beginUpdate(); 87 | 88 | void endUpdate(); 89 | 90 | void setProgress(float t); 91 | 92 | std::optional getNodeAt(vec2 mouse); 93 | 94 | std::optional getConnectorAt(vec2 mouse); 95 | 96 | void drawTempLink(Connector *c, vec2 mouse, const mat4 &pmat, const mat4 &view); 97 | 98 | bool connect(Connector *c1, Connector *c2); 99 | 100 | void disconnectAll(Connector *c); 101 | 102 | void removeNode(Node *node); 103 | 104 | void setLookAndFeel(std::shared_ptr style); 105 | 106 | void reset(); 107 | 108 | void moveSelectedNodes(vec2 offset); 109 | 110 | void boxSelect(vec2 start, vec2 end); 111 | 112 | void deselectAll(); 113 | 114 | void selectNode(Node *node); 115 | 116 | void deselectNode(Node *node); 117 | 118 | bool nodeIsSelected(Node *node); 119 | 120 | void drawBoxSelection(vec2 start, vec2 end, const mat4 &pmat, const mat4 &view); 121 | 122 | void removeSelected(); 123 | 124 | void replaceNode(Node* node, std::function build); 125 | void replaceSelected(std::function build); 126 | 127 | 128 | private: 129 | std::vector selectedNodes; 130 | 131 | std::vector nodes; 132 | std::vector links; 133 | 134 | // look and feel 135 | std::shared_ptr nodeStyle; 136 | 137 | // Layout building 138 | void doNodeLayout(Node *node); 139 | 140 | vec2 getConnectorSize(Node *node, bool output); 141 | 142 | void doConnectorLayout(Node *node, bool output); 143 | 144 | // shaders 145 | Shader nodeShader; 146 | Shader nodeShadowShader; 147 | Shader nodeConnectorShader; 148 | Shader linkShader; 149 | Shader basicShader; 150 | Shader boxSelectShader; 151 | // culling 152 | std::vector visibleNodes; 153 | std::vector visibleLinks; 154 | 155 | void cullNodes(const vec2 &camstart, const vec2 &camend); 156 | 157 | void cullLinks(const vec2 &camstart, const vec2 &camend); 158 | 159 | // batch rendering 160 | void renderShadows(const mat4 &pmat, const mat4 &view); 161 | 162 | void renderNodes(const mat4 &pmat, const mat4 &view); 163 | 164 | void renderText(const mat4 &pmat, const mat4 &view); 165 | 166 | void renderConnectors(const mat4 &pmat, const mat4 &view); 167 | 168 | void renderLinks(const mat4 &pmat, const mat4 &view); 169 | }; 170 | -------------------------------------------------------------------------------- /src/core/logger.hpp: -------------------------------------------------------------------------------- 1 | // A very basic console logger with some template magic. 2 | 3 | #pragma once 4 | 5 | #include "defines.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | // Enable or disable warning & info level logging. 11 | #define LOG_WARN_ENABLED 1 12 | #define LOG_INFO_ENABLED 1 13 | 14 | // Release mode should exclude debug info. 15 | #if defined(BUILD_DEBUG) 16 | #define LOG_DEBUG_ENABLED 1 17 | #else 18 | #define LOG_DEBUG_ENABLED 0 19 | #endif 20 | 21 | // Static class, DO NOT USE DIRECTLY ! USE THE DEFINED MACROS AT THE END OF THIS FILE. 22 | class logger { 23 | private: 24 | // Split string at each "{}" patern. 25 | static const std::vector splitMessage(const string &message) { 26 | std::vector chunks; 27 | enum BraceMatching { 28 | OPENED, 29 | CLOSED, 30 | NONE 31 | }; 32 | BraceMatching state = BraceMatching::NONE; // Current reading state 33 | std::stringstream ss; 34 | for (char const &c: message) { 35 | if (c == '{' && state != BraceMatching::OPENED) 36 | state = BraceMatching::OPENED; 37 | else if (c == '}' && state == BraceMatching::OPENED) // Match found ! 38 | { 39 | state = BraceMatching::CLOSED; 40 | chunks.push_back(ss.str()); 41 | ss.str(string()); // Clear the string stream 42 | } else 43 | ss << c; 44 | } 45 | return chunks; 46 | } 47 | 48 | // Recursively show variadic template args. 49 | template 50 | static void argsToStringStream(std::stringstream &ss, T v) { 51 | ss << v; 52 | } 53 | 54 | template 55 | static void argsToStringStream(std::stringstream &ss, T first, Args... args) { 56 | ss << first << ", "; 57 | argsToStringStream(ss, args...); 58 | } 59 | 60 | // Recursively format the message with variadic template args. 61 | template 62 | static void format(std::stringstream &ss, const std::vector &chunks, int n, T v) { 63 | ss << chunks[n] << v; 64 | } 65 | 66 | template 67 | static void format(std::stringstream &ss, const std::vector &chunks, int n, T first, Args... args) { 68 | ss << chunks[n] << first; 69 | format(ss, chunks, n + 1, args...); 70 | } 71 | 72 | public: 73 | enum LogLevel { 74 | LOG_FATAL, 75 | LOG_ERROR, 76 | LOG_WARN, 77 | LOG_INFO, 78 | LOG_DEBUG 79 | }; 80 | 81 | // Simple text without formating. 82 | static void log(const LogLevel &level, const string &message) { 83 | std::stringstream ss; 84 | switch (level) { 85 | case logger::LOG_FATAL: 86 | ss << "[FATAL] : "; 87 | break; 88 | 89 | case logger::LOG_ERROR: 90 | ss << "[ERROR] : "; 91 | break; 92 | 93 | case logger::LOG_WARN: 94 | ss << "[WARN] : "; 95 | break; 96 | 97 | case logger::LOG_INFO: 98 | ss << "[INFO] : "; 99 | break; 100 | 101 | case logger::LOG_DEBUG: 102 | ss << "[DEBUG] : "; 103 | break; 104 | } 105 | ss << message; 106 | std::cout << ss.str() << std::endl; 107 | } 108 | 109 | // Formating is required with args. 110 | template 111 | static void log(const LogLevel &level, const string &message, Args... args) { 112 | std::stringstream ss; 113 | 114 | // Message level type. 115 | switch (level) { 116 | case logger::LOG_FATAL: 117 | ss << "[FATAL] : "; 118 | break; 119 | 120 | case logger::LOG_ERROR: 121 | ss << "[ERROR] : "; 122 | break; 123 | 124 | case logger::LOG_WARN: 125 | ss << "[WARN] : "; 126 | break; 127 | 128 | case logger::LOG_INFO: 129 | ss << "[INFO] : "; 130 | break; 131 | 132 | case logger::LOG_DEBUG: 133 | ss << "[DEBUG] : "; 134 | break; 135 | } 136 | 137 | const std::vector chunks = splitMessage(message); 138 | 139 | // Check if the formatting matchs with the number of args. 140 | constexpr auto size = sizeof...(Args); 141 | 142 | if (size != chunks.size()) { 143 | std::stringstream listArgs; 144 | argsToStringStream(listArgs, args...); 145 | 146 | // Formating error ! 147 | std::string text = 148 | "**Logger args number Error**\nMSG = " + ss.str() + message + "\nARGS : " + listArgs.str() + '\n'; 149 | std::cout << text; 150 | } else { 151 | // Format and show the message. 152 | format(ss, chunks, 0, args...); 153 | std::cout << ss.str() << std::endl; 154 | } 155 | 156 | if (level == LOG_FATAL) 157 | abort(); 158 | } 159 | }; 160 | 161 | // Macros for ease of use and compile time optimization. 162 | 163 | #if defined(__clang__) || defined(__GNUC__) 164 | #define LOGFATAL(message, ...) logger::log(logger::LOG_FATAL, message __VA_OPT__(, ) __VA_ARGS__) 165 | #define LOGERROR(message, ...) logger::log(logger::LOG_ERROR, message __VA_OPT__(, ) __VA_ARGS__) 166 | #if LOG_WARN_ENABLED 167 | #define LOGWARN(message, ...) logger::log(logger::LOG_WARN, message __VA_OPT__(, ) __VA_ARGS__) 168 | #else 169 | #define LOGWARN(message, ...) 170 | #endif 171 | #if LOG_INFO_ENABLED 172 | #define LOGINFO(message, ...) logger::log(logger::LOG_INFO, message __VA_OPT__(, ) __VA_ARGS__) 173 | #else 174 | #define LOGINFO(message, ...) 175 | #endif 176 | #if LOG_DEBUG_ENABLED 177 | #define LOGDEBUG(message, ...) logger::log(logger::LOG_DEBUG, message __VA_OPT__(, ) __VA_ARGS__) 178 | #else 179 | #define LOGDEBUG(message, ...) 180 | #endif 181 | #elif defined(_MSC_VER) 182 | #define LOGFATAL(message, ...) logger::log(logger::LOG_FATAL, message, __VA_ARGS__) 183 | #define LOGERROR(message, ...) logger::log(logger::LOG_ERROR, message, __VA_ARGS__) 184 | #if LOG_WARN_ENABLED 185 | #define LOGWARN(message, ...) logger::log(logger::LOG_WARN, message, __VA_ARGS__) 186 | #else 187 | #define LOGWARN(message, ...) 188 | #endif 189 | #if LOG_INFO_ENABLED 190 | #define LOGINFO(message, ...) logger::log(logger::LOG_INFO, message, __VA_ARGS__) 191 | #else 192 | #define LOGINFO(message, ...) 193 | #endif 194 | #if LOG_DEBUG_ENABLED 195 | #define LOGDEBUG(message, ...) logger::log(logger::LOG_DEBUG, message, __VA_ARGS__) 196 | #else 197 | #define LOGDEBUG(message, ...) 198 | #endif 199 | #else 200 | #error Unsupported compiler 201 | #endif 202 | 203 | // __VA_OPT__(,) __VA_ARGS__ -------------------------------------------------------------------------------- /src/gui/gui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "core/math.hpp" 4 | #include "platform/platform.hpp" 5 | #include "render/backend.hpp" 6 | #include "render/text.hpp" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace gui { 12 | 13 | struct Theme { 14 | float cornerRadius = 8.0f; 15 | float dropDownTextSize = 14; 16 | float fileMenuTextSize = 14; 17 | float fileMenuMargin = 4.0f; 18 | float dropDownMargin = 4.0f; 19 | vec3 backgoundColor = vec3(0.1f); 20 | vec3 textColor = vec3(1.0f); 21 | vec3 dropDownHoverColor = vec3(1, 0.5f, 0); 22 | vec3 dropDownColor = vec3(0.2f); 23 | Font &font; 24 | 25 | Theme(Font &font) : font(font) {}; 26 | Theme() : font(Font::getDefault()) {}; 27 | }; 28 | 29 | struct Rect { 30 | vec2 pos; 31 | vec2 size; 32 | vec3 color = vec3(0); 33 | 34 | Rect(){} 35 | Rect(vec2 pos, vec2 size) : pos(pos), size(size) {} 36 | Rect(vec2 pos, vec2 size, vec3 color) : pos(pos), size(size), color(color) {} 37 | 38 | static Rect findMinAABB(Rect a, Rect b) { 39 | vec2 p = vec2(std::min(a.pos.x, b.pos.x), std::min(a.pos.y, b.pos.y)); 40 | vec2 s = vec2(std::max(a.pos.x + a.size.x, b.pos.x + b.size.x) - p.x, 41 | std::max(a.pos.y + a.size.y, b.pos.y + b.size.y) - p.y); 42 | return {p,s}; 43 | } 44 | static bool pointInRect(Rect r, vec2 p) { 45 | return (p.x >= r.pos.x && p.y >= r.pos.y && p.x <= r.pos.x + r.size.x && p.y <= r.pos.y + r.size.y); 46 | } 47 | }; 48 | 49 | 50 | class GUIManager { 51 | public: 52 | 53 | GUIManager() : guiRectShader("gui_rect"){} 54 | 55 | 56 | std::pair fileMenu(const std::vector &list, const mat4 &pmat){ 57 | if (list.empty()) 58 | return {-1,vec2(0)}; 59 | 60 | // User interaction 61 | Platform &platform = Platform::getInstance(); 62 | vec2 mouse = vec2(platform.getMouseX(), platform.getMouseY()); 63 | 64 | // min height 65 | float fileMenuHeight = 0; 66 | for(const string &s : list) 67 | fileMenuHeight = std::max(fileMenuHeight, theme.font.getHeight(s, theme.fileMenuTextSize)+2*theme.cornerRadius); 68 | 69 | // draw file menu rect 70 | drawRect(Rect(vec2(0), 71 | vec2(platform.getWidth(),fileMenuHeight+2*theme.fileMenuMargin), 72 | theme.backgoundColor)); 73 | 74 | int selected = -1; 75 | vec2 dropDownPos = vec2(0); 76 | // render each item 77 | float xoffset = theme.fileMenuMargin; 78 | for(int i=0; i < list.size(); i++){ 79 | vec2 textSize = theme.font.getDim(list[i], theme.fileMenuTextSize); 80 | Rect r = Rect(vec2(xoffset,theme.fileMenuMargin), 81 | textSize+vec2(2*theme.cornerRadius), 82 | theme.dropDownColor); 83 | if(Rect::pointInRect(r, mouse)){ 84 | r.color = theme.dropDownHoverColor; 85 | if(platform.isMousePressed(MouseButton::LEFT)){ 86 | dropDownPos = vec2(r.pos.x, fileMenuHeight+2*theme.fileMenuMargin); 87 | selected = i; 88 | } 89 | 90 | } 91 | drawRect(r); 92 | theme.font.text(list[i], vec2(xoffset,theme.fileMenuMargin)+vec2(theme.cornerRadius), theme.fileMenuTextSize, vec3(1)); 93 | xoffset += r.size.x+theme.fileMenuMargin; 94 | 95 | } 96 | 97 | // render widget 98 | renderRect(pmat); 99 | theme.font.render(pmat); 100 | 101 | return {selected, dropDownPos}; 102 | } 103 | 104 | // TODO : Improve this 105 | int dropDownMenu(const std::vector &list, vec2 pos, const mat4 &pmat, std::optional header) { 106 | if (list.empty()) 107 | return -1; 108 | 109 | // User interaction 110 | Platform &platform = Platform::getInstance(); 111 | vec2 mouse = vec2(platform.getMouseX(), platform.getMouseY()); 112 | 113 | // Find dropDowm size 114 | 115 | Rect minAABB; 116 | int startIndex; 117 | 118 | vec2 offset; 119 | if(header){ 120 | startIndex = 0; 121 | minAABB = Rect(pos, theme.font.getDim(header.value(), theme.dropDownTextSize)+vec2(2*theme.cornerRadius)); 122 | 123 | theme.font.text(header.value(), pos+vec2(theme.cornerRadius), theme.dropDownTextSize, theme.textColor); 124 | offset = vec2(0, minAABB.size.y+theme.dropDownMargin); 125 | } else { 126 | startIndex = 1; 127 | minAABB = Rect(pos, theme.font.getDim(list[0], theme.dropDownTextSize)+vec2(2*theme.cornerRadius)); 128 | offset = vec2(0, minAABB.size.y); 129 | } 130 | 131 | 132 | for (int i = startIndex; i < list.size(); i++) { 133 | Rect textRect = Rect(pos + offset, theme.font.getDim(list[i], theme.dropDownTextSize)+vec2(2*theme.cornerRadius)); 134 | minAABB = Rect::findMinAABB(minAABB, textRect); 135 | offset.y += textRect.size.y + theme.dropDownMargin; 136 | } 137 | 138 | // draw bg rectangle 139 | drawRect(Rect( 140 | minAABB.pos-vec2(theme.dropDownMargin), 141 | minAABB.size+vec2(2*theme.dropDownMargin, 3*theme.dropDownMargin), 142 | theme.backgoundColor)); 143 | 144 | // render each item && check which one is selected 145 | int selected = -1; 146 | 147 | if(header) 148 | offset = vec2(0, theme.font.getHeight(header.value(), theme.dropDownTextSize)+ 2*theme.dropDownMargin+2*theme.cornerRadius); 149 | else 150 | offset = vec2(0); 151 | 152 | for (int i = 0; i < list.size(); i++) { 153 | float txtHeight = theme.font.getHeight(list[0], theme.dropDownTextSize); 154 | Rect r = Rect(pos+offset, vec2(minAABB.size.x, txtHeight+theme.cornerRadius*2)); 155 | 156 | vec3 bgColor = theme.dropDownColor; 157 | if (Rect::pointInRect(r, mouse)) { 158 | bgColor = theme.dropDownHoverColor; 159 | if (Platform::getInstance().isMousePressed(MouseButton::LEFT)) 160 | selected = i; 161 | } 162 | r.color = bgColor; 163 | drawRect(r); 164 | theme.font.text(list[i], r.pos+vec2(theme.cornerRadius), theme.dropDownTextSize, theme.textColor); 165 | offset.y += r.size.y + theme.dropDownMargin; 166 | } 167 | 168 | // render widget 169 | renderRect(pmat); 170 | theme.font.render(pmat); 171 | return selected; 172 | } 173 | 174 | private: 175 | Theme theme; 176 | Shader guiRectShader; 177 | std::vector rectDrawCalls; 178 | 179 | void drawRect(Rect r){ 180 | rectDrawCalls.push_back(r); 181 | } 182 | 183 | void renderRect(const mat4 &pmat){ 184 | std::vector verts; 185 | verts.reserve(rectDrawCalls.size()*7); 186 | for(auto &r : rectDrawCalls) 187 | verts.insert(verts.end(),{r.pos.x, r.pos.y,r.size.x, r.size.y, r.color.r, r.color.g, r.color.b}); 188 | VertexArray vao; 189 | VertexBuffer vbo(&verts[0], sizeof(float) * verts.size()); 190 | VertexBufferLayout layout; 191 | layout.push(4); // rect 192 | layout.push(3); // color 193 | vao.addBuffer(vbo, layout); 194 | guiRectShader.use(); 195 | guiRectShader.setMat4("view", pmat); 196 | guiRectShader.setFloat("radius", theme.cornerRadius); 197 | glDrawArrays(GL_POINTS, 0, rectDrawCalls.size()); 198 | 199 | rectDrawCalls.clear(); 200 | } 201 | 202 | }; 203 | 204 | } -------------------------------------------------------------------------------- /src/render/text.cpp: -------------------------------------------------------------------------------- 1 | #include "text.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | // ---- PARSERFNT ---- 7 | 8 | ParserFnt::ParserFnt(const string &path) : m_file(path) {} 9 | 10 | std::vector split(const string &s, char c) { 11 | std::vector data; 12 | std::stringstream buffer; 13 | int i = 0; 14 | while (i < s.length()) { 15 | // ignore sequence of separator 16 | while (i < s.length() && s[i] == c) 17 | i++; 18 | 19 | // no other char 20 | if (i == s.length()) 21 | break; 22 | 23 | // read chunk 24 | // special check for " " -> must read the separator 25 | bool isString = false; 26 | while (i < s.length()) { 27 | if (s[i] == '"' && !isString) 28 | isString = true; 29 | else if (s[i] == '"' && isString) 30 | isString = false; 31 | 32 | if (s[i] != c || (s[i] == c && isString)) { 33 | buffer << s[i]; 34 | i++; 35 | } else 36 | break; 37 | } 38 | 39 | // push chunk 40 | string chunk = buffer.str(); 41 | data.push_back(chunk); 42 | buffer.str(string()); // clear buffer 43 | } 44 | return data; 45 | } 46 | 47 | bool ParserFnt::nextLine(std::pair> &line) { 48 | string s; 49 | if (std::getline(m_file, s)) { 50 | // read all attributes 51 | std::vector params = split(s, ' '); 52 | 53 | if (params.empty()) 54 | return false; 55 | 56 | line.first = params[0]; 57 | 58 | for (int i = 1; i < params.size(); i++) { 59 | std::vector val = split(params[i], '='); 60 | line.second[val[0]] = val[1]; 61 | } 62 | return true; 63 | } 64 | return false; 65 | } 66 | 67 | // ---- FONT --- 68 | 69 | Font::Font(const string &name) : m_texture("res/fonts/" + name + ".png"), textShader("text_sdf") { 70 | string path = "res/fonts/" + name + ".fnt"; 71 | LOGDEBUG("loading font : {}", path); 72 | ParserFnt parser(path); 73 | // Lecture ligne par ligne 74 | std::pair> line; 75 | while (parser.nextLine(line)) { 76 | if (line.first == "info") { 77 | m_name = line.second["face"]; 78 | m_size = std::stof(line.second["size"]); 79 | auto pvals = split(line.second["padding"], ','); 80 | m_padding.x = std::stof(pvals[0]); 81 | m_padding.y = std::stof(pvals[1]); 82 | m_padding.z = std::stof(pvals[2]); 83 | m_padding.w = std::stof(pvals[3]); 84 | } else if (line.first == "common") { 85 | m_lineHeight = std::stof(line.second["lineHeight"]); 86 | } else if (line.first == "char") { 87 | Glyph glyph; 88 | glyph.id = std::stoi(line.second["id"]); 89 | 90 | glyph.advance = std::stof(line.second["xadvance"]); 91 | 92 | glyph.dim.x = std::stof(line.second["width"]); 93 | glyph.dim.y = std::stof(line.second["height"]); 94 | 95 | glyph.pos.x = std::stof(line.second["x"]); 96 | glyph.pos.y = std::stof(line.second["y"]); 97 | 98 | glyph.offset.x = std::stof(line.second["xoffset"]); 99 | glyph.offset.y = std::stof(line.second["yoffset"]); 100 | 101 | m_glyphs[glyph.id] = glyph; // store glyph info 102 | } else if (line.first == "kernings") { 103 | break; 104 | } 105 | } 106 | } 107 | 108 | std::map &Font::getGlyphs() { return m_glyphs; } 109 | 110 | // TODO : improve metrics calculations 111 | float Font::getHeight(const string &text, float size) { 112 | float scale = size / m_size; 113 | float lineHeight = m_lineHeight * scale; 114 | float height = lineHeight; 115 | for (char c: text) 116 | if (c == '\n') 117 | height += lineHeight; 118 | return height; 119 | } 120 | 121 | float Font::getWidth(const string &text, float size) { 122 | float scale = size / m_size; 123 | float x = 0; 124 | float lastAdvance = 0; 125 | float lastWidth = 0; 126 | std::vector widths; 127 | for (char c: text) { 128 | Glyph g; 129 | if (c == '\n') { 130 | widths.push_back(x); 131 | x = 0; 132 | continue; 133 | } 134 | 135 | if (m_glyphs.count(c) == 0) 136 | g = m_glyphs[(char) 127]; 137 | else 138 | g = m_glyphs[c]; 139 | 140 | lastAdvance = (g.advance-m_padding.x) * scale; 141 | lastWidth = g.dim.x * scale; 142 | x += lastAdvance; 143 | } 144 | widths.push_back(x - lastAdvance + lastWidth); 145 | float max = widths[0]; 146 | for (float w: widths) 147 | if (w > max) 148 | max = w; 149 | 150 | return max; 151 | } 152 | 153 | vec2 Font::getDim(const string &text, float size){ 154 | return {getWidth(text, size), getHeight(text, size)}; 155 | } 156 | 157 | void Font::text(const string &textString, vec2 position, float size, vec3 color) { 158 | Text t; 159 | t.fontSize = size; 160 | t.position = vec3(position, 0); 161 | t.textString = textString; 162 | t.color = color; 163 | m_texts.push_back(t); 164 | } 165 | void Font::text(const string &textString, vec3 position, float size, vec3 color) { 166 | Text t; 167 | t.fontSize = size; 168 | t.position = position; 169 | t.textString = textString; 170 | t.color = color; 171 | m_texts.push_back(t); 172 | } 173 | 174 | void Font::render(const mat4 &pmat, const mat4 &view) { 175 | // Nothing to render 176 | if (m_texts.empty()) 177 | return; 178 | 179 | // get total vertices 180 | unsigned int nbMaxChars = 0; 181 | for (const Text &txt: m_texts) 182 | nbMaxChars += txt.textString.size(); 183 | 184 | // build geometry 185 | std::vector vertices; 186 | vertices.reserve(nbMaxChars * 11); 187 | vec2 texDim = vec2(m_texture.getWidth(), m_texture.getHeight()); 188 | 189 | unsigned int nbChars = 0; 190 | for (const Text &txt: m_texts) { 191 | // Text infos 192 | float scale = txt.fontSize / m_size; 193 | const string &s = txt.textString; 194 | vec3 color = txt.color; 195 | vec3 pos = txt.position; // writer head 196 | 197 | float xstart = pos.x; 198 | for (int j = 0; j < s.length(); j++) { 199 | char c = s[j]; 200 | // check if char is in supported charset 201 | // else default to unknown char 202 | Glyph g; 203 | 204 | // new line 205 | if (c == '\n') { 206 | pos.x = xstart; 207 | pos.y += m_lineHeight * scale; 208 | continue; 209 | } 210 | 211 | if (m_glyphs.count(c) == 0) 212 | g = m_glyphs[(char) 127]; 213 | else 214 | g = m_glyphs[c]; 215 | 216 | // build quad for this char 217 | // pos 218 | //vec2 start = pos + (g.offset - m_padding.xy) * scale; 219 | vec2 start = pos.xy + g.offset * scale; 220 | vec2 size = vec2(g.dim.x, g.dim.y) * scale; 221 | 222 | // uvs 223 | vec2 uvStart = vec2(g.pos.x, g.pos.y) / texDim; 224 | vec2 uvSize = vec2(g.dim.x, g.dim.y) / texDim; 225 | 226 | // append data 227 | vertices.insert(vertices.end(), {start.x, start.y, size.x, size.y, 228 | uvStart.x, uvStart.y, uvSize.x, uvSize.y, 229 | color.r, color.g, color.b, 230 | pos.z}); 231 | 232 | // advance writer head 233 | pos.x += (g.advance-m_padding.x) * scale; 234 | nbChars++; 235 | } 236 | } 237 | textShader.use(); 238 | textShader.setMat4("projection", pmat); 239 | textShader.setMat4("view", view); 240 | m_texture.bind(0); 241 | VertexArray vao; 242 | VertexBuffer vbo(&vertices[0], sizeof(float) * vertices.size()); 243 | VertexBufferLayout layout; 244 | layout.push(4); // rect 245 | layout.push(4); // rectUV 246 | layout.push(3); // color 247 | layout.push(1); // depth 248 | vao.addBuffer(vbo, layout); 249 | 250 | glDrawArrays(GL_POINTS, 0, nbChars); 251 | 252 | m_texts.clear(); // remove all draw calls 253 | } 254 | -------------------------------------------------------------------------------- /src/render/backend.cpp: -------------------------------------------------------------------------------- 1 | #include "backend.hpp" 2 | #include "core/logger.hpp" 3 | #include "core/math.hpp" 4 | #include 5 | 6 | #define STB_IMAGE_IMPLEMENTATION 7 | 8 | #include 9 | 10 | // ---- SHADERS ---- 11 | 12 | std::vector Shader::splitShaderSources(const string &glslCode) { 13 | std::vector shaderCodes; 14 | std::stringstream vertexCode; 15 | std::stringstream fragmentCode; 16 | std::stringstream geometryCode; 17 | std::istringstream f(glslCode); 18 | std::string line; 19 | enum CodeType { 20 | NONE, 21 | VEXRTEX, 22 | FRAGMENT, 23 | GEOMETRY 24 | }; 25 | CodeType state = CodeType::NONE; 26 | bool geometryShader = false; 27 | while (std::getline(f, line)) { 28 | // We will read a shader code ! 29 | if (line.find("#SHADER") != string::npos) { 30 | // Vertex code 31 | if (line.find("VERTEX") != string::npos) { 32 | state = CodeType::VEXRTEX; 33 | continue; 34 | } else if (line.find("FRAGMENT") != string::npos) { 35 | state = CodeType::FRAGMENT; 36 | continue; 37 | } else if (line.find("GEOMETRY") != string::npos) { 38 | state = CodeType::GEOMETRY; 39 | continue; 40 | } 41 | } 42 | // Read shader code or skip 43 | switch (state) { 44 | case CodeType::VEXRTEX: 45 | vertexCode << line << '\n'; 46 | break; 47 | 48 | case CodeType::FRAGMENT: 49 | fragmentCode << line << '\n'; 50 | break; 51 | case CodeType::GEOMETRY: 52 | geometryShader = true; 53 | geometryCode << line << '\n'; 54 | break; 55 | } 56 | } 57 | shaderCodes.push_back(vertexCode.str()); 58 | shaderCodes.push_back(fragmentCode.str()); 59 | if (geometryShader) 60 | shaderCodes.push_back(geometryCode.str()); 61 | return shaderCodes; 62 | } 63 | 64 | Shader::Shader(const string &name) { 65 | string path = "res/shaders/" + name + ".glsl"; 66 | LOGDEBUG("Loading shader : {}", path); 67 | std::ifstream t(path); 68 | std::stringstream buffer; 69 | buffer << t.rdbuf(); 70 | string source = buffer.str(); 71 | 72 | auto shaderCodes = splitShaderSources(source); 73 | 74 | const char *vertexCode = shaderCodes[0].c_str(); 75 | const char *fragmentCode = shaderCodes[1].c_str(); 76 | 77 | int success; 78 | char infoLog[512]; 79 | GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); 80 | GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 81 | 82 | glShaderSource(vertexShader, 1, &vertexCode, NULL); 83 | glCompileShader(vertexShader); 84 | glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); 85 | if (!success) { 86 | glGetShaderInfoLog(vertexShader, 512, NULL, infoLog); 87 | LOGERROR("Vertex shader compilation failed :\n{}", infoLog); 88 | } 89 | 90 | glShaderSource(fragmentShader, 1, &fragmentCode, NULL); 91 | glCompileShader(fragmentShader); 92 | glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); 93 | if (!success) { 94 | glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog); 95 | LOGERROR("Fragment shader compilation failed :\n{}", infoLog); 96 | } 97 | 98 | m_program = glCreateProgram(); 99 | glAttachShader(m_program, vertexShader); 100 | glAttachShader(m_program, fragmentShader); 101 | 102 | // We have a geometry shader 103 | GLuint geometryShader = 0; 104 | if (shaderCodes.size() == 3) { 105 | const char *geomatryCode = shaderCodes[2].c_str(); 106 | GLuint geometryShader = glCreateShader(GL_GEOMETRY_SHADER); 107 | glShaderSource(geometryShader, 1, &geomatryCode, NULL); 108 | glCompileShader(geometryShader); 109 | glGetShaderiv(geometryShader, GL_COMPILE_STATUS, &success); 110 | if (!success) { 111 | glGetShaderInfoLog(geometryShader, 512, NULL, infoLog); 112 | LOGERROR("Geometry shader compilation failed :\n{}", infoLog); 113 | } 114 | glAttachShader(m_program, geometryShader); 115 | } 116 | 117 | glLinkProgram(m_program); 118 | glGetProgramiv(m_program, GL_LINK_STATUS, &success); 119 | if (!success) { 120 | glGetProgramInfoLog(m_program, 512, NULL, infoLog); 121 | LOGERROR("Program shader linking failed :\n{}", infoLog); 122 | } 123 | glDeleteShader(vertexShader); 124 | glDeleteShader(fragmentShader); 125 | if (shaderCodes.size() == 3) 126 | glDeleteShader(geometryShader); 127 | } 128 | 129 | Shader::~Shader() { 130 | glDeleteProgram(m_program); 131 | } 132 | 133 | void Shader::use() { 134 | glUseProgram(m_program); 135 | } 136 | 137 | void Shader::setFloat(const string &name, float value) const { 138 | glUniform1f(glGetUniformLocation(m_program, name.c_str()), value); 139 | } 140 | 141 | void Shader::setInt(const string &name, int value) const { 142 | glUniform1i(glGetUniformLocation(m_program, name.c_str()), value); 143 | } 144 | 145 | void Shader::setVec4(const string &name, float v0, float v1, float v2, float v3) const { 146 | glUniform4f(glGetUniformLocation(m_program, name.c_str()), v0, v1, v2, v3); 147 | } 148 | 149 | void Shader::setVec3(const string &name, float v0, float v1, float v2) const { 150 | glUniform3f(glGetUniformLocation(m_program, name.c_str()), v0, v1, v2); 151 | } 152 | 153 | void Shader::setVec2(const string &name, float v0, float v1) const { 154 | glUniform2f(glGetUniformLocation(m_program, name.c_str()), v0, v1); 155 | } 156 | 157 | void Shader::setVec4(const string &name, const vec4 &v) const { 158 | glUniform4fv(glGetUniformLocation(m_program, name.c_str()), 1, (float *) &v); 159 | } 160 | 161 | void Shader::setVec2(const string &name, const vec2 &v) const { 162 | glUniform2fv(glGetUniformLocation(m_program, name.c_str()), 1, (float *) &v); 163 | } 164 | 165 | void Shader::setVec3(const string &name, const vec3 &v) const { 166 | glUniform3fv(glGetUniformLocation(m_program, name.c_str()), 1, (float *) &v); 167 | } 168 | 169 | void Shader::setMat4(const string &name, const mat4 &m) const { 170 | glUniformMatrix4fv(glGetUniformLocation(m_program, name.c_str()), 1, GL_FALSE, (float *) &m); 171 | } 172 | 173 | void Shader::setMat3(const string &name, const mat3 &m) const { 174 | glUniformMatrix3fv(glGetUniformLocation(m_program, name.c_str()), 1, GL_FALSE, (float *) &m); 175 | } 176 | 177 | // ---- VETEXBUFFER ---- 178 | 179 | VertexBuffer::VertexBuffer(const void *data, unsigned int size) { 180 | glGenBuffers(1, &m_vbo); 181 | glBindBuffer(GL_ARRAY_BUFFER, m_vbo); 182 | glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); 183 | } 184 | 185 | VertexBuffer::~VertexBuffer() { 186 | glDeleteBuffers(1, &m_vbo); 187 | } 188 | 189 | void VertexBuffer::bind() const { 190 | glBindBuffer(GL_ARRAY_BUFFER, m_vbo); 191 | } 192 | 193 | void VertexBuffer::unbind() const { 194 | glBindBuffer(GL_ARRAY_BUFFER, 0); 195 | } 196 | 197 | // ---- ELEMENTBUFFER ---- 198 | 199 | ElementBuffer::ElementBuffer(const void *data, unsigned int size) { 200 | glGenBuffers(1, &m_ebo); 201 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); 202 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW); 203 | } 204 | 205 | ElementBuffer::~ElementBuffer() { 206 | glDeleteBuffers(1, &m_ebo); 207 | } 208 | 209 | void ElementBuffer::bind() const { 210 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo); 211 | } 212 | 213 | void ElementBuffer::unbind() const { 214 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 215 | } 216 | 217 | // ---- VERTEXBUFFERELEMENT ---- 218 | unsigned int VertexBufferElement::getTypeSize(unsigned int type) { 219 | switch (type) { 220 | case GL_FLOAT: 221 | return sizeof(float); 222 | } 223 | LOGFATAL("VertexBufferElment::getTypeSize({}) -> unknow type", type); 224 | return 0; 225 | } 226 | 227 | // ---- VERTEXBUFFERLAYOUT ---- 228 | 229 | VertexBufferLayout::VertexBufferLayout() : m_stride(0) {} 230 | 231 | inline const std::vector &VertexBufferLayout::getElements() const { return m_elements; } 232 | 233 | inline unsigned int VertexBufferLayout::getStride() const { return m_stride; } 234 | 235 | // ---- VERTEXARRAY ---- 236 | 237 | VertexArray::VertexArray() { 238 | glGenVertexArrays(1, &m_vao); 239 | glBindVertexArray(m_vao); 240 | } 241 | 242 | VertexArray::~VertexArray() { 243 | glDeleteVertexArrays(1, &m_vao); 244 | } 245 | 246 | void VertexArray::bind() const { 247 | glBindVertexArray(m_vao); 248 | } 249 | 250 | void VertexArray::unbind() const { 251 | glBindVertexArray(0); 252 | } 253 | 254 | void VertexArray::addBuffer(const VertexBuffer &vb, const VertexBufferLayout &layout) { 255 | bind(); 256 | vb.bind(); 257 | const auto &elements = layout.getElements(); 258 | unsigned int offset = 0; 259 | for (unsigned int i = 0; i < elements.size(); i++) { 260 | const auto &element = elements[i]; 261 | glEnableVertexAttribArray(i); 262 | glVertexAttribPointer(i, element.count, element.type, element.normalized, layout.getStride(), 263 | (const void *) offset); 264 | offset += element.count * VertexBufferElement::getTypeSize(element.type); 265 | } 266 | } 267 | 268 | void VertexArray::addBuffer(const ElementBuffer &ebo) { 269 | bind(); 270 | ebo.bind(); 271 | } 272 | 273 | // ---- TEXTURE ---- 274 | 275 | Texture::Texture(const string &path) { 276 | LOGDEBUG("Loading image : {}", path); 277 | stbi_set_flip_vertically_on_load(false); 278 | int nbChannels; 279 | unsigned char *data = stbi_load(path.c_str(), &m_width, &m_height, &nbChannels, 0); 280 | 281 | if (data) { 282 | glGenTextures(1, &m_texture); 283 | glBindTexture(GL_TEXTURE_2D, m_texture); 284 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 285 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 286 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 287 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 288 | 289 | if (nbChannels == 3) 290 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, m_width, m_height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); 291 | else if (nbChannels == 4) 292 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); 293 | 294 | glGenerateMipmap(GL_TEXTURE_2D); 295 | } else { 296 | LOGERROR("Can't load image : {}", path); 297 | } 298 | stbi_image_free(data); 299 | } 300 | 301 | Texture::~Texture() { 302 | glDeleteTextures(1, &m_texture); 303 | } 304 | 305 | void Texture::bind(int unit) { 306 | glActiveTexture(GL_TEXTURE0 + unit); 307 | glBindTexture(GL_TEXTURE_2D, m_texture); 308 | } 309 | 310 | int Texture::getWidth() { return m_width; } 311 | 312 | int Texture::getHeight() { return m_height; } -------------------------------------------------------------------------------- /res/fonts/roboto_regular_sdf.fnt: -------------------------------------------------------------------------------- 1 | info face="Roboto" size=140 bold=1 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=10,10,10,10 spacing=-2,-2 2 | common lineHeight=183 base=130 scaleW=1024 scaleH=1024 pages=1 packed=0 3 | page id=0 file="roboto_regular_sdf.png" 4 | chars count=98 5 | char id=0 x=0 y=0 width=0 height=0 xoffset=-10 yoffset=0 xadvance=18 page=0 chnl=0 6 | char id=10 x=0 y=0 width=0 height=0 xoffset=-10 yoffset=0 xadvance=18 page=0 chnl=0 7 | char id=32 x=0 y=0 width=0 height=0 xoffset=-10 yoffset=0 xadvance=53 page=0 chnl=0 8 | char id=33 x=959 y=302 width=41 height=126 xoffset=0 yoffset=15 xadvance=58 page=0 chnl=0 9 | char id=34 x=545 y=906 width=53 height=59 xoffset=-1 yoffset=10 xadvance=67 page=0 chnl=0 10 | char id=35 x=506 y=680 width=101 height=125 xoffset=-2 yoffset=15 xadvance=108 page=0 chnl=0 11 | char id=36 x=360 y=0 width=89 height=156 xoffset=-3 yoffset=-1 xadvance=101 page=0 chnl=0 12 | char id=37 x=549 y=302 width=114 height=127 xoffset=-3 yoffset=14 xadvance=125 page=0 chnl=0 13 | char id=38 x=663 y=302 width=110 height=127 xoffset=-4 yoffset=14 xadvance=109 page=0 chnl=0 14 | char id=39 x=986 y=171 width=35 height=58 xoffset=-3 yoffset=10 xadvance=46 page=0 chnl=0 15 | char id=40 x=0 y=0 width=61 height=171 xoffset=-1 yoffset=2 xadvance=70 page=0 chnl=0 16 | char id=41 x=61 y=0 width=62 height=171 xoffset=-8 yoffset=2 xadvance=71 page=0 chnl=0 17 | char id=42 x=258 y=906 width=83 height=85 xoffset=-9 yoffset=15 xadvance=82 page=0 chnl=0 18 | char id=43 x=0 y=906 width=93 height=98 xoffset=-5 yoffset=33 xadvance=101 page=0 chnl=0 19 | char id=44 x=500 y=906 width=45 height=61 xoffset=-9 yoffset=100 xadvance=49 page=0 chnl=0 20 | char id=45 x=800 y=906 width=59 height=36 xoffset=-8 yoffset=68 xadvance=61 page=0 chnl=0 21 | char id=46 x=759 y=906 width=41 height=40 xoffset=-1 yoffset=101 xadvance=59 page=0 chnl=0 22 | char id=47 x=787 y=0 width=79 height=134 xoffset=-10 yoffset=15 xadvance=80 page=0 chnl=0 23 | char id=48 x=380 y=302 width=89 height=127 xoffset=-3 yoffset=14 xadvance=101 page=0 chnl=0 24 | char id=49 x=939 y=429 width=64 height=125 xoffset=1 yoffset=15 xadvance=101 page=0 chnl=0 25 | char id=50 x=0 y=429 width=92 height=126 xoffset=-4 yoffset=14 xadvance=101 page=0 chnl=0 26 | char id=51 x=203 y=302 width=88 height=127 xoffset=-4 yoffset=14 xadvance=101 page=0 chnl=0 27 | char id=52 x=317 y=680 width=97 height=125 xoffset=-7 yoffset=15 xadvance=101 page=0 chnl=0 28 | char id=53 x=92 y=429 width=88 height=126 xoffset=0 yoffset=15 xadvance=101 page=0 chnl=0 29 | char id=54 x=180 y=429 width=88 height=126 xoffset=-1 yoffset=15 xadvance=101 page=0 chnl=0 30 | char id=55 x=414 y=680 width=92 height=125 xoffset=-5 yoffset=15 xadvance=101 page=0 chnl=0 31 | char id=56 x=291 y=302 width=89 height=127 xoffset=-3 yoffset=14 xadvance=101 page=0 chnl=0 32 | char id=57 x=268 y=429 width=88 height=126 xoffset=-4 yoffset=14 xadvance=101 page=0 chnl=0 33 | char id=58 x=966 y=680 width=41 height=100 xoffset=-2 yoffset=41 xadvance=56 page=0 chnl=0 34 | char id=59 x=681 y=680 width=46 height=120 xoffset=-8 yoffset=41 xadvance=52 page=0 chnl=0 35 | char id=60 x=93 y=906 width=82 height=90 xoffset=-6 yoffset=39 xadvance=93 page=0 chnl=0 36 | char id=61 x=418 y=906 width=82 height=64 xoffset=0 yoffset=49 xadvance=99 page=0 chnl=0 37 | char id=62 x=175 y=906 width=83 height=90 xoffset=-1 yoffset=39 xadvance=95 page=0 chnl=0 38 | char id=63 x=469 y=302 width=80 height=127 xoffset=-5 yoffset=14 xadvance=88 page=0 chnl=0 39 | char id=64 x=506 y=0 width=138 height=154 xoffset=-3 yoffset=17 xadvance=148 page=0 chnl=0 40 | char id=65 x=356 y=429 width=115 height=125 xoffset=-10 yoffset=15 xadvance=113 page=0 chnl=0 41 | char id=66 x=471 y=429 width=93 height=125 xoffset=1 yoffset=15 xadvance=109 page=0 chnl=0 42 | char id=67 x=743 y=171 width=102 height=127 xoffset=-2 yoffset=14 xadvance=113 page=0 chnl=0 43 | char id=68 x=564 y=429 width=97 height=125 xoffset=1 yoffset=15 xadvance=114 page=0 chnl=0 44 | char id=69 x=661 y=429 width=89 height=125 xoffset=1 yoffset=15 xadvance=102 page=0 chnl=0 45 | char id=70 x=750 y=429 width=87 height=125 xoffset=1 yoffset=15 xadvance=99 page=0 chnl=0 46 | char id=71 x=845 y=171 width=102 height=127 xoffset=-2 yoffset=14 xadvance=117 page=0 chnl=0 47 | char id=72 x=837 y=429 width=102 height=125 xoffset=1 yoffset=15 xadvance=122 page=0 chnl=0 48 | char id=73 x=947 y=171 width=39 height=125 xoffset=2 yoffset=15 xadvance=60 page=0 chnl=0 49 | char id=74 x=773 y=302 width=88 height=126 xoffset=-7 yoffset=15 xadvance=99 page=0 chnl=0 50 | char id=75 x=0 y=555 width=106 height=125 xoffset=1 yoffset=15 xadvance=110 page=0 chnl=0 51 | char id=76 x=106 y=555 width=86 height=125 xoffset=1 yoffset=15 xadvance=97 page=0 chnl=0 52 | char id=77 x=192 y=555 width=124 height=125 xoffset=1 yoffset=15 xadvance=144 page=0 chnl=0 53 | char id=78 x=316 y=555 width=102 height=125 xoffset=1 yoffset=15 xadvance=122 page=0 chnl=0 54 | char id=79 x=0 y=302 width=105 height=127 xoffset=-2 yoffset=14 xadvance=118 page=0 chnl=0 55 | char id=80 x=418 y=555 width=97 height=125 xoffset=1 yoffset=15 xadvance=110 page=0 chnl=0 56 | char id=81 x=644 y=0 width=107 height=144 xoffset=-3 yoffset=14 xadvance=118 page=0 chnl=0 57 | char id=82 x=515 y=555 width=97 height=125 xoffset=1 yoffset=15 xadvance=108 page=0 chnl=0 58 | char id=83 x=105 y=302 width=98 height=127 xoffset=-5 yoffset=14 xadvance=105 page=0 chnl=0 59 | char id=84 x=612 y=555 width=102 height=125 xoffset=-7 yoffset=15 xadvance=106 page=0 chnl=0 60 | char id=85 x=861 y=302 width=98 height=126 xoffset=-1 yoffset=15 xadvance=113 page=0 chnl=0 61 | char id=86 x=714 y=555 width=113 height=125 xoffset=-10 yoffset=15 xadvance=111 page=0 chnl=0 62 | char id=87 x=827 y=555 width=144 height=125 xoffset=-7 yoffset=15 xadvance=146 page=0 chnl=0 63 | char id=88 x=0 y=680 width=109 height=125 xoffset=-8 yoffset=15 xadvance=110 page=0 chnl=0 64 | char id=89 x=109 y=680 width=110 height=125 xoffset=-11 yoffset=15 xadvance=106 page=0 chnl=0 65 | char id=90 x=219 y=680 width=98 height=125 xoffset=-5 yoffset=15 xadvance=106 page=0 chnl=0 66 | char id=91 x=123 y=0 width=52 height=160 xoffset=-1 yoffset=1 xadvance=59 page=0 chnl=0 67 | char id=92 x=866 y=0 width=81 height=134 xoffset=-9 yoffset=15 xadvance=79 page=0 chnl=0 68 | char id=93 x=175 y=0 width=51 height=160 xoffset=-10 yoffset=1 xadvance=59 page=0 chnl=0 69 | char id=94 x=341 y=906 width=77 height=75 xoffset=-7 yoffset=15 xadvance=81 page=0 chnl=0 70 | char id=95 x=859 y=906 width=88 height=36 xoffset=-10 yoffset=115 xadvance=85 page=0 chnl=0 71 | char id=96 x=701 y=906 width=58 height=46 xoffset=-9 yoffset=10 xadvance=65 page=0 chnl=0 72 | char id=97 x=793 y=680 width=86 height=101 xoffset=-3 yoffset=40 xadvance=98 page=0 chnl=0 73 | char id=98 x=0 y=171 width=88 height=131 xoffset=-1 yoffset=10 xadvance=101 page=0 chnl=0 74 | char id=99 x=879 y=680 width=87 height=101 xoffset=-4 yoffset=40 xadvance=95 page=0 chnl=0 75 | char id=100 x=88 y=171 width=88 height=131 xoffset=-4 yoffset=10 xadvance=101 page=0 chnl=0 76 | char id=101 x=0 y=805 width=88 height=101 xoffset=-4 yoffset=40 xadvance=96 page=0 chnl=0 77 | char id=102 x=947 y=0 width=70 height=131 xoffset=-6 yoffset=9 xadvance=71 page=0 chnl=0 78 | char id=103 x=389 y=171 width=88 height=129 xoffset=-4 yoffset=40 xadvance=101 page=0 chnl=0 79 | char id=104 x=176 y=171 width=84 height=130 xoffset=-1 yoffset=10 xadvance=99 page=0 chnl=0 80 | char id=105 x=971 y=555 width=40 height=125 xoffset=-1 yoffset=15 xadvance=56 page=0 chnl=0 81 | char id=106 x=449 y=0 width=57 height=155 xoffset=-15 yoffset=15 xadvance=55 page=0 chnl=0 82 | char id=107 x=260 y=171 width=91 height=130 xoffset=-1 yoffset=10 xadvance=93 page=0 chnl=0 83 | char id=108 x=351 y=171 width=38 height=130 xoffset=0 yoffset=10 xadvance=56 page=0 chnl=0 84 | char id=109 x=265 y=805 width=129 height=100 xoffset=-1 yoffset=40 xadvance=145 page=0 chnl=0 85 | char id=110 x=394 y=805 width=83 height=100 xoffset=-1 yoffset=40 xadvance=99 page=0 chnl=0 86 | char id=111 x=88 y=805 width=93 height=101 xoffset=-4 yoffset=40 xadvance=102 page=0 chnl=0 87 | char id=112 x=567 y=171 width=88 height=128 xoffset=-1 yoffset=40 xadvance=101 page=0 chnl=0 88 | char id=113 x=655 y=171 width=88 height=128 xoffset=-4 yoffset=40 xadvance=102 page=0 chnl=0 89 | char id=114 x=477 y=805 width=61 height=100 xoffset=-1 yoffset=40 xadvance=69 page=0 chnl=0 90 | char id=115 x=181 y=805 width=84 height=101 xoffset=-4 yoffset=40 xadvance=94 page=0 chnl=0 91 | char id=116 x=727 y=680 width=66 height=118 xoffset=-10 yoffset=23 xadvance=68 page=0 chnl=0 92 | char id=117 x=538 y=805 width=83 height=100 xoffset=-1 yoffset=41 xadvance=99 page=0 chnl=0 93 | char id=118 x=621 y=805 width=90 height=99 xoffset=-9 yoffset=41 xadvance=90 page=0 chnl=0 94 | char id=119 x=711 y=805 width=125 height=99 xoffset=-8 yoffset=41 xadvance=127 page=0 chnl=0 95 | char id=120 x=836 y=805 width=93 height=99 xoffset=-10 yoffset=41 xadvance=91 page=0 chnl=0 96 | char id=121 x=477 y=171 width=90 height=129 xoffset=-10 yoffset=41 xadvance=88 page=0 chnl=0 97 | char id=122 x=929 y=805 width=84 height=99 xoffset=-4 yoffset=41 xadvance=91 page=0 chnl=0 98 | char id=123 x=226 y=0 width=67 height=160 xoffset=-6 yoffset=6 xadvance=69 page=0 chnl=0 99 | char id=124 x=751 y=0 width=36 height=143 xoffset=1 yoffset=15 xadvance=56 page=0 chnl=0 100 | char id=125 x=293 y=0 width=67 height=160 xoffset=-10 yoffset=6 xadvance=69 page=0 chnl=0 101 | char id=126 x=598 y=906 width=103 height=53 xoffset=-2 yoffset=60 xadvance=117 page=0 chnl=0 102 | char id=127 x=607 y=680 width=74 height=125 xoffset=-4 yoffset=15 xadvance=84 page=0 chnl=0 103 | -------------------------------------------------------------------------------- /external/glad/include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | 157 | #elif defined(__VMS ) || defined(__sgi) 158 | 159 | /* 160 | * Using 161 | */ 162 | #include 163 | typedef int32_t khronos_int32_t; 164 | typedef uint32_t khronos_uint32_t; 165 | typedef int64_t khronos_int64_t; 166 | typedef uint64_t khronos_uint64_t; 167 | #define KHRONOS_SUPPORT_INT64 1 168 | #define KHRONOS_SUPPORT_FLOAT 1 169 | 170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 171 | 172 | /* 173 | * Win32 174 | */ 175 | typedef __int32 khronos_int32_t; 176 | typedef unsigned __int32 khronos_uint32_t; 177 | typedef __int64 khronos_int64_t; 178 | typedef unsigned __int64 khronos_uint64_t; 179 | #define KHRONOS_SUPPORT_INT64 1 180 | #define KHRONOS_SUPPORT_FLOAT 1 181 | 182 | #elif defined(__sun__) || defined(__digital__) 183 | 184 | /* 185 | * Sun or Digital 186 | */ 187 | typedef int khronos_int32_t; 188 | typedef unsigned int khronos_uint32_t; 189 | #if defined(__arch64__) || defined(_LP64) 190 | typedef long int khronos_int64_t; 191 | typedef unsigned long int khronos_uint64_t; 192 | #else 193 | typedef long long int khronos_int64_t; 194 | typedef unsigned long long int khronos_uint64_t; 195 | #endif /* __arch64__ */ 196 | #define KHRONOS_SUPPORT_INT64 1 197 | #define KHRONOS_SUPPORT_FLOAT 1 198 | 199 | #elif 0 200 | 201 | /* 202 | * Hypothetical platform with no float or int64 support 203 | */ 204 | typedef int khronos_int32_t; 205 | typedef unsigned int khronos_uint32_t; 206 | #define KHRONOS_SUPPORT_INT64 0 207 | #define KHRONOS_SUPPORT_FLOAT 0 208 | 209 | #else 210 | 211 | /* 212 | * Generic fallback 213 | */ 214 | #include 215 | typedef int32_t khronos_int32_t; 216 | typedef uint32_t khronos_uint32_t; 217 | typedef int64_t khronos_int64_t; 218 | typedef uint64_t khronos_uint64_t; 219 | #define KHRONOS_SUPPORT_INT64 1 220 | #define KHRONOS_SUPPORT_FLOAT 1 221 | 222 | #endif 223 | 224 | 225 | /* 226 | * Types that are (so far) the same on all platforms 227 | */ 228 | typedef signed char khronos_int8_t; 229 | typedef unsigned char khronos_uint8_t; 230 | typedef signed short int khronos_int16_t; 231 | typedef unsigned short int khronos_uint16_t; 232 | 233 | /* 234 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 236 | * to be the only LLP64 architecture in current use. 237 | */ 238 | #ifdef _WIN64 239 | typedef signed long long int khronos_intptr_t; 240 | typedef unsigned long long int khronos_uintptr_t; 241 | typedef signed long long int khronos_ssize_t; 242 | typedef unsigned long long int khronos_usize_t; 243 | #else 244 | typedef signed long int khronos_intptr_t; 245 | typedef unsigned long int khronos_uintptr_t; 246 | typedef signed long int khronos_ssize_t; 247 | typedef unsigned long int khronos_usize_t; 248 | #endif 249 | 250 | #if KHRONOS_SUPPORT_FLOAT 251 | /* 252 | * Float type 253 | */ 254 | typedef float khronos_float_t; 255 | #endif 256 | 257 | #if KHRONOS_SUPPORT_INT64 258 | /* Time types 259 | * 260 | * These types can be used to represent a time interval in nanoseconds or 261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 262 | * of nanoseconds since some arbitrary system event (e.g. since the last 263 | * time the system booted). The Unadjusted System Time is an unsigned 264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 265 | * may be either signed or unsigned. 266 | */ 267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 268 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 269 | #endif 270 | 271 | /* 272 | * Dummy value used to pad enum types to 32 bits. 273 | */ 274 | #ifndef KHRONOS_MAX_ENUM 275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 276 | #endif 277 | 278 | /* 279 | * Enumerated boolean type 280 | * 281 | * Values other than zero should be considered to be true. Therefore 282 | * comparisons should not be made against KHRONOS_TRUE. 283 | */ 284 | typedef enum { 285 | KHRONOS_FALSE = 0, 286 | KHRONOS_TRUE = 1, 287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 288 | } khronos_boolean_enum_t; 289 | 290 | #endif /* __khrplatform_h_ */ 291 | -------------------------------------------------------------------------------- /src/platform/platform_win32.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "platform.hpp" 3 | 4 | // Windows platform layer 5 | #if PLATFORM_WINDOWS 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include "core/logger.hpp" 12 | 13 | // Windows specific opengl definitions 14 | // https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt 15 | // 16 | #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 17 | #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 18 | #define WGL_CONTEXT_FLAGS_ARB 0x2094 19 | #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 20 | 21 | typedef HGLRC(WINAPI *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList); 22 | 23 | typedef BOOL(WINAPI *PFNWGLSWAPINTERVALEXTPROC)(int interval); 24 | 25 | struct Win32State { 26 | HINSTANCE hInstance = nullptr; 27 | HWND handle = nullptr; 28 | HDC hDC = nullptr; // Device context 29 | HGLRC glrc = nullptr; // OpenGl context 30 | bool shouldClose = false; 31 | int width, height; 32 | int mouseX, mouseY; 33 | int mouseWheel; // 0 no change, -1 down, 1 up 34 | 35 | bool mouseLeftDown = false; 36 | bool mouseMiddleDown = false; 37 | bool mouseRightDown = false; 38 | 39 | bool prevMouseLeftDown = false; 40 | bool prevMouseMiddleDown = false; 41 | bool prevMouseRightDown = false; 42 | }; 43 | 44 | static Win32State win32State; 45 | 46 | int Platform::getWidth() { 47 | return win32State.width; 48 | } 49 | 50 | int Platform::getHeight() { 51 | return win32State.height; 52 | } 53 | 54 | LRESULT CALLBACK win32ProcessMessages(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) { 55 | switch (umsg) { 56 | case WM_ERASEBKGND: 57 | // Ereasing will by handled by the application to prevent flicker. 58 | return 1; 59 | case WM_CLOSE: 60 | win32State.shouldClose = true; 61 | return 0; 62 | case WM_DESTROY: 63 | win32State.shouldClose = true; 64 | PostQuitMessage(0); 65 | return 0; 66 | 67 | case WM_SIZE: { 68 | RECT newRect; 69 | GetClientRect(hwnd, &newRect); 70 | win32State.width = newRect.right - newRect.left; 71 | win32State.height = newRect.bottom - newRect.top; 72 | } 73 | break; 74 | 75 | case WM_SIZING: { 76 | RECT newRect; 77 | GetClientRect(hwnd, &newRect); 78 | win32State.width = newRect.right - newRect.left; 79 | win32State.height = newRect.bottom - newRect.top; 80 | } 81 | break; 82 | 83 | case WM_KEYDOWN: 84 | case WM_SYSKEYDOWN: 85 | case WM_KEYUP: 86 | case WM_SYSKEYUP: { 87 | bool pressed = umsg == WM_KEYDOWN || umsg == WM_SYSKEYDOWN; 88 | // TODO : handle keyboard input 89 | } 90 | break; 91 | 92 | case WM_MOUSEMOVE: { 93 | win32State.mouseX = GET_X_LPARAM(lParam); 94 | win32State.mouseY = GET_Y_LPARAM(lParam); 95 | } 96 | break; 97 | 98 | case WM_MOUSEWHEEL: { 99 | int delta = GET_WHEEL_DELTA_WPARAM(wParam); 100 | if (delta != 0) 101 | delta = delta < 0 ? -1 : 1; // Normalize input for OS-independent use 102 | win32State.mouseWheel = delta; 103 | } 104 | break; 105 | 106 | case WM_LBUTTONDOWN: 107 | win32State.mouseLeftDown = true; 108 | break; 109 | case WM_MBUTTONDOWN: 110 | win32State.mouseMiddleDown = true; 111 | break; 112 | case WM_RBUTTONDOWN: 113 | win32State.mouseRightDown = true; 114 | break; 115 | case WM_LBUTTONUP: 116 | win32State.mouseLeftDown = false; 117 | break; 118 | case WM_RBUTTONUP: 119 | win32State.mouseRightDown = false; 120 | break; 121 | case WM_MBUTTONUP: 122 | win32State.mouseMiddleDown = false; 123 | break; 124 | 125 | default: 126 | break; 127 | } 128 | 129 | return DefWindowProcA(hwnd, umsg, wParam, lParam); 130 | } 131 | 132 | Platform::Platform(const string &name, int width, int height) { 133 | LOGDEBUG("Initializing platform..."); 134 | // Can't initialize the system twice ! 135 | if (win32State.handle != nullptr || win32State.hInstance != nullptr) { 136 | LOGWARN("Platform already initialized. Skipping initialization."); 137 | return; 138 | } 139 | 140 | win32State.width = width; 141 | win32State.height = height; 142 | 143 | // Get the application handle associated with the current process 144 | win32State.hInstance = GetModuleHandleA(0); 145 | 146 | // Window properties 147 | WNDCLASS wc = {}; 148 | wc.lpfnWndProc = win32ProcessMessages; 149 | wc.hInstance = win32State.hInstance; 150 | wc.lpszClassName = "BasicBool_class_name"; 151 | wc.hCursor = LoadCursor(NULL, IDC_ARROW); 152 | wc.hIcon = LoadIcon(win32State.hInstance, IDI_APPLICATION); 153 | wc.style = CS_DBLCLKS; // Listen double-clicks 154 | wc.cbClsExtra = 0; 155 | wc.cbWndExtra = 0; 156 | wc.hbrBackground = NULL; // transparent 157 | 158 | // Register window 159 | if (!RegisterClassA(&wc)) { 160 | MessageBoxA(0, "Window registration failed !", "FATAL ERROR", MB_ICONEXCLAMATION | MB_OK); 161 | LOGFATAL("Window registration failed !"); 162 | return; 163 | } 164 | 165 | // --- Creating the window --- 166 | 167 | // window decoration 168 | UINT32 windowStyle = WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION; 169 | UINT32 windowExStyle = WS_EX_APPWINDOW; 170 | 171 | windowStyle |= WS_MAXIMIZEBOX; 172 | windowStyle |= WS_MINIMIZEBOX; 173 | windowStyle |= WS_THICKFRAME; 174 | 175 | // Get border size 176 | RECT borderRect = {0, 0, 0, 0}; 177 | AdjustWindowRectEx(&borderRect, windowStyle, 0, windowExStyle); 178 | // Inner window size should match with user specified size 179 | int windowWidth = width + (borderRect.right - borderRect.left); 180 | int windowHeight = height + (borderRect.bottom - borderRect.top); 181 | 182 | win32State.handle = CreateWindowExA( 183 | windowExStyle, "BasicBool_class_name", "BasicBool", windowStyle, 184 | 0, 0, windowWidth, windowHeight, 0, 0, win32State.hInstance, 0); 185 | 186 | if (win32State.handle == 0) { 187 | MessageBoxA(0, "Window creation failed !", "FATAL ERROR", MB_ICONEXCLAMATION | MB_OK); 188 | LOGFATAL("Window creation failed !"); 189 | return; 190 | } 191 | 192 | // --- Setting up OpenGL context --- 193 | win32State.hDC = GetDC(win32State.handle); 194 | 195 | PIXELFORMATDESCRIPTOR pfd = {}; 196 | pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); 197 | pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; 198 | pfd.iPixelType = PFD_TYPE_RGBA; 199 | pfd.cColorBits = 32; // color depth (in bits) 200 | pfd.cDepthBits = 24; // depth buffer precision (int bits) 201 | pfd.cStencilBits = 8; // stencil buffer ALWAYS 8 202 | pfd.iLayerType = PFD_MAIN_PLANE; 203 | 204 | int nPixelFormat = ChoosePixelFormat(win32State.hDC, &pfd); 205 | if (nPixelFormat == 0) { 206 | MessageBoxA(0, "Failed to choose pixel format.", "FATAL ERROR", MB_ICONEXCLAMATION | MB_OK); 207 | LOGFATAL("Failed to choose pixel format."); 208 | return; 209 | } 210 | if (!SetPixelFormat(win32State.hDC, nPixelFormat, &pfd)) { 211 | MessageBoxA(0, "Failed to set pixel format.", "FATAL ERROR", MB_ICONEXCLAMATION | MB_OK); 212 | LOGFATAL("Failed to set pixel format."); 213 | return; 214 | } 215 | 216 | // Dummy OpenGL 2.x context 217 | HGLRC dummyOpenGLContext = wglCreateContext(win32State.hDC); 218 | wglMakeCurrent(win32State.hDC, dummyOpenGLContext); 219 | 220 | // setting up OpenGL 3 context creation 221 | int attributes[] = { 222 | WGL_CONTEXT_MAJOR_VERSION_ARB, 3, 223 | WGL_CONTEXT_MINOR_VERSION_ARB, 3, 224 | WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 225 | 0}; 226 | 227 | // As we have setup an OpenGL context earlier, we can now retrive the necessary functions to create an OpenGL 3 context. 228 | PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress( 229 | "wglCreateContextAttribsARB"); 230 | if (wglCreateContextAttribsARB) { 231 | win32State.glrc = wglCreateContextAttribsARB(win32State.hDC, NULL, attributes); // Create Opengl 3.x context 232 | // Remove dummy context 233 | wglMakeCurrent(NULL, NULL); 234 | wglDeleteContext(dummyOpenGLContext); 235 | 236 | 237 | wglMakeCurrent(win32State.hDC, win32State.glrc); 238 | 239 | PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress( 240 | "wglSwapIntervalEXT"); 241 | wglSwapIntervalEXT(0); // VSYNC not locked to 60 fps 242 | } else { 243 | MessageBoxA(0, "Can't create OpenGL 3 context !", "FATAL ERROR", MB_ICONEXCLAMATION | MB_OK); 244 | LOGFATAL("Can't create OpenGL 3 context !"); 245 | return; 246 | } 247 | 248 | gladLoadGL(); // Load all extensions 249 | 250 | ShowWindow(win32State.handle, SW_SHOW); 251 | } 252 | 253 | Platform::~Platform() { 254 | DestroyWindow(win32State.handle); 255 | win32State.handle = 0; 256 | LOGDEBUG("Platform closed"); 257 | } 258 | 259 | bool Platform::processEvents() { 260 | win32State.mouseWheel = 0; // reset wheel delta 261 | 262 | win32State.prevMouseLeftDown = win32State.mouseLeftDown; 263 | win32State.prevMouseRightDown = win32State.mouseRightDown; 264 | win32State.prevMouseMiddleDown = win32State.mouseMiddleDown; 265 | 266 | MSG message; 267 | while (PeekMessageA(&message, NULL, 0, 0, PM_REMOVE)) { 268 | TranslateMessage(&message); 269 | DispatchMessageA(&message); 270 | } 271 | 272 | return !win32State.shouldClose; 273 | } 274 | 275 | void Platform::swapBuffers() { 276 | SwapBuffers(win32State.hDC); 277 | } 278 | 279 | int Platform::getMouseX() { 280 | return win32State.mouseX; 281 | } 282 | 283 | int Platform::getMouseY() { 284 | return win32State.mouseY; 285 | } 286 | 287 | int Platform::getMouseWheel() { 288 | return win32State.mouseWheel; 289 | } 290 | 291 | 292 | bool Platform::isMouseDown(MouseButton button) { 293 | return (button == MouseButton::LEFT && win32State.mouseLeftDown) || 294 | (button == MouseButton::MIDDLE && win32State.mouseMiddleDown) || 295 | (button == MouseButton::RIGHT && win32State.mouseRightDown); 296 | } 297 | 298 | bool Platform::isMouseUP(MouseButton button) { 299 | return (button == MouseButton::LEFT && !win32State.mouseLeftDown) || 300 | (button == MouseButton::MIDDLE && !win32State.mouseMiddleDown) || 301 | (button == MouseButton::RIGHT && !win32State.mouseRightDown); 302 | } 303 | 304 | bool Platform::isMousePressed(MouseButton button) { 305 | return (button == MouseButton::LEFT && !win32State.prevMouseLeftDown && win32State.mouseLeftDown) || 306 | (button == MouseButton::RIGHT && !win32State.prevMouseRightDown && win32State.mouseRightDown) || 307 | (button == MouseButton::MIDDLE && !win32State.prevMouseMiddleDown && win32State.mouseMiddleDown); 308 | } 309 | 310 | bool Platform::isMouseReleased(MouseButton button) { 311 | return (button == MouseButton::LEFT && win32State.prevMouseLeftDown && !win32State.mouseLeftDown) || 312 | (button == MouseButton::RIGHT && win32State.prevMouseRightDown && !win32State.mouseRightDown) || 313 | (button == MouseButton::MIDDLE && win32State.prevMouseMiddleDown && !win32State.mouseMiddleDown); 314 | } 315 | 316 | #endif -------------------------------------------------------------------------------- /src/core/math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defines.hpp" 4 | #include 5 | 6 | // ----------------- VECTORS ----------------- // 7 | 8 | // Generic 9 | template 10 | struct Vector { 11 | float data[n]; 12 | 13 | constexpr float &operator[](int i) { 14 | return data[i]; 15 | } 16 | 17 | constexpr float operator[](int i) const { 18 | return data[i]; 19 | } 20 | }; 21 | 22 | template 23 | constexpr Vector operator-(const Vector &v) { 24 | Vector vec; 25 | for (int i = 0; i < n; ++i) 26 | vec[i] = -v[i]; 27 | return vec; 28 | } 29 | 30 | template 31 | constexpr Vector operator+(const Vector &v1, const Vector &v2) { 32 | Vector vec; 33 | for (int i = 0; i < n; ++i) 34 | vec[i] = v1[i] + v2[i]; 35 | return vec; 36 | } 37 | 38 | 39 | template 40 | constexpr Vector operator-(const Vector &v1, const Vector &v2) { 41 | Vector vec; 42 | for (int i = 0; i < n; ++i) 43 | vec[i] = v1[i] - v2[i]; 44 | return vec; 45 | } 46 | 47 | 48 | template 49 | constexpr Vector operator*(const Vector &v1, const Vector &v2) { 50 | Vector vec; 51 | for (int i = 0; i < n; ++i) 52 | vec[i] = v1[i] * v2[i]; 53 | return vec; 54 | } 55 | 56 | template 57 | constexpr Vector operator/(const Vector &v1, const Vector &v2) { 58 | Vector vec; 59 | for (int i = 0; i < n; ++i) 60 | vec[i] = v1[i] / v2[i]; 61 | return vec; 62 | } 63 | 64 | template 65 | constexpr Vector operator*(const Vector &v1, const float v2) { 66 | Vector vec; 67 | for (int i = 0; i < n; ++i) 68 | vec[i] = v1[i] * v2; 69 | return vec; 70 | } 71 | 72 | template 73 | constexpr Vector operator*(const float v1, const Vector &v2) { 74 | Vector vec; 75 | for (int i = 0; i < n; ++i) 76 | vec[i] = v2[i] * v1; 77 | return vec; 78 | } 79 | 80 | template 81 | constexpr Vector operator/(const Vector &v1, const float v2) { 82 | Vector vec; 83 | for (int i = 0; i < n; ++i) 84 | vec[i] = v1[i] / v2; 85 | return vec; 86 | } 87 | 88 | template 89 | constexpr Vector operator/(const float v1, const Vector &v2) { 90 | Vector vec; 91 | for (int i = 0; i < n; ++i) 92 | vec[i] = v1 / v2[i]; 93 | return vec; 94 | } 95 | 96 | template 97 | constexpr Vector operator+(const Vector &v1, const float v2) { 98 | Vector vec; 99 | for (int i = 0; i < n; ++i) 100 | vec[i] = v1[i] + v2; 101 | return vec; 102 | } 103 | 104 | template 105 | constexpr Vector operator+(const float v1, const Vector &v2) { 106 | Vector vec; 107 | for (int i = 0; i < n; ++i) 108 | vec[i] = v1 + v2[i]; 109 | return vec; 110 | } 111 | 112 | template 113 | constexpr Vector operator-(const Vector &v1, const float v2) { 114 | Vector vec; 115 | for (int i = 0; i < n; ++i) 116 | vec[i] = v1[i] - v2; 117 | return vec; 118 | } 119 | 120 | template 121 | constexpr Vector operator-(const float v1, const Vector &v2) { 122 | Vector vec; 123 | for (int i = 0; i < n; ++i) 124 | vec[i] = v1 - v2[i]; 125 | return vec; 126 | } 127 | 128 | template 129 | constexpr float dot(const Vector &v1, const Vector &v2) { 130 | float res = 0; 131 | for (int i = 0; i < n; ++i) 132 | res += v1[i] * v2[i]; 133 | return res; 134 | } 135 | 136 | template 137 | constexpr float lengthSq(const Vector &v1) { 138 | float d = 0; 139 | for (int i = 0; i < n; ++i) 140 | d += v1[i] * v1[i]; 141 | return d; 142 | } 143 | 144 | template 145 | constexpr float length(const Vector &v1) { 146 | float d = 0; 147 | for (int i = 0; i < n; ++i) 148 | d += v1[i] * v1[i]; 149 | return sqrt(d); 150 | } 151 | 152 | template 153 | constexpr float distanceSq(const Vector &v1, const Vector &v2) { 154 | return lengthSq(v2 - v1); 155 | } 156 | 157 | template 158 | constexpr float distance(const Vector &v1, const Vector &v2) { 159 | return length(v2 - v1); 160 | } 161 | 162 | template 163 | constexpr Vector normalize(const Vector &v) { 164 | return v / length(v); 165 | } 166 | 167 | // Common type 168 | typedef Vector<2> vec2; 169 | typedef Vector<3> vec3; 170 | typedef Vector<4> vec4; 171 | 172 | // Template specialization 173 | 174 | // vec2 175 | template<> 176 | struct Vector<2> { 177 | union { 178 | float data[2]; 179 | struct { 180 | float x, y; 181 | }; 182 | struct { 183 | float u, v; 184 | }; 185 | }; 186 | 187 | constexpr float &operator[](int i) { 188 | return data[i]; 189 | } 190 | 191 | constexpr float operator[](int i) const { 192 | return data[i]; 193 | } 194 | 195 | Vector<2>(float a, float b) : x(a), y(b) {} 196 | 197 | Vector<2>(float a) : x(a), y(a) {} 198 | 199 | Vector<2>() : x(0), y(0) {} 200 | }; 201 | 202 | // vec3 203 | template<> 204 | struct Vector<3> { 205 | union { 206 | float data[3]; 207 | struct { 208 | float x, y, z; 209 | }; 210 | struct { 211 | float r, g, b; 212 | }; 213 | Vector<2> xy; 214 | Vector<2> rg; 215 | }; 216 | 217 | constexpr float &operator[](int i) { 218 | return data[i]; 219 | } 220 | 221 | constexpr float operator[](int i) const { 222 | return data[i]; 223 | } 224 | 225 | Vector<3>(float a, float b, float c) : x(a), y(b), z(c) {} 226 | 227 | Vector<3>(float a) : x(a), y(a), z(a) {} 228 | 229 | Vector<3>() : x(0), y(0), z(0) {} 230 | 231 | Vector<3>(const Vector<2> &vec, float v) : x(vec.x), y(vec.y), z(v) {} 232 | 233 | }; 234 | 235 | // vec4 236 | template<> 237 | struct Vector<4> { 238 | union { 239 | float data[4]; 240 | struct { 241 | float x, y, z, w; 242 | }; 243 | struct { 244 | float r, g, b, a; 245 | }; 246 | Vector<2> xy; 247 | Vector<3> xyz; 248 | Vector<2> rg; 249 | Vector<3> rgb; 250 | }; 251 | 252 | constexpr float &operator[](int i) { 253 | return data[i]; 254 | } 255 | 256 | constexpr float operator[](int i) const { 257 | return data[i]; 258 | } 259 | 260 | Vector<4>(float a, float b, float c, float d) : x(a), y(b), z(c), w(d) {} 261 | 262 | Vector<4>(float a) : x(a), y(a), z(a), w(a) {} 263 | 264 | Vector<4>() : x(0), y(0), z(0), w(0) {} 265 | 266 | Vector<4>(const Vector<2> &vec, float v1, float v2) : x(vec.x), y(vec.y), z(v1), w(v2) {} 267 | 268 | Vector<4>(const Vector<3> &vec, float v) : x(vec.x), y(vec.y), z(vec.z), w(v) {} 269 | }; 270 | 271 | // ----------------- MATRIX ----------------- // 272 | 273 | // Generic 274 | template 275 | struct Matrix { 276 | float data[n * m]; 277 | 278 | constexpr float &operator[](const std::pair &index) { 279 | return data[index.first * n + index.second]; 280 | } 281 | 282 | constexpr float operator[](const std::pair &index) const { 283 | return data[index.first * n + index.second]; 284 | } 285 | 286 | constexpr float &operator[](const int index) { 287 | return data[index]; 288 | } 289 | 290 | constexpr float operator[](const int index) const { 291 | return data[index]; 292 | } 293 | 294 | operator std::string() const { 295 | string s = "[\n"; 296 | for (int i = 0; i < m; i++) { 297 | for (int j = 0; j < n; j++) 298 | s += std::to_string(data[j * n + i]) + " "; 299 | s += "\n"; 300 | } 301 | s += "]\n"; 302 | return s; 303 | } 304 | }; 305 | 306 | template 307 | constexpr Matrix operator*(const Matrix &m1, const Matrix &m2) { 308 | Matrix mat; 309 | for (int c = 0; c < p; c++) { 310 | for (int l = 0; l < m; l++) { 311 | mat[{c, l}] = 0; 312 | for (int i = 0; i < n; i++) 313 | mat[{c, l}] += m1[{i, l}] * m2[{c, i}]; 314 | } 315 | } 316 | return mat; 317 | } 318 | 319 | template 320 | constexpr Vector operator*(const Matrix &m1, const Vector &v) { 321 | Vector vec; 322 | for (int i = 0; i < m; i++) { 323 | vec[i] = 0; 324 | for (int j = 0; j < n; j++) 325 | vec[i] += v[j] * m1[{j, i}]; 326 | } 327 | return vec; 328 | } 329 | 330 | template 331 | constexpr Matrix operator+(const Matrix &m1, const Matrix &m2) { 332 | Matrix mat; 333 | for (int c = 0; c < n; c++) { 334 | for (int l = 0; l < m; l++) { 335 | mat[{c, l}] = m1[{c, l}] + m2[{c, l}]; 336 | } 337 | } 338 | return mat; 339 | } 340 | 341 | template 342 | constexpr Matrix operator-(const Matrix &m1, const Matrix &m2) { 343 | Matrix mat; 344 | for (int c = 0; c < n; c++) { 345 | for (int l = 0; l < m; l++) { 346 | mat[{c, l}] = m1[{c, l}] - m2[{c, l}]; 347 | } 348 | } 349 | return mat; 350 | } 351 | 352 | template 353 | constexpr Matrix operator*(const Matrix &m1, const float v) { 354 | Matrix mat; 355 | for (int c = 0; c < n; c++) { 356 | for (int l = 0; l < m; l++) { 357 | mat[{c, l}] = v * m1[{c, l}]; 358 | } 359 | } 360 | return mat; 361 | } 362 | 363 | template 364 | constexpr Matrix operator*(const float v, const Matrix &m1) { 365 | Matrix mat; 366 | for (int c = 0; c < n; c++) { 367 | for (int l = 0; l < m; l++) { 368 | mat[{c, l}] = v * m1[{c, l}];; 369 | } 370 | } 371 | return mat; 372 | } 373 | 374 | template 375 | constexpr Matrix operator/(const Matrix &m1, const float v) { 376 | Matrix mat; 377 | for (int c = 0; c < n; c++) { 378 | for (int l = 0; l < m; l++) { 379 | mat[{c, l}] = m1[{c, l}] / v; 380 | } 381 | } 382 | return mat; 383 | } 384 | 385 | template 386 | constexpr Matrix operator/(const float v, const Matrix &m1) { 387 | Matrix mat; 388 | for (int c = 0; c < n; c++) { 389 | for (int l = 0; l < m; l++) { 390 | mat[{c, l}] = v / m1[{c, l}];; 391 | } 392 | } 393 | return mat; 394 | } 395 | 396 | template 397 | constexpr Matrix identity() { 398 | Matrix mat; 399 | for (int i = 0; i < n; i++) 400 | mat[{i, i}] = 1; 401 | return mat; 402 | } 403 | 404 | template 405 | constexpr Matrix translate(const Matrix &m, const Vector &v) { 406 | Matrix mat = m; 407 | for (int i = 0; i < n - 1; i++) 408 | mat[{n - 1, i}] += v[i]; 409 | return mat; 410 | } 411 | 412 | template 413 | constexpr Matrix scale(const Matrix &m, const Vector &v) { 414 | Matrix mat = m; 415 | for (int i = 0; i < n - 1; i++) 416 | mat[{i, i}] *= v[i]; 417 | return mat; 418 | } 419 | 420 | template 421 | constexpr Matrix extract(const Matrix &m1) { 422 | Matrix mat; 423 | for (int i = 0; i < n; i++) 424 | for (int j = 0; j < n; j++) 425 | mat[{i, j}] = m1[{i, j}]; 426 | return mat; 427 | } 428 | 429 | // Common type 430 | typedef Matrix<3, 3> mat3; 431 | typedef Matrix<4, 4> mat4; 432 | 433 | // Template specialization 434 | 435 | // mat3 436 | template<> 437 | struct Matrix<3, 3> { 438 | 439 | float data[9]; 440 | 441 | constexpr float &operator[](const std::pair &index) { 442 | return data[index.first * 3 + index.second]; 443 | } 444 | 445 | constexpr float operator[](const std::pair &index) const { 446 | return data[index.first * 3 + index.second]; 447 | } 448 | 449 | constexpr float &operator[](const int index) { 450 | return data[index]; 451 | } 452 | 453 | constexpr float operator[](const int index) const { 454 | return data[index]; 455 | } 456 | 457 | operator std::string() const { 458 | string s = "[\n"; 459 | for (int i = 0; i < 3; i++) { 460 | for (int j = 0; j < 3; j++) 461 | s += std::to_string(data[j * 3 + i]) + " "; 462 | s += "\n"; 463 | } 464 | s += "]\n"; 465 | return s; 466 | } 467 | 468 | Matrix<3, 3>(const Vector<3> &c1, const Vector<3> &c2, const Vector<3> &c3) : 469 | data{c1.x, 470 | c1.y, 471 | c1.z, 472 | c2.x, 473 | c2.y, 474 | c2.z, 475 | c3.x, 476 | c3.y, 477 | c3.z} {} 478 | 479 | Matrix<3, 3>(const Matrix<2, 2> &m) : data{ 480 | m[{0, 0}], 481 | m[{0, 1}], 482 | 0, 483 | m[{1, 0}], 484 | m[{1, 1}], 485 | 0, 486 | 0, 487 | 0, 488 | 0} {} 489 | 490 | Matrix<3, 3>() : data{0} {} 491 | }; 492 | 493 | // mat4 494 | template<> 495 | struct Matrix<4, 4> { 496 | union { 497 | float data[16]; 498 | }; 499 | 500 | constexpr float &operator[](const std::pair &index) { 501 | return data[index.first * 4 + index.second]; 502 | } 503 | 504 | constexpr float operator[](const std::pair &index) const { 505 | return data[index.first * 4 + index.second]; 506 | } 507 | 508 | constexpr float &operator[](const int index) { 509 | return data[index]; 510 | } 511 | 512 | constexpr float operator[](const int index) const { 513 | return data[index]; 514 | } 515 | 516 | operator std::string() const { 517 | string s = "[\n"; 518 | for (int i = 0; i < 4; i++) { 519 | for (int j = 0; j < 4; j++) 520 | s += std::to_string(data[j * 4 + i]) + " "; 521 | s += "\n"; 522 | } 523 | s += "]\n"; 524 | return s; 525 | } 526 | 527 | Matrix<4, 4>(const Vector<4> &c1, const Vector<4> &c2, const Vector<4> &c3, const Vector<4> &c4) : data{ 528 | c1.x, 529 | c1.y, 530 | c1.z, 531 | c1.w, 532 | c2.x, 533 | c2.y, 534 | c2.z, 535 | c2.w, 536 | c3.x, 537 | c3.y, 538 | c3.z, 539 | c3.w, 540 | c4.x, 541 | c4.y, 542 | c4.z, 543 | c4.w} {} 544 | 545 | Matrix<4, 4>(const Matrix<3, 3> &m) : data{ 546 | m[{0, 0}], 547 | m[{0, 1}], 548 | m[{0, 2}], 549 | 0, 550 | m[{1, 0}], 551 | m[{1, 1}], 552 | m[{1, 2}], 553 | 0, 554 | m[{2, 0}], 555 | m[{2, 1}], 556 | m[{2, 2}], 557 | 0, 558 | 0, 559 | 0, 560 | 0, 561 | 0} {} 562 | 563 | Matrix<4, 4>() : data{0} {} 564 | }; -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "core/defines.hpp" 2 | #include "core/math.hpp" 3 | #include "platform/platform.hpp" 4 | #include "render/backend.hpp" 5 | #include "render/text.hpp" 6 | #include "node/node_system.hpp" 7 | #include "node/nodes.hpp" 8 | #include "gui/gui.hpp" 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std::chrono_literals; 16 | 17 | 18 | // --------------------------------------------------------------------- 19 | 20 | //----------------------------------------------------------------------- 21 | vec2 viewOffset = vec2(0); 22 | float zoom = 1.0f; 23 | vec2 oldMouse = vec2(0); 24 | vec2 mouse = vec2(0); 25 | bool viewPanning = false; 26 | 27 | mat4 getViewMatrix() { 28 | mat4 view = scale(identity<4>(), vec3(zoom, zoom, 1)); 29 | vec3 viewTranslate = vec3((1 - zoom) / 2.0f, (1 - zoom) / 2.0f, 0) + vec3(viewOffset, 0); 30 | view = translate(view, viewTranslate); 31 | return view; 32 | } 33 | 34 | mat4 getInvViewMatrix() { 35 | mat4 invView = scale(identity<4>(), vec3(1.0f / zoom, 1.0f / zoom, 1)); 36 | vec3 viewTranslate = vec3((1 - zoom) / 2.0f, (1 - zoom) / 2.0f, 0) + vec3(viewOffset, 0); 37 | invView = translate(invView, -viewTranslate / zoom); 38 | return invView; 39 | } 40 | 41 | int main(int argc, char const *argv[]) { 42 | // Math vector & matrix packing check 43 | static_assert(sizeof(vec2) == sizeof(GLfloat) * 2, "Can't pack vec2."); 44 | static_assert(sizeof(vec3) == sizeof(GLfloat) * 3, "Can't pack vec3."); 45 | static_assert(sizeof(vec4) == sizeof(GLfloat) * 4, "Can't pack vec4."); 46 | static_assert(sizeof(mat4) == sizeof(GLfloat) * 16, "Can't pack mat4."); 47 | static_assert(sizeof(mat3) == sizeof(GLfloat) * 9, "Can't pack mat3."); 48 | 49 | Platform &platform = Platform::getInstance("BasicBool", 1280, 720); 50 | 51 | // Opengl Setup 52 | glDisable(GL_STENCIL_TEST); 53 | 54 | glEnable(GL_DEPTH_TEST); 55 | glDepthMask(0); 56 | glDepthFunc(GL_ALWAYS); 57 | 58 | glEnable(GL_LINE_SMOOTH); 59 | glEnable(GL_BLEND); 60 | glEnable(GL_TEXTURE_2D); 61 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 62 | 63 | // Shaders 64 | Shader basicShader("basic"); 65 | 66 | // Font 67 | Font &font = Font::getDefault(); 68 | 69 | NodeManager NodeManager; 70 | 71 | unsigned int tickPerSec = 1; 72 | auto simStartTime = std::chrono::steady_clock::now(); 73 | auto tickTime = 1000000000ns / tickPerSec; 74 | bool beginUpdateDone = false; 75 | bool boxSelection = false; 76 | vec2 boxSelectionStart = vec2(0); 77 | bool unlimitedTickTime = false; 78 | int fps = 60; 79 | 80 | vec2 orpos = vec2((float) platform.getWidth() / 2.0f, (float) platform.getHeight() / 2.0f); 81 | 82 | Node *true1 = new TrueNode(orpos + vec2(-350, -100)); 83 | Node *not1 = new NotNode(orpos + vec2(-200, -100)); 84 | Node *not2 = new NotNode(orpos + vec2(-200, 100)); 85 | Node *or1 = new OrNode(orpos); 86 | Node *not3 = new NotNode(orpos + vec2(200, 0)); 87 | 88 | NodeManager.addNode(true1); 89 | NodeManager.addNode(not1); 90 | NodeManager.addNode(not2); 91 | NodeManager.addNode(or1); 92 | NodeManager.addNode(not3); 93 | 94 | // GUI 95 | gui::GUIManager guiManager; 96 | 97 | 98 | // 0 -> action on node 99 | // 1 -> replace nodes 100 | // 2 -> add nodes 101 | // 3 -> File menu 102 | // 4 -> Edit menu 103 | // 5 -> Help menu 104 | int contextMenu = -1; 105 | vec2 contextMenuPos; 106 | std::optional grabNode = {}; 107 | std::optional startConnector = {}; 108 | 109 | while (platform.processEvents()) { 110 | 111 | auto startTime = std::chrono::steady_clock::now(); 112 | 113 | int width = platform.getWidth(); 114 | int height = platform.getHeight(); 115 | // update mouse pos 116 | oldMouse = mouse; 117 | vec2 oldWorldMouse = (getInvViewMatrix() * vec4(mouse, 0, 1)).xy; 118 | 119 | mouse = vec2((float) platform.getMouseX(), (float) platform.getMouseY()); 120 | // move view 121 | if (viewPanning) { 122 | viewOffset = viewOffset + mouse - oldMouse; 123 | } 124 | int delta = platform.getMouseWheel(); 125 | // update zoom 126 | if (delta != 0) { 127 | vec2 tempMouse = (getInvViewMatrix() * vec4(mouse, 0, 1)).xy; 128 | zoom += zoom * 0.05f * ((float) delta); 129 | if (zoom < 0.1f) 130 | zoom = 0.1f; 131 | else if (zoom >= 10) 132 | zoom = 10; 133 | vec2 newWorldMouse = (getInvViewMatrix() * vec4(mouse, 0, 1)).xy; 134 | viewOffset = viewOffset + (newWorldMouse - tempMouse) * zoom; 135 | } 136 | 137 | mat4 view = getViewMatrix(); 138 | mat4 invView = getInvViewMatrix(); 139 | vec2 worldMouse = (invView * vec4(mouse, 0, 1)).xy; 140 | 141 | // Projection matrix 142 | mat4 pmat(vec4(2.0f / (float) width, 0, 0, 0), vec4(0, -2.0f / (float) height, 0, 0), vec4(0, 0, 1, 0), 143 | vec4(-1, 1, 0, 1)); 144 | 145 | if (platform.isMousePressed(MouseButton::LEFT) && contextMenu == -1) { 146 | startConnector = NodeManager.getConnectorAt(worldMouse); 147 | 148 | if (!startConnector) { 149 | grabNode = NodeManager.getNodeAt(worldMouse); 150 | if (grabNode) { 151 | if (!NodeManager.nodeIsSelected(grabNode.value())) { 152 | NodeManager.deselectAll(); 153 | } 154 | NodeManager.selectNode(grabNode.value()); 155 | } else { 156 | NodeManager.deselectAll(); 157 | boxSelection = true; 158 | boxSelectionStart = worldMouse; 159 | } 160 | } 161 | } else if (platform.isMouseReleased(MouseButton::LEFT)) { 162 | grabNode = {}; 163 | if (startConnector) { 164 | auto endConnector = NodeManager.getConnectorAt(worldMouse); 165 | if (endConnector) 166 | NodeManager.connect(startConnector.value(), endConnector.value()); 167 | startConnector = {}; 168 | } 169 | if (boxSelection) { 170 | boxSelection = false; 171 | vec2 boxSelectionEnd = worldMouse; 172 | vec2 boxStart = vec2(std::min(boxSelectionStart.x, boxSelectionEnd.x), 173 | std::min(boxSelectionStart.y, boxSelectionEnd.y)); 174 | vec2 boxEnd = vec2(std::max(boxSelectionStart.x, boxSelectionEnd.x), 175 | std::max(boxSelectionStart.y, boxSelectionEnd.y)); 176 | NodeManager.boxSelect(boxStart, boxEnd); 177 | } 178 | } 179 | 180 | if (platform.isMousePressed(MouseButton::RIGHT)) { 181 | auto c = NodeManager.getConnectorAt(worldMouse); 182 | if (c) { 183 | NodeManager.disconnectAll(c.value()); 184 | } else { 185 | auto node = NodeManager.getNodeAt(worldMouse); 186 | if (node) { 187 | if(!NodeManager.nodeIsSelected(node.value())){ 188 | NodeManager.deselectAll(); 189 | NodeManager.selectNode(node.value()); 190 | } 191 | contextMenu = 0; 192 | contextMenuPos = mouse; 193 | } else { 194 | contextMenu = 2; 195 | contextMenuPos = mouse; 196 | } 197 | } 198 | } 199 | 200 | if (platform.isMousePressed(MouseButton::MIDDLE)) { 201 | viewPanning = true; 202 | } 203 | if (platform.isMouseReleased(MouseButton::MIDDLE)) { 204 | viewPanning = false; 205 | } 206 | 207 | if (grabNode) { 208 | //grabNode.value()->pos = worldMouse + grabOffset; 209 | NodeManager.moveSelectedNodes(worldMouse - oldWorldMouse); 210 | } 211 | 212 | glViewport(0, 0, width, height); 213 | 214 | // Begin drawing 215 | 216 | // --- Background --- // 217 | float bgVal = 35.0f / 255.0f; 218 | glClearColor(bgVal, bgVal, bgVal, 1.0); 219 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 220 | // --- Grid --- // 221 | // TODO : improve this 222 | int nx = width / 16; 223 | int ny = height / 16; 224 | std::vector data; 225 | data.reserve(nx + ny); 226 | for (int i = 0; i <= nx; ++i) { 227 | data.emplace_back(vec2(16.0f * ((float) i), 0)); 228 | data.emplace_back(vec2(16.0f * ((float) i), (float) height)); 229 | } 230 | for (int i = 0; i <= ny; ++i) { 231 | data.emplace_back(vec2(0, 16.0f * ((float) i))); 232 | data.emplace_back(vec2((float) width, 16.0f * ((float) i))); 233 | } 234 | VertexArray vao; 235 | VertexBuffer vbo(&data[0], 4 * (nx + ny + 2) * sizeof(float)); 236 | VertexBufferLayout layout; 237 | layout.push(2); 238 | vao.addBuffer(vbo, layout); 239 | 240 | basicShader.use(); 241 | basicShader.setMat4("projection", pmat); 242 | basicShader.setMat4("view", identity<4>()); 243 | basicShader.setVec3("color", vec3(77.0f / 255.0f)); 244 | 245 | vao.bind(); 246 | glDrawArrays(GL_LINES, 0, 2 * (nx + ny + 2)); 247 | 248 | if (startConnector) { 249 | NodeManager.drawTempLink(startConnector.value(), worldMouse, pmat, view); 250 | } 251 | 252 | // ---- simulation ---- 253 | 254 | 255 | 256 | NodeManager.render(pmat, view, (invView * vec4(0, 0, 0, 1)).xy, 257 | (invView * vec4((float) width, (float) height, 0, 1)).xy); 258 | 259 | if (boxSelection) { 260 | NodeManager.drawBoxSelection(boxSelectionStart, worldMouse, pmat, view); 261 | } 262 | 263 | switch (contextMenu) { 264 | case 0: 265 | { 266 | static std::vector list = {"Remove", "Replace"}; 267 | int index = guiManager.dropDownMenu(list, contextMenuPos, pmat, {"Action menu"}); 268 | if (platform.isMousePressed(MouseButton::LEFT)) { 269 | contextMenu = -1; 270 | vec2 pos = (invView*vec4(contextMenuPos, 0, 1)).xy; 271 | switch (index) { 272 | case 0: 273 | NodeManager.removeSelected(); 274 | break; 275 | case 1: 276 | contextMenu = 1; 277 | break; 278 | } 279 | } 280 | } 281 | break; 282 | case 1:{ 283 | static std::vector list = {"TRUE", "NOT", "OR", "AND", "XOR"}; 284 | int index = guiManager.dropDownMenu(list, contextMenuPos, pmat, {"Replace nodes menu"}); 285 | if (platform.isMousePressed(MouseButton::LEFT)) { 286 | contextMenu = -1; 287 | switch (index) { 288 | case 0: 289 | NodeManager.replaceSelected([](vec2 pos){return new TrueNode(pos);}); 290 | break; 291 | case 1: 292 | NodeManager.replaceSelected([](vec2 pos){return new NotNode(pos);}); 293 | break; 294 | case 2: 295 | NodeManager.replaceSelected([](vec2 pos){return new OrNode(pos);}); 296 | break; 297 | case 3: 298 | NodeManager.replaceSelected([](vec2 pos){return new AndNode(pos);}); 299 | break; 300 | case 4: 301 | NodeManager.replaceSelected([](vec2 pos){return new XorNode(pos);}); 302 | break; 303 | } 304 | } 305 | } 306 | break; 307 | case 2 : { 308 | static std::vector list = {"TRUE", "NOT", "OR", "AND", "XOR", "PERFORMANCE TEST (40k)"}; 309 | int index = guiManager.dropDownMenu(list, contextMenuPos, pmat, {"Add node menu"}); 310 | if (platform.isMousePressed(MouseButton::LEFT)) { 311 | contextMenu = -1; 312 | vec2 pos = (invView*vec4(contextMenuPos, 0, 1)).xy; 313 | switch (index) { 314 | case 0: 315 | NodeManager.addNode(new TrueNode(pos)); 316 | break; 317 | case 1: 318 | NodeManager.addNode(new NotNode(pos)); 319 | break; 320 | case 2: 321 | NodeManager.addNode(new OrNode(pos)); 322 | break; 323 | case 3: 324 | NodeManager.addNode(new AndNode(pos)); 325 | break; 326 | case 4: 327 | NodeManager.addNode(new XorNode(pos)); 328 | break; 329 | case 5: 330 | for (int i = 0; i < 10000; i++) 331 | { 332 | 333 | Node *n1 = new TrueNode(25.0f * vec2(std::rand() % platform.getWidth(), std::rand() % platform.getHeight())); 334 | Node *n2 = new NotNode(25.0f * vec2(std::rand() % platform.getWidth(), std::rand() % platform.getHeight())); 335 | Link *n1n2 = new Link(n1->getOutput("out"), n2->getInput("in")); 336 | NodeManager.addLink(n1n2); 337 | NodeManager.addNode(n1); 338 | NodeManager.addNode(n2); 339 | 340 | Node *n3 = new NotNode(25.0f * vec2(std::rand() % platform.getWidth(), std::rand() % platform.getHeight())); 341 | Node *n4 = new NotNode(25.0f * vec2(std::rand() % platform.getWidth(), std::rand() % platform.getHeight())); 342 | Link *n3n4 = new Link(n3->getOutput("out"), n4->getInput("in")); 343 | Link *n1n3 = new Link(n1->getOutput("out"), n3->getInput("in")); 344 | NodeManager.addLink(n3n4); 345 | NodeManager.addLink(n1n3); 346 | NodeManager.addNode(n3); 347 | NodeManager.addNode(n4); 348 | } 349 | break; 350 | } 351 | } 352 | } break; 353 | // File menu 354 | case 3 : { 355 | static std::vector list = {"New", "Save", "Save As", "Exit"}; 356 | int index = guiManager.dropDownMenu(list, contextMenuPos, pmat, {"File menu"}); 357 | if (platform.isMousePressed(MouseButton::LEFT)) { 358 | contextMenu = -1; 359 | } 360 | } break; 361 | // Edit menu 362 | case 4 : { 363 | static std::vector list = {"Settings", "Cut", "Copy", "Delete"}; 364 | int index = guiManager.dropDownMenu(list, contextMenuPos, pmat, {"Edit menu"}); 365 | if (platform.isMousePressed(MouseButton::LEFT)) { 366 | contextMenu = -1; 367 | } 368 | } break; 369 | // Help menu 370 | case 5 : { 371 | static std::vector list = {"Check for Updates", "About"}; 372 | int index = guiManager.dropDownMenu(list, contextMenuPos, pmat, {"Help menu"}); 373 | if (platform.isMousePressed(MouseButton::LEFT)) { 374 | contextMenu = -1; 375 | } 376 | } break; 377 | } 378 | 379 | static std::vector fileMenu = {"File", "Edit", "Help"}; 380 | auto info = guiManager.fileMenu(fileMenu, pmat); 381 | switch (info.first) { 382 | case 0: 383 | contextMenu = 3; 384 | contextMenuPos = info.second; 385 | break; 386 | case 1: 387 | contextMenu = 4; 388 | contextMenuPos = info.second; 389 | break; 390 | case 2: 391 | contextMenu = 5; 392 | contextMenuPos = info.second; 393 | break; 394 | } 395 | 396 | 397 | auto endTime = std::chrono::steady_clock::now(); 398 | auto workTime = endTime-startTime; 399 | static auto targetTime = std::chrono::duration>{1}; // 60 Hz 400 | 401 | // TODO : improve this busy update loop 402 | while (std::chrono::steady_clock::now()-startTime < targetTime){ 403 | // start cycle 404 | if (!unlimitedTickTime) { 405 | if (!beginUpdateDone) { 406 | NodeManager.beginUpdate(); 407 | beginUpdateDone = true; 408 | simStartTime = std::chrono::steady_clock::now(); 409 | } 410 | auto time = std::chrono::steady_clock::now(); 411 | auto duration = std::chrono::duration_cast(time - simStartTime); 412 | auto t = (float) ((double) duration.count() / (double) tickTime.count()); 413 | if (t > 1) 414 | t = 1; 415 | // update animation 416 | NodeManager.setProgress(t); 417 | // end cycle 418 | if (duration >= tickTime) { 419 | NodeManager.endUpdate(); 420 | beginUpdateDone = false; 421 | } 422 | } else { 423 | NodeManager.beginUpdate(); 424 | NodeManager.setProgress(1); 425 | NodeManager.endUpdate(); 426 | } 427 | } 428 | 429 | string text = std::to_string(fps); 430 | font.text("fps : " + text, vec2(0, height-font.getHeight("fps : " + text, 20)), 20, vec3(1)); 431 | font.render(pmat); 432 | 433 | // End drawing 434 | platform.swapBuffers(); 435 | 436 | // FPS info 437 | auto afterSleep = std::chrono::steady_clock::now(); 438 | long long finalElapsedTime = std::chrono::duration_cast(afterSleep-startTime).count(); 439 | fps = (int) (1000000.0 / (double) finalElapsedTime); 440 | } 441 | return 0; 442 | } 443 | -------------------------------------------------------------------------------- /src/node/node_system.cpp: -------------------------------------------------------------------------------- 1 | #include "node_system.hpp" 2 | #include "render/backend.hpp" 3 | #include 4 | #include 5 | 6 | 7 | // Connector 8 | Connector::Connector(string name, Node *parent, bool isInput) : name(name), parent(parent), isInput(isInput) {} 9 | 10 | // Links 11 | Link::Link(Connector *in, Connector *out) : input(in), output(out) { 12 | in->links.push_back(this); 13 | out->links.push_back(this); 14 | fetchNextState(); 15 | } 16 | 17 | Link::~Link() { 18 | input->links.erase(std::remove(input->links.begin(), input->links.end(), this), input->links.end()); 19 | output->links.erase(std::remove(output->links.begin(), output->links.end(), this), output->links.end()); 20 | output->state = false; 21 | } 22 | 23 | void Link::fetchNextState() { 24 | nextState = input->state; 25 | } 26 | 27 | void Link::propagate() { 28 | output->state = nextState; 29 | state = nextState; // current state 30 | } 31 | 32 | void Link::reset() { 33 | state = false; 34 | fetchNextState(); 35 | } 36 | 37 | // Node 38 | Node::Node(string name, vec2 pos) : name(name), pos(pos) {} 39 | 40 | Node::~Node() { 41 | for (Connector *c: inputs) 42 | delete c; 43 | for (Connector *c: outputs) 44 | delete c; 45 | } 46 | 47 | bool Node::addOutput(string name) { 48 | auto i = std::find_if(outputs.begin(), outputs.end(), [&name](Connector *c) { return c->name == name; }); 49 | if (i == outputs.end()) { 50 | outputs.push_back(new Connector(name, this, false)); 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | bool Node::addInput(string name) { 57 | auto i = std::find_if(inputs.begin(), inputs.end(), [&name](Connector *c) { return c->name == name; }); 58 | if (i == inputs.end()) { 59 | inputs.push_back(new Connector(name, this, true)); 60 | return true; 61 | } 62 | return false; 63 | } 64 | 65 | Connector *Node::getInput(string name) { 66 | auto i = std::find_if(inputs.begin(), inputs.end(), [&name](Connector *c) { return c->name == name; }); 67 | if (i != inputs.end()) 68 | return inputs[std::distance(inputs.begin(), i)]; 69 | return nullptr; 70 | } 71 | 72 | Connector *Node::getOutput(string name) { 73 | auto i = std::find_if(outputs.begin(), outputs.end(), [&name](Connector *c) { return c->name == name; }); 74 | if (i != outputs.end()) 75 | return outputs[std::distance(outputs.begin(), i)]; 76 | return nullptr; 77 | } 78 | 79 | // Node manager 80 | 81 | NodeManager::NodeManager() : nodeShader("node"), 82 | nodeShadowShader("node_shadow"), 83 | nodeConnectorShader("node_connector"), 84 | linkShader("link"), 85 | basicShader("basic"), 86 | boxSelectShader("box_select") { 87 | setLookAndFeel(std::make_shared()); 88 | } 89 | 90 | NodeManager::~NodeManager() { 91 | // LINKS FIRST 92 | for (Link *link: links) 93 | delete link; 94 | for (Node *node: nodes) 95 | delete node; 96 | } 97 | 98 | void NodeManager::reset() { 99 | for (Node *node: nodes) 100 | node->reset(); 101 | for (Link *link: links) 102 | link->reset(); 103 | } 104 | 105 | void NodeManager::addNode(Node *node) { 106 | nodes.push_back(node); 107 | doNodeLayout(node); 108 | } 109 | 110 | void NodeManager::addLink(Link *link) { 111 | links.push_back(link); 112 | } 113 | 114 | void NodeManager::render(const mat4 &pmat, const mat4 &view, const vec2 &camstart, const vec2 &camend) { 115 | cullNodes(camstart, camend); 116 | cullLinks(camstart, camend); 117 | renderLinks(pmat, view); 118 | renderShadows(pmat, view); 119 | //renderNodes(pmat, view); 120 | 121 | glDepthMask(1); // enable depth buffer write 122 | glColorMask(1,1,1,1); // write to the color buffer 123 | glDepthFunc(GL_ALWAYS); 124 | renderNodes(pmat, view); 125 | 126 | // Render Text+connector in batch while culling their pixels with the depth buffer 127 | glDepthMask(0); 128 | glColorMask(1,1,1,1); 129 | glDepthFunc(GL_EQUAL); // texts & connectors must be on the same level as their parent node. 130 | renderConnectors(pmat, view); 131 | renderText(pmat, view); 132 | glDepthFunc(GL_ALWAYS); 133 | 134 | visibleNodes.clear(); 135 | visibleLinks.clear(); 136 | } 137 | 138 | void NodeManager::beginUpdate() { 139 | #if MULTI_CORES 140 | #pragma omp parallel for 141 | for (int i = 0; i < nodes.size(); i++) 142 | { 143 | nodes[i]->update(); 144 | } 145 | #pragma omp parallel for 146 | for (int i = 0; i < links.size(); i++) 147 | { 148 | links[i]->fetchNextState(); 149 | } 150 | #else 151 | for (Node *node: nodes) 152 | node->update(); 153 | for (Link *li: links) 154 | li->fetchNextState(); 155 | #endif 156 | } 157 | 158 | void NodeManager::endUpdate() { 159 | #if MULTI_CORES 160 | #pragma omp parallel for 161 | for (int i = 0; i < links.size(); i++) 162 | { 163 | links[i]->propagate(); 164 | } 165 | #else 166 | for (Link *li: links) 167 | li->propagate(); 168 | #endif 169 | } 170 | 171 | void NodeManager::setProgress(float t) { 172 | #if MULTI_CORES 173 | #pragma omp parallel for 174 | for (int i = 0; i < links.size(); i++) 175 | { 176 | links[i]->t = t; 177 | } 178 | #else 179 | for (Link *li: links) 180 | li->t = t; 181 | #endif 182 | } 183 | 184 | void NodeManager::removeSelected() { 185 | for (Node *node: selectedNodes) { 186 | removeNode(node); 187 | } 188 | selectedNodes.clear(); 189 | } 190 | 191 | std::optional NodeManager::getNodeAt(vec2 mouse) { 192 | for (int i = nodes.size() - 1; i >= 0; --i) { 193 | Node *node = nodes[i]; 194 | vec2 size = node->size; 195 | vec2 pos = node->pos; 196 | if (mouse.x >= pos.x && mouse.x <= pos.x + size.x && mouse.y >= pos.y && mouse.y <= pos.y + size.y) 197 | return node; 198 | } 199 | return {}; 200 | } 201 | 202 | std::optional NodeManager::getConnectorAt(vec2 mouse) { 203 | for (int i = nodes.size() - 1; i >= 0; --i) { 204 | const Node *node = nodes[i]; 205 | vec2 size = node->size; 206 | vec2 pos = node->pos; 207 | if (mouse.x >= pos.x && mouse.x <= pos.x + size.x && mouse.y >= pos.y && mouse.y <= pos.y + size.y) { 208 | // check inputs 209 | for (Connector *c: node->inputs) { 210 | vec2 cpos = node->pos + c->pos; 211 | float r = nodeStyle->connectorRadius; 212 | if (distanceSq(mouse, cpos) <= r * r) 213 | return c; 214 | } 215 | 216 | // check outputs 217 | for (Connector *c: node->outputs) { 218 | vec2 cpos = node->pos + c->pos; 219 | float r = nodeStyle->connectorRadius; 220 | if (distanceSq(mouse, cpos) <= r * r) 221 | return c; 222 | } 223 | } 224 | } 225 | return {}; 226 | } 227 | 228 | void NodeManager::replaceSelected(std::function build){ 229 | for(Node* node : selectedNodes) 230 | replaceNode(node, build); 231 | } 232 | 233 | void NodeManager::replaceNode(Node* node, std::function build){ 234 | Node* rnode = build(node->pos); 235 | // ---- Inputs ---- 236 | for(int i=0; i < node->inputs.size() && i < rnode->inputs.size(); i++){ 237 | Connector* c = node->inputs[i]; 238 | // copy links 239 | for(Link* l : c->links){ 240 | Link* nl = new Link(l->input, rnode->inputs[i]); 241 | addLink(nl); 242 | nl->nextState = l->nextState; 243 | } 244 | } 245 | // ---- Outputs ---- 246 | for(int i=0; i < node->outputs.size() && i < rnode->outputs.size(); i++){ 247 | Connector* c = node->outputs[i]; 248 | // copy links 249 | for(Link* l : c->links){ 250 | Link* nl = new Link(rnode->outputs[i], l->output); 251 | addLink(nl); 252 | } 253 | } 254 | removeNode(node); 255 | addNode(rnode); 256 | } 257 | 258 | 259 | void NodeManager::moveSelectedNodes(vec2 offset) { 260 | for (Node *node: selectedNodes) { 261 | node->pos = node->pos + offset; 262 | } 263 | } 264 | 265 | void NodeManager::selectNode(Node *node) { 266 | node->selected = true; 267 | if (std::find(selectedNodes.begin(), selectedNodes.end(), node) == selectedNodes.end()) 268 | selectedNodes.push_back(node); 269 | } 270 | 271 | void NodeManager::boxSelect(vec2 start, vec2 end) { 272 | vec2 boxStart = vec2(std::min(start.x, end.x), std::min(start.y, end.y)); 273 | vec2 boxEnd = vec2(std::max(start.x, end.x), std::max(start.y, end.y)); 274 | 275 | for (Node *node: nodes) { 276 | vec2 pos = node->pos; 277 | vec2 size = node->size; 278 | if (pos.x + size.x >= boxStart.x && pos.x <= boxEnd.x && pos.y + size.y >= boxStart.y && pos.y <= boxEnd.y) 279 | selectNode(node); 280 | } 281 | } 282 | 283 | void NodeManager::deselectAll() { 284 | for (Node *node: nodes) { 285 | node->selected = false; 286 | } 287 | selectedNodes.clear(); 288 | } 289 | 290 | void NodeManager::deselectNode(Node *node) { 291 | node->selected = false; 292 | selectedNodes.erase(std::remove(selectedNodes.begin(), selectedNodes.end(), node), selectedNodes.end()); 293 | } 294 | 295 | bool NodeManager::nodeIsSelected(Node *node) { 296 | return node->selected; 297 | } 298 | 299 | 300 | void NodeManager::drawTempLink(Connector *c, vec2 mouse, const mat4 &pmat, const mat4 &view) { 301 | vec2 start; 302 | vec2 end; 303 | if (c->isInput) { 304 | start = mouse; 305 | end = c->parent->pos + c->pos; 306 | } else { 307 | start = c->parent->pos + c->pos; 308 | end = mouse; 309 | } 310 | float sv[] = { 311 | start.x, start.y, 1.0f, 0.0f, 312 | end.x, end.y, 1.0f, 0.0f}; 313 | VertexArray svao; 314 | VertexBuffer svbo(sv, sizeof(sv)); 315 | VertexBufferLayout slayout; 316 | slayout.push(2); 317 | slayout.push(1); 318 | slayout.push(1); 319 | svao.addBuffer(svbo, slayout); 320 | linkShader.use(); 321 | linkShader.setMat4("projection", pmat); 322 | linkShader.setMat4("view", view); 323 | linkShader.setFloat("width", nodeStyle->connectorRadius / 2.0f); 324 | linkShader.setVec3("falseColor", nodeStyle->falseColor); 325 | linkShader.setVec3("trueColor", nodeStyle->trueColor); 326 | glDrawArrays(GL_LINES, 0, 2); 327 | } 328 | 329 | void NodeManager::drawBoxSelection(vec2 start, vec2 end, const mat4 &pmat, const mat4 &view) { 330 | vec2 boxStart = vec2(std::min(start.x, end.x), std::min(start.y, end.y)); 331 | vec2 boxEnd = vec2(std::max(start.x, end.x), std::max(start.y, end.y)); 332 | 333 | float sv[] = {boxStart.x, boxStart.y, boxEnd.x - boxStart.x, boxEnd.y - boxStart.y}; 334 | VertexArray vao; 335 | VertexBuffer vbo(sv, sizeof(sv)); 336 | VertexBufferLayout layout; 337 | layout.push(4); // rect 338 | vao.addBuffer(vbo, layout); 339 | boxSelectShader.use(); 340 | boxSelectShader.setMat4("projection", pmat); 341 | boxSelectShader.setMat4("view", view); 342 | boxSelectShader.setVec3("selectedColor", nodeStyle->nodeSelectedColor); 343 | glDrawArrays(GL_POINTS, 0, 1); 344 | } 345 | 346 | bool NodeManager::connect(Connector *c1, Connector *c2) { 347 | if (!c1->isInput && c2->isInput) { 348 | if (!(c2->links.empty())) { 349 | Link *li = c2->links[0]; 350 | links.erase(std::remove(links.begin(), links.end(), li), links.end()); 351 | delete li; 352 | } 353 | links.push_back(new Link(c1, c2)); 354 | return true; 355 | } else if (c1->isInput && !c2->isInput) { 356 | 357 | if (!(c1->links.empty())) { 358 | Link *li = c1->links[0]; 359 | links.erase(std::remove(links.begin(), links.end(), li), links.end()); 360 | delete li; 361 | } 362 | links.push_back(new Link(c2, c1)); 363 | return true; 364 | } 365 | return false; 366 | } 367 | 368 | void NodeManager::disconnectAll(Connector *c) { 369 | std::vector temp = c->links; 370 | for (Link *li: temp) { 371 | links.erase(std::remove(links.begin(), links.end(), li), links.end()); 372 | delete li; 373 | } 374 | } 375 | 376 | void NodeManager::removeNode(Node *node) { 377 | // remove & delete links 378 | for (Connector *c: node->inputs) 379 | disconnectAll(c); 380 | for (Connector *c: node->outputs) 381 | disconnectAll(c); 382 | // remove & delete node 383 | nodes.erase(std::remove(nodes.begin(), nodes.end(), node), nodes.end()); 384 | delete node; 385 | } 386 | 387 | void NodeManager::cullNodes(const vec2 &camstart, const vec2 &camend) { 388 | for (Node *node: nodes) { 389 | vec2 pos = node->pos - vec2(nodeStyle->shadowSize); 390 | vec2 size = node->size + vec2(2 * nodeStyle->shadowSize); 391 | if (pos.x + size.x >= camstart.x && pos.x <= camend.x && pos.y + size.y >= camstart.y && pos.y <= camend.y) 392 | visibleNodes.push_back(node); 393 | } 394 | } 395 | 396 | void NodeManager::cullLinks(const vec2 &camstart, const vec2 &camend) { 397 | for (Link *link: links) { 398 | vec2 start = link->input->parent->pos + link->input->pos; 399 | vec2 end = link->output->parent->pos + link->output->pos; 400 | // construct bounding box 401 | vec2 boxstart = vec2(std::min(start.x, end.x), std::min(start.y, end.y)); 402 | vec2 boxeend = vec2(std::max(start.x, end.x), std::max(start.y, end.y)); 403 | if (boxstart.x <= camend.x && boxstart.y <= camend.y && boxeend.x >= camstart.x && boxeend.y >= camstart.y) 404 | visibleLinks.push_back(link); 405 | } 406 | } 407 | 408 | void NodeManager::renderShadows(const mat4 &pmat, const mat4 &view) { 409 | if (visibleNodes.empty()) 410 | return; 411 | std::vector vertices; 412 | vertices.reserve(visibleNodes.size() * 5); 413 | 414 | for (const Node *node: visibleNodes) { 415 | vec2 pos = node->pos - vec2(nodeStyle->shadowSize); 416 | vec2 size = node->size + vec2(nodeStyle->shadowSize * 2); 417 | vertices.insert(vertices.end(), {pos.x, pos.y, size.x, size.y, node->selected ? 1.0f : 0.0f}); 418 | } 419 | VertexArray vao; 420 | VertexBuffer vbo(&vertices[0], sizeof(float) * vertices.size()); 421 | VertexBufferLayout layout; 422 | layout.push(4); // rect 423 | layout.push(1); // selected 424 | vao.addBuffer(vbo, layout); 425 | nodeShadowShader.use(); 426 | nodeShadowShader.setMat4("projection", pmat); 427 | nodeShadowShader.setMat4("view", view); 428 | nodeShadowShader.setFloat("smoothing", nodeStyle->shadowSize); 429 | nodeShadowShader.setFloat("radius", nodeStyle->nodeRadius); 430 | nodeShadowShader.setVec3("selectedColor", nodeStyle->nodeSelectedColor); 431 | glDrawArrays(GL_POINTS, 0, visibleNodes.size()); 432 | } 433 | 434 | void NodeManager::renderNodes(const mat4 &pmat, const mat4 &view) { 435 | if (visibleNodes.empty()) 436 | return; 437 | 438 | std::vector vertices; 439 | vertices.reserve(visibleNodes.size() * 6); 440 | 441 | for (int i=0; i < visibleNodes.size(); i++) { 442 | const Node *node = visibleNodes[i]; 443 | vec2 pos = node->pos; 444 | vec2 size = node->size; 445 | float headerHeight = node->headerSize.y; 446 | vertices.insert(vertices.end(), { 447 | pos.x, pos.y, size.x, size.y, 448 | headerHeight, 449 | node->selected ? 1.0f : 0.0f, 450 | 1.0f - (float) i/ (float) visibleNodes.size()}); 451 | } 452 | VertexArray vao; 453 | VertexBuffer vbo(&vertices[0], sizeof(float) * vertices.size()); 454 | VertexBufferLayout layout; 455 | layout.push(4); // rect 456 | layout.push(1); // headerHeight 457 | layout.push(1); // selected 458 | layout.push(1); // depth 459 | vao.addBuffer(vbo, layout); 460 | nodeShader.use(); 461 | nodeShader.setMat4("projection", pmat); 462 | nodeShader.setMat4("view", view); 463 | nodeShader.setFloat("radius", nodeStyle->nodeRadius); 464 | nodeShader.setVec3("headerColor", nodeStyle->headerColor); 465 | nodeShader.setVec3("bodyColor", nodeStyle->bodyColor); 466 | nodeShader.setVec3("selectedColor", nodeStyle->nodeSelectedColor); 467 | glDrawArrays(GL_POINTS, 0, visibleNodes.size()); 468 | } 469 | 470 | void NodeManager::renderText(const mat4 &pmat, const mat4 &view) { 471 | if (visibleNodes.empty()) 472 | return; 473 | for (int i=0; i < visibleNodes.size(); i++) { 474 | const Node *node = visibleNodes[i]; 475 | nodeStyle->font.text(node->name, vec3(node->pos + node->textPos, 1.0f - (float) i / (float) visibleNodes.size()), nodeStyle->headerTextSize, 476 | nodeStyle->headerTextColor); 477 | for (const Connector *c: node->inputs) { 478 | nodeStyle->font.text(c->name, vec3(c->parent->pos + c->textPos, 1.0f - (float) i / (float) visibleNodes.size()), nodeStyle->connectorTextSize, 479 | nodeStyle->connectorTextColor); 480 | } 481 | for (const Connector *c: node->outputs) { 482 | nodeStyle->font.text(c->name, vec3(c->parent->pos + c->textPos, 1.0f - (float) i / (float) visibleNodes.size()), nodeStyle->connectorTextSize, 483 | nodeStyle->connectorTextColor); 484 | } 485 | } 486 | nodeStyle->font.render(pmat, view); 487 | } 488 | 489 | void NodeManager::renderConnectors(const mat4 &pmat, const mat4 &view) { 490 | if (visibleNodes.empty()) 491 | return; 492 | 493 | std::vector vertices; 494 | int n = 0; 495 | for (int i=0; i < visibleNodes.size(); i++) { 496 | const Node *node = visibleNodes[i]; 497 | 498 | for (const Connector *c: node->inputs) { 499 | vec2 pos = c->parent->pos + c->pos; 500 | float state = c->state ? 1.0f : 0.0f; 501 | vertices.insert(vertices.end(), {pos.x, pos.y, nodeStyle->connectorRadius, state, 1.0f - (float) i / (float) visibleNodes.size()}); 502 | n++; 503 | } 504 | for (const Connector *c: node->outputs) { 505 | vec2 pos = c->parent->pos + c->pos; 506 | float state = c->state ? 1.0f : 0.0f; 507 | vertices.insert(vertices.end(), {pos.x, pos.y, nodeStyle->connectorRadius, state, 1.0f - (float) i / (float) visibleNodes.size()}); 508 | n++; 509 | } 510 | } 511 | VertexArray vao; 512 | VertexBuffer vbo(&vertices[0], sizeof(float) * vertices.size()); 513 | VertexBufferLayout layout; 514 | layout.push(4); // (x,y,r,state) 515 | layout.push(1); // depth 516 | vao.addBuffer(vbo, layout); 517 | nodeConnectorShader.use(); 518 | nodeConnectorShader.setMat4("projection", pmat); 519 | nodeConnectorShader.setMat4("view", view); 520 | nodeConnectorShader.setVec3("falseColor", nodeStyle->falseColor); 521 | nodeConnectorShader.setVec3("trueColor", nodeStyle->trueColor); 522 | glDrawArrays(GL_POINTS, 0, n); 523 | } 524 | 525 | void NodeManager::renderLinks(const mat4 &pmat, const mat4 &view) { 526 | if (visibleLinks.empty()) 527 | return; 528 | std::vector vertices; 529 | vertices.reserve(visibleLinks.size() * 8); 530 | 531 | for (const Link *li: visibleLinks) { 532 | vec2 inPos = li->input->parent->pos + li->input->pos; 533 | vec2 outPos = li->output->parent->pos + li->output->pos; 534 | 535 | float startTrue = li->state == false && li->nextState == true || li->state == true && li->nextState == true; 536 | float t = li->state == false && li->nextState == true || li->state == true && li->nextState == false ? li->t 537 | : 1.0; 538 | 539 | vertices.insert(vertices.end(), {inPos.x, inPos.y, t, startTrue, 540 | outPos.x, outPos.y, t, startTrue}); 541 | } 542 | VertexArray vao; 543 | VertexBuffer vbo(&vertices[0], sizeof(float) * vertices.size()); 544 | VertexBufferLayout layout; 545 | layout.push(2); // pos 546 | layout.push(1); // t 547 | layout.push(1); // startTrue 548 | vao.addBuffer(vbo, layout); 549 | 550 | linkShader.use(); 551 | linkShader.setMat4("projection", pmat); 552 | linkShader.setMat4("view", view); 553 | linkShader.setFloat("width", nodeStyle->connectorRadius / 2.0f); 554 | linkShader.setVec3("falseColor", nodeStyle->falseColor); 555 | linkShader.setVec3("trueColor", nodeStyle->trueColor); 556 | glDrawArrays(GL_LINES, 0, 2 * visibleLinks.size()); 557 | } 558 | 559 | void NodeManager::doNodeLayout(Node *node) { 560 | const float headerHeight = nodeStyle->font.getHeight(node->name, nodeStyle->headerTextSize) + nodeStyle->nodeRadius; 561 | const float headerWidth = 562 | nodeStyle->font.getWidth(node->name, nodeStyle->headerTextSize) + 2 * nodeStyle->nodeRadius; 563 | node->textPos = vec2(nodeStyle->nodeRadius, nodeStyle->nodeRadius); 564 | vec2 inputsSize = getConnectorSize(node, false); 565 | vec2 outputsSize = getConnectorSize(node, true); 566 | node->size = vec2(std::max(headerWidth, inputsSize.x + nodeStyle->inOutDist + outputsSize.x), 567 | headerHeight + std::max(inputsSize.y, outputsSize.y)); 568 | 569 | node->headerSize = vec2(node->size.x, headerHeight); 570 | doConnectorLayout(node, false); 571 | doConnectorLayout(node, true); 572 | } 573 | 574 | vec2 NodeManager::getConnectorSize(Node *node, bool output) { 575 | vec2 connectorSize = vec2(0); 576 | std::vector &connectors = output ? node->outputs : node->inputs; 577 | for (Connector *c: connectors) { 578 | connectorSize.y += nodeStyle->connectorMargin.y * 2 + std::max(nodeStyle->connectorRadius * 2, 579 | nodeStyle->font.getHeight(c->name, 580 | nodeStyle->connectorTextSize)); 581 | connectorSize.x = std::max(connectorSize.x, nodeStyle->connectorMargin.x + nodeStyle->connectorRadius * 2 + 582 | nodeStyle->font.getWidth(c->name, nodeStyle->connectorTextSize) + 583 | nodeStyle->textOutletMargin); 584 | } 585 | return connectorSize; 586 | } 587 | 588 | void NodeManager::doConnectorLayout(Node *node, bool output) { 589 | float inOffsetY = node->headerSize.y; 590 | std::vector &connectors = output ? node->outputs : node->inputs; 591 | for (Connector *c: connectors) { 592 | inOffsetY += nodeStyle->connectorMargin.y; 593 | vec2 outletPos; 594 | vec2 connectorTextPos; 595 | if (output) { 596 | float textWidth = nodeStyle->font.getWidth(c->name, nodeStyle->connectorTextSize); 597 | outletPos.x = node->size.x - nodeStyle->connectorMargin.x - nodeStyle->connectorRadius; 598 | connectorTextPos.x = 599 | node->size.x - nodeStyle->connectorMargin.x - 2 * nodeStyle->connectorRadius - textWidth - 600 | nodeStyle->textOutletMargin; 601 | } else { 602 | outletPos.x = nodeStyle->connectorMargin.x + nodeStyle->connectorRadius; 603 | connectorTextPos.x = 604 | nodeStyle->connectorMargin.x + 2 * nodeStyle->connectorRadius + nodeStyle->textOutletMargin; 605 | } 606 | float textHeight = nodeStyle->font.getHeight(c->name, nodeStyle->connectorTextSize); 607 | float totalHeight = textHeight; 608 | if (nodeStyle->connectorRadius * 2 <= textHeight) { 609 | connectorTextPos.y = inOffsetY; 610 | outletPos.y = inOffsetY + textHeight / 2.0f; 611 | } else { 612 | outletPos.y = inOffsetY + nodeStyle->connectorRadius; 613 | connectorTextPos.y = inOffsetY + nodeStyle->connectorRadius - textHeight / 2.0f; 614 | totalHeight = nodeStyle->connectorRadius * 2; 615 | } 616 | // update connector pos 617 | c->pos = outletPos; 618 | c->textPos = connectorTextPos; 619 | inOffsetY += totalHeight; 620 | } 621 | } 622 | 623 | void NodeManager::setLookAndFeel(std::shared_ptr style) { 624 | nodeStyle = style; 625 | for (Node *node: nodes) 626 | doNodeLayout(node); 627 | } --------------------------------------------------------------------------------