├── src ├── CMakeLists.txt └── glow │ ├── GlTextureBuffer.cpp │ ├── util │ ├── RandomColorGenerator.cpp │ ├── CMakeLists.txt │ ├── EglOffscreenContext.cpp │ ├── GlCamera.cpp │ ├── X11OffscreenContext.cpp │ ├── FpsCamera.cpp │ └── RoSeCamera.cpp │ ├── CMakeLists.txt │ ├── glexception.cpp │ ├── GlRenderbuffer.cpp │ ├── GlVertexArray.cpp │ ├── GlSampler.cpp │ ├── GlShaderCache.cpp │ ├── GlQuery.cpp │ ├── GlUniform.cpp │ ├── GlColor.cpp │ ├── GlTransformFeedback.cpp │ ├── GlProgram.cpp │ ├── glutil.cpp │ ├── GlFramebuffer.cpp │ ├── GlShader.cpp │ └── GlCapabilities.cpp ├── .clang-format ├── include └── glow │ ├── GlColorMap.h │ ├── util │ ├── EglOffscreenContext.h │ ├── RandomColorGenerator.h │ ├── X11OffscreenContext.h │ ├── enum_utils.h │ ├── FpsCamera.h │ ├── RoSeCamera.h │ └── GlCamera.h │ ├── colormaps.h │ ├── ScopedBinder.h │ ├── GlRenderbuffer.h │ ├── GlShaderCache.h │ ├── GlPixelFormat.h │ ├── GlObject.h │ ├── GlTextureFormat.h │ ├── glexception.h │ ├── GlQuery.h │ ├── GlTextureBuffer.h │ ├── GlSampler.h │ ├── GlCapabilities.h │ ├── glbase.h │ ├── GlColor.h │ ├── GlShader.h │ ├── GlProgram.h │ ├── GlColor.cpp │ ├── glutil.h │ ├── GlTransformFeedback.h │ ├── GlState.h │ ├── GlUniform.h │ ├── GlFramebuffer.h │ ├── GlVertexArray.h │ ├── GlFramebuffer.cpp │ ├── GlTextureRectangle.h │ └── GlTexture.h ├── test ├── main.cpp ├── CMakeLists.txt ├── color-test.cpp ├── test_utils.h ├── framebuffer-test.cpp ├── camera-test.cpp ├── texture-test.cpp └── buffer-test.cpp ├── .gitlab-ci.yml ├── LICENSE ├── .gitignore ├── CMakeLists.txt ├── cmake ├── GlowShaderCompilation.cmake └── GenCppFile.cmake └── README.md /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(glow) 2 | add_subdirectory(glow/util) -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | IndentWidth: 2 3 | ColumnLimit: 120 4 | AllowShortFunctionsOnASingleLine: Inline 5 | -------------------------------------------------------------------------------- /src/glow/GlTextureBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlTextureBuffer.h" 2 | 3 | namespace glow { 4 | 5 | GLuint GlTextureBuffer::boundTexture_ = 0; 6 | 7 | } /* namespace rv */ 8 | -------------------------------------------------------------------------------- /include/glow/GlColorMap.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_GLOW_GLCOLORMAP_H_ 2 | #define SRC_GLOW_GLCOLORMAP_H_ 3 | 4 | namespace glow { 5 | 6 | /** \brief colormap producing GlColors for values in [0.0,1.0] 7 | * 8 | * \author behley 9 | */ 10 | class GlColorMap { 11 | public: 12 | virtual ~GlColorMap() {} 13 | virtual GlColor operator()(float value) = 0; 14 | }; 15 | } 16 | #endif /* SRC_GLOW_GLCOLORMAP_H_ */ 17 | -------------------------------------------------------------------------------- /src/glow/util/RandomColorGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/util/RandomColorGenerator.h" 2 | #include 3 | 4 | namespace glow { 5 | 6 | RandomColorGenerator::RandomColorGenerator(uint32_t _seed) : seed(_seed), rng_(seed) {} 7 | 8 | void RandomColorGenerator::reset() { rng_ = std::default_random_engine(seed); } 9 | 10 | GlColor RandomColorGenerator::nextColor() { return GlColor::FromHSV(2.0f * M_PI * uniform_(rng_), 1.0f, 1.0f); } 11 | } 12 | -------------------------------------------------------------------------------- /include/glow/util/EglOffscreenContext.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_GLOW_UTIL_EGLOFFSCREENCONTEXT_H_ 2 | #define SRC_GLOW_UTIL_EGLOFFSCREENCONTEXT_H_ 3 | 4 | #include 5 | 6 | namespace glow { 7 | 8 | /** \brief offscreen context based on EGL. 9 | * 10 | * \author behley 11 | */ 12 | class EglOffscreenContext { 13 | 14 | public: 15 | EglOffscreenContext(); 16 | 17 | ~EglOffscreenContext(); 18 | 19 | protected: 20 | EGLContext eglCtx; 21 | EGLDisplay eglDpy; 22 | EGLConfig eglCfg; 23 | EGLSurface eglSurf; 24 | }; 25 | 26 | } /* namespace velo_sim */ 27 | 28 | #endif /* SRC_GLOW_UTIL_EGLOFFSCREENCONTEXT_H_ */ 29 | -------------------------------------------------------------------------------- /include/glow/util/RandomColorGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_UTIL_RANDOMCOLORGENERATOR_H_ 2 | #define SRC_UTIL_RANDOMCOLORGENERATOR_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace glow { 8 | 9 | /** \brief random colors generated from a RNG. 10 | * 11 | * \author behley 12 | */ 13 | class RandomColorGenerator { 14 | public: 15 | RandomColorGenerator(uint32_t seed = 0); 16 | 17 | void reset(); 18 | 19 | /** \brief generates a random color **/ 20 | GlColor nextColor(); 21 | 22 | protected: 23 | uint32_t seed; 24 | std::default_random_engine rng_; 25 | std::uniform_real_distribution uniform_; 26 | }; 27 | } 28 | #endif /* SRC_UTIL_RANDOMCOLORGENERATOR_H_ */ 29 | -------------------------------------------------------------------------------- /include/glow/util/X11OffscreenContext.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_UTIL_X11OFFSCREENCONTEXT_H_ 2 | #define SRC_UTIL_X11OFFSCREENCONTEXT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace glow { 9 | 10 | /** \brief simple offscreen context under X11. 11 | * 12 | * On creation a offscreen context is generated, which binds a Pbuffer. 13 | * 14 | * \author behley 15 | */ 16 | class X11OffscreenContext { 17 | public: 18 | X11OffscreenContext(int32_t major_version = 4, int32_t minor_version = 5); 19 | ~X11OffscreenContext(); 20 | 21 | // void makeCurrent(); 22 | 23 | protected: 24 | Display* dpy{nullptr}; 25 | GLXContext ctx{nullptr}; 26 | GLXPbuffer pbuf; 27 | }; 28 | } 29 | #endif /* SRC_UTIL_X11OFFSCREENCONTEXT_H_ */ 30 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace glow; 10 | 11 | int main(int argc, char** argv) { 12 | X11OffscreenContext ctx(3,3); 13 | 14 | /* try it out */ 15 | inititializeGLEW(); 16 | 17 | if (argc > 1 && std::string(argv[1]) == "info") { 18 | std::cout << "Device's OpenGL Capabilities: \n" << std::endl; 19 | std::cout << GlCapabilities::getInstance() << std::endl; 20 | 21 | std::cout << "Device's current OpenGL state: \n" << std::endl; 22 | std::cout << GlState::queryAll() << std::endl; 23 | } 24 | ::testing::InitGoogleTest(&argc, argv); 25 | 26 | return RUN_ALL_TESTS(); 27 | } 28 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # GTest Setup ###### 2 | message(STATUS "Fetching GTest.") 3 | include(FetchContent) 4 | FetchContent_Declare( 5 | googletest 6 | GIT_REPOSITORY https://github.com/google/googletest.git 7 | GIT_TAG e2239ee6043f73722e7aa812a459f54a28552929 # release-1.11.0 8 | ) 9 | 10 | FetchContent_MakeAvailable(googletest) 11 | 12 | set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) 13 | set(BUILD_GTEST ON CACHE BOOL "" FORCE) 14 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 15 | set(gtest_build_tests OFF CACHE BOOL "" FORCE) 16 | 17 | # build the test cases 18 | 19 | add_executable(test_opengl 20 | main.cpp 21 | texture-test.cpp 22 | buffer-test.cpp 23 | framebuffer-test.cpp 24 | color-test.cpp 25 | #camera-test.cpp 26 | ) 27 | 28 | 29 | target_link_libraries(test_opengl gtest_main glow glow_util pthread) 30 | 31 | add_test(test_opengl test_opengl) 32 | -------------------------------------------------------------------------------- /src/glow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(glow STATIC) 2 | add_library(glow::glow ALIAS glow) 3 | 4 | target_sources(glow PRIVATE 5 | glexception.cpp 6 | GlProgram.cpp 7 | GlShader.cpp 8 | GlVertexArray.cpp 9 | GlUniform.cpp 10 | glutil.cpp 11 | GlTexture.cpp 12 | GlTextureRectangle.cpp 13 | GlFramebuffer.cpp 14 | GlRenderbuffer.cpp 15 | GlTransformFeedback.cpp 16 | GlQuery.cpp 17 | GlSampler.cpp 18 | GlState.cpp 19 | GlColor.cpp 20 | GlShaderCache.cpp 21 | GlCapabilities.cpp 22 | GlTextureBuffer.cpp) 23 | 24 | target_include_directories(glow PUBLIC ${PROJECT_SOURCE_DIR}/include) 25 | target_link_libraries(glow PUBLIC ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} ${Boost_LIBRARIES} Eigen3::Eigen) 26 | 27 | target_compile_options(glow PUBLIC "$<$:${GCC_COMPILE_DEBUG_OPTIONS}>") 28 | target_compile_options(glow PUBLIC "$<$:${GCC_COMPILE_RELEASE_OPTIONS}>") 29 | target_compile_definitions(glow PUBLIC "${GL_DEFINITIONS}") 30 | target_compile_features(glow PUBLIC cxx_std_11) -------------------------------------------------------------------------------- /src/glow/glexception.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/glexception.h" 2 | 3 | namespace glow { 4 | 5 | GlShaderError::GlShaderError(const std::string& msg) : std::runtime_error(msg) { 6 | } 7 | 8 | GlProgramError::GlProgramError(const std::string& msg) : std::runtime_error(msg) { 9 | } 10 | 11 | GlOffscreenContextError::GlOffscreenContextError(const std::string& msg) : std::runtime_error(msg) { 12 | } 13 | 14 | GlVertexArrayError::GlVertexArrayError(const std::string& msg) : std::runtime_error(msg) { 15 | } 16 | 17 | GlTextureError::GlTextureError(const std::string& msg) : std::runtime_error(msg) { 18 | } 19 | 20 | GlFramebufferError::GlFramebufferError(const std::string& msg) : std::runtime_error(msg) { 21 | } 22 | 23 | GlTransformFeedbackError::GlTransformFeedbackError(const std::string& msg) : std::runtime_error(msg) { 24 | } 25 | 26 | GlQueryError::GlQueryError(const std::string& msg) : std::runtime_error(msg) { 27 | } 28 | 29 | GlTextureRectangleError::GlTextureRectangleError(const std::string& msg) : std::runtime_error(msg) { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /include/glow/colormaps.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_GLOW_COLORMAPS_H_ 2 | #define SRC_GLOW_COLORMAPS_H_ 3 | 4 | #include "GlColorMap.h" 5 | 6 | namespace glow { 7 | 8 | /** \brief approximate Matplotlib's viridis colormap. 9 | * 10 | * \author behley 11 | **/ 12 | class ViridisColorMap : public GlColorMap { 13 | public: 14 | GlColor operator()(float t) { 15 | vec4 tt(t * t * t, t * t, t, 1.0); 16 | 17 | GlColor result; 18 | result.R = tt.x * rviridis.x + tt.y * rviridis.y + tt.z * rviridis.z + tt.w * rviridis.w; 19 | result.G = tt.x * gviridis.x + tt.y * gviridis.y + tt.z * gviridis.z + tt.w * gviridis.w; 20 | result.B = tt.x * bviridis.x + tt.y * bviridis.y + tt.z * bviridis.z + tt.w * bviridis.w; 21 | 22 | return result; 23 | } 24 | 25 | protected: 26 | const vec4 rviridis{2.90912735, -2.14404531, 0.04439198, 0.29390206}; 27 | const vec4 gviridis{-0.17293242, -0.16906214, 1.24131122, 0.01871256}; 28 | const vec4 bviridis{0.17848859, -1.72405244, 1.23042564, 0.34479632}; 29 | }; 30 | } 31 | 32 | #endif /* SRC_GLOW_COLORMAPS_H_ */ 33 | -------------------------------------------------------------------------------- /src/glow/GlRenderbuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlRenderbuffer.h" 2 | 3 | namespace glow { 4 | 5 | GlRenderbuffer::GlRenderbuffer(uint32_t width, uint32_t height, RenderbufferFormat fmt) 6 | : format_(static_cast(fmt)), width_(width), height_(height) { 7 | glGenRenderbuffers(1, &id_); 8 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 9 | glDeleteRenderbuffers(1, ptr); 10 | delete ptr; 11 | }); 12 | 13 | // allocate storage. 14 | glBindRenderbuffer(GL_RENDERBUFFER, id_); 15 | glRenderbufferStorage(GL_RENDERBUFFER, static_cast(fmt), width_, height_); 16 | glBindRenderbuffer(GL_RENDERBUFFER, 0); 17 | 18 | CheckGlError(); 19 | } 20 | 21 | void GlRenderbuffer::bind() { 22 | glBindRenderbuffer(GL_RENDERBUFFER, id_); 23 | } 24 | 25 | void GlRenderbuffer::release() { 26 | glBindRenderbuffer(GL_RENDERBUFFER, 0); 27 | } 28 | 29 | uint32_t GlRenderbuffer::width() const { 30 | return width_; 31 | } 32 | 33 | uint32_t GlRenderbuffer::height() const { 34 | return height_; 35 | } 36 | 37 | } /* namespace rv */ 38 | -------------------------------------------------------------------------------- /src/glow/util/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(glow_util STATIC) 2 | add_library(glow::util ALIAS glow_util) 3 | 4 | if(X11_FOUND) 5 | target_sources(glow_util PRIVATE 6 | GlCamera.cpp 7 | RoSeCamera.cpp 8 | FpsCamera.cpp 9 | RandomColorGenerator.cpp 10 | X11OffscreenContext.cpp) 11 | 12 | target_link_libraries(glow_util PUBLIC ${X11_LIBRARIES} ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} Eigen3::Eigen) 13 | else() 14 | target_sources(glow_util PRIVATE 15 | GlCamera.cpp 16 | RoSeCamera.cpp 17 | FpsCamera.cpp 18 | RandomColorGenerator.cpp) 19 | 20 | target_link_libraries(glow_util PUBLIC ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} Eigen3::Eigen) 21 | endif() 22 | 23 | target_include_directories(glow_util PUBLIC ${PROJECT_SOURCE_DIR}/include) 24 | 25 | target_compile_options(glow_util PUBLIC "$<$:${GCC_COMPILE_DEBUG_OPTIONS}>") 26 | target_compile_options(glow_util PUBLIC "$<$:${GCC_COMPILE_RELEASE_OPTIONS}>") 27 | target_compile_definitions(glow_util PUBLIC "${GL_DEFINITIONS}") 28 | target_compile_features(glow_util PUBLIC cxx_std_11) -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: gitlab.ipb.uni-bonn.de:4567/global/docker-images:ipb_default 2 | 3 | build_release: 4 | script: 5 | - export TERM=xterm 6 | - export SHELL=/bin/bash 7 | - apt-get update -yq 8 | - apt-get install -y libeigen3-dev libboost-filesystem-dev libboost-system-dev libglew-dev xvfb mesa-utils 9 | - mkdir build 10 | - cd build 11 | - cmake -DCMAKE_BUILD_TYPE=Release -DOPENGL_VERSION=330 -DFORCE_BUILD_TEST=On .. 12 | - make 13 | # finally run tests. 14 | - xvfb-run -s "-screen 0 640x480x24" ctest -VV 15 | stage: build 16 | tags: 17 | - docker 18 | 19 | build_debug: 20 | script: 21 | - export TERM=xterm 22 | - export SHELL=/bin/bash 23 | - apt-get update -yq 24 | - apt-get install -y libeigen3-dev libboost-filesystem-dev libboost-system-dev libglew-dev xvfb mesa-utils 25 | - mkdir build 26 | - cd build 27 | - cmake -DCMAKE_BUILD_TYPE=Debug -DOPENGL_VERSION=330 -DFORCE_BUILD_TEST=On .. 28 | - make 29 | # finally run tests. 30 | - xvfb-run -s "-screen 0 640x480x24" ctest -VV 31 | stage: build 32 | tags: 33 | - docker 34 | 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Jens Behley 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 | -------------------------------------------------------------------------------- /include/glow/ScopedBinder.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_SCOPEDBINDER_H_ 2 | #define INCLUDE_RV_SCOPEDBINDER_H_ 3 | 4 | #include "GlObject.h" 5 | 6 | namespace glow { 7 | 8 | /** \brief Automatic bind and release of an GlObject for the current scope. 9 | * 10 | * For simple binding and releasing of GlObjects. The binder is actually templated 11 | * to ensure that we can make an appropriate copy without a polymorphic clone method. 12 | * A "deep" copy of the object ensures that the GlObject's lifetime is longer then 13 | * the lifetime of the ScopedBinder. 14 | * 15 | * \author behley 16 | */ 17 | template 18 | class ScopedBinder { 19 | public: 20 | ScopedBinder(const GlObject& object); 21 | ~ScopedBinder(); 22 | 23 | protected: 24 | GlObject object_; // active copy of the object ensures that the lifetime of the object is as long as the binder. 25 | }; 26 | 27 | template 28 | ScopedBinder::ScopedBinder(const GlObject& object) 29 | : object_(object) { 30 | object_.bind(); 31 | } 32 | 33 | template 34 | ScopedBinder::~ScopedBinder() { 35 | object_.release(); 36 | } 37 | 38 | } /* namespace rv */ 39 | 40 | #endif /* INCLUDE_RV_SCOPEDBINDER_H_ */ 41 | -------------------------------------------------------------------------------- /include/glow/GlRenderbuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLRENDERBUFFER_H_ 2 | #define INCLUDE_RV_GLRENDERBUFFER_H_ 3 | 4 | #include "GlObject.h" 5 | 6 | namespace glow { 7 | 8 | // forward declarations. 9 | class GlFramebuffer; 10 | 11 | enum class RenderbufferFormat { 12 | RGBA = GL_RGBA, 13 | RGB = GL_RGB, 14 | RG = GL_RG, 15 | R = GL_R, 16 | DEPTH = GL_DEPTH, 17 | DEPTH_STENCIL = GL_DEPTH24_STENCIL8 18 | }; 19 | 20 | /** 21 | * \brief Representation of OpenGL's renderbuffer object. 22 | * 23 | * 24 | * Renderbuffer objects are write-only buffers for rendering purposes. You may use these as attachments 25 | * for frambuffers if you do not care about the rendering results. If you are interested in the results, 26 | * you should use GlTexture instead. 27 | * 28 | * \see GlFramebuffer 29 | * 30 | * \author behley 31 | */ 32 | class GlRenderbuffer : public GlObject { 33 | public: 34 | friend class GlFramebuffer; 35 | 36 | GlRenderbuffer(uint32_t width, uint32_t height, RenderbufferFormat fmt = RenderbufferFormat::RGB); 37 | 38 | void bind() override; 39 | void release() override; 40 | 41 | uint32_t width() const; 42 | uint32_t height() const; 43 | 44 | protected: 45 | GLenum format_; 46 | uint32_t width_, height_; 47 | }; 48 | 49 | } /* namespace rv */ 50 | 51 | #endif /* INCLUDE_RV_GLRENDERBUFFER_H_ */ 52 | -------------------------------------------------------------------------------- /src/glow/util/EglOffscreenContext.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/util/EglOffscreenContext.h" 2 | 3 | namespace glow { 4 | 5 | EglOffscreenContext::EglOffscreenContext() { 6 | static const EGLint configAttribs[] = { 7 | EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 8, 8 | EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE}; 9 | 10 | static const int pbufferWidth = 9; 11 | static const int pbufferHeight = 9; 12 | 13 | static const EGLint pbufferAttribs[] = { 14 | EGL_WIDTH, pbufferWidth, EGL_HEIGHT, pbufferHeight, EGL_NONE, 15 | }; 16 | 17 | // 1. Initialize EGL 18 | eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); 19 | 20 | EGLint major = 4, minor = 5; 21 | 22 | eglInitialize(eglDpy, &major, &minor); 23 | 24 | // 2. Select an appropriate configuration 25 | EGLint numConfigs; 26 | 27 | eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); 28 | 29 | // 3. Create a surface 30 | eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); 31 | 32 | // 4. Bind the API 33 | eglBindAPI(EGL_OPENGL_API); 34 | 35 | // 5. Create a context and make it current 36 | eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL); 37 | 38 | eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); 39 | 40 | 41 | return 0; 42 | } 43 | 44 | EglOffscreenContext::~EglOffscreenContext() { 45 | eglTerminate(eglDpy); 46 | } 47 | 48 | } /* namespace velo_sim */ 49 | -------------------------------------------------------------------------------- /include/glow/GlShaderCache.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOW_GLSHADERCACHE_H_ 2 | #define GLOW_GLSHADERCACHE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace glow { 9 | 10 | /** \brief Caching of shader source files. 11 | * 12 | * The cache is a singleton and therefore can be only accessed via the getInstance() method. 13 | * Currently only shaders with distinct basenames are supported, since same shader names in 14 | * different directories are not considered. 15 | * 16 | * \author behley 17 | */ 18 | class GlShaderCache { 19 | public: 20 | 21 | /** \brief get cache instance. **/ 22 | static GlShaderCache& getInstance(); 23 | 24 | /** \brief has source cached for given filename. **/ 25 | bool hasSource(const std::string& filename); 26 | 27 | /** \brief get cached source. **/ 28 | std::string getSource(const std::string& filename); 29 | 30 | /** \brief add source for given cache key value. **/ 31 | void insertSource(const std::string& filename, const std::string& source); 32 | /** \brief read and add source from a file **/ 33 | void insertSource(const std::string& filename); 34 | 35 | protected: 36 | GlShaderCache(); 37 | GlShaderCache(const GlShaderCache&); 38 | GlShaderCache& operator=(const GlShaderCache&); 39 | 40 | std::string basename(const std::string& filename) const; 41 | 42 | 43 | std::map sources_; 44 | }; 45 | 46 | } /* namespace glow */ 47 | 48 | #endif /* GLOW_GLSHADERCACHE_H_ */ 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/c++,cmake,cuda,qt 2 | 3 | ### C++ ### 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | 33 | 34 | ### CMake ### 35 | CMakeCache.txt 36 | CMakeFiles 37 | CMakeScripts 38 | Makefile 39 | cmake_install.cmake 40 | install_manifest.txt 41 | 42 | 43 | ### CUDA ### 44 | *.i 45 | *.ii 46 | *.gpu 47 | *.ptx 48 | *.cubin 49 | *.fatbin 50 | 51 | 52 | ### Qt ### 53 | # C++ objects and libs 54 | 55 | *.slo 56 | *.lo 57 | *.o 58 | *.a 59 | *.la 60 | *.lai 61 | *.so 62 | *.dll 63 | *.dylib 64 | 65 | # Qt-es 66 | 67 | /.qmake.cache 68 | /.qmake.stash 69 | *.pro.user 70 | *.pro.user.* 71 | *.qbs.user 72 | *.qbs.user.* 73 | *.moc 74 | moc_*.cpp 75 | qrc_*.cpp 76 | ui_*.h 77 | Makefile* 78 | *build-* 79 | 80 | # QtCreator 81 | 82 | *.autosave 83 | 84 | # QtCtreator Qml 85 | *.qmlproject.user 86 | *.qmlproject.user.* 87 | 88 | # QtCtreator CMake 89 | CMakeLists.txt.user 90 | 91 | # project-related files 92 | build 93 | .* 94 | IDEAS.md 95 | *~ 96 | evaluation/result 97 | *.pyc 98 | 99 | # generated folders 100 | bin 101 | lib 102 | 103 | evaluation 104 | -------------------------------------------------------------------------------- /test/color-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace glow; 6 | 7 | namespace { 8 | 9 | TEST(ColorTest, testConversionFromHSV) { 10 | // values generated with GIMP. 11 | GlColor col = GlColor::FromHSV(radians(193), 45.0f / 100.f, 54.0f / 100.0f); 12 | ASSERT_NEAR(76.0f / 255.0f, col.R, 0.01f); 13 | ASSERT_NEAR(124.0f / 255.0f, col.G, 0.01f); 14 | ASSERT_NEAR(138.0f / 255.0f, col.B, 0.01f); 15 | 16 | GlColor col0 = GlColor::FromHSV(radians(193), 45.0f / 100.f, 0); 17 | ASSERT_NEAR(0.0f, col0.R, 0.01); 18 | ASSERT_NEAR(0.0f, col0.G, 0.01f); 19 | ASSERT_NEAR(0.0f, col0.B, 0.01f); 20 | 21 | GlColor col1 = GlColor::FromHSV(radians(13), 82.0f / 100.f, 88.0f / 100.0f); 22 | ASSERT_NEAR(223.0f / 255.0f, col1.R, 0.01f); 23 | ASSERT_NEAR(79.0f / 255.0f, col1.G, 0.01f); 24 | ASSERT_NEAR(40.0f / 255.0f, col1.B, 0.01f); 25 | } 26 | 27 | TEST(ColorTest, testBrightness) { 28 | GlColor col = GlColor::FromHSV(radians(193), 45.0f / 100.f, 54.0f / 100.0f); 29 | col.lighter(); 30 | ASSERT_NEAR(1.42 * 76.0f / 255.0f, col.R, 0.01f); 31 | ASSERT_NEAR(1.42 * 124.0f / 255.0f, col.G, 0.01f); 32 | ASSERT_NEAR(1.42 * 138.0f / 255.0f, col.B, 0.01f); 33 | 34 | GlColor col1 = GlColor::FromHSV(radians(193), 45.0f / 100.f, 54.0f / 100.0f); 35 | col1.darker(); 36 | ASSERT_NEAR(0.7 * 76.0f / 255.0f, col1.R, 0.01f); 37 | ASSERT_NEAR(0.7 * 124.0f / 255.0f, col1.G, 0.01f); 38 | ASSERT_NEAR(0.7 * 138.0f / 255.0f, col1.B, 0.01f); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /include/glow/GlPixelFormat.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLPIXELFORMAT_H_ 2 | #define INCLUDE_RV_GLPIXELFORMAT_H_ 3 | 4 | #include "GlObject.h" 5 | 6 | /** \brief pixel format and pixel type definitions. 7 | * 8 | * 9 | * \author behley 10 | **/ 11 | 12 | namespace glow { 13 | 14 | enum class PixelFormat { 15 | // pixels colors are stored in [0.0, 1.0] 16 | R = GL_RED, 17 | RG = GL_RG, 18 | RGB = GL_RGB, 19 | BRG = GL_BGR, 20 | RGBA = GL_RGBA, 21 | BGRA = GL_BGRA, 22 | // pixels colors are stored in range [0,2^B-1], where B corresponds to the number of bits. 23 | R_INTEGER = GL_RED_INTEGER, 24 | RG_INTEGER = GL_RG_INTEGER, 25 | RGB_INTEGER = GL_RGB_INTEGER, 26 | BGR_INTEGER = GL_BGRA_INTEGER, 27 | RGBA_INTEGER = GL_RGBA_INTEGER, 28 | BGRA_INTEGER = GL_BGRA_INTEGER, 29 | R_UNSIGNED = GL_RED_INTEGER, 30 | RG_UNSIGNED = GL_RG_INTEGER, 31 | RGB_UNSIGNED = GL_RGB_INTEGER, 32 | BGR_UNSIGNED = GL_BGRA_INTEGER, 33 | RGBA_UNSIGNED = GL_RGBA_INTEGER, 34 | DEPTH = GL_DEPTH_COMPONENT, 35 | DEPTH_STENCIL = GL_DEPTH24_STENCIL8, 36 | STENCIL = GL_STENCIL_INDEX, 37 | 38 | }; 39 | 40 | enum class PixelType { 41 | UNSIGNED_BYTE = GL_UNSIGNED_BYTE, // [0, 255] 42 | BYTE = GL_BYTE, // [-127, 128] 43 | UNSIGNED_SHORT = GL_UNSIGNED_SHORT, // ... 44 | SHORT = GL_SHORT, 45 | UNSIGNED_INT = GL_UNSIGNED_INT, 46 | INT = GL_INT, 47 | HALF_FLOAT = GL_HALF_FLOAT, 48 | FLOAT = GL_FLOAT, // [0.0, 1.0] 49 | // TODO: add packed formats, ... 50 | }; 51 | 52 | } /* namespace rv */ 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/glow/GlObject.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_OPENGL_GLOBJECT_H_ 2 | #define SRC_OPENGL_GLOBJECT_H_ 3 | 4 | #include "glbase.h" 5 | #include 6 | 7 | namespace glow { 8 | 9 | /** \brief Base class for OpenGL objects. 10 | * 11 | * Each object in OpenGL has an object identifier used to refer to an object in 12 | * the device memory. Therefore, each GlObject has also an identifier. To simplify, 13 | * the resource management of an GlObject, each object should use the shared pointer 14 | * to delete the OpenGL resource on the device if no reference to the shared pointer 15 | * exists anymore. This also ensures that we can take ownership of an GlObject by 16 | * copying the shared pointer. As long as the one instance of that shared pointer 17 | * exists, we are guaranteed to have a valid OpenGL object with the given identifier. 18 | * 19 | * If the reference count of the shared pointer is zero, the custom deleter specified 20 | * by each implementation of GlObject frees the device memory. 21 | * 22 | * \author behley 23 | */ 24 | class GlObject { 25 | public: 26 | virtual ~GlObject() {} 27 | 28 | /** \brief return the OpenGL's object identifier. **/ 29 | inline virtual GLuint id() const { return id_; } 30 | 31 | /** \brief bind (or activate) the object. **/ 32 | virtual void bind() = 0; 33 | 34 | /** \brief unbind (or deactive) the object. **/ 35 | virtual void release() = 0; 36 | 37 | protected: 38 | GLuint id_{0}; 39 | std::shared_ptr ptr_; 40 | }; 41 | 42 | } /* namespace rv */ 43 | 44 | #endif /* SRC_OPENGL_GLOBJECT_H_ */ 45 | -------------------------------------------------------------------------------- /src/glow/GlVertexArray.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlVertexArray.h" 2 | #include "glow/glexception.h" 3 | #include 4 | 5 | namespace glow { 6 | 7 | GLuint GlVertexArray::boundVAO_ = 0; 8 | 9 | GlVertexArray::GlVertexArray() { 10 | glGenVertexArrays(1, &id_); 11 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 12 | glDeleteVertexArrays(1, ptr); 13 | delete ptr; 14 | }); 15 | } 16 | 17 | GlVertexArray::~GlVertexArray() { 18 | } 19 | 20 | void GlVertexArray::bind() { 21 | assert((boundVAO_ == 0 || boundVAO_ == id_) && "Other vertex array object still active?"); 22 | boundVAO_ = id_; 23 | glBindVertexArray(id_); 24 | } 25 | 26 | void GlVertexArray::release() { 27 | assert(boundVAO_ == id_ && "Different vertex array object bound in between?"); 28 | boundVAO_ = 0; 29 | glBindVertexArray(0); 30 | } 31 | 32 | void GlVertexArray::enableVertexAttribute(uint32_t idx) { 33 | GLuint oldvao = bindTransparently(); 34 | glEnableVertexAttribArray(static_cast(idx)); 35 | releaseTransparently(oldvao); 36 | } 37 | 38 | void GlVertexArray::disableVertexAttribute(uint32_t idx) { 39 | GLuint oldvao = bindTransparently(); 40 | glDisableVertexAttribArray(static_cast(idx)); 41 | releaseTransparently(oldvao); 42 | } 43 | 44 | GLuint GlVertexArray::bindTransparently() { 45 | if (boundVAO_ == id_) return id_; 46 | 47 | glBindVertexArray(id_); 48 | 49 | return boundVAO_; 50 | } 51 | 52 | void GlVertexArray::releaseTransparently(GLuint old_vao) { 53 | if (old_vao == id_) return; // nothing changed. 54 | 55 | glBindVertexArray(old_vao); 56 | } 57 | 58 | } /* namespace rv */ 59 | -------------------------------------------------------------------------------- /include/glow/GlTextureFormat.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLTEXTUREFORMAT_H_ 2 | #define INCLUDE_RV_GLTEXTUREFORMAT_H_ 3 | 4 | #include "GlObject.h" 5 | 6 | namespace glow { 7 | 8 | /** \brief texture format definitions. 9 | * 10 | * TODO: Define only GPU-capability compatible formats depending on the architecture. 11 | * 12 | * See chapter 8.5.1 for required and optional texture formats. (This differs at least 13 | * for OpenGL 4.3 and OpenGL 4.5...) 14 | * 15 | * Caveat: In OpenGL 4.3 there are only RGBA*, R*, and RG* color formats need to be 16 | * supported. As of OpenGL 4.5, all formats R*,RG*, RGB*, RGBA* must be supported. 17 | **/ 18 | 19 | enum class TextureFormat { 20 | // TODO: Flags and replacements for supported texture types. 21 | 22 | // base texture formats ("best fit decided by driver..."): 23 | RGBA = GL_RGBA, // values in [0.0, 1.0] 24 | RGB = GL_RGB, // values in [0.0, 1.0] 25 | RG = GL_RG, // values in [0.0, 1.0] 26 | R = GL_RED, // values in [0.0, 1.0] 27 | DEPTH = GL_DEPTH, 28 | DEPTH_STENCIL = GL_DEPTH24_STENCIL8, 29 | 30 | // Some explicit 32-bit texture formats: 31 | R_INTEGER = GL_R32I, 32 | RG_INTEGER = GL_RG32I, 33 | RGB_INTEGER = GL_RGB32I, 34 | RGBA_INTEGER = GL_RGBA32I, 35 | 36 | R_UNSIGNED = GL_R32UI, 37 | RG_UNSIGNED = GL_RG32UI, 38 | RGB_UNSIGNED = GL_RGB32UI, 39 | RGBA_UNSIGNED = GL_RGBA32UI, 40 | 41 | R_FLOAT = GL_R32F, 42 | RG_FLOAT = GL_RG32F, 43 | RGB_FLOAT = GL_RGB32F, 44 | RGBA_FLOAT = GL_RGBA32F 45 | 46 | // custom formats 47 | // TODO: INTENSITY = GL_R, // GRAY => GL_R and swizzle mask (R, R, R, 1.0) 48 | 49 | }; 50 | } 51 | 52 | #endif /* INCLUDE_RV_GLTEXTUREFORMAT_H_ */ 53 | -------------------------------------------------------------------------------- /test/test_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_TEST_UTILS_H_ 2 | #define TEST_TEST_UTILS_H_ 3 | 4 | #include 5 | 6 | namespace { 7 | class Random { 8 | public: 9 | Random() : uniform_(0.0, 1.0), normal_(0.0, 1.0) {} 10 | explicit Random(uint32_t seed) : rng_(seed), uniform_(0.0, 1.0), normal_(0.0, 1.0) {} 11 | 12 | /** \brief Returns a random integer value in the range [0,\a n - 1] inclusive. */ 13 | int32_t getInt(int32_t n) { return static_cast(n * uniform_(rng_)); } 14 | /** \brief Returns a random integer value in the range [\a min,\a max] inclusive. */ 15 | int32_t getInt(int32_t min, int32_t max) { return static_cast((max - min) * uniform_(rng_) + min); } 16 | 17 | /** \brief Function operator for use with STL. 18 | * Returns a random value between 0 and \a n - 1 inclusive. 19 | */ 20 | int32_t operator()(int32_t n) { return getInt(n); } 21 | 22 | /** \brief Returns a random float in the range [0,1] inclusive. */ 23 | float getFloat() { return static_cast(uniform_(rng_)); } 24 | 25 | /** \brief Returns a random float from the Gaussian distribution with mean 0 and std. deviation 1 */ 26 | float getGaussianFloat() { return static_cast(uniform_(rng_)); } 27 | /** \brief Returns a random double in the range [0,1] inclusive. */ 28 | double getDouble() { return uniform_(rng_); } 29 | 30 | /** \brief Returns a random double from the Gaussian distribution with mean 0 and std. deviation 1 */ 31 | double getGaussianDouble() { return normal_(rng_); } 32 | 33 | protected: 34 | std::default_random_engine rng_; 35 | std::uniform_real_distribution uniform_; 36 | std::normal_distribution normal_; 37 | }; 38 | } 39 | 40 | #endif /* TEST_TEST_UTILS_H_ */ 41 | -------------------------------------------------------------------------------- /include/glow/glexception.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLEXCEPTION_H_ 2 | #define INCLUDE_RV_GLEXCEPTION_H_ 3 | 4 | /** Some custom exceptions ... **/ 5 | 6 | #include 7 | 8 | namespace glow { 9 | 10 | /** \brief Error in shader creation or compilation. **/ 11 | class GlShaderError : public std::runtime_error { 12 | public: 13 | GlShaderError(const std::string& msg); 14 | }; 15 | 16 | /** \brief Error in the GlProgram, most likely a linking error. */ 17 | class GlProgramError : public std::runtime_error { 18 | public: 19 | GlProgramError(const std::string& msg); 20 | }; 21 | 22 | /** \brief Error with GlOffscreenContext: creation of object, etc. */ 23 | class GlOffscreenContextError : public std::runtime_error { 24 | public: 25 | GlOffscreenContextError(const std::string& msg); 26 | }; 27 | 28 | /** \brief Error while using GlVertexArray: missing bind()/release(). **/ 29 | class GlVertexArrayError : public std::runtime_error { 30 | public: 31 | GlVertexArrayError(const std::string& msg); 32 | }; 33 | 34 | /** \brief Error while using GlTexture. **/ 35 | class GlTextureError : public std::runtime_error { 36 | public: 37 | GlTextureError(const std::string& msg); 38 | }; 39 | 40 | class GlFramebufferError : public std::runtime_error { 41 | public: 42 | GlFramebufferError(const std::string& msg); 43 | }; 44 | 45 | class GlTransformFeedbackError : public std::runtime_error { 46 | public: 47 | GlTransformFeedbackError(const std::string& msg); 48 | }; 49 | 50 | class GlQueryError : public std::runtime_error { 51 | public: 52 | GlQueryError(const std::string& msg); 53 | }; 54 | 55 | class GlTextureRectangleError : public std::runtime_error { 56 | public: 57 | GlTextureRectangleError(const std::string& msg); 58 | }; 59 | } 60 | // ... 61 | 62 | #endif /* INCLUDE_RV_GLEXCEPTION_H_ */ 63 | -------------------------------------------------------------------------------- /include/glow/GlQuery.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLQUERY_H_ 2 | #define INCLUDE_RV_GLQUERY_H_ 3 | 4 | #include "GlObject.h" 5 | 6 | namespace glow { 7 | 8 | enum class QueryTarget { 9 | SAMPLES_PASSED = GL_SAMPLES_PASSED, 10 | ANY_SAMPLES_PASSED = GL_ANY_SAMPLES_PASSED, 11 | ANY_SAMPLES_PASSED_CONSERVATIVE = GL_ANY_SAMPLES_PASSED_CONSERVATIVE, 12 | PRIMITIVES_GENERATED = GL_PRIMITIVES_GENERATED, 13 | TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 14 | TIME_ELAPSED = GL_TIME_ELAPSED 15 | }; 16 | 17 | /** \brief representation of OpenGL's query object. 18 | * 19 | * A query object can be used to query information about OpenGL context. 20 | * 21 | * \author behley. 22 | */ 23 | class GlQuery : public GlObject { 24 | public: 25 | GlQuery(QueryTarget target); 26 | 27 | /** conversion operators. **/ 28 | operator uint32_t(); 29 | operator int32_t(); 30 | 31 | /** \brief begin indexed query for specified index. 32 | * If the specified target does not support an index by definition, this function calls 33 | * simply the index 0 version. 34 | **/ 35 | void begin(uint32_t index = 0); 36 | void end(); 37 | 38 | /** \brief retrieve if query result is available. **/ 39 | bool ready() const; 40 | 41 | /** \brief get the value of the query with specified type. **/ 42 | template 43 | void value(T& value) const; 44 | 45 | protected: 46 | void bind() override; 47 | void release() override; 48 | 49 | bool started_{false}; 50 | GLenum target_; 51 | GLuint index_{0}; 52 | }; 53 | 54 | template 55 | void GlQuery::value(T& value) const { 56 | GLint params; 57 | glGetQueryObjectiv(id_, GL_QUERY_RESULT, ¶ms); 58 | 59 | value = T(params); 60 | } 61 | 62 | } /* namespace rv */ 63 | 64 | #endif /* INCLUDE_RV_GLQUERY_H_ */ 65 | -------------------------------------------------------------------------------- /src/glow/GlSampler.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlSampler.h" 2 | 3 | namespace glow { 4 | 5 | GlSampler::GlSampler() { 6 | glGenSamplers(1, &id_); 7 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 8 | glDeleteSamplers(1, ptr); 9 | delete ptr; 10 | }); 11 | } 12 | 13 | /** \brief use sampler for specific texture unit identified by its index (0, 1, ...). **/ 14 | void GlSampler::bind(uint32_t textureUnitId) { 15 | glBindSampler(static_cast(textureUnitId), id_); 16 | } 17 | 18 | /** \brief "unuse" sampler for given texture unit. **/ 19 | void GlSampler::release(uint32_t textureUnitId) { 20 | glBindSampler(static_cast(textureUnitId), 0); 21 | } 22 | 23 | void GlSampler::bind() { 24 | } 25 | 26 | void GlSampler::release() { 27 | } 28 | 29 | void GlSampler::setMinifyingOperation(TexMinOp minifyingOperation) { 30 | glSamplerParameteri(id_, GL_TEXTURE_MIN_FILTER, static_cast(minifyingOperation)); 31 | } 32 | 33 | void GlSampler::setMagnifyingOperation(TexMagOp magnifyingOperation) { 34 | glSamplerParameteri(id_, GL_TEXTURE_MAG_FILTER, static_cast(magnifyingOperation)); 35 | } 36 | 37 | void GlSampler::setWrapOperation(TexWrapOp wrap_s) { 38 | glSamplerParameteri(id_, GL_TEXTURE_WRAP_S, static_cast(wrap_s)); 39 | } 40 | 41 | void GlSampler::setWrapOperation(TexWrapOp wrap_s, TexWrapOp wrap_t) { 42 | glSamplerParameteri(id_, GL_TEXTURE_WRAP_S, static_cast(wrap_s)); 43 | glSamplerParameteri(id_, GL_TEXTURE_WRAP_T, static_cast(wrap_t)); 44 | } 45 | 46 | void GlSampler::setWrapOperation(TexWrapOp wrap_s, TexWrapOp wrap_t, TexWrapOp wrap_r) { 47 | glSamplerParameteri(id_, GL_TEXTURE_WRAP_S, static_cast(wrap_s)); 48 | glSamplerParameteri(id_, GL_TEXTURE_WRAP_T, static_cast(wrap_t)); 49 | glSamplerParameteri(id_, GL_TEXTURE_WRAP_R, static_cast(wrap_r)); 50 | } 51 | 52 | } /* namespace rv */ 53 | -------------------------------------------------------------------------------- /include/glow/GlTextureBuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLTEXTUREBUFFER_H_ 2 | #define INCLUDE_RV_GLTEXTUREBUFFER_H_ 3 | 4 | #include "GlBuffer.h" 5 | #include "GlTextureFormat.h" 6 | 7 | namespace glow { 8 | 9 | /** \brief Texture based on a GlBuffer object. 10 | * 11 | * A texture object, where the texture's data is stored in a buffer object. 12 | * 13 | * 14 | * \author behley 15 | **/ 16 | class GlTextureBuffer : public GlObject { 17 | public: 18 | template 19 | GlTextureBuffer(GlBuffer& buffer, TextureFormat format) 20 | : format_(format) { 21 | buffer_ = buffer.ptr_; // hold pointer to avoid deallocation before texture object is deallocated. 22 | glGenTextures(1, &id_); 23 | 24 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 25 | glDeleteTextures(1, ptr); 26 | delete ptr; 27 | }); 28 | 29 | GLuint old_id = bindTransparently(); 30 | GLint texFormat = static_cast(format_); 31 | buffer.bind(); 32 | glTexBuffer(GL_TEXTURE_BUFFER, texFormat, buffer.id()); 33 | buffer.release(); 34 | releaseTransparently(old_id); 35 | 36 | CheckGlError(); 37 | } 38 | 39 | void bind() override { 40 | glBindTexture(GL_TEXTURE_BUFFER, id_); 41 | boundTexture_ = id_; 42 | } 43 | 44 | void release() override { 45 | glBindTexture(GL_TEXTURE_BUFFER, 0); 46 | boundTexture_ = 0; 47 | } 48 | 49 | protected: 50 | GLuint bindTransparently() const { 51 | if (boundTexture_ == id_) return id_; 52 | 53 | glBindTexture(GL_TEXTURE_BUFFER, id_); 54 | 55 | return boundTexture_; 56 | } 57 | 58 | void releaseTransparently(GLuint old_id) const { 59 | if (old_id == id_) return; 60 | 61 | glBindTexture(GL_TEXTURE_BUFFER, old_id); 62 | } 63 | 64 | static GLuint boundTexture_; 65 | std::shared_ptr buffer_; 66 | TextureFormat format_; 67 | }; 68 | 69 | } /* namespace rv */ 70 | 71 | #endif /* INCLUDE_RV_GLTEXTUREBUFFER_H_ */ 72 | -------------------------------------------------------------------------------- /src/glow/GlShaderCache.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlShaderCache.h" 2 | 3 | #include 4 | #include 5 | 6 | namespace glow { 7 | 8 | GlShaderCache& GlShaderCache::getInstance() { 9 | static std::unique_ptr instance(new GlShaderCache()); 10 | 11 | return *instance; 12 | } 13 | 14 | bool GlShaderCache::hasSource(const std::string& filename) { 15 | std::string b = basename(filename); 16 | 17 | return (sources_.find(b) != sources_.end()); 18 | } 19 | 20 | std::string GlShaderCache::getSource(const std::string& filename) { 21 | std::string b = basename(filename); 22 | 23 | return sources_[b]; 24 | } 25 | 26 | void GlShaderCache::insertSource(const std::string& filename) { 27 | std::ios::openmode mode = std::ios::in | std::ios::ate; 28 | std::ifstream ifs(filename, mode); 29 | std::string source; 30 | if (ifs) { 31 | const std::streamsize size = static_cast(ifs.tellg()); 32 | ifs.seekg(0, std::ios::beg); 33 | source.resize(size); 34 | ifs.read(const_cast(source.data()), size); 35 | source.resize(ifs.gcount()); 36 | ifs.close(); 37 | } else { 38 | std::cerr << "Error: Reading from file '" << filename << "' failed." << std::endl; 39 | source = ""; 40 | } 41 | insertSource(filename, source); 42 | } 43 | 44 | void GlShaderCache::insertSource(const std::string& filename, const std::string& source) { 45 | std::string b = basename(filename); 46 | if (sources_.find(b) != sources_.end()) { 47 | std::cerr << "Warning: Overwriting cached shader source for filename '" << filename << "'" << std::endl; 48 | } 49 | sources_[b] = source; 50 | } 51 | 52 | std::string GlShaderCache::basename(const std::string& filename) const { 53 | std::string::size_type n = filename.rfind("/"); 54 | if (n == std::string::npos) return filename; 55 | 56 | return filename.substr(n); 57 | } 58 | 59 | GlShaderCache::GlShaderCache() {} 60 | GlShaderCache::GlShaderCache(const GlShaderCache&){}; 61 | 62 | } /* namespace glow */ 63 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(glow VERSION 1.0 4 | DESCRIPTION "glow -- a high-level wrapper for OpenGL" 5 | LANGUAGES CXX) 6 | 7 | find_package(OpenGL REQUIRED) 8 | find_package(GLEW REQUIRED) 9 | find_package(Boost REQUIRED COMPONENTS filesystem system) 10 | find_package(Eigen3 REQUIRED) 11 | find_package(X11) 12 | 13 | set(GCC_COMPILE_DEBUG_OPTIONS "-g2;-DDEBUG") 14 | set(GCC_COMPILE_RELEASE_OPTIONS "-Wall;-O3;-DNDEBUG") 15 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 16 | 17 | option(ENABLE_NVIDIA_EXT "Enable Nvidia GL capabilites." OFF) 18 | set(OPENGL_VERSION 330 CACHE STRING "Available OpenGL version") 19 | 20 | if(ENABLE_NVIDIA_EXT) 21 | message(STATUS "Enabling Nvidia OpenGL extensions.") 22 | add_definitions(-DQUERY_MEMORY_NV) 23 | endif() 24 | 25 | add_definitions(-D__GL_VERSION=${OPENGL_VERSION}) 26 | message(STATUS "Using OpenGL version ${OPENGL_VERSION}.") 27 | 28 | add_subdirectory(src) 29 | 30 | # modify the module path so that the custom macros can be loaded downstream. 31 | # Kinda hacky 32 | if(NOT PROJECT_IS_TOP_LEVEL) 33 | set(CMAKE_MODULE_PATH 34 | "${CMAKE_MODULE_PATH};${PROJECT_SOURCE_DIR}/cmake" 35 | PARENT_SCOPE) 36 | endif() 37 | 38 | # Prevent building tests for FetchContent build: https://www.foonathan.net/2022/06/cmake-fetchcontent/ 39 | if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 40 | option(GLOW_BUILD_TESTS "whether or not tests should be built" ON) 41 | 42 | # Grep running processes for hints of gui. We only want to enable tests if they 43 | # can run. OpenGL tests depend on running X server and cannot be run without it. 44 | execute_process( 45 | COMMAND service --status-all 46 | COMMAND grep -P \\+.*\(gdm3|lightdm\) 47 | OUTPUT_VARIABLE XSERVER_FOUND) 48 | message(STATUS "Found X server related running processes: \n${XSERVER_FOUND}") 49 | 50 | if(XSERVER_FOUND OR GLOW_BUILD_TESTS) 51 | message(STATUS "Building tests") 52 | enable_testing() 53 | add_subdirectory(test) 54 | endif() 55 | 56 | endif() -------------------------------------------------------------------------------- /include/glow/GlSampler.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLSAMPLER_H_ 2 | #define INCLUDE_RV_GLSAMPLER_H_ 3 | 4 | #include "GlObject.h" 5 | #include "GlTexture.h" 6 | 7 | namespace glow { 8 | 9 | /** \brief Representation of OpenGL's sampler object for texture sampling options. 10 | * 11 | * Usually, you can specify the texture sampling options right with the texture itself. However, 12 | * sometimes it would be nice to have sampling parameters in one place or different sampling 13 | * parameters for the same texture. This is exactly the use case of sampler objects: if a sampler 14 | * object is bound to a specified texture unit, 15 | * 16 | * TODO: have bind with multiple texture units. (glBindSamplers?) 17 | * TODO: Ensure consistent setting of sampler for TEXTURE_RECTANGLE? 18 | * 19 | * \author behley 20 | */ 21 | class GlSampler : public GlObject { 22 | public: 23 | GlSampler(); 24 | 25 | /** \brief use sampler for specific texture unit identified by its index (0, 1, ...). **/ 26 | void bind(uint32_t textureUnitId); 27 | 28 | /** \brief "unuse" sampler for given texture unit. **/ 29 | void release(uint32_t textureUnitId); 30 | 31 | /** \brief set the filtering operation if texture is projected on smaller elements (squashed). **/ 32 | void setMinifyingOperation(TexMinOp minifyingOperation); 33 | /** \brief set the filtering operation if texture is projected on larger elements (enlarged). **/ 34 | void setMagnifyingOperation(TexMagOp magnifyingOperation); 35 | 36 | /** \brief set the wrapping function for accessing values outside the texture. **/ 37 | void setWrapOperation(TexWrapOp wrap_s); 38 | /** \brief set the wrapping function for accessing values outside the texture. **/ 39 | void setWrapOperation(TexWrapOp wrap_s, TexWrapOp wrap_t); 40 | /** \brief set the wrapping function for accessing values outside the texture. **/ 41 | void setWrapOperation(TexWrapOp wrap_s, TexWrapOp wrap_t, TexWrapOp wrap_r); 42 | 43 | protected: 44 | void bind() override; 45 | void release() override; 46 | 47 | uint32_t boundUnit_{0}; 48 | }; 49 | } 50 | /* namespace rv */ 51 | 52 | #endif /* INCLUDE_RV_GLSAMPLER_H_ */ 53 | -------------------------------------------------------------------------------- /test/framebuffer-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace glow; 9 | 10 | namespace { 11 | 12 | TEST(FrambufferTest, initTest) { 13 | GlFramebuffer buf(10, 10); 14 | GlRenderbuffer rbo(10, 10, RenderbufferFormat::DEPTH_STENCIL); 15 | buf.attach(FramebufferAttachment::DEPTH_STENCIL, rbo); 16 | // note: as always...not a framebuffer until first time bound. 17 | buf.bind(); 18 | buf.release(); 19 | 20 | ASSERT_TRUE(glIsFramebuffer(buf.id())); 21 | ASSERT_NO_THROW(CheckGlError()); 22 | } 23 | 24 | TEST(FramebufferTest, testStatelessness) { 25 | GlState priorState = GlState::queryAll(); 26 | CheckGlError(); 27 | 28 | GlFramebuffer buf(640, 480); 29 | if (priorState != GlState::queryAll()) priorState.difference(GlState::queryAll()); 30 | ASSERT_EQ(true, (priorState == GlState::queryAll())); 31 | 32 | GlTexture texture(640, 480, TextureFormat::RGBA); 33 | GlRenderbuffer rbo(640, 480, RenderbufferFormat::DEPTH_STENCIL); 34 | if (priorState != GlState::queryAll()) priorState.difference(GlState::queryAll()); 35 | ASSERT_EQ(true, (priorState == GlState::queryAll())); 36 | 37 | buf.attach(FramebufferAttachment::COLOR0, texture); 38 | // glBindFramebuffer(GL_FRAMEBUFFER, 0); 39 | // glFinish(); 40 | if (priorState != GlState::queryAll()) priorState.difference(GlState::queryAll()); 41 | ASSERT_EQ(true, (priorState == GlState::queryAll())); 42 | 43 | buf.attach(FramebufferAttachment::DEPTH_STENCIL, rbo); 44 | // glBindFramebuffer(GL_FRAMEBUFFER, 0); 45 | // glFinish(); 46 | if (priorState != GlState::queryAll()) priorState.difference(GlState::queryAll()); 47 | ASSERT_EQ(true, (priorState == GlState::queryAll())); 48 | 49 | buf.bind(); 50 | 51 | buf.release(); 52 | if (priorState != GlState::queryAll()) priorState.difference(GlState::queryAll()); 53 | ASSERT_EQ(true, (priorState == GlState::queryAll())); 54 | 55 | buf.valid(); 56 | if (priorState != GlState::queryAll()) priorState.difference(GlState::queryAll()); 57 | ASSERT_EQ(true, (priorState == GlState::queryAll())); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/glow/GlQuery.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlQuery.h" 2 | #include "glow/glexception.h" 3 | 4 | namespace glow { 5 | 6 | template <> 7 | void GlQuery::value(int32_t& value) const { 8 | GLint params{0}; 9 | glGetQueryObjectiv(id_, GL_QUERY_RESULT, ¶ms); 10 | value = params; 11 | } 12 | 13 | template <> 14 | void GlQuery::value(uint32_t& value) const { 15 | GLuint params{0}; 16 | glGetQueryObjectuiv(id_, GL_QUERY_RESULT, ¶ms); 17 | value = params; 18 | } 19 | 20 | GlQuery::GlQuery(QueryTarget target) : target_(static_cast(target)) { 21 | glGenQueries(1, &id_); 22 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 23 | glDeleteQueries(1, ptr); 24 | delete ptr; 25 | }); 26 | } 27 | 28 | /** conversion operators. **/ 29 | GlQuery::operator uint32_t() { 30 | GLuint params = 0; 31 | glGetQueryObjectuiv(id_, GL_QUERY_RESULT, ¶ms); 32 | 33 | return params; 34 | } 35 | 36 | GlQuery::operator int32_t() { 37 | GLint params = 0; 38 | glGetQueryObjectiv(id_, GL_QUERY_RESULT, ¶ms); 39 | 40 | return params; 41 | } 42 | 43 | /** \brief begin indexed query for specified index. 44 | * If the specified target does not support an index by definition, this function calls 45 | * simply the index 0 version. 46 | **/ 47 | void GlQuery::begin(uint32_t index) { 48 | if (started_) throw GlQueryError("Query object already active."); 49 | 50 | // if VERSION < GL_4_0 => only glBeginQuery(target_, id_); is available. 51 | #if __GL_VERSION >= 400L 52 | if (target_ == GL_TIME_ELAPSED || target_ == GL_ANY_SAMPLES_PASSED || target_ == GL_SAMPLES_PASSED) index = 0; 53 | glBeginQueryIndexed(target_, index, id_); 54 | index_ = index; 55 | #else 56 | glBeginQuery(target_, id_); 57 | index_ = 0; 58 | #endif 59 | 60 | started_ = true; 61 | } 62 | 63 | void GlQuery::end() { 64 | #if __GL_VERSION >= 400L 65 | glEndQueryIndexed(target_, index_); 66 | #else 67 | glEndQuery(target_); 68 | #endif 69 | started_ = false; 70 | } 71 | 72 | /** \brief retrieve if query result is available. **/ 73 | bool GlQuery::ready() const { 74 | GLint params; 75 | glGetQueryObjectiv(id_, GL_QUERY_RESULT_AVAILABLE, ¶ms); 76 | 77 | return (params == GL_TRUE); 78 | } 79 | 80 | /** \brief get the value of the query with specified type. **/ 81 | 82 | void GlQuery::bind() { 83 | // noop. 84 | } 85 | 86 | void GlQuery::release() { 87 | // noop. 88 | } 89 | 90 | } /* namespace rv */ 91 | -------------------------------------------------------------------------------- /cmake/GlowShaderCompilation.cmake: -------------------------------------------------------------------------------- 1 | if(_GLOW_COMPILE_INCLUDED_) 2 | return() 3 | endif() 4 | # guard again multiple inclusion: 5 | set(_GLOW_COMPILE_INCLUDED_ TRUE) 6 | # get directory of the macros (inside the macro it will return the CMakeLists.txt directory of calling code) 7 | set(GLOW_MODULE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 8 | 9 | 10 | ## CompileShaders 11 | ## -------------- 12 | ## Generates a cpp file with initialization code for the cache. 13 | ## 14 | ## COMPILE_SHADERS( ...) 15 | ## 16 | ## Write source file with given to initialize shader cache 17 | ## with given one or multiple . 18 | ## 19 | ## 20 | macro(COMPILE_SHADERS outfile ) 21 | 22 | # Avoid overwriting non-cache files. 23 | if(EXISTS ${outfile}) 24 | file(READ ${outfile} HEADER LIMIT 17) 25 | if(NOT "${HEADER}" STREQUAL "") 26 | 27 | string(STRIP ${HEADER} HEADER) 28 | 29 | if(NOT "${HEADER}" STREQUAL "// GLOW_CACHE V10") 30 | message(FATAL_ERROR "Trying to overwrite file '${outfile}', which appears not to be a generated cache file!") 31 | endif() 32 | endif() 33 | endif() 34 | 35 | set(absolute_filenames "") 36 | foreach(file ${ARGN}) 37 | get_filename_component(abs_file ${file} ABSOLUTE) 38 | if(EXISTS ${abs_file}) 39 | set(absolute_filenames ${absolute_filenames} ${abs_file}) 40 | else() 41 | message(WARNING "Shader with filename '${file}' does not exists!") 42 | endif() 43 | endforeach() 44 | 45 | #message(STATUS "Running COMPILE_SHADERS ${outfile}") 46 | #message(STATUS "absolute_filenames = ${absolute_filenames}") 47 | 48 | 49 | set(GLOW_CACHE_CPP_FILENAME ${outfile} CACHE INTERNAL "output filename") 50 | set(GLOW_CACHE_SHADER_FILENAMES ${absolute_filenames} CACHE INTERNAL "shader filenames") 51 | 52 | # Needed to get the command line options: 53 | separate_arguments(SHADER_ARG UNIX_COMMAND "${GLOW_CACHE_SHADER_FILENAMES}") 54 | 55 | 56 | add_custom_command(OUTPUT ${outfile} 57 | COMMAND ${CMAKE_COMMAND} 58 | ARGS -DGLOW_CACHE_CPP_FILENAME:INTERNAL=${GLOW_CACHE_CPP_FILENAME} -DGLOW_CACHE_SHADER_FILENAMES:INTERNAL=${SHADER_ARG} -P ${GLOW_MODULE_DIRECTORY}/GenCppFile.cmake 59 | DEPENDS ${ARGN} ${GLOW_MODULE_DIRECTORY}/GenCppFile.cmake ${GLOW_MODULE_DIRECTORY}/GlowShaderCompilation.cmake 60 | VERBATIM) # <-- VERBATIM needed to escape the semicolons. 61 | 62 | 63 | 64 | endmacro() -------------------------------------------------------------------------------- /include/glow/GlCapabilities.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLCAPABILITIES_H_ 2 | #define INCLUDE_RV_GLCAPABILITIES_H_ 3 | 4 | #include 5 | 6 | #include "GlState.h" 7 | 8 | 9 | namespace glow { 10 | 11 | /** \brief Get the limits and capabilities of the associated OpenGL context. 12 | * 13 | * Queries all the fixed capabilities of the currently associated OpenGL context, 14 | * such as maximal number of available texture units, maximum number of buffer bindings, etc. 15 | * 16 | * This query is quite expensive, therefore only once really performed when generating the 17 | * singleton. Thus, if you need to query the capabilities of the underlying GL context, do 18 | * it once in not time critical. 19 | * 20 | * In contrast to the state of the OpenGL context, these values can not be changed. Thus, 21 | * GlState captures all values that can be set by the driver and GlCapabilities contains all 22 | * constants that cannot be adjusted. 23 | * 24 | * \author behley 25 | */ 26 | class GlCapabilities { 27 | public: 28 | static GlCapabilities& getInstance(); 29 | 30 | /** \brief get value for given parameter name. **/ 31 | template 32 | T get(GLenum variable) const; 33 | 34 | friend std::ostream& operator<<(std::ostream& out, const GlCapabilities& cap); 35 | 36 | protected: 37 | GlCapabilities(); 38 | 39 | std::string stringify_name(GLenum name) const; 40 | std::string stringify_value(const std::pair& entry) const; 41 | 42 | template 43 | void initGlParameter(GLenum name, uint32_t num_values); 44 | 45 | static std::unique_ptr instance_; 46 | std::map state_; 47 | }; 48 | 49 | //template 50 | //T GlCapabilities::get(GLenum variable) const { 51 | // if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 52 | // auto it = state_.find(variable); 53 | // 54 | // if (it->second.type == GlState::GlStateVariable::INT) { 55 | // return T(it->second.vali[0]); 56 | // } else if (it->second.type == GlState::GlStateVariable::FLOAT) { 57 | // return T(it->second.valf[0]); 58 | // } else if (it->second.type == GlState::GlStateVariable::BOOL) { 59 | // return T(it->second.valb); 60 | // } 61 | // 62 | // // should never reach this... 63 | // throw std::runtime_error("Unknown state variable type."); 64 | //} 65 | 66 | } // namespace glow 67 | 68 | #endif /* INCLUDE_RV_GLCAPABILITIES_H_ */ 69 | -------------------------------------------------------------------------------- /include/glow/glbase.h: -------------------------------------------------------------------------------- 1 | #ifndef RV_GLPLATFORM_H 2 | #define RV_GLPLATFORM_H 3 | 4 | /** 5 | * some generic definitions needed by most OpenGL related functions. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include 12 | #include "glexception.h" 13 | 14 | // If no version is specified, fallback to OpenGL version 3.30 15 | #ifndef __GL_VERSION 16 | #define __GL_VERSION 330L 17 | #endif 18 | 19 | namespace glow { 20 | 21 | inline void _CheckGlError(const char *sFile, const int nLine) { 22 | GLenum glError = glGetError(); 23 | if (glError != GL_NO_ERROR) { 24 | std::cerr << "OpenGL Error: " << gluErrorString(glError) << "(" << glError << ")" << std::endl; 25 | std::cerr << "In: " << sFile << " on Line: " << nLine << std::endl; 26 | throw std::runtime_error("OpenGL error detected."); 27 | }; 28 | } 29 | 30 | static bool glewInitiatalized = false; 31 | 32 | // inspired by CheckGlDieOnError in pangolin. 33 | #undef CheckGlError 34 | 35 | //#define CheckGlError() (glow::_CheckGlError(__FILE__, __LINE__)) 36 | 37 | #ifdef NDEBUG 38 | #define CheckGlError() (static_cast(0)) 39 | #else 40 | #define CheckGlError() (glow::_CheckGlError(__FILE__, __LINE__)) 41 | #endif 42 | 43 | inline void inititializeGLEW() { 44 | if (!glewInitiatalized) { 45 | glewExperimental = GL_TRUE; 46 | GLenum err = glewInit(); 47 | if (err != GLEW_OK) { 48 | std::cerr << glewGetErrorString(err) << std::endl; 49 | throw std::runtime_error("Failed to initialize GLEW."); 50 | } 51 | 52 | std::cout << "GLEW initialized." << std::endl; 53 | glewInitiatalized = true; 54 | 55 | int32_t version[2]; 56 | glGetIntegerv(GL_MAJOR_VERSION, &version[0]); 57 | glGetIntegerv(GL_MINOR_VERSION, &version[1]); 58 | std::cout << "OpenGL context version: " << version[0] << "." << version[1] << std::endl; 59 | std::cout << "OpenGL vendor string : " << glGetString(GL_VENDOR) << std::endl; 60 | std::cout << "OpenGL renderer string: " << glGetString(GL_RENDERER) << std::endl; 61 | 62 | // consume here any OpenGL error and reset to NO_GL_ERROR: 63 | glGetError(); 64 | } 65 | 66 | #ifdef NDEBUG 67 | std::cout << "[info] Using glow in RELEASE mode." << std::endl; 68 | #else 69 | std::cout << "[info] Using glow in DEBUG mode." << std::endl; 70 | #endif 71 | } 72 | 73 | #define PRINT_VALUE(CMD) std::cout << #CMD << " = " << CMD << std::endl; 74 | #define PRINT_CLASSVALUE(CLASS, CMD) std::cout << #CLASS << "::" << #CMD << " = " << CMD << std::endl; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /include/glow/GlColor.h: -------------------------------------------------------------------------------- 1 | #ifndef GLCOLOR_H_ 2 | #define GLCOLOR_H_ 3 | 4 | #include 5 | #include 6 | #include "glutil.h" 7 | 8 | namespace glow { 9 | 10 | /** 11 | * \brief GlColor represents a RGB color given by [0.0,1.0] values and an alpha value also in [0.0,1.0]. 12 | * 13 | * This class represents RGB colors and provides some helper functions. 14 | * 15 | * Usage example setting color to red: 16 | * 17 | * program.setUniform(GlUniform("my_color", GlColor::RED)); 18 | * 19 | * 20 | * \author behley 21 | */ 22 | class GlColor { 23 | public: 24 | GlColor() : R(0.0f), G(0.0f), B(0.0f), A(1.0f) {} 25 | 26 | GlColor(int32_t r, int32_t g, int32_t b, int32_t a = 255) 27 | : R(float(r) / 255.), G(float(g) / 255.), B(float(b) / 255.), A(float(a) / 255.) {} 28 | 29 | /** \brief Initialize color with rgb in values in [0,1] **/ 30 | GlColor(float r, float g, float b, float a = 1.0f) : R(r), G(g), B(b), A(a) {} 31 | 32 | /** \brief convert to float* 33 | * NOTE: array is valid as long as object is not destroyed. 34 | */ 35 | operator float*() { return &R; } 36 | 37 | operator const float*() const { return &R; } 38 | 39 | /** \brief access to the RGB values as in an array **/ 40 | inline float operator[](int i) { return (&R)[i]; } 41 | 42 | float toFloat() { 43 | int32_t rgb = int32_t(round(R * 255.0f)); 44 | rgb = (rgb << 8) + int32_t(round(G * 255.0f)); 45 | rgb = (rgb << 8) + int32_t(round(B * 255.0f)); 46 | 47 | return float(rgb); 48 | } 49 | 50 | /** \brief generate Color from HSV values. 51 | * The values must be provided as follows: 52 | * - hue in [0,2pi], 53 | * - saturation in [0.0, 1.0f] 54 | * - value in [0.0, 1.0f] 55 | **/ 56 | static GlColor FromHSV(float h, float s, float v, float a = 1.0f); 57 | /** \brief generate Color from RGB values in [0, 255] **/ 58 | static GlColor FromRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255); 59 | 60 | void lighter(float factor = 1.0); 61 | void darker(float factor = 1.0); 62 | 63 | float R, G, B, A; 64 | 65 | /** some common predefined colors. **/ 66 | static const GlColor WHITE; 67 | static const GlColor GRAY; 68 | static const GlColor BLACK; 69 | static const GlColor RED; 70 | static const GlColor GREEN; 71 | static const GlColor BLUE; 72 | static const GlColor YELLOW; 73 | static const GlColor PINK; 74 | static const GlColor ORANGE; 75 | static const GlColor CYAN; 76 | static const GlColor GOLD; 77 | 78 | protected: 79 | void toHSV(float& h, float& s, float& v, float& a); 80 | void setHSV(float h, float s, float v, float a); 81 | }; 82 | } 83 | #endif /* GlColor_H */ 84 | -------------------------------------------------------------------------------- /src/glow/util/GlCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/util/GlCamera.h" 2 | #include "glow/glutil.h" 3 | 4 | namespace glow { 5 | 6 | GlCamera::GlCamera() { 7 | } 8 | 9 | GlCamera::~GlCamera() { 10 | } 11 | 12 | const Eigen::Matrix4f& GlCamera::matrix() { 13 | return view_; 14 | } 15 | 16 | void GlCamera::setMatrix(const Eigen::Matrix4f& m) { 17 | view_ = m; 18 | } 19 | 20 | void GlCamera::setPosition(float x, float y, float z) { 21 | view_(0, 3) = 0; 22 | view_(1, 3) = 0; 23 | view_(2, 3) = 0; 24 | view_ = view_ * glTranslate(-x, -y, -z); 25 | } 26 | 27 | Eigen::Vector4f GlCamera::getPosition() const { 28 | return Eigen::Vector4f(view_(0, 3), view_(1, 3), view_(2, 3), 1.0f); 29 | } 30 | 31 | void GlCamera::lookAt(float x_ref, float y_ref, float z_ref) { 32 | Eigen::Matrix4f m = view_.inverse(); 33 | lookAt(m(0, 3), m(1, 3), m(2, 3), x_ref, y_ref, z_ref); 34 | } 35 | 36 | void GlCamera::lookAt(float x_cam, float y_cam, float z_cam, float x_ref, float y_ref, float z_ref) { 37 | // determine rotation from current location: 38 | Eigen::Vector3f pos_cam(x_cam, y_cam, z_cam); 39 | Eigen::Vector3f pos(x_ref, y_ref, z_ref); 40 | Eigen::Vector3f up(0.0f, 1.0f, 0.0f); 41 | Eigen::Vector3f f = (pos - pos_cam).normalized(); 42 | Eigen::Vector3f x_axis = f.cross(up).normalized(); 43 | Eigen::Vector3f y_axis = x_axis.cross(f).normalized(); 44 | Eigen::Vector3f z_axis = -f; 45 | 46 | view_ = Eigen::Matrix4f::Zero(); 47 | 48 | view_(0, 0) = x_axis[0]; 49 | view_(0, 1) = x_axis[1]; 50 | view_(0, 2) = x_axis[2]; 51 | 52 | view_(1, 0) = y_axis[0]; 53 | view_(1, 1) = y_axis[1]; 54 | view_(1, 2) = y_axis[2]; 55 | 56 | view_(2, 0) = z_axis[0]; 57 | view_(2, 1) = z_axis[1]; 58 | view_(2, 2) = z_axis[2]; 59 | 60 | view_(3, 3) = 1.0f; 61 | 62 | // effectively => R * T 63 | view_(0, 3) = -pos_cam.dot(x_axis); 64 | view_(1, 3) = -pos_cam.dot(y_axis); 65 | view_(2, 3) = -pos_cam.dot(z_axis); 66 | } 67 | 68 | bool GlCamera::mousePressed(float x, float y, MouseButton btn, KeyboardModifier modifier) { 69 | return false; 70 | } 71 | 72 | bool GlCamera::mouseReleased(float x, float y, MouseButton btn, KeyboardModifier modifier) { 73 | return false; 74 | } 75 | 76 | bool GlCamera::mouseMoved(float x, float y, MouseButton btn, KeyboardModifier modifier) { 77 | return false; 78 | } 79 | 80 | bool GlCamera::wheelEvent(float delta, KeyboardModifier modifier) { 81 | return false; 82 | } 83 | 84 | bool GlCamera::keyPressed(KeyboardKey key, KeyboardModifier modifier) { 85 | return false; 86 | } 87 | 88 | bool GlCamera::keyReleased(KeyboardKey key, KeyboardModifier modifier) { 89 | return false; 90 | } 91 | 92 | } /* namespace glow */ 93 | -------------------------------------------------------------------------------- /include/glow/GlShader.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOW_GLSHADER_H_ 2 | #define GLOW_GLSHADER_H_ 3 | 4 | #include "GlObject.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace glow { 10 | 11 | enum class ShaderType { 12 | VERTEX_SHADER = GL_VERTEX_SHADER, 13 | TESS_CONTROL_SHADER = GL_TESS_CONTROL_SHADER, 14 | TESS_EVALUTION_SHADER = GL_TESS_EVALUATION_SHADER, 15 | GEOMETRY_SHADER = GL_GEOMETRY_SHADER, 16 | FRAGMENT_SHADER = GL_FRAGMENT_SHADER, 17 | COMPUTE_SHADER = GL_COMPUTE_SHADER 18 | }; 19 | 20 | /** 21 | * \brief Object for OpenGL's shader 22 | * 23 | * The \a GlShader offer methods initialize and handle OpenGL shaders. 24 | * On initialization via constructor or the method fromFile(), the OpenGL 25 | * shader object is created and the source is pre-processed. Finally, the 26 | * pre-processed code is compiled. The GlShader can be then attached to a 27 | * GlProgram object. 28 | * 29 | * Inspired by Pangolin, we also add #include preprocessing directive to 30 | * allow inclusion of common code. 31 | * 32 | * \author behley 33 | */ 34 | class GlShader : public GlObject { 35 | public: 36 | friend class GlProgram; 37 | 38 | /** \brief Create Shader of a specific \p type with given \p source. 39 | * 40 | * Creates the shader object with \a type and compiles the given \a source. 41 | * 42 | * \throw GlShaderError 43 | **/ 44 | GlShader(const ShaderType& type, const std::string& source, bool useCache = false); 45 | 46 | /** \brief return the type of the shader **/ 47 | ShaderType type() const; 48 | 49 | /** \brief Create Shader from given file with \p filename 50 | * 51 | * \return compiled shader 52 | * \throw GlShaderError if reading of file or shader compilation fails. 53 | **/ 54 | static GlShader fromFile(const ShaderType& type, const std::string& filename); 55 | 56 | /** \brief Create Shader from compiled cache source with given \p filename 57 | * 58 | * \return compiled shader 59 | * \throw GlShaderError if cache does not contain given file or shader compilation fails. 60 | */ 61 | static GlShader fromCache(const ShaderType& type, const std::string& filename); 62 | 63 | protected: 64 | // hide bind() and release() 65 | void bind() override; 66 | void release() override; 67 | 68 | ShaderType type_; 69 | 70 | static std::string getCachedSource(const std::string& filename); 71 | static std::string readSource(const std::string& filename); 72 | 73 | std::string preprocess(const std::string& source); 74 | 75 | struct Attribute { 76 | public: 77 | std::string type; 78 | std::string name; 79 | }; 80 | 81 | // shader meta information accessible from GlProgram. 82 | std::string filename; 83 | bool useCache_{false}; 84 | 85 | std::vector inAttribs_; 86 | std::vector outAttribs_; 87 | }; 88 | } 89 | 90 | #endif /* INCLUDE_GLOW_GLSHADER_H_ */ 91 | -------------------------------------------------------------------------------- /cmake/GenCppFile.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # CMake script which is executed via cmake -P GenCppFile.cmake 3 | # Generating a static cache initializer class and its source to statically initialize 4 | # the GLOW shader cache. The variables are passed via the CMake cache. 5 | # 6 | # GLOW_CACHE_CPP_FILENAME: filename of the generated .cpp file. 7 | # GLOW_CACHE_SHADER_FILENAMES: filenames of the shader files for which an entry should be generated. 8 | # 9 | # 10 | # author: jens behley 11 | # 12 | 13 | # needed to avoid calls with empty destination filename 14 | if(GLOW_CACHE_CPP_FILENAME) 15 | 16 | file(LOCK ${GLOW_CACHE_CPP_FILENAME} RESULT_VARIABLE lock_status) 17 | # message(STATUS "lock_status = ${lock_status}") 18 | if(${lock_status} EQUAL "0") 19 | 20 | set(FILE_CONTENT "") 21 | 22 | # message(STATUS ">> GenCppFile: Writing shader source to '${GLOW_CACHE_CPP_FILENAME}'") 23 | # message(STATUS ">> GenCppFile: Shader files: ${GLOW_CACHE_SHADER_FILENAMES}") 24 | 25 | get_filename_component(GLOW_CACHE_NAMESPACE ${GLOW_CACHE_CPP_FILENAME} NAME_WE) 26 | string(MAKE_C_IDENTIFIER ${GLOW_CACHE_NAMESPACE} GLOW_CACHE_NAMESPACE) 27 | # message(STATUS ">> namespace: '${GLOW_CACHE_NAMESPACE}'") 28 | 29 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "// GLOW_CACHE V10\n") 30 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "// This file is automatically generated. All changes will be lost when calling cmake.\n\n") 31 | 32 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "\#include \n\#include \n\n") 33 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "namespace ${GLOW_CACHE_NAMESPACE} {\n") 34 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "class CacheInitalizer {\n public:\n CacheInitalizer() {\n") 35 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" " glow::GlShaderCache& cache = glow::GlShaderCache::getInstance();\n") 36 | 37 | foreach(shader_file ${GLOW_CACHE_SHADER_FILENAMES}) 38 | file(READ ${shader_file} content) 39 | 40 | # escape special characters for string variable. It might be neccessary to escape more then these? 41 | # Reminder: Quotes are important to avoid removal of ";" 42 | string(REPLACE "\n" "\\n" content "${content}") 43 | string(REPLACE "\"" "\\\"" content "${content}") 44 | 45 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" " cache.insertSource(\"${shader_file}\", \"${content}\");\n") 46 | endforeach() 47 | 48 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" " };\n};\n\n") 49 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "static CacheInitalizer __init__;\n") 50 | string(CONCAT FILE_CONTENT "${FILE_CONTENT}" "} // end namespace ${GLOW_CACHE_NAMESPACE}") 51 | 52 | 53 | file(LOCK ${GLOW_CACHE_CPP_FILENAME} RELEASE) 54 | file(WRITE ${GLOW_CACHE_CPP_FILENAME} "${FILE_CONTENT}") 55 | 56 | endif() 57 | 58 | endif() -------------------------------------------------------------------------------- /include/glow/util/enum_utils.h: -------------------------------------------------------------------------------- 1 | // declare bit operations on given enum. 2 | 3 | // credit goes to emsr: http://stackoverflow.com/questions/12059774/c11-standard-conformant-bitmasks-using-enum-class 4 | #ifndef ENUM_BIT_OPERATIONS 5 | 6 | #define ENUM_BIT_OPERATIONS(T) \ 7 | inline constexpr T operator&(T __x, T __y) { return static_cast(static_cast(__x) & static_cast(__y)); } \ 8 | inline constexpr T operator|(T __x, T __y) { return static_cast(static_cast(__x) | static_cast(__y)); } \ 9 | inline constexpr T operator^(T __x, T __y) { \ 10 | return static_cast(static_cast(__x) ^ static_cast(__y)); \ 11 | } inline constexpr T \ 12 | operator~(T __x) { \ 13 | return static_cast(~static_cast(__x)); \ 14 | } \ 15 | inline T& operator&=(T& __x, T __y) { \ 16 | __x = __x & __y; \ 17 | return __x; \ 18 | } \ 19 | inline T& operator|=(T& __x, T __y) \ 20 | \ 21 | { \ 22 | __x = __x | __y; \ 23 | return __x; \ 24 | } \ 25 | inline T& operator^=(T& __x, T __y) { \ 26 | __x = __x ^ __y; \ 27 | return __x; \ 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/glow/GlProgram.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLPROGRAM_H_ 2 | #define INCLUDE_RV_GLPROGRAM_H_ 3 | 4 | #include "GlObject.h" 5 | #include "GlShader.h" 6 | 7 | #include "GlUniform.h" 8 | #include "GlTransformFeedback.h" 9 | 10 | #include 11 | 12 | namespace glow { 13 | 14 | /** 15 | * \brief Object for OpenGL's programs binding multiple shaders. 16 | * 17 | * Each \a GlProgram can only have a single shader for each shader stage. 18 | * The method \a attach keeps track of the shaders for each stage and only the 19 | * last shader for a shader stage is used for linking the program. 20 | * 21 | * For linking of a program, all shaders are first attached, the program is 22 | * linked by matching of input/output variables, and finally all shaders 23 | * are detached. The detaching enables to finally delete the shaders. Unmatched 24 | * input output variables or attributes of shaders will issue warnings. 25 | * 26 | * \a GlProgram offers bind() to enable the program for subsequent usage and 27 | * release() to unbind the program. 28 | * 29 | * \author behley 30 | */ 31 | class GlProgram : public GlObject { 32 | public: 33 | /** \brief Create empty program object. **/ 34 | GlProgram(); 35 | 36 | /** \brief attach a \p shader to the program. 37 | * 38 | * Each shader stage can only have a single associated \a shader, i.e., 39 | * if more than one shader is added for a shader stage, only the last 40 | * added shader is kept. 41 | **/ 42 | void attach(const GlShader& shader); 43 | 44 | /** \brief attach \a transform feedback to the program. **/ 45 | void attach(const GlTransformFeedback& feedback); 46 | 47 | /** \brief link the attached shaders. 48 | * 49 | * Verify that at least a vertex and fragment shader is attached. 50 | * Tries to link the attached shaders and if linking succeeds the 51 | * shaders are detached. 52 | * 53 | * The link process might issue warnings for unmatched input/output 54 | * attributes between shaders. This should enable to avoid mistakes. 55 | * 56 | * \throw GlProgramError, if linking fails or if mandatory shader is missing. 57 | **/ 58 | void link(); 59 | 60 | /** \brief use program for rendering. **/ 61 | void bind() override; 62 | 63 | /** \brief "unuse" program **/ 64 | void release() override; 65 | 66 | /** \brief set uniform value 67 | * 68 | * \throw ProgramError, if setting of value failed, i.e., the uniform does not 69 | * exist or the program is currently not linked. 70 | **/ 71 | void setUniform(const GlAbstractUniform& uniform); 72 | 73 | protected: 74 | static GLuint boundProgram_; 75 | bool linked_{false}; 76 | 77 | // rational: each shader stage has a single shader; replace it if not needed. 78 | std::map shaders_; 79 | std::vector feedbacks_; 80 | 81 | GLuint bindTransparently(); 82 | void releaseTransparently(GLuint oldProgram); 83 | }; 84 | 85 | } /* namespace rv */ 86 | 87 | #endif /* INCLUDE_RV_GLPROGRAM_H_ */ 88 | -------------------------------------------------------------------------------- /src/glow/util/X11OffscreenContext.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/util/X11OffscreenContext.h" 2 | #include 3 | #include 4 | 5 | namespace glow { 6 | 7 | typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); 8 | typedef Bool (*glXMakeContextCurrentARBProc)(Display*, GLXDrawable, GLXDrawable, GLXContext); 9 | 10 | X11OffscreenContext::X11OffscreenContext(int32_t major_version, int32_t minor_version) { 11 | static int visual_attribs[] = {None}; 12 | int context_attribs[] = {GLX_CONTEXT_MAJOR_VERSION_ARB, 13 | major_version, 14 | GLX_CONTEXT_MINOR_VERSION_ARB, 15 | minor_version, 16 | GLX_CONTEXT_PROFILE_MASK_ARB, 17 | GLX_CONTEXT_CORE_PROFILE_BIT_ARB, 18 | None}; 19 | int fbcount = 0; 20 | GLXFBConfig* fbc = NULL; 21 | glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0; 22 | glXMakeContextCurrentARBProc glXMakeContextCurrentARB = 0; 23 | 24 | if (!(dpy = XOpenDisplay(0))) throw std::runtime_error("Failed to open display."); 25 | 26 | /* get framebuffer configs, any is usable (might want to add proper attribs) */ 27 | if (!(fbc = glXChooseFBConfig(dpy, DefaultScreen(dpy), visual_attribs, &fbcount))) 28 | throw std::runtime_error("Failed to get FBConfig."); 29 | 30 | if (fbcount == 0) throw std::runtime_error("Failed to get FBConfig."); 31 | 32 | /* get the required extensions */ 33 | glXCreateContextAttribsARB = 34 | (glXCreateContextAttribsARBProc)glXGetProcAddressARB((const GLubyte*)"glXCreateContextAttribsARB"); 35 | glXMakeContextCurrentARB = 36 | (glXMakeContextCurrentARBProc)glXGetProcAddressARB((const GLubyte*)"glXMakeContextCurrent"); 37 | if (!(glXCreateContextAttribsARB && glXMakeContextCurrentARB)) 38 | throw std::runtime_error("missing support for GLX_ARB_create_context."); 39 | 40 | /* create a context using glXCreateContextAttribsARB */ 41 | if (!(ctx = glXCreateContextAttribsARB(dpy, fbc[0], 0, True, context_attribs))) 42 | throw std::runtime_error("Failed to create opengl context."); 43 | 44 | /* create temporary pbuffer */ 45 | int pbuffer_attribs[] = {GLX_PBUFFER_WIDTH, 640, GLX_PBUFFER_HEIGHT, 480, None}; 46 | pbuf = glXCreatePbuffer(dpy, fbc[0], pbuffer_attribs); 47 | 48 | XFree(fbc); 49 | XSync(dpy, False); 50 | 51 | /* try to make it the current context */ 52 | if (!glXMakeContextCurrent(dpy, pbuf, pbuf, ctx)) { 53 | /* some drivers does not support context without default framebuffer, so fallback on 54 | * using the default window. 55 | */ 56 | std::cout << "not using pbuffer." << std::endl; 57 | if (!glXMakeContextCurrent(dpy, DefaultRootWindow(dpy), DefaultRootWindow(dpy), ctx)) 58 | throw std::runtime_error("Failed to make current."); 59 | } 60 | } 61 | 62 | X11OffscreenContext::~X11OffscreenContext() { 63 | if (ctx != nullptr) glXDestroyContext(dpy, ctx); 64 | if (dpy != nullptr) XCloseDisplay(dpy); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/glow/GlUniform.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlUniform.h" 2 | #include "glow/glexception.h" 3 | #include "glow/glutil.h" 4 | 5 | namespace glow { 6 | 7 | // explicit definition of uniforms for some common types. 8 | 9 | // Common Eigen types: 10 | template <> 11 | void GlUniform::bind(GLuint program_id) const { 12 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 13 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 14 | glUniformMatrix4fv(loc, 1, GL_FALSE, data_.data()); 15 | } 16 | 17 | template <> 18 | void GlUniform::bind(GLuint program_id) const { 19 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 20 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 21 | glUniform4fv(loc, 1, data_.data()); 22 | } 23 | 24 | template <> 25 | void GlUniform::bind(GLuint program_id) const { 26 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 27 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 28 | glUniform3fv(loc, 1, data_.data()); 29 | } 30 | 31 | template <> 32 | void GlUniform::bind(GLuint program_id) const { 33 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 34 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 35 | glUniform4fv(loc, 1, &data_.x); 36 | } 37 | 38 | template <> 39 | void GlUniform::bind(GLuint program_id) const { 40 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 41 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 42 | glUniform3fv(loc, 1, &data_.x); 43 | } 44 | 45 | template <> 46 | void GlUniform::bind(GLuint program_id) const { 47 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 48 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 49 | glUniform2fv(loc, 1, &data_.x); 50 | } 51 | 52 | // geometry.h types. 53 | 54 | // some primitive types 55 | template <> 56 | void GlUniform::bind(GLuint program_id) const { 57 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 58 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 59 | glUniform1i(loc, static_cast(data_)); 60 | } 61 | 62 | template <> 63 | void GlUniform::bind(GLuint program_id) const { 64 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 65 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 66 | glUniform1ui(loc, static_cast(data_)); 67 | } 68 | 69 | template <> 70 | void GlUniform::bind(GLuint program_id) const { 71 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 72 | // assert(loc >= 0 && "Warning: Uniform unkown or unused in program."); 73 | glUniform1i(loc, static_cast(data_)); 74 | } 75 | 76 | template <> 77 | void GlUniform::bind(GLuint program_id) const { 78 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 79 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 80 | glUniform1f(loc, static_cast(data_)); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenGL Object Wrapper (GLOW) -- a high-level layer for OpenGL. 2 | 3 | With this side project, I want to provide a simple high-level interface to OpenGL's objects with the following goals in mind: 4 | 5 | 1. simple resource management with automatic generation and deletion of OpenGL objects, 6 | 2. object related functions should be related to the object without the need to bind the right object at the right time, 7 | 3. catch potential errors or misuse of OpenGL functionality as early as possible, 8 | 4. allow only access to **core profile** functionality, since I needed it at some point in time. 9 | 10 | These goals should align with the main purpose of this framework: simple, effortless usage of OpenGL's functionality. 11 | However, it does not aim at providing some framework for GUI creation, etc. 12 | A OpenGL context has to be created independently by virtue of a context/window creation framework, like: 13 | - GLFW3 (http://www.glfw.org/) 14 | - Qt (http://doc.qt.io/qt-5/qt5-intro.html) 15 | - Pangolin (https://github.com/stevenlovegrove/Pangolin) 16 | 17 | I only included a simple headless context for computations & testing. 18 | 19 | This project is work in progress and will be extended over time. 20 | Most of the functionality included was implemented as it was needed by my own projects. 21 | Therefore, you cannot (or better should not) expect feature completness or stability of the interface. 22 | 23 | ## Related work 24 | 25 | This project is inspired by some other frameworks, which are perfectly fine, but lack some features or functionality that I want to have in my work. 26 | However, this framework is heavily inspired by these frameworks: 27 | 28 | - Pangolin (https://github.com/stevenlovegrove/Pangolin): Cross-platform GUI framework with OpenGL wrapper objects. 29 | Does not enforce the usage of core functionality. Furthermore, in some parts the code might not run on "pure" core profile contexts and 30 | due to the usage of NVIDIA specific extensions with context from other GPU vendors, like ATI and Intel. 31 | - OOGL (https://github.com/Overv/OOGL): didn't look deeply into this. 32 | - globjects (https://github.com/cginternals/globjects): Pretty complete high-level API for OpenGL. Also wrapping all objects into complete C++-classes. 33 | 34 | ## Requirements 35 | - GLEW (`libglew-dev`) 36 | - Eigen 3 (`libeigen-dev`) 37 | 38 | ## Features 39 | - *Shader compilation*: I usally have my shader files in separate text files and it's somehow annoying to move these stuff around and make it available relative to the executable. For this purpose, I build a solution based on CMAKE, which simply generates a statically initialized "storage" of shader strings, which can accessed by a call to a static method of `GlShaderCache`. It's somehow nice as it only needs cmake to generate the source, which can be then included in the build and therefore compiled into a library or executable. 40 | 41 | 42 | ## Future work 43 | - More documentation, examples, etc. 44 | - Enable/disable functionality for given OpenGL version by virtue of compile flags. 45 | - Feature completness for OpenGL version 4.5 (core profile) 46 | - Cross-plattform functionality 47 | - Extend GLSL parser with additional convenience functions a la Pangolin. 48 | 49 | -------------------------------------------------------------------------------- /include/glow/GlColor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "GlColor.h" 4 | #include "GlUniform.h" 5 | 6 | namespace glow { 7 | 8 | const GlColor GlColor::WHITE = GlColor(1.0f, 1.0f, 1.0f); 9 | const GlColor GlColor::GRAY = GlColor(0.5f, 0.5f, 0.5f); 10 | const GlColor GlColor::BLACK = GlColor(0.0f, 0.0f, 0.0f); 11 | const GlColor GlColor::RED = GlColor(1.0f, 0.0f, 0.0f); 12 | const GlColor GlColor::GREEN = GlColor(0.0f, 1.0f, 0.0f); 13 | const GlColor GlColor::BLUE = GlColor(0.0f, 0.0f, 1.0f); 14 | const GlColor GlColor::YELLOW = GlColor(1.0f, 1.0f, 0.0f); 15 | const GlColor GlColor::PINK = GlColor(1.0f, 0.0f, 1.0f); 16 | const GlColor GlColor::ORANGE = GlColor(1.0f, 0.65f, 0.0f); 17 | const GlColor GlColor::CYAN = GlColor(0.0f, 1.0f, 1.0f); 18 | const GlColor GlColor::GOLD = GlColor(0.85f, 0.65, 0.13f); 19 | 20 | template <> 21 | void GlUniform::bind(GLuint program_id) const { 22 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 23 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 24 | glUniform4fv(loc, 1, &data_.R); 25 | } 26 | 27 | GlColor GlColor::FromRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 28 | return GlColor((float)r / 255.f, (float)g / 255.f, (float)b / 255.f, (float)a / 255.f); 29 | } 30 | 31 | void GlColor::toHSV(float& h, float& s, float& v, float& a) { 32 | float M = std::max(R, std::max(G, B)); 33 | float m = std::min(R, std::min(G, B)); 34 | float C = M - m; 35 | float sixty = 1.04719755f; 36 | 37 | h = s = v = 0.0f; 38 | if (M == R) 39 | h = std::fmod((G - B) / C, 6.0f); 40 | else if (M == G) 41 | h = (B - R) / C + 2.0f; 42 | else if (M == B) 43 | h = (R - G) / C + 4.0f; 44 | 45 | h = sixty * h; 46 | v = M; 47 | if (v > 0.0f) s = C / v; 48 | a = A; 49 | } 50 | 51 | void GlColor::setHSV(float h, float s, float v, float a) { 52 | R = G = B = 0.0f; 53 | A = a; 54 | 55 | if (std::abs(s) < 0.00001) { 56 | R = G = B = v; 57 | return; 58 | } 59 | 60 | float sixty = 1.04719755f; // == 60 deg 61 | int32_t h_i = std::floor(h / sixty); /** integer part of h **/ 62 | float f = h / sixty - h_i; /** fractional part of h **/ 63 | float p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); 64 | 65 | switch (h_i) { 66 | case 0: 67 | case 6: 68 | R = v; 69 | G = t; 70 | B = p; 71 | break; 72 | case 1: 73 | R = q; 74 | G = v; 75 | B = p; 76 | break; 77 | case 2: 78 | R = p; 79 | G = v; 80 | B = t; 81 | break; 82 | case 3: 83 | R = p; 84 | G = q; 85 | B = v; 86 | break; 87 | case 4: 88 | R = t; 89 | G = p; 90 | B = v; 91 | break; 92 | case 5: 93 | R = v; 94 | G = p; 95 | B = q; 96 | break; 97 | } 98 | } 99 | 100 | GlColor GlColor::FromHSV(float h, float s, float v, float a) { 101 | GlColor col; 102 | col.setHSV(h, s, v, a); 103 | 104 | return col; 105 | } 106 | 107 | void GlColor::lighter(float factor) { 108 | float v = std::pow(1.0 / 0.7, factor); 109 | R *= v; 110 | G *= v; 111 | B *= v; 112 | } 113 | 114 | void GlColor::darker(float factor) { 115 | float v = std::pow(0.7, factor); 116 | R *= v; 117 | G *= v; 118 | B *= v; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/glow/GlColor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "glow/GlColor.h" 4 | #include "glow/GlUniform.h" 5 | 6 | namespace glow { 7 | 8 | const GlColor GlColor::WHITE = GlColor(1.0f, 1.0f, 1.0f); 9 | const GlColor GlColor::GRAY = GlColor(0.5f, 0.5f, 0.5f); 10 | const GlColor GlColor::BLACK = GlColor(0.0f, 0.0f, 0.0f); 11 | const GlColor GlColor::RED = GlColor(1.0f, 0.0f, 0.0f); 12 | const GlColor GlColor::GREEN = GlColor(0.0f, 1.0f, 0.0f); 13 | const GlColor GlColor::BLUE = GlColor(0.0f, 0.0f, 1.0f); 14 | const GlColor GlColor::YELLOW = GlColor(1.0f, 1.0f, 0.0f); 15 | const GlColor GlColor::PINK = GlColor(1.0f, 0.0f, 1.0f); 16 | const GlColor GlColor::ORANGE = GlColor(1.0f, 0.65f, 0.0f); 17 | const GlColor GlColor::CYAN = GlColor(0.0f, 1.0f, 1.0f); 18 | const GlColor GlColor::GOLD = GlColor(0.85f, 0.65, 0.13f); 19 | 20 | template <> 21 | void GlUniform::bind(GLuint program_id) const { 22 | GLint loc = glGetUniformLocation(program_id, name_.c_str()); 23 | // assert(loc >= 0 && "Warning: Uniform unknown or unused in program."); 24 | glUniform4fv(loc, 1, &data_.R); 25 | } 26 | 27 | GlColor GlColor::FromRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { 28 | return GlColor((float)r / 255.f, (float)g / 255.f, (float)b / 255.f, (float)a / 255.f); 29 | } 30 | 31 | void GlColor::toHSV(float& h, float& s, float& v, float& a) { 32 | float M = std::max(R, std::max(G, B)); 33 | float m = std::min(R, std::min(G, B)); 34 | float C = M - m; 35 | float sixty = 1.04719755f; 36 | 37 | h = s = v = 0.0f; 38 | if (M == R) 39 | h = std::fmod((G - B) / C, 6.0f); 40 | else if (M == G) 41 | h = (B - R) / C + 2.0f; 42 | else if (M == B) 43 | h = (R - G) / C + 4.0f; 44 | 45 | h = sixty * h; 46 | v = M; 47 | if (v > 0.0f) s = C / v; 48 | a = A; 49 | } 50 | 51 | void GlColor::setHSV(float h, float s, float v, float a) { 52 | R = G = B = 0.0f; 53 | A = a; 54 | 55 | if (std::abs(s) < 0.00001) { 56 | R = G = B = v; 57 | return; 58 | } 59 | 60 | float sixty = 1.04719755f; // == 60 deg 61 | int32_t h_i = std::floor(h / sixty); /** integer part of h **/ 62 | float f = h / sixty - h_i; /** fractional part of h **/ 63 | float p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); 64 | 65 | switch (h_i) { 66 | case 0: 67 | case 6: 68 | R = v; 69 | G = t; 70 | B = p; 71 | break; 72 | case 1: 73 | R = q; 74 | G = v; 75 | B = p; 76 | break; 77 | case 2: 78 | R = p; 79 | G = v; 80 | B = t; 81 | break; 82 | case 3: 83 | R = p; 84 | G = q; 85 | B = v; 86 | break; 87 | case 4: 88 | R = t; 89 | G = p; 90 | B = v; 91 | break; 92 | case 5: 93 | R = v; 94 | G = p; 95 | B = q; 96 | break; 97 | } 98 | } 99 | 100 | GlColor GlColor::FromHSV(float h, float s, float v, float a) { 101 | GlColor col; 102 | col.setHSV(h, s, v, a); 103 | 104 | return col; 105 | } 106 | 107 | void GlColor::lighter(float factor) { 108 | float v = std::pow(1.0 / 0.7, factor); 109 | R *= v; 110 | G *= v; 111 | B *= v; 112 | } 113 | 114 | void GlColor::darker(float factor) { 115 | float v = std::pow(0.7, factor); 116 | R *= v; 117 | G *= v; 118 | B *= v; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /include/glow/glutil.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLUTIL_H_ 2 | #define INCLUDE_RV_GLUTIL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /** \brief some utility functions and classes. **/ 9 | 10 | namespace glow { 11 | 12 | struct uvec2 { 13 | public: 14 | uvec2() : x(0), y(0) {} 15 | uvec2(uint32_t xx, uint32_t yy) : x(xx), y(yy) {} 16 | uint32_t x, y; 17 | }; 18 | 19 | /** \brief two-dimensional vector **/ 20 | struct vec2 { 21 | public: 22 | vec2() : x(0.0f), y(0.0f) {} 23 | 24 | vec2(float xx, float yy) : x(xx), y(yy) {} 25 | 26 | float x, y; 27 | }; 28 | 29 | /** \brief three-dimensional vector **/ 30 | struct vec3 { 31 | public: 32 | vec3() : x(0.0f), y(0.0f), z(0.0f) {} 33 | 34 | vec3(float xx, float yy, float zz) : x(xx), y(yy), z(zz) {} 35 | float x, y, z; 36 | }; 37 | 38 | /** \brief four-dimensional vector **/ 39 | struct vec4 { 40 | public: 41 | vec4() : x(0.0f), y(0.0f), z(0.0f), w(0.0f) {} 42 | 43 | vec4(float xx, float yy, float zz, float ww) : x(xx), y(yy), z(zz), w(ww) {} 44 | float x, y, z, w; 45 | }; 46 | 47 | /** \bried translate in x-,y-, and z-direction. **/ 48 | Eigen::Matrix4f glTranslate(float x, float y, float z); 49 | 50 | Eigen::Matrix4f glScale(float x, float y, float z); 51 | 52 | /** \brief rotate about x-axis by \a angle (radians). **/ 53 | Eigen::Matrix4f glRotateX(float angle); 54 | 55 | /** \brief rotate about y-axis by \a angle (radians). **/ 56 | Eigen::Matrix4f glRotateY(float angle); 57 | 58 | /** \brief rotate about z-axis by \a angle (radians). **/ 59 | Eigen::Matrix4f glRotateZ(float angle); 60 | 61 | /** \brief perspective projection matrix. /gluPerspective...**/ 62 | Eigen::Matrix4f glPerspective(float fov, float aspect, float znear, float zfar); 63 | 64 | /** \brief orthographic project matrix. /gluOrthographic.. **/ 65 | Eigen::Matrix4f glOrthographic(float left, float right, float bottom, float top, float znear, float zfar); 66 | 67 | /** \brief rotate about axis (x,y,z) by \a angle (radians) **/ 68 | Eigen::Matrix4f glRotateAxis(float angle, float x, float y, float z); 69 | 70 | /** \brief convert an angle given in degrees to radian **/ 71 | inline float radians(float deg) { 72 | return deg * M_PI / 180.0f; 73 | } 74 | 75 | /** \brief convert an angle given in radian to degrees **/ 76 | inline float degrees(float rad) { 77 | return rad * 180.0f / M_PI; 78 | } 79 | 80 | // coordinate transformations: 81 | 82 | /** \brief coordinate transform from RoSe to OpenGL coordinates. **/ 83 | struct RoSe2GL { 84 | public: 85 | RoSe2GL() = delete; 86 | 87 | static Eigen::Matrix4f matrix; 88 | }; 89 | 90 | /** \brief coordinate transform from RoSe to OpenGL coordinates. **/ 91 | struct GL2RoSe { 92 | public: 93 | GL2RoSe() = delete; 94 | 95 | static Eigen::Matrix4f matrix; 96 | }; 97 | 98 | /** \brief encode (r,g,b) value in [0,1]^3 to a single float (24-bits). **/ 99 | float rgb2float(float r, float g, float b); 100 | 101 | std::string extension(const std::string& path, int32_t level = 1); 102 | } // namespace glow 103 | 104 | std::ostream& operator<<(std::ostream& stream, glow::vec2& vec); 105 | std::ostream& operator<<(std::ostream& stream, glow::vec3& vec); 106 | std::ostream& operator<<(std::ostream& stream, glow::vec4& vec); 107 | 108 | std::ostream& operator<<(std::ostream& stream, const glow::vec2& vec); 109 | std::ostream& operator<<(std::ostream& stream, const glow::vec3& vec); 110 | std::ostream& operator<<(std::ostream& stream, const glow::vec4& vec); 111 | 112 | #endif /* INCLUDE_RV_GLUTIL_H_ */ 113 | -------------------------------------------------------------------------------- /include/glow/GlTransformFeedback.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLTRANSFORMFEEDBACK_H_ 2 | #define INCLUDE_RV_GLTRANSFORMFEEDBACK_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "GlBuffer.h" 8 | #include "GlCapabilities.h" 9 | #include "GlObject.h" 10 | #include "GlQuery.h" 11 | 12 | namespace glow { 13 | 14 | class GlProgram; 15 | 16 | /** \brief primitive modes for transform feedback. **/ 17 | enum class TransformFeedbackMode { POINTS = GL_POINTS, LINES = GL_LINES, TRIANGLES = GL_TRIANGLES }; 18 | 19 | /** \brief Object for transform feedbacks. 20 | * 21 | * Transform feedbacks can be used to capture transformed vertex data before the rasterization stage, 22 | * just before clipping. This enables the program to record data and use this for later processing in 23 | * the rendering pipeline. 24 | * 25 | * \see GlProgram::attach(GLTransformFeedback) 26 | * 27 | * \author behley 28 | */ 29 | class GlTransformFeedback : public GlObject { 30 | public: 31 | friend class GlProgram; 32 | 33 | GlTransformFeedback(); 34 | 35 | /** \brief bind transform feedback for current drawing calls. **/ 36 | void bind() override; 37 | 38 | /** \brief begin transform feedback **/ 39 | void begin(TransformFeedbackMode mode); 40 | 41 | /** \brief end transform feedback and return primitives written to buffer. **/ 42 | uint32_t end(); 43 | 44 | #if __GL_VERSION >= 400L 45 | /** \brief pause the capture of vertex data. Only available with OpenGL 4.0+. **/ 46 | void pause(); 47 | 48 | /** \brief resume previously pause transform feedback. **/ 49 | void resume(); 50 | 51 | /** \brief draw data from previous transform feedback call. **/ 52 | void draw(GLenum mode) const; 53 | #endif 54 | 55 | /** \brief release transform feedback. **/ 56 | void release() override; 57 | 58 | /** \brief add a buffer for given varyings to the transform feedback. 59 | * 60 | * The caller has to ensure that the buffer has enough space to capture the transform feedback. 61 | * 62 | * Adding a single buffer with a single or multiple attributes implies GL_INTERLEAVED_ATTRIBS. 63 | * Adding multiple buffers with a single attribute implies GL_SEPARATE_ATTRIBS. 64 | * 65 | * Since Open GL 4.0, it is also possible to have multiple buffers with interleaved vertex 66 | * attributes, which results in the option to add multiple buffers with each multiple attributes. 67 | * 68 | * \throws GlTransformBufferError if buffer attachment is invalid. 69 | **/ 70 | template 71 | void attach(const std::vector& varyings, GlBuffer& buffer); 72 | 73 | protected: 74 | /** \brief register transform feedback varyings for given program id. **/ 75 | void registerVaryings(GLuint program_id); 76 | 77 | std::shared_ptr bound_; 78 | std::shared_ptr linked_; 79 | std::vector, std::shared_ptr > > buffers_; 80 | GlQuery countquery_{QueryTarget::TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN}; 81 | }; 82 | 83 | template 84 | void GlTransformFeedback::attach(const std::vector& varyings, GlBuffer& buffer) { 85 | uint32_t maxBuffers = GlCapabilities::getInstance().get(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS); 86 | if (buffers_.size() + 1 > maxBuffers) { 87 | std::stringstream msg; 88 | msg << "Only " << maxBuffers << " transform feedback buffers allowed. See also GL_MAX_TRANSFORM_FEEDBACK_BUFFERS."; 89 | throw std::runtime_error(msg.str()); 90 | } 91 | buffers_.push_back(std::make_pair(varyings, buffer.ptr_)); 92 | } 93 | 94 | } /* namespace glow */ 95 | 96 | #endif /* INCLUDE_RV_GLTRANSFORMFEEDBACK_H_ */ 97 | -------------------------------------------------------------------------------- /test/camera-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using namespace glow; 7 | 8 | TEST(RoSeCameraTest, testSetMatrix) { 9 | float EPS = 0.0001; 10 | 11 | RoSeCamera cam; 12 | std::vector gold_params(5); 13 | std::vector result_params(5); 14 | 15 | cam.setPosition(1, 2, 3); 16 | Eigen::Matrix4f m_gold = cam.matrix(); 17 | cam.getCameraParameters(gold_params[0], gold_params[1], gold_params[2], gold_params[3], gold_params[4]); 18 | 19 | cam.setMatrix(cam.matrix()); 20 | Eigen::Matrix4f result = cam.matrix(); 21 | cam.getCameraParameters(result_params[0], result_params[1], result_params[2], result_params[3], result_params[4]); 22 | 23 | ASSERT_FLOAT_EQ(gold_params[0], result_params[0]); // x 24 | ASSERT_FLOAT_EQ(gold_params[1], result_params[1]); // y 25 | ASSERT_FLOAT_EQ(gold_params[2], result_params[2]); // z 26 | ASSERT_FLOAT_EQ(gold_params[3], result_params[3]); // yaw 27 | ASSERT_FLOAT_EQ(gold_params[4], result_params[4]); // pitch 28 | 29 | for (uint32_t i = 0; i < 4; ++i) { 30 | for (uint32_t j = 0; j < 4; ++j) { 31 | ASSERT_NEAR(m_gold(i, j), result(i, j), 0.00001) << "(" << i << ", " << j << ") expected: " << m_gold(i, j) 32 | << ", but got: " << result(i, j); 33 | } 34 | } 35 | 36 | // more complicated example. 37 | 38 | cam.lookAt(5., 2., 1., 0., 0., 0.); 39 | 40 | m_gold = cam.matrix(); 41 | cam.getCameraParameters(gold_params[0], gold_params[1], gold_params[2], gold_params[3], gold_params[4]); 42 | 43 | cam.setMatrix(cam.matrix()); 44 | result = cam.matrix(); 45 | 46 | cam.getCameraParameters(result_params[0], result_params[1], result_params[2], result_params[3], result_params[4]); 47 | 48 | // std::cout << "gold = " << rv::stringify(gold_params) << std::endl; 49 | // std::cout << "result = " << rv::stringify(result_params) << std::endl; 50 | 51 | ASSERT_FLOAT_EQ(gold_params[0], result_params[0]); // x 52 | ASSERT_FLOAT_EQ(gold_params[1], result_params[1]); // y 53 | ASSERT_FLOAT_EQ(gold_params[2], result_params[2]); // z 54 | ASSERT_FLOAT_EQ(gold_params[3], result_params[3]); // yaw 55 | ASSERT_FLOAT_EQ(gold_params[4], result_params[4]); // pitch 56 | 57 | for (uint32_t i = 0; i < 4; ++i) { 58 | for (uint32_t j = 0; j < 4; ++j) { 59 | ASSERT_NEAR(m_gold(i, j), result(i, j), EPS) << "(" << i << ", " << j << ") expected: " << m_gold(i, j) 60 | << ", but got: " << result(i, j); 61 | } 62 | } 63 | 64 | // even more complicated example. 65 | 66 | cam.lookAt(15., -22.3, 11.3, 2., -1., -1.); 67 | 68 | m_gold = cam.matrix(); 69 | cam.getCameraParameters(gold_params[0], gold_params[1], gold_params[2], gold_params[3], gold_params[4]); 70 | 71 | cam.setMatrix(cam.matrix()); 72 | result = cam.matrix(); 73 | 74 | cam.getCameraParameters(result_params[0], result_params[1], result_params[2], result_params[3], result_params[4]); 75 | 76 | // std::cout << "gold = " << rv::stringify(gold_params) << std::endl; 77 | // std::cout << "result = " << rv::stringify(result_params) << std::endl; 78 | 79 | ASSERT_NEAR(gold_params[0], result_params[0], EPS); // x 80 | ASSERT_NEAR(gold_params[1], result_params[1], EPS); // y 81 | ASSERT_NEAR(gold_params[2], result_params[2], EPS); // z 82 | ASSERT_NEAR(gold_params[3], result_params[3], EPS); // yaw 83 | ASSERT_NEAR(gold_params[4], result_params[4], EPS); // pitch 84 | 85 | for (uint32_t i = 0; i < 4; ++i) { 86 | for (uint32_t j = 0; j < 4; ++j) { 87 | ASSERT_NEAR(m_gold(i, j), result(i, j), EPS) << "(" << i << ", " << j << ") expected: " << m_gold(i, j) 88 | << ", but got: " << result(i, j); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/glow/GlTransformFeedback.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlTransformFeedback.h" 2 | #include "glow/glexception.h" 3 | 4 | namespace glow { 5 | 6 | GlTransformFeedback::GlTransformFeedback() 7 | : bound_(std::make_shared(false)), linked_(std::make_shared(false)) { 8 | #if __GL_VERSION >= 400L 9 | glGenTransformFeedbacks(1, &id_); 10 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 11 | glDeleteTransformFeedbacks(1, ptr); 12 | delete ptr; 13 | }); 14 | #endif 15 | } 16 | 17 | void GlTransformFeedback::bind() { 18 | if (!*linked_) throw GlTransformFeedbackError("Transform feedback not linked with any program!"); 19 | #if __GL_VERSION >= 400L 20 | glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, id_); // if OpenGl4.0+ 21 | #endif 22 | 23 | // bind buffers. 24 | 25 | for (uint32_t i = 0; i < buffers_.size(); ++i) { 26 | glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, *(buffers_[i].second)); 27 | } 28 | CheckGlError(); 29 | *bound_ = true; 30 | } 31 | 32 | void GlTransformFeedback::begin(TransformFeedbackMode mode) { 33 | if (!*bound_) throw GlTransformFeedbackError("Transform feedback must be bound using bind() before calling begin()."); 34 | countquery_.begin(); 35 | glBeginTransformFeedback(static_cast(mode)); 36 | 37 | CheckGlError(); 38 | } 39 | 40 | uint32_t GlTransformFeedback::end() { 41 | glEndTransformFeedback(); 42 | 43 | glFinish(); 44 | 45 | countquery_.end(); 46 | 47 | return (uint32_t)countquery_; 48 | } 49 | 50 | #if __GL_VERSION >= 400L 51 | void GlTransformFeedback::pause() { 52 | glPauseTransformFeedback(); // if OpenGl4.0+ 53 | } 54 | 55 | void GlTransformFeedback::resume() { 56 | glResumeTransformFeedback(); // if OpenGl4.0+ 57 | } 58 | 59 | void GlTransformFeedback::draw(GLenum mode) const { 60 | // OpenGL 4.0+ 61 | glDrawTransformFeedback(mode, id_); 62 | } 63 | #endif 64 | 65 | void GlTransformFeedback::release() { 66 | #if __GL_VERSION >= 400L 67 | glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); // if OpenGl4.0+ 68 | #endif 69 | // bind buffers. 70 | for (uint32_t i = 0; i < buffers_.size(); ++i) { 71 | glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, 0); 72 | } 73 | 74 | *bound_ = false; 75 | } 76 | 77 | void GlTransformFeedback::registerVaryings(GLuint program_id) { 78 | // here the magic happens. All attached varyings are bound to the given program. 79 | // Should it be possible to have a single transform feedback to bind on multiple programs? 80 | 81 | // 1. determine which buffer mode... 82 | GLenum bufferMode = (buffers_.size() == 1) ? GL_INTERLEAVED_ATTRIBS : GL_SEPARATE_ATTRIBS; 83 | bool addNextBuffer = false; 84 | std::string nextBuffer = "gl_NextBuffer"; 85 | 86 | for (auto& p : buffers_) { 87 | if (p.first.size() > 1 && buffers_.size() > 1) { 88 | #if __GL_VERSION >= 400L 89 | bufferMode = GL_INTERLEAVED_ATTRIBS; 90 | // TODO: Check: ONLY SUPPORTED IN OpenGL 4.0+ 91 | addNextBuffer = true; 92 | break; 93 | #else 94 | throw GlTransformFeedbackError( 95 | "Multiple interleaved buffers using gl_NextBuffer only supported beginning with GL version 4.0"); 96 | #endif 97 | } 98 | } 99 | 100 | std::vector varyings_stack; 101 | for (auto& p : buffers_) { 102 | for (uint32_t i = 0; i < p.first.size(); ++i) varyings_stack.push_back(p.first[i]); 103 | 104 | if (addNextBuffer) varyings_stack.push_back(nextBuffer); 105 | } 106 | 107 | // 2. set varyings. 108 | const char* vars[varyings_stack.size()]; 109 | uint32_t count = 0; 110 | for (const auto& v : varyings_stack) { 111 | vars[count++] = v.c_str(); 112 | } 113 | 114 | glTransformFeedbackVaryings(program_id, varyings_stack.size(), vars, bufferMode); 115 | *linked_ = true; 116 | } 117 | 118 | } /* namespace rv */ 119 | -------------------------------------------------------------------------------- /include/glow/GlState.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLSTATE_H_ 2 | #define INCLUDE_RV_GLSTATE_H_ 3 | 4 | #include "glbase.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace glow { 10 | 11 | /** \brief Representation of current OpenGL state that can be queried by glGet. 12 | * 13 | * In OpenGL, we sometimes screw up the state of OpenGL, but we don't know exactly where. 14 | * Here, the GlState class comes into play to get a snapshot of the current state that is 15 | * accessible via glGet*. On initialization all available information is gathered, therefore 16 | * it is not a good idea to call this frequently in production and time-critical code. 17 | * 18 | * TODO: implement querying of single values and specific set of values. 19 | * 20 | * \author behley 21 | **/ 22 | class GlState { 23 | public: 24 | class GlStateVariable { 25 | public: 26 | GlStateVariable(bool value); 27 | GlStateVariable(int32_t i1); 28 | GlStateVariable(int32_t i1, int32_t i2); 29 | GlStateVariable(int32_t i1, int32_t i2, int32_t i3); 30 | GlStateVariable(int32_t i1, int32_t i2, int32_t i3, int32_t i4); 31 | GlStateVariable(float f1); 32 | GlStateVariable(float f1, float f2); 33 | GlStateVariable(float f1, float f2, float f3); 34 | GlStateVariable(float f1, float f2, float f3, float f4); 35 | 36 | bool operator==(const GlStateVariable& other) const; 37 | bool operator!=(const GlStateVariable& other) const; 38 | 39 | enum DataType { BOOL, INT, FLOAT }; 40 | 41 | DataType type; 42 | 43 | union { 44 | bool valb; 45 | int32_t vali[4]; 46 | float valf[4]; 47 | }; 48 | 49 | uint32_t size; 50 | }; 51 | 52 | /** \brief get value of given state variable. */ 53 | template 54 | T get(GLenum variable) const; 55 | 56 | /** \brief resore the values to the values inside the state. **/ 57 | void restore(); 58 | 59 | /** \brief get all state variables of current OpenGL state. **/ 60 | static GlState queryAll(); 61 | 62 | /** \brief compare state and print variables that differ on std::cout. **/ 63 | void difference(const GlState& other) const; 64 | 65 | /** \brief determine if states are the same for all variables. 66 | * 67 | * \return true, if states match; false otherwise. 68 | **/ 69 | bool operator==(const GlState& other) const; 70 | 71 | /** \brief determine if states are not the same, at least for one variable. 72 | * 73 | * \return true, if states match; false otherwise. 74 | **/ 75 | bool operator!=(const GlState& other) const; 76 | 77 | /** \brief get some state variables. **/ 78 | // TODO: static GlState query(GLenum vars...); 79 | friend std::ostream& operator<<(std::ostream& stream, const GlState& state); 80 | 81 | protected: 82 | GlState(); 83 | 84 | std::string stringify_name(GLenum value) const; 85 | std::string stringify_value(const std::pair& entry) const; 86 | 87 | template 88 | void initGlParameter(GLenum name, uint32_t num_values); 89 | 90 | std::map state_; 91 | }; 92 | 93 | template 94 | T GlState::get(GLenum variable) const { 95 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 96 | auto it = state_.find(variable); 97 | 98 | if (it->second.type == GlStateVariable::INT) { 99 | return T(it->second.vali[0]); 100 | } else if (it->second.type == GlStateVariable::FLOAT) { 101 | return T(it->second.valf[0]); 102 | } else if (it->second.type == GlStateVariable::BOOL) { 103 | return T(it->second.valb); 104 | } 105 | 106 | // should never reach this... 107 | throw std::runtime_error("Unkown state variable type."); 108 | } 109 | } 110 | /* namespace rv */ 111 | 112 | #endif /* INCLUDE_RV_GLSTATE_H_ */ 113 | -------------------------------------------------------------------------------- /include/glow/GlUniform.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLUNIFORM_H_ 2 | #define INCLUDE_RV_GLUNIFORM_H_ 3 | 4 | #include "glbase.h" 5 | 6 | namespace glow { 7 | 8 | class GlProgram; 9 | 10 | /** 11 | * \brief Interface of a GlUniform. 12 | * 13 | * Each uniform has to implement a bind method enabling programs to 14 | * bind the value to their shaders. With this indirection, we are able to store 15 | * uniform values in a single vector and still have the convenience of the `bind` 16 | * method. 17 | * 18 | * \see GlProgram::setUniform(GlAbstractUniform& uniform) 19 | * 20 | * \author behley 21 | */ 22 | class GlAbstractUniform { 23 | public: 24 | friend class GlProgram; 25 | 26 | GlAbstractUniform(const std::string& name) : name_(name) {} 27 | 28 | virtual ~GlAbstractUniform() {} 29 | 30 | /** \brief get the name of the uniform. **/ 31 | const std::string& name() const { return name_; } 32 | 33 | protected: 34 | /** \brief use uniform for the specified active program. 35 | * 36 | * We assume that the program is currently in use via the 37 | * method glUseProgram() or using GlProgram's bind() method. 38 | * 39 | * Therefore, we will not try to "activate" the program before 40 | * setting the uniform. 41 | **/ 42 | virtual void bind(GLuint program_id) const = 0; 43 | 44 | std::string name_; 45 | }; 46 | 47 | /** 48 | * \brief Explicit representation of an uniform for OpenGL shader program. 49 | * 50 | * The idea is to enable an simple to use setting of uniforms including: 51 | * 1. Getting the location of the uniform via glUniformLocation 52 | * 2. Setting the variable with bind for the given program id with the correct/appropriate glUniform* method. 53 | * 54 | * A \a GlUniform holds therefore the name of the uniform and its value. A specific uniform implements the 55 | * `bind(GLuint id)` method, which enables a \a GlProgam to set the value with its id. 56 | * 57 | * A concrete uniform also has convenient getter, i.e., conversion functions, and setters, i.e., overloading of 58 | * the assignment operator, for access of the internal value. 59 | * 60 | * Example: 61 | * // set uniform's value and get it again. 62 | * GlUniform integer_uniform("my_uniform"); 63 | * integer_uniform = 1234; 64 | * int32_t integer = integer_uniform; 65 | * std::cout << "The uniform's value is " << integer << std::endl; 66 | * 67 | * \see GlProgram 68 | * 69 | * \author behley 70 | */ 71 | template 72 | class GlUniform : public GlAbstractUniform { 73 | public: 74 | friend class GlProgram; 75 | 76 | /** \brief construct uniform with specific name and specific value. **/ 77 | GlUniform(const std::string& name, const T& data); 78 | 79 | GlUniform& operator=(const T& rhs); 80 | // FIXME: introduce rvalue assignment operator? 81 | 82 | /** \brief conversion functions returning the value of the uniform **/ 83 | operator T(); 84 | operator T() const; 85 | const T& value() const; 86 | 87 | protected: 88 | /** \brief use uniform for the specified active program. 89 | * 90 | * We assume that the program is currently in use via the 91 | * method glUseProgram() or using GlProgram's bind() method. 92 | * 93 | * Therefore, we will not try to "activate" the program before 94 | * setting the uniform. 95 | **/ 96 | void bind(GLuint program_id) const override; 97 | 98 | T data_; 99 | }; 100 | 101 | // generic definitions 102 | 103 | template 104 | GlUniform::GlUniform(const std::string& name, const T& value) 105 | : GlAbstractUniform(name), data_(value) { 106 | } 107 | 108 | template 109 | GlUniform& GlUniform::operator=(const T& rhs) { 110 | data_ = rhs; 111 | 112 | return *this; 113 | } 114 | 115 | template 116 | GlUniform::operator T() { 117 | return data_; 118 | } 119 | 120 | template 121 | GlUniform::operator T() const { 122 | return data_; 123 | } 124 | 125 | template 126 | const T& GlUniform::value() const { 127 | return data_; 128 | } 129 | 130 | } /* namespace rv */ 131 | 132 | #endif /* INCLUDE_RV_GLUNIFORM_H_ */ 133 | -------------------------------------------------------------------------------- /include/glow/util/FpsCamera.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Emanuele Palazzolo (emanuele.palazzolo@uni-bonn.de) 2 | #pragma once 3 | #include "GlCamera.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace glow { 10 | 11 | /** \brief FPS-Style OpenGL Camera 12 | * 13 | * \author Emanuele Palazzolo (emanuele.palazzolo@uni-bonn.de) 14 | **/ 15 | class FpsCamera : public glow::GlCamera { 16 | public: 17 | FpsCamera(); 18 | /** \brief update the view matrix and return it. **/ 19 | const Eigen::Matrix4f& matrix() override; 20 | 21 | /** \brief set camera to specified transformation matrix. **/ 22 | void setMatrix(const Eigen::Matrix4f& m) override; 23 | 24 | /** \brief set location of the camera. **/ 25 | void setPosition(float x, float y, float z) override; 26 | 27 | /** \brief get current location of the camera. **/ 28 | Eigen::Vector4f getPosition() const override; 29 | 30 | /** \brief set camera directions such that the reference point is centered **/ 31 | void lookAt(float x_ref, float y_ref, float z_ref) override; 32 | /** \brief set camera to specified position looking at given reference point. 33 | * **/ 34 | void lookAt(float x_cam, float y_cam, float z_cam, float x_ref, float y_ref, float z_ref) override; 35 | 36 | /** \brief set yaw angle. 37 | * 38 | * yaw = rotation about y-axis. 39 | ***/ 40 | void setYaw(float yaw); 41 | 42 | /** \brief set pitch angle. 43 | * 44 | * pitch = rotation about x-axis. 45 | **/ 46 | void setPitch(float pitch); 47 | 48 | /** \brief get camera parameters. 49 | * 50 | * \see setPosition, setYaw, setPitch. 51 | **/ 52 | void getCameraParameters(float& x, float& y, float& z, float& yaw, float& pitch); 53 | 54 | /** \brief process mouse pressed at position (x,y) with given KeyModifier 55 | * 56 | * \return true, if event was processed. false, otherwise.* 57 | **/ 58 | bool mousePressed(float x, float y, MouseButton btn, KeyboardModifier modifier) override; 59 | 60 | /** \brief process mouse released at position (x,y) with given KeyModifier 61 | * 62 | * \return true, if event was processed. false, otherwise. 63 | **/ 64 | bool mouseReleased(float x, float y, MouseButton btn, KeyboardModifier modifier) override; 65 | 66 | /** \brief process mouse moved event at position (x,y) with given KeyModifier 67 | * 68 | * \return true, if event was processed. false, otherwise. 69 | **/ 70 | bool mouseMoved(float x, float y, MouseButton btn, KeyboardModifier modifier) override; 71 | 72 | /** \brief process mouse wheel events by delta values, i.e., how much the 73 | * wheel position changed. **/ 74 | bool wheelEvent(float delta, KeyboardModifier modifier) override; 75 | 76 | /** \brief process key pressed with given KeyboardModifier 77 | * 78 | * \return true, if event was processed. false, otherwise.* 79 | **/ 80 | bool keyPressed(KeyboardKey key, KeyboardModifier modifier) override; 81 | 82 | /** \brief process key released with given KeyboardModifier 83 | * 84 | * \return true, if event was processed. false, otherwise. 85 | **/ 86 | bool keyReleased(KeyboardKey key, KeyboardModifier modifier) override; 87 | 88 | protected: 89 | struct KeyHash { 90 | template 91 | std::size_t operator()(T t) const { 92 | return static_cast(t); 93 | } 94 | }; 95 | 96 | void translate(float forward, float up, float sideways, float dt); 97 | void rotate(float yaw, float pitch); 98 | 99 | std::chrono::system_clock::time_point startTime_; // of mouse pressed. 100 | float startx_{0.0f}, starty_{0.0f}; 101 | float startyaw_{0.0f}, startpitch_{0.0f}; 102 | 103 | float x_{0.0f}, y_{0.0f}, z_{0.0f}; 104 | float yaw_{0.0f}, pitch_{0.0f}; 105 | 106 | // velocity vector: 107 | float forwardVel_{0.0f}, upVel_{0.0f}, sideVel_{0.0f}, turnVel_{0.0f}; 108 | 109 | // TODO: expose other parameters (sensitivity, etc.) 110 | 111 | std::mutex mutex_; 112 | std::unordered_map pressed_keys_; 113 | float movement_speed_{0.15f}; 114 | }; 115 | 116 | } // namespace glow 117 | -------------------------------------------------------------------------------- /include/glow/GlFramebuffer.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLFRAMEBUFFER_H_ 2 | #define INCLUDE_RV_GLFRAMEBUFFER_H_ 3 | 4 | #include 5 | #include "GlRenderbuffer.h" 6 | #include "GlTexture.h" 7 | #include "GlTextureRectangle.h" 8 | 9 | namespace glow { 10 | 11 | enum class FramebufferTarget { BOTH = GL_FRAMEBUFFER, READ = GL_READ_FRAMEBUFFER, DRAW = GL_DRAW_FRAMEBUFFER }; 12 | 13 | enum class FramebufferAttachment { 14 | COLOR0 = GL_COLOR_ATTACHMENT0, 15 | COLOR1 = GL_COLOR_ATTACHMENT1, 16 | COLOR2 = GL_COLOR_ATTACHMENT2, 17 | COLOR3 = GL_COLOR_ATTACHMENT3, 18 | COLOR4 = GL_COLOR_ATTACHMENT4, 19 | COLOR5 = GL_COLOR_ATTACHMENT5, 20 | COLOR6 = GL_COLOR_ATTACHMENT6, 21 | // TODO: remaining color attachments, 22 | DEPTH = GL_DEPTH_ATTACHMENT, 23 | STENCIL = GL_STENCIL_ATTACHMENT, 24 | DEPTH_STENCIL = GL_DEPTH_STENCIL_ATTACHMENT 25 | }; 26 | 27 | /** \brief Representation of an OpenGL's framebuffer object 28 | * 29 | * Framebuffer objects can be used as target for (offscreen) rendering instead of the default framebuffer. 30 | * However, before the framebuffer can be used as target for rendering there should be buffers added to 31 | * the framebuffer. At least, COLOR0 and DEPTH + STENCIL or DEPTH_STENCIL attachments either by attaching 32 | * a GlRenderbuffer or a Texture must be made. You can check with the valid method if everything needed is 33 | * present. 34 | * 35 | * TODO: integrate glDrawBuffers: attach automatically enables the buffers? 36 | * different mapping or deactivating buffers must be set explicitly? map(), enable()? 37 | * 38 | * \author behley 39 | */ 40 | class GlFramebuffer : public GlObject { 41 | public: 42 | /** \brief initialize an empty framebuffer object with given size (width x height) without any attachments. **/ 43 | GlFramebuffer(uint32_t width, uint32_t height, FramebufferTarget target = FramebufferTarget::BOTH); 44 | 45 | ~GlFramebuffer(); 46 | 47 | /** \brief clear the framebuffer. **/ 48 | void clear(GLbitfield clear_options = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 49 | 50 | /** \brief bind framebuffer to current context. 51 | * 52 | * Bind framebuffer to current context. Each subsequent drawing operations will use the 53 | * framebuffer. If the framebuffer is invalid, i.e., some attachments are missing, a 54 | * GlFramebufferException is thrown. 55 | * 56 | * \throws GlFramebufferException invalid framebuffer object. 57 | * 58 | * \see GlFramebuffer::valid() 59 | */ 60 | void bind() override; 61 | 62 | void release() override; 63 | 64 | /** \brief attach texture to given target. 65 | * 66 | * The texture must have at least the width and height of the framebuffer object. 67 | **/ 68 | void attach(FramebufferAttachment target, GlTexture& texture); 69 | 70 | /** \brief attach rectangular texture to given target. 71 | * 72 | * The texture must have at least the width and height of the framebuffer object. 73 | **/ 74 | void attach(FramebufferAttachment target, GlTextureRectangle& texture); 75 | 76 | /** \brief attach renderbuffer object to given target. 77 | * 78 | * The renderbuffer must have at least the width and height of the framebuffer object. 79 | */ 80 | void attach(FramebufferAttachment target, GlRenderbuffer& buffer); 81 | 82 | /** \brief is Framebuffer object valid? 83 | * 84 | * Check if all needed targets are attached. 85 | * \return false if something is missing; true, otherwise. 86 | */ 87 | bool valid() const; 88 | 89 | /** \brief get framebuffer width **/ 90 | uint32_t width() const; 91 | 92 | /** \brief get framebuffer height **/ 93 | uint32_t height() const; 94 | 95 | /** \brief resize the framebuffer and invalidate. **/ 96 | void resize(uint32_t width, uint32_t height); 97 | 98 | protected: 99 | GLuint bindTransparently(); 100 | void releaseTransparently(GLuint old_id); 101 | 102 | static GLuint boundFramebuffer_; 103 | 104 | GLenum target_; 105 | bool valid_{false}; 106 | uint32_t width_, height_; 107 | // hold pointers to ensure that resources are not deleted. 108 | std::map > attachments_; 109 | }; 110 | 111 | } /* namespace rv */ 112 | 113 | #endif /* INCLUDE_RV_GLFRAMEBUFFER_H_ */ 114 | -------------------------------------------------------------------------------- /include/glow/util/RoSeCamera.h: -------------------------------------------------------------------------------- 1 | /* 2 | * RoSeCamera.h 3 | * 4 | * Created on: Apr 21, 2016 5 | * Author: behley 6 | */ 7 | 8 | #ifndef INCLUDE_RV_ROSECAMERA_H_ 9 | #define INCLUDE_RV_ROSECAMERA_H_ 10 | 11 | #include "GlCamera.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace glow { 17 | 18 | /** \brief velocity-based OpenGL camera used in FKIE's RoSe 19 | * 20 | * Adoption of the camera used in FKIE applications. This is also an example for velocity-based navigation, where we 21 | * apply some velocity on the camera to "fly" through the scene. 22 | * 23 | * Basically, we have with this camera a camera position and a yaw/pitch angle defining the view direction. 24 | * 25 | * Shortly, this corresponds to: 26 | * 27 | * LMB = move forward and rotation in x/z-plane 28 | * MMB = move upwards and sidewaysn in x/y-plane 29 | * RMB = change of yaw and pitch angles (viewing direction.) 30 | * 31 | * no key "overloads", no mouse wheel... 32 | * 33 | * \author behley 34 | */ 35 | class RoSeCamera : public GlCamera { 36 | public: 37 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW 38 | /** \brief update the view matrix and return it. **/ 39 | const Eigen::Matrix4f& matrix() override; 40 | 41 | /** \brief set camera to specified transformation matrix. **/ 42 | void setMatrix(const Eigen::Matrix4f& m) override; 43 | 44 | /** \brief set location of the camera. **/ 45 | void setPosition(float x, float y, float z) override; 46 | 47 | /** \brief get current location of the camera. **/ 48 | Eigen::Vector4f getPosition() const override; 49 | 50 | /** \brief set camera directions such that the reference point is centered **/ 51 | void lookAt(float x_ref, float y_ref, float z_ref) override; 52 | /** \brief set camera to specified position looking at given reference point. **/ 53 | void lookAt(float x_cam, float y_cam, float z_cam, float x_ref, float y_ref, float z_ref) override; 54 | 55 | /** \brief set yaw angle. 56 | * 57 | * yaw = rotation about y-axis. 58 | ***/ 59 | void setYaw(float yaw); 60 | 61 | /** \brief set pitch angle. 62 | * 63 | * pitch = rotation about x-axis. 64 | **/ 65 | void setPitch(float pitch); 66 | 67 | /** \brief get camera parameters. 68 | * 69 | * \see setPosition, setYaw, setPitch. 70 | **/ 71 | void getCameraParameters(float& x, float& y, float& z, float& yaw, float& pitch); 72 | 73 | /** \brief process mouse pressed at position (x,y) with given KeyModifier 74 | * 75 | * \return true, if event was processed. false, otherwise.* 76 | **/ 77 | bool mousePressed(float x, float y, MouseButton btn, KeyboardModifier modifier) override; 78 | 79 | /** \brief process mouse released at position (x,y) with given KeyModifier 80 | * 81 | * \return true, if event was processed. false, otherwise. 82 | **/ 83 | bool mouseReleased(float x, float y, MouseButton btn, KeyboardModifier modifier) override; 84 | 85 | /** \brief process mouse moved event at position (x,y) with given KeyModifier 86 | * 87 | * \return true, if event was processed. false, otherwise. 88 | **/ 89 | bool mouseMoved(float x, float y, MouseButton btn, KeyboardModifier modifier) override; 90 | 91 | /** \brief process mouse wheel events by delta values, i.e., how much the wheel position changed. **/ 92 | bool wheelEvent(float delta, KeyboardModifier modifier) override; 93 | bool keyPressed(KeyboardKey key, KeyboardModifier modifier) override; 94 | bool keyReleased(KeyboardKey key, KeyboardModifier modifier) override; 95 | 96 | protected: 97 | void translate(float forward, float up, float sideways); 98 | void rotate(float yaw, float pitch); 99 | 100 | std::chrono::system_clock::time_point startTime_; // of mouse pressed. 101 | float startx_{0.0f}, starty_{0.0f}; 102 | float startyaw_{0.0f}, startpitch_{0.0f}; 103 | 104 | float x_{0.0f}, y_{0.0f}, z_{0.0f}; 105 | float yaw_{0.0f}, pitch_{0.0f}; 106 | 107 | bool startdrag_{false}; 108 | 109 | // velocity vector: 110 | float forwardVel_{0.0f}, upVel_{0.0f}, sideVel_{0.0f}, turnVel_{0.0f}; 111 | 112 | // TODO: expose other parameters (sensitivity, etc.) 113 | 114 | std::mutex mutex_; 115 | }; 116 | 117 | } /* namespace rv */ 118 | 119 | #endif /* INCLUDE_RV_ROSECAMERA_H_ */ 120 | -------------------------------------------------------------------------------- /src/glow/GlProgram.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "glow/GlProgram.h" 5 | #include "glow/GlTransformFeedback.h" 6 | 7 | namespace glow { 8 | 9 | GLuint GlProgram::boundProgram_ = 0; 10 | 11 | GlProgram::GlProgram() { 12 | id_ = glCreateProgram(); 13 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 14 | glDeleteProgram(*ptr); 15 | delete ptr; 16 | }); 17 | } 18 | 19 | void GlProgram::attach(const GlShader& shader) { 20 | if (shaders_.find(shader.type_) != shaders_.end()) shaders_.erase(shader.type_); 21 | shaders_.emplace(shader.type_, shader); 22 | } 23 | 24 | void GlProgram::attach(const GlTransformFeedback& feedback) { 25 | feedbacks_.push_back(feedback); 26 | } 27 | 28 | void GlProgram::link() { 29 | if (shaders_.find(ShaderType::VERTEX_SHADER) == shaders_.end() || 30 | shaders_.find(ShaderType::FRAGMENT_SHADER) == shaders_.end()) 31 | throw GlProgramError("Program must have attached at least VERTEX_SHADER and FRAGMENT_SHADER"); 32 | 33 | for (auto it = shaders_.cbegin(); it != shaders_.cend(); ++it) { 34 | glAttachShader(id_, it->second.id_); 35 | CheckGlError(); 36 | } 37 | 38 | // produce warnings for unmatched inputs or outputs. 39 | std::vector ordering{ShaderType::VERTEX_SHADER, ShaderType::GEOMETRY_SHADER, ShaderType::FRAGMENT_SHADER}; 40 | std::vector names{"vertex shader", "geometry shader", "fragment shader"}; 41 | for (uint32_t i = 0; i < ordering.size(); ++i) { 42 | if (shaders_.find(ordering[i]) == shaders_.end()) continue; 43 | const GlShader& shader = shaders_.find(ordering[i])->second; 44 | if (shader.inAttribs_.size() == 0) continue; // nothing to match. 45 | 46 | for (auto attrib : shader.inAttribs_) { 47 | bool unmatched = true; 48 | for (int32_t j = i - 1; j >= 0; --j) { 49 | if (shaders_.find(ordering[j]) == shaders_.end()) continue; 50 | const GlShader& prevShader = shaders_.find(ordering[j])->second; 51 | for (auto prevAttrib : prevShader.outAttribs_) { 52 | if (prevAttrib.type == attrib.type && prevAttrib.name == prevAttrib.name) unmatched = false; 53 | } 54 | if (!unmatched) break; 55 | } 56 | 57 | if (unmatched) { 58 | std::cerr << "Warning: Unmatched attribute " << attrib.name << " in " << names[i] << "(" << shader.filename 59 | << ") found" << std::endl; 60 | } 61 | } 62 | } 63 | 64 | // register varyings. 65 | for (auto feedback : feedbacks_) feedback.registerVaryings(id_); 66 | 67 | glLinkProgram(id_); 68 | 69 | // check if linking was successful. 70 | GLint isLinked = 0; 71 | glGetProgramiv(id_, GL_LINK_STATUS, static_cast(&isLinked)); 72 | if (isLinked == GL_FALSE) { 73 | GLint length = 0; 74 | glGetProgramiv(id_, GL_INFO_LOG_LENGTH, &length); 75 | 76 | // The maxLength includes the NULL character 77 | std::vector error_string(length); 78 | glGetProgramInfoLog(id_, length, &length, &error_string[0]); 79 | 80 | for (auto it = shaders_.cbegin(); it != shaders_.cend(); ++it) glDetachShader(id_, it->second.id_); 81 | 82 | throw GlProgramError(std::string(&error_string[0], length)); 83 | } 84 | 85 | linked_ = true; 86 | 87 | // cleanup: detach + remove shader from map. 88 | for (auto it = shaders_.cbegin(); it != shaders_.cend(); ++it) glDetachShader(id_, it->second.id_); 89 | shaders_.clear(); 90 | 91 | // inform program that feedbacks that linking was successful. 92 | for (auto feedback : feedbacks_) *(feedback.linked_) = true; 93 | 94 | feedbacks_.clear(); // not needed anymore. 95 | 96 | CheckGlError(); 97 | } 98 | 99 | void GlProgram::bind() { 100 | assert(linked_ && "GlProgram should be linked with link() before usage!"); 101 | glUseProgram(id_); 102 | boundProgram_ = id_; 103 | 104 | CheckGlError(); 105 | } 106 | 107 | void GlProgram::release() { 108 | glUseProgram(0); 109 | boundProgram_ = 0; 110 | 111 | CheckGlError(); 112 | } 113 | 114 | void GlProgram::setUniform(const GlAbstractUniform& uniform) { 115 | if (!linked_) throw GlProgramError("Unable to set Uniform: program not linked!"); 116 | GLuint id = bindTransparently(); 117 | uniform.bind(id_); 118 | releaseTransparently(id); 119 | 120 | CheckGlError(); 121 | } 122 | 123 | GLuint GlProgram::bindTransparently() { 124 | assert(linked_ && "GlProgram should be linked with link() before usage!"); 125 | if (boundProgram_ == id_) return id_; 126 | 127 | glUseProgram(id_); 128 | return boundProgram_; 129 | } 130 | 131 | void GlProgram::releaseTransparently(GLuint oldProgram) { 132 | if (oldProgram == id_) return; 133 | 134 | glUseProgram(oldProgram); 135 | } 136 | 137 | } /* namespace rv */ 138 | -------------------------------------------------------------------------------- /include/glow/GlVertexArray.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLVERTEXARRAY_H_ 2 | #define INCLUDE_RV_GLVERTEXARRAY_H_ 3 | 4 | #include "GlObject.h" 5 | #include "GlBuffer.h" 6 | #include 7 | #include 8 | 9 | namespace glow { 10 | /** \brief data type of the vertex attribute. **/ 11 | enum class AttributeType { 12 | BYTE = GL_BYTE, 13 | UNSIGNED_BYTE = GL_UNSIGNED_BYTE, 14 | SHORT = GL_SHORT, 15 | UNSIGNED_SHORT = GL_UNSIGNED_SHORT, 16 | INT = GL_INT, 17 | UNSIGNED_INT = GL_UNSIGNED_INT, 18 | HALF_FLOAT = GL_HALF_FLOAT, 19 | FLOAT = GL_FLOAT, 20 | DOUBLE = GL_DOUBLE, 21 | FIXED = GL_FIXED, 22 | INT_2_10_10_10_REV = GL_INT_2_10_10_10_REV, 23 | UNSIGNED_INT_2_10_10_10_REV = GL_UNSIGNED_INT_2_10_10_10_REV, 24 | UNSIGNED_INT_10F_11F_11F_REV = GL_UNSIGNED_INT_10F_11F_11F_REV 25 | }; 26 | 27 | /** \brief representation of a Vertex Array Object. 28 | * 29 | * In core profile OpenGL, the vertex array object is needed for specification of the vertex 30 | * attributes. Therefore, this class provides a convenient access to a Vertex Array Object 31 | * with resource management. 32 | * 33 | * It furthermore enables a more "natural" usage of vertex buffer objects with vertex arrays 34 | * by making the actual definition of attributes a part of this object. 35 | * 36 | * 37 | * \author behley 38 | * 39 | * TODO: Compatibility with glBindVertexBuffer / glVertexBindingDivisor​? Available with OpenGL version 4.3 40 | **/ 41 | class GlVertexArray : public GlObject { 42 | public: 43 | GlVertexArray(); 44 | ~GlVertexArray(); 45 | 46 | /** \brief bind the vertex array object. **/ 47 | void bind() override; 48 | /** \brief release the vertex array object. **/ 49 | void release() override; 50 | 51 | // template 52 | // void unbindVertexBuffer() 53 | 54 | /** \brief set the vertex attribute pointer for the given vertex buffer 55 | * 56 | * Vertex attributes define the layout of the data inside memory. This actually maps 57 | * an array or its contents to a attribute in the vertex shader and activates the attribute. 58 | * 59 | * \param idx index of the vertex attribute 60 | * \param buffer vertex buffer represented by a GlBuffer object. 61 | * \param size size or number of components of the attribute (1,2,3, or 4) 62 | * \param type data type of the attribute inside the vertex buffer. 63 | * \param normalized should the data be normalized? 64 | * \param stride specifies the byte offset between consecutive generic vertex attributes 65 | * \param offset specifies the byte offset of the first component of the first generic vertex attribute 66 | * in the array in the data store of the buffer. 67 | **/ 68 | template 69 | void setVertexAttribute(uint32_t idx, GlBuffer& buffer, int32_t size, AttributeType type, bool normalized, 70 | uint32_t stride, GLvoid* offset); 71 | 72 | /** \brief enable vertex attribute with given index \a idx **/ 73 | void enableVertexAttribute(uint32_t idx); 74 | /** \brief disable vertex attribute with given index \a idx **/ 75 | void disableVertexAttribute(uint32_t idx); 76 | 77 | protected: 78 | /** \brief bind vertex array object only if needed and return overwritten vertex array object. **/ 79 | GLuint bindTransparently(); 80 | /** \brief release vertex array object and restore state before calling bindTranparently. **/ 81 | void releaseTransparently(GLuint old_vao); 82 | 83 | static GLuint boundVAO_; 84 | 85 | // TODO: should we also check if the vertex attributes are set when enabled? 86 | struct VertexAttributeState { 87 | public: 88 | GlObject* buffer{0}; 89 | bool initialized{false}; 90 | bool enabled{false}; 91 | }; 92 | 93 | // only needed for book keeping purposes. 94 | std::map > vertexBuffers_; 95 | }; 96 | 97 | template 98 | void GlVertexArray::setVertexAttribute(uint32_t idx, GlBuffer& buffer, int32_t numComponents, AttributeType type, 99 | bool normalized, uint32_t stride_in_bytes, GLvoid* offset) { 100 | // replaces & frees shared pointer if present; copies the shared pointer of buffer => buffer is not deleted. 101 | vertexBuffers_[idx] = buffer.ptr_; 102 | 103 | // assert(buffer.target() == BufferTarget::ARRAY_BUFFER); What about element indexes? 104 | 105 | GLuint oldvao = bindTransparently(); 106 | 107 | buffer.bind(); 108 | 109 | if (type == AttributeType::INT || type == AttributeType::UNSIGNED_INT) { 110 | glVertexAttribIPointer(static_cast(idx), static_cast(numComponents), static_cast(type), 111 | static_cast(stride_in_bytes), offset); 112 | } else { 113 | glVertexAttribPointer(static_cast(idx), static_cast(numComponents), static_cast(type), 114 | static_cast(normalized), static_cast(stride_in_bytes), offset); 115 | } 116 | 117 | glEnableVertexAttribArray(static_cast(idx)); 118 | 119 | releaseTransparently(oldvao); 120 | 121 | buffer.release(); 122 | CheckGlError(); 123 | } 124 | 125 | } /* namespace rv */ 126 | 127 | #endif /* INCLUDE_RV_GLVERTEXARRAY_H_ */ 128 | -------------------------------------------------------------------------------- /include/glow/util/GlCamera.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_OPENGL_GLCAMERA_H_ 2 | #define SRC_OPENGL_GLCAMERA_H_ 3 | 4 | #include "enum_utils.h" 5 | 6 | #include 7 | 8 | namespace glow { 9 | 10 | /** \brief Camera model for specifying a view matrix using mouse events. 11 | * 12 | * A general camera which can be modified by the provided methods. We want to keep the interface very, very general 13 | * allowing the implementation of a preferred camera model. 14 | * 15 | * The general idea is that the camera gets screen coordinates and modifiers, like left mouse button, 16 | * right mouse button, etc. With these inputs one can now call the methods mousePressed, mouseReleased and mouseMoved 17 | * to update the current mouse position. Depending on the underlying implementation, this triggers some update of 18 | * the view matrix. (velocity-based, i.e., regularly updated view positions, or simple position based 19 | * updates, where a change in mouse position directly corresponds to a change in camera location, are possible.) 20 | * 21 | * Each event handler returns true, if the corresponding event has been consumed and therefore might be now irrelevant 22 | * for further event processing. 23 | * 24 | * A call to matrix() returns the current view matrix, which might be updated since the last call to matrix(). 25 | * 26 | * 27 | * \see RoSeCamera for a velocity-based navigation. 28 | * \see FpsCamera for a camera with additional key bindings. 29 | * TODO: \see IsometricCamera for a direct modification of the camera, similar to Blender. 30 | * 31 | * \author behley 32 | */ 33 | class GlCamera { 34 | public: 35 | enum class MouseButton { NoButton = 0, LeftButton = 1, MiddleButton = 2, RightButton = 4 }; 36 | 37 | enum class KeyboardModifier { None = 0, CtrlDown = 1, AltDown = 2, ShiftDown = 4 }; 38 | 39 | enum class KeyboardKey { KeyA = 0, KeyB = 1, KeyC = 2, KeyD = 3, KeyE = 4, KeyF = 5, KeyG = 6, KeyH = 7, KeyI = 8, 40 | KeyJ = 9, KeyK = 10, KeyL = 11, KeyM = 12, KeyN = 13, KeyO = 14, KeyP = 15, KeyQ = 16, 41 | KeyR = 17, KeyS = 18, KeyT = 19, KeyU = 20, KeyV = 21, KeyW = 22, KeyX = 23, KeyY = 24, 42 | KeyZ = 25, Key1 = 26, Key2 = 27, Key3 = 28, Key4 = 29, Key5 = 30, Key6 = 31, Key7 = 32, 43 | Key8 = 33, Key9 = 34, Key0 = 35, KeyF1 = 36, KeyF2 = 37, KeyF3 = 38, KeyF4 = 39, KeyF5 = 40, 44 | KeyF6 = 41, KeyF7 = 42, KeyF8 = 43, KeyF9 = 44, KeyF10 = 45, KeyF11 = 46, KeyF12 = 47, 45 | KeyEsc = 48, KeyEnter = 49, KeyUpArrow = 50, KeyDownArrow = 51, KeyLeftArrow = 52, 46 | KeyRightArrow = 53, KeySpace = 54, KeyNotSupported = -1 }; 47 | virtual ~GlCamera(); 48 | 49 | /** \brief return the current view matrix. **/ 50 | virtual const Eigen::Matrix4f& matrix(); 51 | 52 | /** \brief update position of camera such that camera's origin is at (x,y,z) **/ 53 | virtual void setPosition(float x, float y, float z); 54 | 55 | virtual Eigen::Vector4f getPosition() const; 56 | 57 | /** \brief set camera such that it looks at specific location. **/ 58 | virtual void lookAt(float x_ref, float y_ref, float z_ref); 59 | virtual void lookAt(float x_cam, float y_cam, float z_cam, float x_ref, float y_ref, float z_ref); 60 | 61 | /** \brief process mouse pressed at position (x,y) with given KeyboardModifier 62 | * 63 | * \return true, if event was processed. false, otherwise.* 64 | **/ 65 | virtual bool mousePressed(float x, float y, MouseButton btn, KeyboardModifier modifier); 66 | 67 | /** \brief process mouse released at position (x,y) with given KeyboardModifier 68 | * 69 | * \return true, if event was processed. false, otherwise. 70 | **/ 71 | virtual bool mouseReleased(float x, float y, MouseButton btn, KeyboardModifier modifier); 72 | 73 | /** \brief process mouse moved event at position (x,y) with given KeyboardModifier 74 | * 75 | * \return true, if event was processed. false, otherwise. 76 | **/ 77 | virtual bool mouseMoved(float x, float y, MouseButton btn, KeyboardModifier modifier); 78 | 79 | /** \brief process mouse wheel events by delta values, i.e., how much the wheel position changed. **/ 80 | virtual bool wheelEvent(float delta, KeyboardModifier modifier); 81 | 82 | /** \brief process key pressed with given KeyboardModifier 83 | * 84 | * \return true, if event was processed. false, otherwise.* 85 | **/ 86 | virtual bool keyPressed(KeyboardKey key, KeyboardModifier modifier); 87 | 88 | /** \brief process key released with given KeyboardModifier 89 | * 90 | * \return true, if event was processed. false, otherwise. 91 | **/ 92 | virtual bool keyReleased(KeyboardKey key, KeyboardModifier modifier); 93 | 94 | virtual void setMatrix(const Eigen::Matrix4f& m); 95 | 96 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW 97 | 98 | protected: 99 | GlCamera(); // cannot be instanced, only inherited from. 100 | 101 | Eigen::Matrix4f view_{Eigen::Matrix4f::Identity()}; 102 | }; 103 | 104 | // TODO: more elegant way than this? Allowing essentially something like if(v & ev) instead of if((v & ev) = ev)? 105 | // ENUM_BIT_OPERATIONS(GlCamera::MouseButton) 106 | // ENUM_BIT_OPERATIONS(GlCamera::KeyModifier) 107 | 108 | } /* namespace glow */ 109 | 110 | #endif /* SRC_OPENGL_GLCAMERA_H_ */ 111 | -------------------------------------------------------------------------------- /src/glow/glutil.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/glutil.h" 2 | #include 3 | #include 4 | 5 | namespace glow { 6 | 7 | Eigen::Matrix4f glTranslate(float x, float y, float z) { 8 | Eigen::Matrix4f m; 9 | m << 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1; 10 | return m; 11 | } 12 | 13 | Eigen::Matrix4f glScale(float x, float y, float z) { 14 | Eigen::Matrix4f m; 15 | m << x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1; 16 | return m; 17 | } 18 | 19 | Eigen::Matrix4f glRotateX(float angle) { 20 | float sin_t = std::sin(angle); 21 | float cos_t = std::cos(angle); 22 | 23 | Eigen::Matrix4f m; 24 | m << 1, 0, 0, 0, 0, cos_t, -sin_t, 0, 0, sin_t, cos_t, 0, 0, 0, 0, 1; 25 | 26 | return m; 27 | } 28 | 29 | Eigen::Matrix4f glRotateY(float angle) { 30 | float sin_t = std::sin(angle); 31 | float cos_t = std::cos(angle); 32 | Eigen::Matrix4f m; 33 | m << cos_t, 0, sin_t, 0, 0, 1, 0, 0, -sin_t, 0, cos_t, 0, 0, 0, 0, 1; 34 | 35 | return m; 36 | } 37 | 38 | Eigen::Matrix4f glRotateZ(float angle) { 39 | float sin_t = std::sin(angle); 40 | float cos_t = std::cos(angle); 41 | Eigen::Matrix4f m; 42 | m << cos_t, -sin_t, 0, 0, sin_t, cos_t, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1; 43 | return m; 44 | } 45 | 46 | Eigen::Matrix4f glRotateAxis(float angle, float x, float y, float z) { 47 | Eigen::Matrix4f m; 48 | 49 | float s = std::sin(angle); 50 | float c = std::cos(angle); 51 | 52 | m(0, 0) = x * x * (1 - c) + c; 53 | m(0, 1) = x * y * (1.0f - c) - z * s; 54 | m(0, 2) = x * z * (1.0f - c) + y * s; 55 | m(0, 3) = 0.0f; 56 | 57 | m(1, 0) = x * y * (1.0f - c) + z * s; 58 | m(1, 1) = c + (1.0f - c) * y * y; 59 | m(1, 2) = y * z * (1.0f - c) - x * s; 60 | m(1, 3) = 0.0f; 61 | 62 | m(2, 0) = x * z * (1.0f - c) - y * s; 63 | m(2, 1) = y * z * (1.0f - c) + x * s; 64 | m(2, 2) = z * z + (1.0f - z * z) * c; 65 | m(2, 3) = 0.0f; 66 | 67 | m(3, 0) = 0.0f; 68 | m(3, 1) = 0.0f; 69 | m(3, 2) = 0.0f; 70 | m(3, 3) = 1.0f; 71 | 72 | return m; 73 | } 74 | 75 | Eigen::Matrix4f glPerspective(float fov, float aspect, float znear, float zfar) { 76 | // https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml 77 | assert(znear > 0.0f); 78 | Eigen::Matrix4f M = Eigen::Matrix4f::Zero(); 79 | 80 | // Copied from gluPerspective 81 | float f = 1.0f / std::tan(0.5f * fov); 82 | 83 | M(0, 0) = f / aspect; 84 | M(1, 1) = f; 85 | M(2, 2) = (znear + zfar) / (znear - zfar); 86 | M(2, 3) = (2.0f * zfar * znear) / (znear - zfar); 87 | M(3, 2) = -1.0f; 88 | 89 | return M; 90 | } 91 | 92 | Eigen::Matrix4f glOrthographic(float left, float right, float bottom, float top, float znear, float zfar) { 93 | 94 | // copied from https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml 95 | 96 | Eigen::Matrix4f M = Eigen::Matrix4f::Zero(); 97 | 98 | M(0, 0) = 2.0f / (right - left); 99 | M(1, 1) = 2.0f / (top - bottom); 100 | M(2, 2) = -2.0f / (zfar - znear); 101 | 102 | M(0, 3) = -(right + left) / (right - left); 103 | M(1, 3) = -(top + bottom) / (top - bottom); 104 | M(2, 3) = -(zfar + znear) / (zfar - znear); 105 | M(3, 3) = 1.0f; 106 | 107 | return M; 108 | } 109 | 110 | // clang-format off 111 | Eigen::Matrix4f initializeMatrix(float t00, float t01, float t02, float t03, 112 | float t10, float t11, float t12, float t13, 113 | float t20, float t21, float t22, float t23, 114 | float t30, float t31, float t32, float t33) { 115 | Eigen::Matrix4f m; 116 | m << t00, t01, t02, t03, t10, t11, t12, t13, t20, t21, t22, t23, t30, t31, t32, t33; 117 | return m; 118 | } 119 | 120 | Eigen::Matrix4f RoSe2GL::matrix = initializeMatrix(0, -1, 0, 0, 121 | 0, 0, 1, 0, 122 | -1, 0, 0, 0, 123 | 0, 0, 0, 1); 124 | 125 | 126 | Eigen::Matrix4f GL2RoSe::matrix = initializeMatrix(0, 0, -1, 0, 127 | -1, 0, 0, 0, 128 | 0, 1, 0, 0, 129 | 0, 0, 0, 1); 130 | // clang-format on 131 | 132 | float rgb2float(float r, float g, float b) { 133 | int32_t rgb = int32_t(round(r * 255.0f)); 134 | rgb = (rgb << 8) + int32_t(round(g * 255.0f)); 135 | rgb = (rgb << 8) + int32_t(round(b * 255.0f)); 136 | 137 | return float(rgb); 138 | } 139 | 140 | std::string extension(const std::string& path, int32_t level) { 141 | std::string filename = boost::filesystem::path(path).filename().string(); 142 | if (filename == "" || filename == "." || filename == "..") return ""; 143 | 144 | std::string ext; 145 | while (level-- > 0) { 146 | std::string::size_type idx = filename.rfind("."); 147 | if (idx == std::string::npos) break; 148 | ext.insert(0, filename.substr(idx)); 149 | filename.resize(idx); 150 | } 151 | 152 | return ext; 153 | } 154 | } 155 | 156 | std::ostream& operator<<(std::ostream& stream, glow::vec2& vec) { 157 | stream << "(" << vec.x << ", " << vec.y << ")"; 158 | 159 | return stream; 160 | } 161 | 162 | std::ostream& operator<<(std::ostream& stream, glow::vec3& vec) { 163 | stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")"; 164 | 165 | return stream; 166 | } 167 | 168 | std::ostream& operator<<(std::ostream& stream, glow::vec4& vec) { 169 | stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ")"; 170 | 171 | return stream; 172 | } 173 | 174 | std::ostream& operator<<(std::ostream& stream, const glow::vec2& vec) { 175 | stream << "(" << vec.x << ", " << vec.y << ")"; 176 | 177 | return stream; 178 | } 179 | 180 | std::ostream& operator<<(std::ostream& stream, const glow::vec3& vec) { 181 | stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")"; 182 | 183 | return stream; 184 | } 185 | 186 | std::ostream& operator<<(std::ostream& stream, const glow::vec4& vec) { 187 | stream << "(" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ")"; 188 | 189 | return stream; 190 | } 191 | -------------------------------------------------------------------------------- /include/glow/GlFramebuffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * GlFramebuffer.cpp 3 | * 4 | * Created on: Apr 28, 2016 5 | * Author: behley 6 | */ 7 | 8 | #include "GlFramebuffer.h" 9 | #include "glexception.h" 10 | 11 | #include 12 | 13 | namespace glow { 14 | 15 | GLuint GlFramebuffer::boundFramebuffer_ = 0; 16 | 17 | GlFramebuffer::GlFramebuffer(uint32_t width, uint32_t height, FramebufferTarget target) 18 | : target_(static_cast(target)), width_(width), height_(height) { 19 | glGenFramebuffers(1, &id_); 20 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 21 | glDeleteFramebuffers(1, ptr); 22 | delete ptr; 23 | }); 24 | 25 | CheckGlError(); 26 | } 27 | 28 | GlFramebuffer::~GlFramebuffer() { 29 | if (boundFramebuffer_ == id_) { 30 | release(); 31 | assert((boundFramebuffer_ != id_) && "Framebuffer object still bound."); 32 | } 33 | attachments_.clear(); 34 | 35 | CheckGlError(); 36 | } 37 | 38 | inline std::string error_string(GLint id) { 39 | switch (id) { 40 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 41 | return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; 42 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 43 | return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; 44 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: 45 | return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; 46 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: 47 | return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; 48 | case GL_FRAMEBUFFER_UNSUPPORTED: 49 | return "GL_FRAMEBUFFER_UNSUPPORTED"; 50 | } 51 | 52 | return "UNKNOWN ERROR."; 53 | } 54 | 55 | void GlFramebuffer::clear(GLbitfield options) { 56 | GLuint old_id = bindTransparently(); 57 | 58 | glViewport(0, 0, width_, height_); 59 | glClear(options); 60 | 61 | releaseTransparently(old_id); 62 | 63 | CheckGlError(); 64 | } 65 | 66 | void GlFramebuffer::bind() { 67 | if (!valid_) { 68 | std::stringstream reason; 69 | reason << "Invalid framebuffer object. Code "; 70 | glBindFramebuffer(target_, id_); 71 | GLint errid = glCheckFramebufferStatus(target_); 72 | reason << errid << "(" << error_string(errid) << ")"; 73 | glBindFramebuffer(target_, 0); 74 | throw GlFramebufferError(reason.str()); 75 | } 76 | 77 | boundFramebuffer_ = id_; 78 | glBindFramebuffer(target_, id_); 79 | 80 | CheckGlError(); 81 | } 82 | 83 | void GlFramebuffer::release() { 84 | boundFramebuffer_ = 0; 85 | glBindFramebuffer(target_, 0); 86 | 87 | CheckGlError(); 88 | } 89 | 90 | GLuint GlFramebuffer::bindTransparently() { 91 | glBindFramebuffer(target_, id_); 92 | 93 | return boundFramebuffer_; 94 | } 95 | 96 | void GlFramebuffer::releaseTransparently(GLuint old_id) { 97 | glBindFramebuffer(target_, old_id); 98 | } 99 | 100 | void GlFramebuffer::attach(FramebufferAttachment target, GlTexture& texture) { 101 | if (texture.target_ != GL_TEXTURE_2D) throw GlFramebufferError("Expected two-dimensional texture"); 102 | if (texture.width() < width_) { 103 | std::stringstream error; 104 | error << "Texture's width should be at least " << width_; 105 | throw GlFramebufferError(error.str()); 106 | } 107 | 108 | if (texture.height() < height_) { 109 | std::stringstream error; 110 | error << "Texture's height should be at least " << height_; 111 | throw GlFramebufferError(error.str()); 112 | } 113 | 114 | GLuint old_buffer = bindTransparently(); 115 | 116 | glFramebufferTexture2D(target_, static_cast(target), texture.target_, texture.id(), 0); 117 | 118 | attachments_[target] = texture.ptr_; // get pointer to prevent deallocation before framebuffer is deallocated. 119 | valid_ = (glCheckFramebufferStatus(target_) == GL_FRAMEBUFFER_COMPLETE); 120 | 121 | releaseTransparently(old_buffer); 122 | 123 | CheckGlError(); 124 | } 125 | 126 | /** \brief attach rectangular texture to given target. 127 | * 128 | * The texture must have at least the width and height of the framebuffer object. 129 | **/ 130 | void GlFramebuffer::attach(FramebufferAttachment target, GlTextureRectangle& texture) { 131 | if (texture.width() < width_) { 132 | std::stringstream error; 133 | error << "Texture's width should be at least " << width_; 134 | throw GlFramebufferError(error.str()); 135 | } 136 | 137 | if (texture.height() < height_) { 138 | std::stringstream error; 139 | error << "Texture's height should be at least " << height_; 140 | throw GlFramebufferError(error.str()); 141 | } 142 | 143 | GLuint old_buffer = bindTransparently(); 144 | 145 | glFramebufferTexture2D(target_, static_cast(target), GL_TEXTURE_RECTANGLE, texture.id(), 0); 146 | 147 | attachments_[target] = texture.ptr_; // get pointer to prevent deallocation before framebuffer is deallocated. 148 | valid_ = (glCheckFramebufferStatus(target_) == GL_FRAMEBUFFER_COMPLETE); 149 | 150 | releaseTransparently(old_buffer); 151 | 152 | CheckGlError(); 153 | } 154 | 155 | void GlFramebuffer::attach(FramebufferAttachment target, GlRenderbuffer& buffer) { 156 | if (buffer.width() < width_) { 157 | std::stringstream error; 158 | error << "Renderbuffer's width should be at least " << width_; 159 | throw GlFramebufferError(error.str()); 160 | } 161 | 162 | if (buffer.height() < height_) { 163 | std::stringstream error; 164 | error << "Renderbuffer's width should be at least " << height_; 165 | throw GlFramebufferError(error.str()); 166 | } 167 | 168 | GLuint old_buffer = bindTransparently(); 169 | 170 | glFramebufferRenderbuffer(target_, static_cast(target), GL_RENDERBUFFER, buffer.id()); 171 | 172 | attachments_[target] = buffer.ptr_; // get pointer to prevent deallocation before framebuffer is deallocated. 173 | valid_ = (glCheckFramebufferStatus(target_) == GL_FRAMEBUFFER_COMPLETE); 174 | 175 | releaseTransparently(old_buffer); 176 | 177 | CheckGlError(); 178 | } 179 | 180 | bool GlFramebuffer::valid() const { 181 | return valid_; 182 | } 183 | 184 | uint32_t GlFramebuffer::width() const { 185 | return width_; 186 | } 187 | 188 | uint32_t GlFramebuffer::height() const { 189 | return height_; 190 | } 191 | 192 | void GlFramebuffer::resize(uint32_t width, uint32_t height) { 193 | valid_ = false; 194 | width_ = width; 195 | height_ = height; 196 | } 197 | 198 | } /* namespace rv */ 199 | -------------------------------------------------------------------------------- /src/glow/GlFramebuffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * GlFramebuffer.cpp 3 | * 4 | * Created on: Apr 28, 2016 5 | * Author: behley 6 | */ 7 | 8 | #include "glow/GlFramebuffer.h" 9 | #include "glow/glexception.h" 10 | 11 | #include 12 | 13 | namespace glow { 14 | 15 | GLuint GlFramebuffer::boundFramebuffer_ = 0; 16 | 17 | GlFramebuffer::GlFramebuffer(uint32_t width, uint32_t height, FramebufferTarget target) 18 | : target_(static_cast(target)), width_(width), height_(height) { 19 | glGenFramebuffers(1, &id_); 20 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 21 | glDeleteFramebuffers(1, ptr); 22 | delete ptr; 23 | }); 24 | 25 | CheckGlError(); 26 | } 27 | 28 | GlFramebuffer::~GlFramebuffer() { 29 | if (boundFramebuffer_ == id_) { 30 | release(); 31 | assert((boundFramebuffer_ != id_) && "Framebuffer object still bound."); 32 | } 33 | attachments_.clear(); 34 | 35 | CheckGlError(); 36 | } 37 | 38 | inline std::string error_string(GLint id) { 39 | switch (id) { 40 | case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 41 | return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; 42 | case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 43 | return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; 44 | case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: 45 | return "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"; 46 | case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: 47 | return "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"; 48 | case GL_FRAMEBUFFER_UNSUPPORTED: 49 | return "GL_FRAMEBUFFER_UNSUPPORTED"; 50 | } 51 | 52 | return "UNKNOWN ERROR."; 53 | } 54 | 55 | void GlFramebuffer::clear(GLbitfield options) { 56 | GLuint old_id = bindTransparently(); 57 | 58 | glViewport(0, 0, width_, height_); 59 | glClear(options); 60 | 61 | releaseTransparently(old_id); 62 | 63 | CheckGlError(); 64 | } 65 | 66 | void GlFramebuffer::bind() { 67 | if (!valid_) { 68 | std::stringstream reason; 69 | reason << "Invalid framebuffer object. Code "; 70 | glBindFramebuffer(target_, id_); 71 | GLint errid = glCheckFramebufferStatus(target_); 72 | reason << errid << "(" << error_string(errid) << ")"; 73 | glBindFramebuffer(target_, 0); 74 | throw GlFramebufferError(reason.str()); 75 | } 76 | 77 | boundFramebuffer_ = id_; 78 | glBindFramebuffer(target_, id_); 79 | 80 | CheckGlError(); 81 | } 82 | 83 | void GlFramebuffer::release() { 84 | boundFramebuffer_ = 0; 85 | glBindFramebuffer(target_, 0); 86 | 87 | CheckGlError(); 88 | } 89 | 90 | GLuint GlFramebuffer::bindTransparently() { 91 | glBindFramebuffer(target_, id_); 92 | 93 | return boundFramebuffer_; 94 | } 95 | 96 | void GlFramebuffer::releaseTransparently(GLuint old_id) { 97 | glBindFramebuffer(target_, old_id); 98 | } 99 | 100 | void GlFramebuffer::attach(FramebufferAttachment target, GlTexture& texture) { 101 | if (texture.target_ != GL_TEXTURE_2D) throw GlFramebufferError("Expected two-dimensional texture"); 102 | if (texture.width() < width_) { 103 | std::stringstream error; 104 | error << "Texture's width should be at least " << width_; 105 | throw GlFramebufferError(error.str()); 106 | } 107 | 108 | if (texture.height() < height_) { 109 | std::stringstream error; 110 | error << "Texture's height should be at least " << height_; 111 | throw GlFramebufferError(error.str()); 112 | } 113 | 114 | GLuint old_buffer = bindTransparently(); 115 | 116 | glFramebufferTexture2D(target_, static_cast(target), texture.target_, texture.id(), 0); 117 | 118 | attachments_[target] = texture.ptr_; // get pointer to prevent deallocation before framebuffer is deallocated. 119 | valid_ = (glCheckFramebufferStatus(target_) == GL_FRAMEBUFFER_COMPLETE); 120 | 121 | releaseTransparently(old_buffer); 122 | 123 | CheckGlError(); 124 | } 125 | 126 | /** \brief attach rectangular texture to given target. 127 | * 128 | * The texture must have at least the width and height of the framebuffer object. 129 | **/ 130 | void GlFramebuffer::attach(FramebufferAttachment target, GlTextureRectangle& texture) { 131 | if (texture.width() < width_) { 132 | std::stringstream error; 133 | error << "Texture's width should be at least " << width_; 134 | throw GlFramebufferError(error.str()); 135 | } 136 | 137 | if (texture.height() < height_) { 138 | std::stringstream error; 139 | error << "Texture's height should be at least " << height_; 140 | throw GlFramebufferError(error.str()); 141 | } 142 | 143 | GLuint old_buffer = bindTransparently(); 144 | 145 | glFramebufferTexture2D(target_, static_cast(target), GL_TEXTURE_RECTANGLE, texture.id(), 0); 146 | 147 | attachments_[target] = texture.ptr_; // get pointer to prevent deallocation before framebuffer is deallocated. 148 | valid_ = (glCheckFramebufferStatus(target_) == GL_FRAMEBUFFER_COMPLETE); 149 | 150 | releaseTransparently(old_buffer); 151 | 152 | CheckGlError(); 153 | } 154 | 155 | void GlFramebuffer::attach(FramebufferAttachment target, GlRenderbuffer& buffer) { 156 | if (buffer.width() < width_) { 157 | std::stringstream error; 158 | error << "Renderbuffer's width should be at least " << width_; 159 | throw GlFramebufferError(error.str()); 160 | } 161 | 162 | if (buffer.height() < height_) { 163 | std::stringstream error; 164 | error << "Renderbuffer's width should be at least " << height_; 165 | throw GlFramebufferError(error.str()); 166 | } 167 | 168 | GLuint old_buffer = bindTransparently(); 169 | 170 | glFramebufferRenderbuffer(target_, static_cast(target), GL_RENDERBUFFER, buffer.id()); 171 | 172 | attachments_[target] = buffer.ptr_; // get pointer to prevent deallocation before framebuffer is deallocated. 173 | valid_ = (glCheckFramebufferStatus(target_) == GL_FRAMEBUFFER_COMPLETE); 174 | 175 | releaseTransparently(old_buffer); 176 | 177 | CheckGlError(); 178 | } 179 | 180 | bool GlFramebuffer::valid() const { 181 | return valid_; 182 | } 183 | 184 | uint32_t GlFramebuffer::width() const { 185 | return width_; 186 | } 187 | 188 | uint32_t GlFramebuffer::height() const { 189 | return height_; 190 | } 191 | 192 | void GlFramebuffer::resize(uint32_t width, uint32_t height) { 193 | valid_ = false; 194 | width_ = width; 195 | height_ = height; 196 | } 197 | 198 | } /* namespace rv */ 199 | -------------------------------------------------------------------------------- /src/glow/GlShader.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlShader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "glow/GlShaderCache.h" 9 | 10 | namespace glow { 11 | 12 | GlShader::GlShader(const ShaderType& type, const std::string& source, bool useCache) { 13 | id_ = glCreateShader(static_cast(type)); 14 | type_ = type; 15 | useCache_ = useCache; 16 | 17 | std::string preprocessed = preprocess(source); 18 | 19 | const char* src = preprocessed.c_str(); 20 | glShaderSource(id_, 1, &src, nullptr); 21 | 22 | glCompileShader(id_); 23 | 24 | GLint success = 1337; 25 | glGetShaderiv(id_, GL_COMPILE_STATUS, &success); 26 | if (success == GL_FALSE) { 27 | GLint log_size = 0; 28 | glGetShaderiv(id_, GL_INFO_LOG_LENGTH, &log_size); 29 | 30 | std::vector error_string(log_size); 31 | glGetShaderInfoLog(id_, log_size, &log_size, &error_string[0]); 32 | 33 | throw GlShaderError(std::string(&error_string[0], log_size)); 34 | } 35 | 36 | ptr_ = std::shared_ptr(new GLuint(id_), [](GLuint* ptr) { 37 | glDeleteShader(*ptr); 38 | delete ptr; 39 | }); 40 | } 41 | 42 | ShaderType GlShader::type() const { 43 | return type_; 44 | } 45 | 46 | GlShader GlShader::fromFile(const ShaderType& type, const std::string& filename) { 47 | std::string source = readSource(filename); 48 | 49 | try { 50 | // exploit move semantics to generate an rvalue. 51 | GlShader shader(type, source); 52 | shader.filename = filename; 53 | return shader; 54 | } catch (const GlShaderError& err) { 55 | // rethrow error with filename attached. 56 | std::string error_msg = filename; 57 | error_msg += ": " + std::string(err.what()); 58 | throw GlShaderError(error_msg); 59 | } 60 | } 61 | 62 | GlShader GlShader::fromCache(const ShaderType& type, const std::string& filename) { 63 | std::string source = getCachedSource(filename); 64 | 65 | try { 66 | // exploit move semantics to generate an rvalue. 67 | GlShader shader(type, source, true); 68 | shader.filename = filename; 69 | return shader; 70 | } catch (const GlShaderError& err) { 71 | // rethrow error with filename attached. 72 | std::string error_msg = filename; 73 | error_msg += ": " + std::string(err.what()); 74 | throw GlShaderError(error_msg); 75 | } 76 | } 77 | 78 | void GlShader::bind() {} 79 | 80 | void GlShader::release() {} 81 | 82 | std::string trim(const std::string& str, const std::string& whitespaces = " \0\t\n\r\x0B") { 83 | int32_t beg = 0; 84 | int32_t end = 0; 85 | 86 | /** find the beginning **/ 87 | for (beg = 0; beg < (int32_t)str.size(); ++beg) { 88 | bool found = false; 89 | for (uint32_t i = 0; i < whitespaces.size(); ++i) { 90 | if (str[beg] == whitespaces[i]) { 91 | found = true; 92 | break; 93 | } 94 | } 95 | if (!found) break; 96 | } 97 | 98 | /** find the end **/ 99 | for (end = int32_t(str.size()) - 1; end > beg; --end) { 100 | bool found = false; 101 | for (uint32_t i = 0; i < whitespaces.size(); ++i) { 102 | if (str[end] == whitespaces[i]) { 103 | found = true; 104 | break; 105 | } 106 | } 107 | if (!found) break; 108 | } 109 | 110 | return str.substr(beg, end - beg + 1); 111 | } 112 | 113 | std::vector split(const std::string& line, const std::string& delim, bool skipEmpty = false) { 114 | std::vector tokens; 115 | 116 | boost::char_separator sep(delim.c_str(), "", (skipEmpty ? boost::drop_empty_tokens : boost::keep_empty_tokens)); 117 | boost::tokenizer > tokenizer(line, sep); 118 | 119 | for (auto it = tokenizer.begin(); it != tokenizer.end(); ++it) tokens.push_back(*it); 120 | 121 | return tokens; 122 | } 123 | 124 | std::string GlShader::readSource(const std::string& filename) { 125 | std::ifstream in_file(filename); 126 | if (!in_file.is_open()) throw GlShaderError("Unable to open shader file: " + filename); 127 | 128 | std::string line; 129 | std::string source; 130 | in_file.peek(); 131 | while (!in_file.eof()) { 132 | std::getline(in_file, line); 133 | source += line + "\n"; 134 | in_file.peek(); 135 | } 136 | 137 | in_file.close(); 138 | 139 | return source; 140 | } 141 | 142 | std::string GlShader::getCachedSource(const std::string& filename) { 143 | GlShaderCache& cache = GlShaderCache::getInstance(); 144 | 145 | if (!cache.hasSource(filename)) throw GlShaderError("Cache does not contain entry with name '" + filename + "'"); 146 | 147 | return cache.getSource(filename); 148 | } 149 | 150 | std::string GlShader::preprocess(const std::string& source) { 151 | std::stringstream in(source); 152 | std::stringstream out; 153 | std::string line; 154 | 155 | int32_t line_no = 0; 156 | 157 | in.peek(); 158 | while (!in.eof()) { 159 | std::getline(in, line); 160 | line = trim(line); 161 | 162 | std::vector tokens = split(line, " "); 163 | 164 | if (tokens.size() == 2 && tokens[0] == "#include") { 165 | if (tokens.size() != 2) { 166 | throw GlShaderError("Filename of included file missing in line " + std::to_string(line_no)); 167 | } 168 | 169 | std::string filename = trim(tokens[1], "\""); 170 | std::string include_source; 171 | if (useCache_) 172 | include_source = getCachedSource(filename); 173 | else 174 | include_source = readSource(filename); 175 | 176 | out << preprocess(include_source) << std::endl; 177 | } else if (tokens.size() > 3 && tokens[0] == "in") { 178 | Attribute attr; 179 | attr.name = trim(tokens[2], ";"); 180 | attr.type = tokens[1]; 181 | 182 | inAttribs_.push_back(attr); 183 | 184 | out << line << std::endl; 185 | } else if (tokens.size() > 4 && tokens[0] == "flat" && tokens[1] == "in") { 186 | Attribute attr; 187 | attr.name = trim(tokens[3], ";"); 188 | attr.type = tokens[2]; 189 | 190 | inAttribs_.push_back(attr); 191 | 192 | out << line << std::endl; 193 | } else if (tokens.size() > 3 && tokens[0] == "out") { 194 | Attribute attr; 195 | attr.name = trim(tokens[2], ";"); 196 | attr.type = tokens[1]; 197 | 198 | outAttribs_.push_back(attr); 199 | 200 | out << line << std::endl; 201 | } else if (tokens.size() > 3 && tokens[0] == "flat" && tokens[1] == "out") { 202 | Attribute attr; 203 | attr.name = trim(tokens[3], ";"); 204 | attr.type = tokens[2]; 205 | 206 | outAttribs_.push_back(attr); 207 | 208 | out << line << std::endl; 209 | } else { 210 | out << line << std::endl; 211 | } 212 | 213 | in.peek(); 214 | } 215 | 216 | if (out.str().size() == 0) std::cerr << "Warning: empty shader source." << std::endl; 217 | return out.str(); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /test/texture-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace glow; 8 | 9 | // TODO: write tests for resize, etc. 10 | // TODO: test side effects & pre and post conditions, i.e., GlState::queryAll() should be essentially the same before 11 | // and after copy. 12 | 13 | TEST(TextureTest, loadTexture) { 14 | } 15 | 16 | TEST(TextureTest, assignTest) { 17 | GlTexture texture(100, 50, TextureFormat::RGBA_FLOAT); 18 | ASSERT_NO_THROW(CheckGlError()); 19 | std::vector img(100 * 50 * 4); 20 | ASSERT_EQ(100 * 50 * 4, img.size()); 21 | for (uint32_t i = 0; i < img.size(); ++i) { 22 | img[i] = 1.45f * i; 23 | } 24 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 25 | ASSERT_NO_THROW(CheckGlError()); 26 | 27 | std::vector device_mem; 28 | texture.download(device_mem); 29 | ASSERT_EQ(img.size(), device_mem.size()); 30 | for (uint32_t i = 0; i < img.size(); ++i) { 31 | ASSERT_EQ(img[i], device_mem[i]); 32 | } 33 | } 34 | 35 | TEST(TextureTest, copyTest) { 36 | GlTexture texture(100, 50, TextureFormat::RGBA_FLOAT); 37 | ASSERT_NO_THROW(CheckGlError()); 38 | 39 | std::vector img(100 * 50 * 4); 40 | for (uint32_t i = 0; i < img.size(); ++i) { 41 | img[i] = 1.45f * i; 42 | } 43 | 44 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 45 | ASSERT_NO_THROW(CheckGlError()); 46 | 47 | GlTexture texture2(100, 50, TextureFormat::RGBA_FLOAT); 48 | 49 | GlState state_before = GlState::queryAll(); 50 | 51 | texture2.copy(texture); 52 | 53 | GlState state_afterwards = GlState::queryAll(); 54 | 55 | if (state_before != state_afterwards) { 56 | state_before.difference(state_afterwards); 57 | } 58 | 59 | ASSERT_TRUE(state_before == state_afterwards); 60 | 61 | std::vector device_mem; 62 | texture2.download(device_mem); 63 | ASSERT_EQ(img.size(), device_mem.size()); 64 | // for (uint32_t i = 0; i < 12; ++i) 65 | // { 66 | // std::cout << device_mem[i] << ", "; 67 | // } 68 | // std::cout << std::endl; 69 | 70 | for (uint32_t i = 0; i < img.size(); ++i) { 71 | ASSERT_EQ(img[i], device_mem[i]); 72 | } 73 | } 74 | 75 | TEST(TextureTest, cloneTest) { 76 | GlTexture texture(100, 50, TextureFormat::RGBA_FLOAT); 77 | ASSERT_NO_THROW(CheckGlError()); 78 | 79 | std::vector img(100 * 50 * 4); 80 | for (uint32_t i = 0; i < img.size(); ++i) { 81 | img[i] = 1234.3 * i; 82 | } 83 | 84 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 85 | ASSERT_NO_THROW(CheckGlError()); 86 | 87 | GlTexture clone = texture.clone(); 88 | 89 | ASSERT_EQ(texture.width(), clone.width()); 90 | ASSERT_EQ(texture.height(), clone.height()); 91 | 92 | std::vector device_mem; 93 | clone.download(device_mem); 94 | ASSERT_EQ(img.size(), device_mem.size()); 95 | // for (uint32_t i = 0; i < 12; ++i) 96 | // { 97 | // std::cout << device_mem[i] << ", "; 98 | // } 99 | // std::cout << std::endl; 100 | 101 | for (uint32_t i = 0; i < img.size(); ++i) { 102 | ASSERT_EQ(img[i], device_mem[i]); 103 | } 104 | } 105 | 106 | TEST(TextureRectangleTest, loadTexture) { 107 | } 108 | 109 | TEST(TextureRectangleTest, assignTest) { 110 | GlTextureRectangle texture(100, 50, TextureFormat::RGBA_FLOAT); 111 | ASSERT_NO_THROW(CheckGlError()); 112 | std::vector img(100 * 50 * 4); 113 | ASSERT_EQ(100 * 50 * 4, img.size()); 114 | for (uint32_t i = 0; i < img.size(); ++i) { 115 | img[i] = 1.45f * i; 116 | } 117 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 118 | ASSERT_NO_THROW(CheckGlError()); 119 | 120 | std::vector device_mem; 121 | texture.download(device_mem); 122 | ASSERT_EQ(img.size(), device_mem.size()); 123 | for (uint32_t i = 0; i < img.size(); ++i) { 124 | ASSERT_EQ(img[i], device_mem[i]); 125 | } 126 | } 127 | 128 | TEST(TextureRectangleTest, copyTest) { 129 | GlTextureRectangle texture(100, 50, TextureFormat::RGBA_FLOAT); 130 | ASSERT_NO_THROW(CheckGlError()); 131 | 132 | std::vector img(100 * 50 * 4); 133 | for (uint32_t i = 0; i < img.size(); ++i) { 134 | img[i] = 1.45f * i; 135 | } 136 | 137 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 138 | ASSERT_NO_THROW(CheckGlError()); 139 | 140 | GlTextureRectangle texture2(100, 50, TextureFormat::RGBA_FLOAT); 141 | texture2.copy(texture); 142 | 143 | std::vector device_mem; 144 | texture2.download(device_mem); 145 | 146 | // for (uint32_t i = 0; i < 12; ++i) 147 | // { 148 | // std::cout << device_mem[i] << ", "; 149 | // } 150 | // std::cout << std::endl; 151 | 152 | ASSERT_EQ(img.size(), device_mem.size()); 153 | // for (uint32_t i = 0; i < 12; ++i) 154 | // { 155 | // std::cout << device_mem[i] << ", "; 156 | // } 157 | // std::cout << std::endl; 158 | 159 | for (uint32_t i = 0; i < img.size(); ++i) { 160 | ASSERT_EQ(img[i], device_mem[i]); 161 | } 162 | } 163 | 164 | TEST(TextureRectangleTest, copyTextureTest) { 165 | GlTexture texture(100, 50, TextureFormat::RGBA_FLOAT); 166 | ASSERT_NO_THROW(CheckGlError()); 167 | 168 | std::vector img(100 * 50 * 4); 169 | for (uint32_t i = 0; i < img.size(); ++i) { 170 | img[i] = 1.45f * i; 171 | } 172 | 173 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 174 | ASSERT_NO_THROW(CheckGlError()); 175 | 176 | GlTextureRectangle texture2(100, 50, TextureFormat::RGBA_FLOAT); 177 | texture2.copy(texture); 178 | 179 | std::vector device_mem; 180 | texture2.download(device_mem); 181 | 182 | // for (uint32_t i = 0; i < 12; ++i) 183 | // { 184 | // std::cout << device_mem[i] << ", "; 185 | // } 186 | // std::cout << std::endl; 187 | 188 | ASSERT_EQ(img.size(), device_mem.size()); 189 | // for (uint32_t i = 0; i < 12; ++i) 190 | // { 191 | // std::cout << device_mem[i] << ", "; 192 | // } 193 | // std::cout << std::endl; 194 | 195 | for (uint32_t i = 0; i < img.size(); ++i) { 196 | ASSERT_EQ(img[i], device_mem[i]); 197 | } 198 | } 199 | 200 | TEST(TextureRectangleTest, cloneTest) { 201 | GlTextureRectangle texture(100, 50, TextureFormat::RGBA_FLOAT); 202 | ASSERT_NO_THROW(CheckGlError()); 203 | 204 | std::vector img(100 * 50 * 4); 205 | for (uint32_t i = 0; i < img.size(); ++i) { 206 | img[i] = 1234.3 * i; 207 | } 208 | 209 | texture.assign(PixelFormat::RGBA, PixelType::FLOAT, &img[0]); 210 | ASSERT_NO_THROW(CheckGlError()); 211 | 212 | GlTextureRectangle clone = texture.clone(); 213 | 214 | ASSERT_EQ(texture.width(), clone.width()); 215 | ASSERT_EQ(texture.height(), clone.height()); 216 | 217 | std::vector device_mem; 218 | clone.download(device_mem); 219 | ASSERT_EQ(img.size(), device_mem.size()); 220 | // for (uint32_t i = 0; i < 12; ++i) 221 | // { 222 | // std::cout << device_mem[i] << ", "; 223 | // } 224 | // std::cout << std::endl; 225 | 226 | for (uint32_t i = 0; i < img.size(); ++i) { 227 | ASSERT_EQ(img[i], device_mem[i]); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/glow/util/FpsCamera.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Emanuele Palazzolo (emanuele.palazzolo@uni-bonn.de) 2 | #include "glow/util/FpsCamera.h" 3 | #include 4 | #include 5 | 6 | namespace glow { 7 | 8 | FpsCamera::FpsCamera() { 9 | pressed_keys_.insert(std::make_pair(KeyboardKey::KeyW, false)); 10 | pressed_keys_.insert(std::make_pair(KeyboardKey::KeyA, false)); 11 | pressed_keys_.insert(std::make_pair(KeyboardKey::KeyS, false)); 12 | pressed_keys_.insert(std::make_pair(KeyboardKey::KeyD, false)); 13 | pressed_keys_.insert(std::make_pair(KeyboardKey::KeyC, false)); 14 | pressed_keys_.insert(std::make_pair(KeyboardKey::KeySpace, false)); 15 | } 16 | 17 | const Eigen::Matrix4f& FpsCamera::matrix() { 18 | mutex_.lock(); 19 | 20 | auto end = std::chrono::system_clock::now(); 21 | double dt = 0.0001 * std::chrono::duration_cast(end - startTime_).count(); 22 | 23 | if (dt > 0) { 24 | // apply velocity & reset timer... 25 | rotate(turnVel_ * dt, 0.0f); 26 | translate(forwardVel_, upVel_, sideVel_, dt); 27 | startTime_ = end; 28 | } 29 | 30 | forwardVel_ = 0.0; 31 | upVel_ = 0.0; 32 | sideVel_ = 0.0; 33 | turnVel_ = 0.0; 34 | 35 | if (pressed_keys_[KeyboardKey::KeyW]) { 36 | forwardVel_ = 1; 37 | } 38 | if (pressed_keys_[KeyboardKey::KeyS]) { 39 | forwardVel_ = -1; 40 | } 41 | if (pressed_keys_[KeyboardKey::KeyC]) { 42 | upVel_ = -1; 43 | } 44 | if (pressed_keys_[KeyboardKey::KeySpace]) { 45 | upVel_ = 1; 46 | } 47 | if (pressed_keys_[KeyboardKey::KeyA]) { 48 | sideVel_ = -1; 49 | } 50 | if (pressed_keys_[KeyboardKey::KeyD]) { 51 | sideVel_ = 1; 52 | } 53 | 54 | // recompute the view matrix (Euler angles) Remember: Inv(AB) = Inv(B)*Inv(A) 55 | // Inv(translate*rotateYaw*rotatePitch) = Inv(rotatePitch)*Inv(rotateYaw)*Inv(translate) 56 | 57 | view_ = glRotateX(-pitch_); 58 | view_ = view_ * glRotateY(-yaw_); 59 | view_ = view_ * glTranslate(-x_, -y_, -z_); 60 | 61 | mutex_.unlock(); 62 | 63 | return view_; 64 | } 65 | 66 | void FpsCamera::setMatrix(const Eigen::Matrix4f& m) { 67 | mutex_.lock(); 68 | 69 | Eigen::Vector4f view_dir = m.transpose() * Eigen::Vector4f(0, 0, -1, 0); 70 | Eigen::Vector4f new_cam = m.inverse() * Eigen::Vector4f(0, 0, 0, 1); 71 | Eigen::Vector4f new_ref = new_cam + view_dir; 72 | 73 | mutex_.unlock(); 74 | 75 | lookAt(new_cam.x(), new_cam.y(), new_cam.z(), new_ref.x(), new_ref.y(), new_ref.z()); 76 | } 77 | 78 | // since we are using here a different representation of the camera model we 79 | // have to re-implement everything to allow an appropriate modification. 80 | void FpsCamera::setPosition(float x, float y, float z) { 81 | mutex_.lock(); 82 | 83 | x_ = x; 84 | y_ = y; 85 | z_ = z; 86 | 87 | mutex_.unlock(); 88 | } 89 | 90 | Eigen::Vector4f FpsCamera::getPosition() const { 91 | return Eigen::Vector4f(x_, y_, z_, 1.0f); 92 | } 93 | 94 | void FpsCamera::lookAt(float x_ref, float y_ref, float z_ref) { 95 | mutex_.lock(); 96 | 97 | float x = x_ref - x_; 98 | float y = y_ref - y_; 99 | float z = z_ref - z_; 100 | 101 | Eigen::Vector3f dir(x, y, z); 102 | dir.normalize(); 103 | 104 | pitch_ = std::asin(dir.y()); 105 | yaw_ = atan2(-x, -z); 106 | 107 | mutex_.unlock(); 108 | } 109 | 110 | void FpsCamera::lookAt(float x_cam, float y_cam, float z_cam, float x_ref, float y_ref, float z_ref) { 111 | mutex_.lock(); 112 | 113 | x_ = x_cam; 114 | y_ = y_cam; 115 | z_ = z_cam; 116 | 117 | float x = x_ref - x_; 118 | float y = y_ref - y_; 119 | float z = z_ref - z_; 120 | 121 | Eigen::Vector3f dir(x, y, z); 122 | dir.normalize(); 123 | 124 | pitch_ = std::asin(dir.y()); 125 | yaw_ = atan2(-x, -z); 126 | 127 | mutex_.unlock(); 128 | } 129 | 130 | void FpsCamera::setYaw(float yaw) { 131 | mutex_.lock(); 132 | yaw_ = yaw; 133 | mutex_.unlock(); 134 | } 135 | 136 | void FpsCamera::setPitch(float pitch) { 137 | mutex_.lock(); 138 | pitch_ = pitch; 139 | mutex_.unlock(); 140 | } 141 | 142 | void FpsCamera::getCameraParameters(float& x, float& y, float& z, float& yaw, float& pitch) { 143 | mutex_.lock(); 144 | 145 | x = x_; 146 | y = y_; 147 | z = z_; 148 | yaw = yaw_; 149 | pitch = pitch_; 150 | 151 | mutex_.unlock(); 152 | } 153 | 154 | bool FpsCamera::mousePressed(float x, float y, MouseButton btn, KeyboardModifier modifier) { 155 | if (btn == MouseButton::RightButton) { 156 | startx_ = x; 157 | starty_ = y; 158 | startyaw_ = yaw_; 159 | startpitch_ = pitch_; 160 | // startTime_ = std::chrono::system_clock::now(); 161 | } 162 | return true; 163 | } 164 | 165 | bool FpsCamera::mouseReleased(float x, float y, MouseButton btn, KeyboardModifier modifier) { 166 | return true; 167 | } 168 | 169 | bool FpsCamera::keyPressed(KeyboardKey key, KeyboardModifier modifier) { 170 | if (pressed_keys_.count(key) > 0) { 171 | pressed_keys_[key] = true; 172 | } 173 | // TODO movement speed parametric 174 | if (modifier == KeyboardModifier::ShiftDown) { 175 | movement_speed_ = 200.0f; 176 | } else { 177 | movement_speed_ = 100.0f; 178 | } 179 | return true; 180 | } 181 | 182 | bool FpsCamera::keyReleased(KeyboardKey key, KeyboardModifier modifier) { 183 | if (pressed_keys_.count(key) > 0) { 184 | pressed_keys_[key] = false; 185 | } 186 | return true; 187 | } 188 | 189 | void FpsCamera::translate(float forward, float up, float sideways, float dt) { 190 | Eigen::Vector4f hom_translation(sideways, 0, -forward, 1); 191 | Eigen::Matrix4f R = glow::glRotateY(yaw_) * glow::glRotateX(pitch_); 192 | hom_translation = R * hom_translation; 193 | Eigen::Vector3f translation = hom_translation.block<3, 1>(0, 0); 194 | // translation /= hom_translation(3); 195 | translation(1) += up; 196 | if (translation.norm() != 0) { 197 | translation.normalize(); 198 | } 199 | 200 | translation.block<3, 1>(0, 0) *= movement_speed_ * dt; 201 | x_ += translation(0); 202 | y_ += translation(1); 203 | z_ += translation(2); 204 | } 205 | 206 | /*****************************************************************************/ 207 | 208 | void FpsCamera::rotate(float yaw, float pitch) { 209 | yaw_ += yaw; 210 | pitch_ += pitch; 211 | if (pitch_ < -M_PI_2) pitch_ = -M_PI_2; 212 | if (pitch_ > M_PI_2) pitch_ = M_PI_2; 213 | } 214 | 215 | bool FpsCamera::mouseMoved(float x, float y, MouseButton btn, KeyboardModifier modifier) { 216 | mutex_.lock(); 217 | 218 | // TODO: expose parameters: 219 | static const float LOOK_SENSITIVITY = 0.01f; 220 | static const float FREE_TURN_SENSITIVITY = 0.01f; 221 | 222 | float dx = x - startx_; 223 | float dy = y - starty_; 224 | 225 | if (btn == MouseButton::RightButton) { 226 | yaw_ = startyaw_ - FREE_TURN_SENSITIVITY * dx; 227 | pitch_ = startpitch_ - LOOK_SENSITIVITY * dy; 228 | 229 | // ensure valid values. 230 | if (pitch_ <= -M_PI_2) pitch_ = -M_PI_2; 231 | if (pitch_ >= M_PI_2) pitch_ = M_PI_2; 232 | } 233 | 234 | mutex_.unlock(); 235 | 236 | return true; 237 | } 238 | 239 | bool FpsCamera::wheelEvent(float delta, KeyboardModifier modifier) { 240 | return true; 241 | } 242 | 243 | } // namespace glow 244 | -------------------------------------------------------------------------------- /include/glow/GlTextureRectangle.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_RV_GLTEXTURERECTANGLE_H_ 2 | #define INCLUDE_RV_GLTEXTURERECTANGLE_H_ 3 | 4 | #include 5 | #include "GlObject.h" 6 | #include "GlPixelFormat.h" 7 | #include "GlTextureFormat.h" 8 | 9 | namespace glow { 10 | 11 | // forward declaration 12 | class GlTexture; 13 | 14 | enum class TexRectMinOp { LINEAR = GL_LINEAR, NEAREST = GL_NEAREST }; 15 | 16 | enum class TexRectMagOp { LINEAR = GL_LINEAR, NEAREST = GL_NEAREST }; 17 | 18 | /** 19 | - GL_CLAMP_TO_EDGE causes s coordinates to be clamped to the range 20 | [1/2N,1−1/2N], where N is the size of the texture in the direction of 21 | clamping. 22 | - GL_CLAMP_TO_BORDER evaluates s coordinates in a similar manner to 23 | GL_CLAMP_TO_EDGE. However, in cases where clamping would have occurred in 24 | - GL_CLAMP_TO_EDGE mode, the fetched texel data is substituted with the 25 | values specified by GL_TEXTURE_BORDER_COLOR. 26 | - GL_REPEAT causes the integer part of the s coordinate to be ignored; the 27 | GL uses only the fractional part, thereby creating a repeating pattern. 28 | - GL_MIRRORED_REPEAT causes the s coordinate to be set to the fractional 29 | part of the texture coordinate if the integer part of s is even; if the 30 | integer part of s is odd, then the s texture coordinate is set to 1−frac(s), 31 | where frac(s) represents the fractional part of s. 32 | - GL_MIRROR_CLAMP_TO_EDGE causes the the s coordinate to be repeated as for 33 | GL_MIRRORED_REPEAT for one repitition of the texture, at which point the 34 | coordinate to be clamped as in GL_CLAMP_TO_EDGE. 35 | 36 | **/ 37 | enum class TexRectWrapOp { 38 | CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, 39 | CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER, 40 | MIRRORED_REPEAT = GL_MIRRORED_REPEAT, 41 | // REPEAT = GL_REPEAT, -- not supported by rectangular textures. 42 | MIRROR_CLAMP_TO_EDGE = GL_MIRROR_CLAMP_TO_EDGE 43 | }; 44 | 45 | enum class TexRectSwizzle { R = GL_RED, G = GL_GREEN, B = GL_BLUE, A = GL_ALPHA, ZERO = GL_ZERO, ONE = GL_ONE }; 46 | 47 | /** \brief a rectangular texture object. 48 | * 49 | * A GlTextureRectangle is a wrapper for a texture of type GL_TEXTURE_RECTANGLE. As a 50 | * rectangular texture does not do the conversion to "texture coordinates" in range 51 | * [0,1], this type of texture does only support a single layer of textures and thus no 52 | * mipmapping. Furthermore, only two-dimensional rectangular textures are supported by 53 | * OpenGL. Apart from that, it should be the same as a "normal" texture. 54 | * 55 | * TODO: implement compatible texture's functionality. 56 | * 57 | * \see GlFramebuffer 58 | * 59 | * \author behley 60 | **/ 61 | class GlTextureRectangle : public GlObject { 62 | public: 63 | friend class GlFramebuffer; 64 | 65 | /** \brief create and allocate rectangular texture of given size and format. **/ 66 | GlTextureRectangle(uint32_t width, uint32_t height, TextureFormat); 67 | 68 | ~GlTextureRectangle(); 69 | 70 | /** \brief generate a copy of the texture. **/ 71 | GlTextureRectangle clone() const; 72 | 73 | /** \brief copy data from other to this texture. 74 | * 75 | * If textures have different sizes, the values are interpolated by using NEAREST texture value. 76 | **/ 77 | void copy(const GlTextureRectangle& other); 78 | 79 | /** \brief copy data from another texture. **/ 80 | void copy(const GlTexture& other); 81 | 82 | /** \brief bind the texture to the currently active texture unit. */ 83 | void bind() override; 84 | 85 | void release() override; 86 | 87 | void bind(uint32_t textureUnitId); 88 | void release(uint32_t textureUnitId); 89 | 90 | // TODO: expose remaining texture parameters by virtue of getter/setters. 91 | 92 | /** \brief set the filtering operation if texture is projected on smaller elements (squashed). **/ 93 | void setMinifyingOperation(TexRectMinOp minifyingOperation); 94 | /** \brief set the filtering operation if texture is projected on larger elements (enlarged). **/ 95 | void setMagnifyingOperation(TexRectMagOp magnifyingOperation); 96 | 97 | /** \brief set the wrapping function for accessing values outside the texture. **/ 98 | void setWrapOperation(TexRectWrapOp wrap_s, TexRectWrapOp wrap_t); 99 | 100 | /** \brief specifies how to swizzle values for access. 101 | * 102 | * Set the swizzling of color channels for access, i.e., a color component is replaced by 103 | * the specified value. 104 | * 105 | * For example, setTextureSwizzle(TexSwizzle::R, TexSwizzle::R, TexSwizzle::R, TexSwizzle::ONE) 106 | * causes to result for every read in (R,R,R,1) instead of the R,G,B,A color inside the texture. 107 | */ 108 | void setTextureSwizzle(TexRectSwizzle red, TexRectSwizzle green, TexRectSwizzle blue, TexRectSwizzle alpha); 109 | 110 | /** \brief Assign data to the texture. **/ 111 | template 112 | void assign(PixelFormat pixelfmt, PixelType type, T* data); 113 | 114 | /** \brief resize texture to given dimensions. 115 | * 116 | * FIXME: currently resize causes loss of all data. 117 | */ 118 | void resize(uint32_t width, uint32_t height); 119 | 120 | uint32_t width() const; 121 | uint32_t height() const; 122 | 123 | /** \brief save texture to specified file with given filename. 124 | * 125 | * Depending on the available libraries, different file types are supported. The 126 | * file type is inferred from the extension of the filename. 127 | * 128 | * \return true, if file could be written to the specified location. false, otherwise. 129 | **/ 130 | bool save(const std::string& filename) const; 131 | 132 | /** \brief load texture from specified file with given filename. 133 | * 134 | * Depending on the available libraries, different input file types are supported. The 135 | * actual file type is inferred from the extension of the filename. 136 | * 137 | * \throw GlTextureRectangle 138 | */ 139 | static GlTextureRectangle loadTexture(const std::string& filename); 140 | 141 | /** \brief download the texture to the given vector. 142 | * 143 | * TODO: enforce correct format. 144 | **/ 145 | template 146 | void download(std::vector& data) const; 147 | 148 | template 149 | void download(T* ptr) const; 150 | 151 | template 152 | void download(PixelFormat pixelfmt, T* ptr) const; 153 | 154 | protected: 155 | GLuint bindTransparently() const; 156 | void releaseTransparently(GLuint old_id) const; 157 | 158 | void allocateMemory(); 159 | 160 | static uint32_t numComponents(TextureFormat format); 161 | static GLuint boundTexture_; 162 | 163 | uint32_t width_, height_; 164 | TextureFormat format_; 165 | }; 166 | 167 | template 168 | void GlTextureRectangle::assign(PixelFormat pixelfmt, PixelType pixeltype, T* data) { 169 | GLuint old_id = bindTransparently(); 170 | 171 | // TODO: possible to have pixeltype == T? 172 | 173 | glTexImage2D(GL_TEXTURE_RECTANGLE, 0, static_cast(format_), width_, height_, 0, static_cast(pixelfmt), 174 | static_cast(pixeltype), data); 175 | 176 | releaseTransparently(old_id); 177 | } 178 | } 179 | /* namespace rv */ 180 | 181 | #endif /* INCLUDE_RV_GLTEXTURERECTANGLE_H_ */ 182 | -------------------------------------------------------------------------------- /test/buffer-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include "test_utils.h" 7 | 8 | using namespace glow; 9 | 10 | namespace { 11 | 12 | TEST(BufferTest, initTest) { 13 | GlBuffer buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 14 | // Note: a buffer is not a buffer until the first time bound, 15 | // even though it was created with glGenBuffers. 16 | buffer.bind(); 17 | buffer.release(); 18 | ASSERT_TRUE(glIsBuffer(buffer.id())); 19 | } 20 | 21 | TEST(BufferTest, assignTest) { 22 | uint32_t num_values = 17; 23 | std::vector values(num_values); 24 | Random rand(1234); 25 | for (uint32_t i = 0; i < values.size(); ++i) { 26 | values[i] = rand.getFloat(); 27 | } 28 | 29 | GlBuffer buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 30 | 31 | ASSERT_EQ(BufferTarget::ARRAY_BUFFER, buffer.target()); 32 | ASSERT_EQ(BufferUsage::DYNAMIC_DRAW, buffer.usage()); 33 | ASSERT_EQ(0, buffer.size()); // empty buffer. 34 | ASSERT_EQ(0, buffer.capacity()); // really empty buffer. 35 | 36 | buffer.assign(values); 37 | ASSERT_EQ(values.size(), buffer.size()); // now not empty buffer. 38 | 39 | std::vector buf; 40 | buffer.get(buf); 41 | ASSERT_EQ(values.size(), buf.size()); 42 | 43 | for (uint32_t i = 0; i < values.size(); ++i) { 44 | ASSERT_FLOAT_EQ(values[i], buf[i]); 45 | } 46 | 47 | GlBuffer assign_buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 48 | 49 | assign_buffer.assign(buffer); 50 | 51 | buf.clear(); 52 | assign_buffer.get(buf); 53 | ASSERT_EQ(values.size(), buf.size()); 54 | 55 | for (uint32_t i = 0; i < values.size(); ++i) { 56 | ASSERT_FLOAT_EQ(values[i], buf[i]); 57 | } 58 | 59 | ASSERT_NO_THROW(CheckGlError()); 60 | } 61 | 62 | TEST(BufferTest, reserveTest) { 63 | GlBuffer buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 64 | 65 | buffer.reserve(1000); 66 | ASSERT_EQ(1000, buffer.capacity()); 67 | ASSERT_EQ(0, buffer.size()); 68 | 69 | ASSERT_NO_THROW(CheckGlError()); 70 | } 71 | 72 | TEST(BufferTest, resizeTest) { 73 | GlBuffer buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 74 | 75 | buffer.reserve(1000); 76 | ASSERT_EQ(1000, buffer.capacity()); 77 | ASSERT_EQ(0, buffer.size()); 78 | 79 | buffer.resize(123); 80 | ASSERT_EQ(123, buffer.size()); 81 | ASSERT_EQ(1000, buffer.capacity()); 82 | 83 | GlBuffer buffer2(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 84 | buffer2.resize(145); 85 | ASSERT_EQ(145, buffer2.size()); 86 | ASSERT_EQ(145, buffer2.capacity()); 87 | 88 | ASSERT_NO_THROW(CheckGlError()); 89 | } 90 | 91 | TEST(BufferTest, replaceTest) { 92 | GlBuffer buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::STATIC_DRAW); 93 | 94 | uint32_t num_values = 100; 95 | std::vector values(num_values); 96 | 97 | Random rand(1234); 98 | for (uint32_t i = 0; i < values.size(); ++i) { 99 | values[i] = rand.getInt(100, 147); 100 | } 101 | 102 | buffer.assign(values); 103 | ASSERT_EQ(values.size(), buffer.size()); 104 | 105 | std::vector buf_values; 106 | buffer.get(buf_values); 107 | ASSERT_EQ(values.size(), buf_values.size()); 108 | 109 | // just to be sure that everything is as expected. 110 | for (uint32_t i = 0; i < buf_values.size(); ++i) { 111 | ASSERT_EQ(values[i], buf_values[i]); 112 | } 113 | 114 | ASSERT_NO_THROW(CheckGlError()); 115 | 116 | std::vector replace_values(13, 1); 117 | 118 | uint32_t offset = 10; 119 | buffer.replace(offset, replace_values); 120 | ASSERT_EQ(values.size(), buffer.size()); 121 | 122 | buffer.get(buf_values); 123 | for (uint32_t i = 0; i < buf_values.size(); ++i) { 124 | if (i < offset || i >= offset + replace_values.size()) { 125 | ASSERT_EQ(values[i], buf_values[i]); 126 | } else { 127 | ASSERT_EQ(replace_values[i - offset], buf_values[i]); 128 | values[i] = buf_values[i]; // ensure that values contains last buffer content. 129 | } 130 | } 131 | 132 | ASSERT_NO_THROW(CheckGlError()); 133 | 134 | // CHECK BOUNDARY CASE. 135 | offset = 95; 136 | replace_values.assign(124, 2); 137 | buffer.replace(offset, replace_values); 138 | ASSERT_EQ(values.size(), buffer.size()); 139 | 140 | ASSERT_NO_THROW(CheckGlError()); 141 | 142 | buffer.get(buf_values); 143 | for (uint32_t i = 0; i < buf_values.size(); ++i) { 144 | if (i < offset) { 145 | ASSERT_EQ(values[i], buf_values[i]); 146 | } else { 147 | ASSERT_EQ(replace_values[i - offset], buf_values[i]); 148 | values[i] = buf_values[i]; // ensure that values contains last buffer content. 149 | } 150 | } 151 | 152 | ASSERT_NO_THROW(CheckGlError()); 153 | } 154 | 155 | TEST(BufferTest, eigenTest) { 156 | GlBuffer mats(BufferTarget::ARRAY_BUFFER, BufferUsage::STATIC_READ); 157 | 158 | std::vector orig_mats; 159 | Eigen::Matrix4f t1; 160 | t1 << 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15; 161 | 162 | orig_mats.push_back(t1); 163 | Eigen::Matrix4f t2 = -1.0f * t1; 164 | orig_mats.push_back(t2); 165 | 166 | // std::cout << "sizeof(Eigen::Matrix4f) = " << sizeof(Eigen::Matrix4f) << ", " 167 | // << sizeof(Eigen::Matrix4f) / sizeof(float) << std::endl; 168 | 169 | // for (uint32_t i = 0; i < 16; ++i) { 170 | // std::cout << ((float*)&(orig_mats[0]) + i) << ","; 171 | // } 172 | // std::cout << std::endl; 173 | 174 | mats.assign(orig_mats); 175 | std::vector buffered_mats; 176 | mats.get(buffered_mats); 177 | 178 | ASSERT_EQ(orig_mats.size(), buffered_mats.size()); 179 | 180 | for (uint32_t k = 0; k < buffered_mats.size(); ++k) { 181 | // std::cout << buffered_mats[k] << std::endl; 182 | const Eigen::Matrix4f& A = buffered_mats[k]; 183 | const Eigen::Matrix4f& B = orig_mats[k]; 184 | for (uint32_t i = 0; i < 4; ++i) { 185 | for (uint32_t j = 0; j < 4; ++j) { 186 | ASSERT_LT(std::abs(A(i, j) - B(i, j)), 0.001f); 187 | } 188 | } 189 | } 190 | } 191 | 192 | TEST(BufferTest, copyTest) { 193 | 194 | // TODO: add meaningful test. 195 | } 196 | 197 | TEST(BufferTest, getTest) { 198 | uint32_t num_values = 17; 199 | std::vector values(num_values); 200 | Random rand(1234); 201 | for (uint32_t i = 0; i < values.size(); ++i) { 202 | values[i] = rand.getFloat(); 203 | } 204 | 205 | GlBuffer buffer(BufferTarget::ARRAY_BUFFER, BufferUsage::DYNAMIC_DRAW); 206 | buffer.assign(values); 207 | ASSERT_EQ(values.size(), buffer.size()); // now not empty buffer. 208 | 209 | std::vector buf; 210 | buffer.get(buf); 211 | ASSERT_EQ(values.size(), buf.size()); 212 | 213 | for (uint32_t i = 0; i < values.size(); ++i) { 214 | ASSERT_FLOAT_EQ(values[i], buf[i]); 215 | } 216 | 217 | uint32_t offset = 2; 218 | buffer.get(buf, offset, buffer.size() - offset); 219 | ASSERT_EQ(values.size() - offset, buf.size()); 220 | 221 | for (uint32_t i = offset; i < values.size() - offset; ++i) { 222 | ASSERT_FLOAT_EQ(values[i], buf[i - offset]); 223 | } 224 | 225 | offset = 3; 226 | uint32_t size = 5; 227 | buffer.get(buf, offset, size); 228 | ASSERT_EQ(size, buf.size()); 229 | 230 | for (uint32_t i = offset; i < size + offset; ++i) { 231 | ASSERT_FLOAT_EQ(values[i], buf[i - offset]); 232 | } 233 | 234 | offset = 4; 235 | buffer.get(buf, offset, 2 * buffer.size()); 236 | ASSERT_EQ(values.size() - offset, buf.size()); 237 | 238 | for (uint32_t i = offset; i < values.size() - offset; ++i) { 239 | ASSERT_FLOAT_EQ(values[i], buf[i - offset]); 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /include/glow/GlTexture.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_OPENGL_GLTEXTURE_H_ 2 | #define SRC_OPENGL_GLTEXTURE_H_ 3 | 4 | #include 5 | 6 | #include "GlObject.h" 7 | #include "GlPixelFormat.h" 8 | #include "GlTextureFormat.h" 9 | 10 | namespace glow { 11 | 12 | class GlFramebuffer; 13 | class GlTextureRectangle; 14 | 15 | enum class TexMinOp { 16 | LINEAR = GL_LINEAR, 17 | NEAREST = GL_NEAREST, 18 | NEAREST_MIPMAP_NEAREST = GL_NEAREST_MIPMAP_NEAREST, 19 | NEAREST_MIPMAP_LINEAR = GL_NEAREST_MIPMAP_LINEAR, 20 | LINEAR_MIPMAP_NEAREST = GL_LINEAR_MIPMAP_NEAREST, 21 | LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR 22 | }; 23 | 24 | enum class TexMagOp { LINEAR = GL_LINEAR, NEAREST = GL_NEAREST }; 25 | 26 | enum class TexWrapOp { 27 | CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, 28 | CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER, 29 | MIRRORED_REPEAT = GL_MIRRORED_REPEAT, 30 | REPEAT = GL_REPEAT, 31 | MIRROR_CLAMP_TO_EDGE = GL_MIRROR_CLAMP_TO_EDGE 32 | }; 33 | 34 | enum class TexSwizzle { R = GL_RED, G = GL_GREEN, B = GL_BLUE, A = GL_ALPHA, ZERO = GL_ZERO, ONE = GL_ONE }; 35 | 36 | /** 37 | * \brief Wrapper for OpenGl's texture 38 | * 39 | * The class provides simpler access to texture by encapsulating most of the 40 | * functionality associated with a texture. 41 | * 42 | * OpenGL 4.5 Core specification: 43 | * > Texture objects or textures include a collection of texture images built from arrays 44 | * > of image elements. The image elements are referred to as texels. There are many 45 | * > types of texture objects varying by dimensionality and structure; [...] 46 | * > exture objects also include state describing the image parameters of the tex- 47 | * > ture images, and state describing how sampling is performed when a shader ac- 48 | * > cesses a texture. 49 | * > Shaders may sample a texture at a location indicated by specified texture co- 50 | * > ordinates, with details of sampling determined by the sampler state of the texture. 51 | * > The resulting texture samples are typically used to modify a fragment’s color, in 52 | * > order to map an image onto a geometric primitive being drawn, but may be used 53 | * > for any purpose in a shader. 54 | * 55 | * TODO: To ensure consistency: the assign, resize on non-unique objects should generate a new 56 | * texture object, i.e., if ptr_->unique is true, then these functions simply resue the texture object? 57 | * 58 | * TODO: Implement for each dimension a own class: GlTexture1D, GlTexture2D, GlTexture3D...this, would 59 | * firstly enable a more consistent enforcement of constraints for framebuffers, etc. and secondly make 60 | * it unnecessary to check always the dimensions of the underlying opengl texture. 61 | * 62 | * \see GlFramebuffer 63 | * 64 | * \author behley 65 | * 66 | */ 67 | class GlTexture : public GlObject { 68 | public: 69 | friend class GlFramebuffer; 70 | friend class GlTextureRectangle; 71 | 72 | /** \brief create a one-dimensional empty texture with specified internal format **/ 73 | GlTexture(uint32_t width, TextureFormat format = TextureFormat::RGB); 74 | /** \brief create a two-dimensional empty texture with specified internal format **/ 75 | GlTexture(uint32_t width, uint32_t height, TextureFormat format = TextureFormat::RGB); 76 | /** \brief create a three-dimensional empty texture with specified internal format **/ 77 | GlTexture(uint32_t width, uint32_t height, uint32_t depth, TextureFormat format = TextureFormat::RGB); 78 | 79 | ~GlTexture(); 80 | 81 | /** \brief generate a copy of the texture. **/ 82 | GlTexture clone() const; 83 | 84 | /** \brief copy data from other to this texture. 85 | * 86 | * If textures have different sizes, the values are interpolated by using NEAREST texture value. 87 | **/ 88 | void copy(const GlTexture& other); 89 | 90 | /** \brief bind the texture to the currently active texture unit. */ 91 | void bind() override; 92 | 93 | void release() override; 94 | 95 | void bind(uint32_t textureUnitId); 96 | void release(uint32_t textureUnitId); 97 | 98 | // TODO: expose remaining texture parameters by virtue of getter/setters. 99 | 100 | /** \brief set the filtering operation if texture is projected on smaller elements (squashed). **/ 101 | void setMinifyingOperation(TexMinOp minifyingOperation); 102 | /** \brief set the filtering operation if texture is projected on larger elements (enlarged). **/ 103 | void setMagnifyingOperation(TexMagOp magnifyingOperation); 104 | 105 | /** \brief set the wrapping function for accessing values outside the texture. **/ 106 | void setWrapOperation(TexWrapOp wrap_s); 107 | /** \brief set the wrapping function for accessing values outside the texture. **/ 108 | void setWrapOperation(TexWrapOp wrap_s, TexWrapOp wrap_t); 109 | /** \brief set the wrapping function for accessing values outside the texture. **/ 110 | void setWrapOperation(TexWrapOp wrap_s, TexWrapOp wrap_t, TexWrapOp wrap_r); 111 | 112 | /** \brief specifies how to swizzle values for access. 113 | * 114 | * Set the swizzling of color channels for access, i.e., a color component is replaced by 115 | * the spefcified value. 116 | * 117 | * For example, setTextureSwizzle(TexSwizzle::R, TexSwizzle::R, TexSwizzle::R, TexSwizzle::ONE) 118 | * causes to result for every read in (R,R,R,1) instead of the R,G,B,A color inside the texture. 119 | */ 120 | void setTextureSwizzle(TexSwizzle red, TexSwizzle green, TexSwizzle blue, TexSwizzle alpha); 121 | 122 | /** \brief Assign data to the texture. **/ 123 | template 124 | void assign(PixelFormat pixelfmt, PixelType type, T* data); 125 | 126 | /** \brief resizing the texture to given dimensions. 127 | * 128 | * FIXME: resize currently causes loss of all old information. 129 | **/ 130 | void resize(uint32_t width); 131 | 132 | /** \brief resizing the texture to given dimensions. 133 | * 134 | * FIXME: resize currently causes loss of all old information. 135 | **/ 136 | void resize(uint32_t width, uint32_t height); 137 | 138 | /** \brief resizing the texture to given dimensions. 139 | * 140 | * FIXME: resize currently causes loss of all old information. 141 | **/ 142 | void resize(uint32_t width, uint32_t height, uint32_t depth); 143 | 144 | uint32_t width() const; 145 | uint32_t height() const; 146 | uint32_t depth() const; 147 | 148 | /** \brief save texture to specified file with given filename. 149 | * 150 | * Depending on the available libraries, different file types are supported. The 151 | * file type is inferred from the extension of the filename. 152 | * 153 | * \return true, if file could be written to the specified location. false, otherwise. 154 | **/ 155 | bool save(const std::string& filename) const; 156 | 157 | /** \brief load texture from specified file with given filename. 158 | * 159 | * Depending on the available libraries, different input file types are supported. The 160 | * actual file type is inferred from the extension of the filename. 161 | * 162 | * \throw GlTextureError 163 | */ 164 | static GlTexture loadTexture(const std::string& filename); 165 | 166 | /** \brief download the texture to the given vector. 167 | * 168 | * TODO: enforce correct format. 169 | **/ 170 | template 171 | void download(std::vector& data) const; 172 | 173 | template 174 | void download(T* ptr) const; 175 | 176 | template 177 | void download(PixelFormat pixelfmt, T* ptr) const; 178 | 179 | /** \brief generate Mipmaps. **/ 180 | void generateMipmaps(); 181 | 182 | protected: 183 | // const std::shared_ptr& ptr() const 184 | // { 185 | // return ptr_; 186 | // } 187 | 188 | GLuint bindTransparently() const; 189 | void releaseTransparently(GLuint old_id) const; 190 | 191 | void allocateMemory(); 192 | 193 | static uint32_t numComponents(TextureFormat format); 194 | static GLuint boundTexture_; 195 | 196 | uint32_t width_, height_{0}, depth_{0}; 197 | GLenum target_; 198 | TextureFormat format_; 199 | }; 200 | 201 | template 202 | void GlTexture::assign(PixelFormat pixelfmt, PixelType pixeltype, T* data) { 203 | GLuint old_id = bindTransparently(); 204 | 205 | // TODO: possible to have pixeltype == T? 206 | 207 | if (target_ == GL_TEXTURE_1D) { 208 | glTexImage1D(target_, 0, static_cast(format_), width_, 0, static_cast(pixelfmt), 209 | static_cast(pixeltype), data); 210 | } else if (target_ == GL_TEXTURE_2D) { 211 | glTexImage2D(target_, 0, static_cast(format_), width_, height_, 0, static_cast(pixelfmt), 212 | static_cast(pixeltype), data); 213 | } else if (target_ == GL_TEXTURE_3D) { 214 | glTexImage3D(target_, 0, static_cast(format_), width_, height_, depth_, 0, static_cast(pixelfmt), 215 | static_cast(pixeltype), data); 216 | } 217 | 218 | releaseTransparently(old_id); 219 | } 220 | 221 | } /* namespace rv */ 222 | 223 | #endif /* SRC_OPENGL_GLTEXTURE_H_ */ 224 | -------------------------------------------------------------------------------- /src/glow/util/RoSeCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/util/RoSeCamera.h" 2 | #include "glow/glutil.h" 3 | 4 | namespace glow { 5 | 6 | const Eigen::Matrix4f& RoSeCamera::matrix() { 7 | mutex_.lock(); 8 | 9 | auto end = std::chrono::system_clock::now(); 10 | double dt = 0.0001 * std::chrono::duration_cast(end - startTime_).count(); 11 | 12 | if (dt > 0 && startdrag_) { 13 | // apply velocity & reset timer... 14 | rotate(turnVel_ * dt, 0.0f); 15 | translate(forwardVel_ * dt, upVel_ * dt, sideVel_ * dt); 16 | startTime_ = end; 17 | } 18 | 19 | // recompute the view matrix (Euler angles) Remember: Inv(AB) = Inv(B)*Inv(A) 20 | // Inv(translate*rotateYaw*rotatePitch) = Inv(rotatePitch)*Inv(rotateYaw)*Inv(translate) 21 | view_ = glRotateX(-pitch_); 22 | view_ = view_ * glRotateY(-yaw_); 23 | view_ = view_ * glTranslate(-x_, -y_, -z_); 24 | 25 | mutex_.unlock(); 26 | 27 | return view_; 28 | } 29 | 30 | void RoSeCamera::setMatrix(const Eigen::Matrix4f& m) { 31 | mutex_.lock(); 32 | 33 | Eigen::Vector4f view_dir = m.transpose() * Eigen::Vector4f(0, 0, -1, 0); 34 | Eigen::Vector4f new_cam = m.inverse() * Eigen::Vector4f(0, 0, 0, 1); 35 | Eigen::Vector4f new_ref = new_cam + view_dir; 36 | 37 | mutex_.unlock(); 38 | 39 | lookAt(new_cam.x(), new_cam.y(), new_cam.z(), new_ref.x(), new_ref.y(), new_ref.z()); 40 | } 41 | 42 | // since we are using here a different representation of the camera model we 43 | // have to re-implement everything to allow an appropriate modification. 44 | void RoSeCamera::setPosition(float x, float y, float z) { 45 | mutex_.lock(); 46 | 47 | x_ = x; 48 | y_ = y; 49 | z_ = z; 50 | 51 | mutex_.unlock(); 52 | } 53 | 54 | Eigen::Vector4f RoSeCamera::getPosition() const { 55 | return Eigen::Vector4f(x_, y_, z_, 1.0f); 56 | } 57 | 58 | void RoSeCamera::lookAt(float x_ref, float y_ref, float z_ref) { 59 | mutex_.lock(); 60 | 61 | float x = x_ref - x_; 62 | float y = y_ref - y_; 63 | float z = z_ref - z_; 64 | 65 | Eigen::Vector3f dir(x, y, z); 66 | dir.normalize(); 67 | 68 | // = std::acos(-dir.y()) - M_PI_2 = M_PI - std::acos(dir.y()) - M_PI_2; // in [-pi/2,pi/2] 69 | pitch_ = std::asin(dir.y()); 70 | yaw_ = atan2(-x, -z); 71 | 72 | mutex_.unlock(); 73 | } 74 | 75 | void RoSeCamera::lookAt(float x_cam, float y_cam, float z_cam, float x_ref, float y_ref, float z_ref) { 76 | mutex_.lock(); 77 | 78 | x_ = x_cam; 79 | y_ = y_cam; 80 | z_ = z_cam; 81 | 82 | float x = x_ref - x_; 83 | float y = y_ref - y_; 84 | float z = z_ref - z_; 85 | 86 | Eigen::Vector3f dir(x, y, z); 87 | dir.normalize(); 88 | 89 | pitch_ = std::asin(dir.y()); // = std::acos(-dir.y()) - M_PI_2 in [-pi/2,pi/2] 90 | yaw_ = atan2(-x, -z); 91 | 92 | mutex_.unlock(); 93 | 94 | // lookAt(x_ref, y_ref, z_ref); 95 | } 96 | 97 | void RoSeCamera::setYaw(float yaw) { 98 | mutex_.lock(); 99 | yaw_ = yaw; 100 | mutex_.unlock(); 101 | } 102 | 103 | void RoSeCamera::setPitch(float pitch) { 104 | mutex_.lock(); 105 | pitch_ = pitch; 106 | mutex_.unlock(); 107 | } 108 | 109 | void RoSeCamera::getCameraParameters(float& x, float& y, float& z, float& yaw, float& pitch) { 110 | mutex_.lock(); 111 | 112 | x = x_; 113 | y = y_; 114 | z = z_; 115 | yaw = yaw_; 116 | pitch = pitch_; 117 | 118 | mutex_.unlock(); 119 | } 120 | 121 | bool RoSeCamera::mousePressed(float x, float y, MouseButton btn, KeyboardModifier modifier) { 122 | startx_ = x; 123 | starty_ = y; 124 | startyaw_ = yaw_; 125 | startpitch_ = pitch_; 126 | startTime_ = std::chrono::system_clock::now(); 127 | startdrag_ = true; 128 | 129 | return true; 130 | } 131 | 132 | bool RoSeCamera::mouseReleased(float x, float y, MouseButton btn, KeyboardModifier modifier) { 133 | forwardVel_ = 0.0; 134 | upVel_ = 0.0; 135 | sideVel_ = 0.0; 136 | turnVel_ = 0.0; 137 | startdrag_ = false; 138 | 139 | return true; 140 | } 141 | 142 | void RoSeCamera::translate(float forward, float up, float sideways) { 143 | // forward = -z, sideways = x , up = y. Remember: inverse of yaw is applied, i.e., we have to apply yaw (?) 144 | // Also keep in mind: sin(-alpha) = -sin(alpha) and cos(-alpha) = -cos(alpha) 145 | // We only apply the yaw to move along the yaw direction; 146 | // x' = x*cos(yaw) - z*sin(yaw) 147 | // z' = x*sin(yaw) + z*cos(yaw) 148 | float s = std::sin(yaw_); 149 | float c = std::cos(yaw_); 150 | 151 | x_ += sideways * c - forward * s; 152 | y_ += up; 153 | z_ -= sideways * s + forward * c; 154 | } 155 | 156 | /*****************************************************************************/ 157 | 158 | void RoSeCamera::rotate(float yaw, float pitch) { 159 | yaw_ += yaw; 160 | pitch_ += pitch; 161 | if (pitch_ < -M_PI_2) pitch_ = -M_PI_2; 162 | if (pitch_ > M_PI_2) pitch_ = M_PI_2; 163 | } 164 | 165 | bool RoSeCamera::mouseMoved(float x, float y, MouseButton btn, KeyboardModifier modifier) { 166 | mutex_.lock(); 167 | 168 | // TODO: expose parameters: 169 | static const float MIN_MOVE = 0; 170 | static const float WALK_SENSITIVITY = 0.5f; 171 | static const float TURN_SENSITIVITY = 0.01f; 172 | static const float SLIDE_SENSITIVITY = 0.5f; 173 | static const float RAISE_SENSITIVITY = 0.5f; 174 | 175 | static const float LOOK_SENSITIVITY = 0.01f; 176 | static const float FREE_TURN_SENSITIVITY = 0.01f; 177 | 178 | float dx = x - startx_; 179 | float dy = y - starty_; 180 | 181 | if (dx > 0.0f) dx = std::max(0.0f, dx - MIN_MOVE); 182 | if (dx < 0.0f) dx = std::min(0.0f, dx + MIN_MOVE); 183 | if (dy > 0.0f) dy = std::max(0.0f, dy - MIN_MOVE); 184 | if (dy < 0.0f) dy = std::min(0.0f, dy + MIN_MOVE); 185 | 186 | // idea: if the velocity changes, we have to reset the start_time and update the camera parameters. 187 | 188 | if (btn == MouseButton::RightButton) { 189 | forwardVel_ = 0; 190 | upVel_ = 0; 191 | sideVel_ = 0; 192 | turnVel_ = 0; 193 | 194 | yaw_ = startyaw_ - FREE_TURN_SENSITIVITY * dx; 195 | pitch_ = startpitch_ - LOOK_SENSITIVITY * dy; 196 | 197 | // ensure valid values. 198 | if (pitch_ < -M_PI_2) pitch_ = -M_PI_2; 199 | if (pitch_ > M_PI_2) pitch_ = M_PI_2; 200 | } else if (btn == MouseButton::LeftButton) { 201 | // apply transformation: 202 | auto end = std::chrono::system_clock::now(); 203 | double dt = std::chrono::duration_cast(end - startTime_).count(); 204 | 205 | if (dt > 0.0) { 206 | rotate(turnVel_ * dt, 0.0f); 207 | translate(forwardVel_ * dt, upVel_ * dt, sideVel_ * dt); 208 | 209 | startTime_ = end; // reset timer. 210 | } 211 | 212 | forwardVel_ = -WALK_SENSITIVITY * dy; 213 | upVel_ = 0; 214 | sideVel_ = 0; 215 | turnVel_ = -(TURN_SENSITIVITY * dx); 216 | } else if (btn == MouseButton::MiddleButton) { 217 | // apply transformation: 218 | auto end = std::chrono::system_clock::now(); 219 | double dt = std::chrono::duration_cast(end - startTime_).count(); 220 | 221 | if (dt > 0.0) { 222 | rotate(turnVel_ * dt, 0.0f); 223 | translate(forwardVel_ * dt, upVel_ * dt, sideVel_ * dt); 224 | 225 | startTime_ = end; // reset timer. 226 | } 227 | 228 | forwardVel_ = 0; 229 | upVel_ = -RAISE_SENSITIVITY * dy; 230 | sideVel_ = SLIDE_SENSITIVITY * dx; 231 | turnVel_ = 0; 232 | } 233 | 234 | mutex_.unlock(); 235 | 236 | return true; 237 | } 238 | 239 | bool RoSeCamera::wheelEvent(float delta, KeyboardModifier modifier) { 240 | mutex_.lock(); 241 | static const float ZOOM_SENSITIVITY = 3.f; 242 | // move along the viewing direction specified by yaw and pitch. 243 | 244 | 245 | float forward = ZOOM_SENSITIVITY * delta * std::cos(pitch_); 246 | float up = ZOOM_SENSITIVITY * delta * std::sin(pitch_) * (-1); 247 | float s = std::sin(yaw_); 248 | float c = std::cos(yaw_); 249 | 250 | 251 | 252 | x_ -= forward * s; 253 | y_ -= up; 254 | z_ += forward * c * (-1); 255 | // TODO: implement me! 256 | mutex_.unlock(); 257 | 258 | return true; 259 | } 260 | 261 | bool RoSeCamera::keyPressed(KeyboardKey key, KeyboardModifier modifier){ 262 | float factor = (y_>50?50:(y_>1?y_:1)); 263 | switch(key){ 264 | case KeyboardKey::KeyA: 265 | startTime_ = std::chrono::system_clock::now(); 266 | startdrag_ = true; 267 | sideVel_ = -10*factor; 268 | return true; 269 | case KeyboardKey::KeyD: 270 | startTime_ = std::chrono::system_clock::now(); 271 | startdrag_ = true; 272 | sideVel_ = 10*factor; 273 | return true; 274 | case KeyboardKey::KeyW: 275 | startTime_ = std::chrono::system_clock::now(); 276 | startdrag_ = true; 277 | forwardVel_ = 10*factor; 278 | return true; 279 | case KeyboardKey::KeyS: 280 | startTime_ = std::chrono::system_clock::now(); 281 | startdrag_ = true; 282 | forwardVel_ = -10*factor; 283 | return true; 284 | default: 285 | return false; 286 | } 287 | } 288 | 289 | 290 | bool RoSeCamera::keyReleased(KeyboardKey key, KeyboardModifier modifier){ 291 | switch(key){ 292 | case KeyboardKey::KeyA: 293 | case KeyboardKey::KeyD: 294 | startTime_ = std::chrono::system_clock::now(); 295 | 296 | sideVel_ = 0; 297 | if (forwardVel_==0) startdrag_ = false; 298 | return true; 299 | case KeyboardKey::KeyW: 300 | case KeyboardKey::KeyS: 301 | startTime_ = std::chrono::system_clock::now(); 302 | 303 | forwardVel_ = 0; 304 | if (sideVel_==0) startdrag_ = false; 305 | return true; 306 | default: 307 | return false; 308 | } 309 | } 310 | 311 | } /* namespace rv */ 312 | -------------------------------------------------------------------------------- /src/glow/GlCapabilities.cpp: -------------------------------------------------------------------------------- 1 | #include "glow/GlCapabilities.h" 2 | 3 | #include 4 | 5 | #include "glow/glutil.h" 6 | 7 | namespace glow { 8 | 9 | std::unique_ptr GlCapabilities::instance_ = nullptr; 10 | 11 | template <> 12 | void GlCapabilities::initGlParameter(GLenum name, uint32_t num) { 13 | int32_t v[4]; 14 | glGetIntegerv(name, v); 15 | if (num == 1) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0]))); 16 | if (num == 2) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0], v[1]))); 17 | if (num == 3) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0], v[1], v[2]))); 18 | if (num == 4) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0], v[1], v[2], v[3]))); 19 | } 20 | 21 | template <> 22 | void GlCapabilities::initGlParameter(GLenum name, uint32_t num) { 23 | float v[4]; 24 | glGetFloatv(name, v); 25 | if (num == 1) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0]))); 26 | if (num == 2) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0], v[1]))); 27 | if (num == 3) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0], v[1], v[2]))); 28 | if (num == 4) state_.insert(std::make_pair(name, GlState::GlStateVariable(v[0], v[1], v[2], v[3]))); 29 | } 30 | 31 | template <> 32 | void GlCapabilities::initGlParameter(GLenum name, uint32_t num) { 33 | GLboolean v[4]; 34 | glGetBooleanv(name, v); 35 | if (num == 1) state_.insert(std::make_pair(name, GlState::GlStateVariable(static_cast(v[0])))); 36 | } 37 | 38 | GlCapabilities::GlCapabilities() { 39 | // query capabilites. 40 | 41 | // Framebuffers. 42 | 43 | initGlParameter(GL_DOUBLEBUFFER, 1); 44 | initGlParameter(GL_MAX_COLOR_ATTACHMENTS, 1); 45 | initGlParameter(GL_MAX_COLOR_TEXTURE_SAMPLES, 1); 46 | initGlParameter(GL_MAX_DEPTH_TEXTURE_SAMPLES, 1); 47 | initGlParameter(GL_MAX_DRAW_BUFFERS, 1); 48 | // GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 49 | // OpenGL 4.3: GL_MAX_FRAMEBUFFER_HEIGHT 50 | // OpenGL 4.3: GL_MAX_FRAMEBUFFER_LAYERS 51 | // OpenGL 4.3: GL_MAX_FRAMEBUFFER_SAMPLES 52 | // OpenGL 4.3: GL_MAX_FRAMEBUFFER_WIDTH 53 | // GL_MAX_INTEGER_SAMPLES 54 | // GL_MAX_SAMPLES 55 | 56 | // Programs: 57 | initGlParameter(GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE, 1); 58 | initGlParameter(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES, 1); 59 | initGlParameter(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, 1); 60 | initGlParameter(GL_MAX_IMAGE_SAMPLES, 1); 61 | initGlParameter(GL_MAX_IMAGE_UNITS, 1); 62 | initGlParameter(GL_MAX_PROGRAM_TEXEL_OFFSET, 1); 63 | // initGlParameter(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, 1); INT64! 64 | initGlParameter(GL_MAX_SUBROUTINES, 1); 65 | initGlParameter(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, 1); 66 | initGlParameter(GL_MAX_UNIFORM_BLOCK_SIZE, 1); 67 | initGlParameter(GL_MAX_UNIFORM_LOCATIONS, 1); 68 | initGlParameter(GL_MAX_VARYING_VECTORS, 1); 69 | initGlParameter(GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET, 1); 70 | initGlParameter(GL_MAX_VERTEX_ATTRIB_BINDINGS, 1); 71 | initGlParameter(GL_MAX_VERTEX_ATTRIB_STRIDE, 1); 72 | initGlParameter(GL_MIN_PROGRAM_TEXEL_OFFSET, 1); 73 | initGlParameter(GL_NUM_PROGRAM_BINARY_FORMATS, 1); 74 | initGlParameter(GL_NUM_SHADER_BINARY_FORMATS, 1); 75 | // Rasterization: 76 | initGlParameter(GL_ALIASED_LINE_WIDTH_RANGE, 2); 77 | initGlParameter(GL_POINT_SIZE_RANGE, 2); 78 | initGlParameter(GL_SMOOTH_LINE_WIDTH_RANGE, 2); 79 | 80 | // TODO: shader execution, Comptue, Fragment, Geometry, Tesselation, Vertex, etc. 81 | 82 | // Texture: 83 | 84 | // TODO: GL_COMPRESSED_TEXTURE_FORMATS 85 | initGlParameter(GL_MAX_TEXTURE_IMAGE_UNITS, 1); 86 | initGlParameter(GL_MAX_3D_TEXTURE_SIZE, 1); 87 | initGlParameter(GL_MAX_ARRAY_TEXTURE_LAYERS, 1); 88 | initGlParameter(GL_MAX_CUBE_MAP_TEXTURE_SIZE, 1); 89 | initGlParameter(GL_MAX_RECTANGLE_TEXTURE_SIZE, 1); 90 | initGlParameter(GL_MAX_TEXTURE_BUFFER_SIZE, 1); 91 | initGlParameter(GL_MAX_TEXTURE_LOD_BIAS, 1); 92 | initGlParameter(GL_MAX_TEXTURE_SIZE, 1); 93 | initGlParameter(GL_NUM_COMPRESSED_TEXTURE_FORMATS, 1); 94 | initGlParameter(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, 1); 95 | 96 | // Transformation state. 97 | initGlParameter(GL_MAX_CLIP_DISTANCES, 1); 98 | initGlParameter(GL_MAX_VIEWPORT_DIMS, 2); 99 | // OpenGl 4.1: initGlParameter(GL_MAX_VIEWPORTS, 1); 100 | // OpenGl 4.1: initGlParameter(GL_VIEWPORT_BOUNDS_RANGE, 2); 101 | // OpenGl 4.1: initGlParameter(GL_VIEWPORT_SUBPIXEL_BITS, 1); 102 | 103 | // Vertex arrays: 104 | // OpenGL 4.3: initGlParameter(GL_MAX_ELEMENT_INDEX, 1); 105 | initGlParameter(GL_MAX_ELEMENTS_INDICES, 1); 106 | initGlParameter(GL_MAX_ELEMENTS_VERTICES, 1); 107 | // initGlParameter(GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED, 1); ??? 108 | 109 | // TODO: > OpenGL 4.0? GL_PROGRAM_BINARY_FORMATS, GL_SHADER_BINARY_FORMATS, GL_SHADER_COMPILER, 110 | // ??? GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT+ 111 | 112 | initGlParameter(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, 1); 113 | } 114 | 115 | GlCapabilities& GlCapabilities::getInstance() { 116 | if (instance_ == nullptr) { 117 | instance_ = std::unique_ptr(new GlCapabilities()); 118 | } 119 | 120 | return *instance_; 121 | } 122 | 123 | std::ostream& operator<<(std::ostream& out, const GlCapabilities& cap) { 124 | for (auto it = cap.state_.begin(); it != cap.state_.end(); ++it) { 125 | out.width(40); 126 | out << cap.stringify_name(it->first); 127 | out.width(0); 128 | out << ": " << cap.stringify_value(*it) << std::endl; 129 | } 130 | 131 | return out; 132 | } 133 | 134 | #undef CASE 135 | #define CASE(name) \ 136 | case name: \ 137 | return #name; 138 | std::string GlCapabilities::stringify_name(GLenum name) const { 139 | switch (name) { 140 | CASE(GL_MAX_COLOR_ATTACHMENTS) 141 | CASE(GL_MAX_COLOR_TEXTURE_SAMPLES) 142 | CASE(GL_MAX_3D_TEXTURE_SIZE) 143 | CASE(GL_MAX_DRAW_BUFFERS) 144 | CASE(GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE) 145 | CASE(GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES) 146 | CASE(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS) 147 | CASE(GL_MAX_IMAGE_SAMPLES) 148 | CASE(GL_MAX_IMAGE_UNITS) 149 | CASE(GL_MAX_TEXTURE_SIZE) 150 | CASE(GL_MAX_PROGRAM_TEXEL_OFFSET) 151 | CASE(GL_MAX_SHADER_STORAGE_BLOCK_SIZE) 152 | CASE(GL_MAX_SUBROUTINES) 153 | CASE(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS) 154 | CASE(GL_MAX_UNIFORM_BLOCK_SIZE) 155 | CASE(GL_MAX_UNIFORM_LOCATIONS) 156 | CASE(GL_MAX_VARYING_VECTORS) 157 | CASE(GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET) 158 | CASE(GL_MAX_VERTEX_ATTRIB_BINDINGS) 159 | CASE(GL_MAX_VERTEX_ATTRIB_STRIDE) 160 | CASE(GL_MIN_PROGRAM_TEXEL_OFFSET) 161 | CASE(GL_NUM_PROGRAM_BINARY_FORMATS) 162 | CASE(GL_NUM_SHADER_BINARY_FORMATS) 163 | CASE(GL_POINT_SIZE_RANGE) 164 | CASE(GL_SMOOTH_LINE_WIDTH_RANGE) 165 | CASE(GL_DOUBLEBUFFER) 166 | CASE(GL_MAX_CLIP_PLANES) 167 | CASE(GL_MAX_VIEWPORT_DIMS) 168 | CASE(GL_MAX_ELEMENTS_VERTICES) 169 | CASE(GL_MAX_ELEMENTS_INDICES) 170 | CASE(GL_ALIASED_LINE_WIDTH_RANGE) 171 | CASE(GL_MAX_RECTANGLE_TEXTURE_SIZE) 172 | CASE(GL_MAX_TEXTURE_LOD_BIAS) 173 | CASE(GL_MAX_CUBE_MAP_TEXTURE_SIZE) 174 | CASE(GL_NUM_COMPRESSED_TEXTURE_FORMATS) 175 | CASE(GL_MAX_TEXTURE_IMAGE_UNITS) 176 | CASE(GL_MAX_ARRAY_TEXTURE_LAYERS) 177 | CASE(GL_MAX_TEXTURE_BUFFER_SIZE) 178 | CASE(GL_MAX_DEPTH_TEXTURE_SAMPLES) 179 | CASE(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT) 180 | } 181 | 182 | std::stringstream sstr; 183 | sstr << name; 184 | 185 | return sstr.str(); 186 | } 187 | #undef CASE 188 | 189 | std::string GlCapabilities::stringify_value(const std::pair& entry) const { 190 | std::stringstream sstr; 191 | 192 | if (entry.second.type == GlState::GlStateVariable::INT) { 193 | for (uint32_t i = 0; i < entry.second.size; ++i) sstr << ((i > 0) ? ", " : "") << entry.second.vali[i]; 194 | } else if (entry.second.type == GlState::GlStateVariable::FLOAT) { 195 | for (uint32_t i = 0; i < entry.second.size; ++i) sstr << ((i > 0) ? ", " : "") << entry.second.valf[i]; 196 | } else if (entry.second.type == GlState::GlStateVariable::BOOL) { 197 | sstr << (entry.second.valb ? "true" : "false"); 198 | } 199 | return sstr.str(); 200 | } 201 | 202 | template <> 203 | glow::vec2 GlCapabilities::get(GLenum variable) const { 204 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 205 | auto it = state_.find(variable); 206 | 207 | if (it->second.type == GlState::GlStateVariable::INT) { 208 | return glow::vec2(it->second.vali[0], it->second.vali[1]); 209 | } else if (it->second.type == GlState::GlStateVariable::FLOAT) { 210 | return glow::vec2(it->second.valf[0], it->second.valf[1]); 211 | } else if (it->second.type == GlState::GlStateVariable::BOOL) { 212 | return glow::vec2(it->second.valb, 0); 213 | } 214 | 215 | // should never reach this... 216 | throw std::runtime_error("Unknown conversion."); 217 | } 218 | 219 | template <> 220 | glow::vec3 GlCapabilities::get(GLenum variable) const { 221 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 222 | auto it = state_.find(variable); 223 | 224 | if (it->second.type == GlState::GlStateVariable::INT) { 225 | return glow::vec3(it->second.vali[0], it->second.vali[1], it->second.vali[2]); 226 | } else if (it->second.type == GlState::GlStateVariable::FLOAT) { 227 | return glow::vec3(it->second.valf[0], it->second.valf[1], it->second.valf[2]); 228 | } else if (it->second.type == GlState::GlStateVariable::BOOL) { 229 | return glow::vec3(it->second.valb, 0, 0); 230 | } 231 | 232 | // should never reach this... 233 | throw std::runtime_error("Unknown conversion."); 234 | } 235 | 236 | template <> 237 | glow::vec4 GlCapabilities::get(GLenum variable) const { 238 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 239 | auto it = state_.find(variable); 240 | 241 | if (it->second.type == GlState::GlStateVariable::INT) { 242 | return glow::vec4(it->second.vali[0], it->second.vali[1], it->second.vali[2], it->second.valf[3]); 243 | } else if (it->second.type == GlState::GlStateVariable::FLOAT) { 244 | return glow::vec4(it->second.valf[0], it->second.valf[1], it->second.valf[2], it->second.valf[3]); 245 | } else if (it->second.type == GlState::GlStateVariable::BOOL) { 246 | return glow::vec4(it->second.valb, 0, 0, 0); 247 | } 248 | 249 | // should never reach this... 250 | throw std::runtime_error("Unknown conversion."); 251 | } 252 | 253 | template <> 254 | int32_t GlCapabilities::get(GLenum variable) const { 255 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 256 | auto it = state_.find(variable); 257 | 258 | if (it->second.type == GlState::GlStateVariable::INT) { 259 | return it->second.vali[0]; 260 | } 261 | 262 | // should never reach this... 263 | throw std::runtime_error("Unknown conversion."); 264 | } 265 | 266 | template <> 267 | float GlCapabilities::get(GLenum variable) const { 268 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 269 | auto it = state_.find(variable); 270 | 271 | if (it->second.type == GlState::GlStateVariable::FLOAT) { 272 | return it->second.valf[0]; 273 | } 274 | 275 | // should never reach this... 276 | throw std::runtime_error("Unknown conversion."); 277 | } 278 | 279 | template <> 280 | bool GlCapabilities::get(GLenum variable) const { 281 | if (state_.find(variable) == state_.end()) throw std::runtime_error("No such variable found in GlState."); 282 | auto it = state_.find(variable); 283 | 284 | if (it->second.type == GlState::GlStateVariable::BOOL) { 285 | return it->second.valb; 286 | } 287 | 288 | // should never reach this... 289 | throw std::runtime_error("Unknown conversion."); 290 | } 291 | 292 | } // namespace glow 293 | --------------------------------------------------------------------------------