├── .clang-format ├── .clang-tidy ├── .github └── workflows │ ├── macos.yaml │ ├── ubuntu.yaml │ └── windows.yaml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── FindGLFW3.cmake └── FindGLM.cmake ├── data ├── Roboto-Medium.ttf ├── clipped_cavity_small.png ├── gradation_squares.png ├── plane.obj ├── roughness_teaser.png └── small_plane.obj ├── images └── demo01.png ├── shaders ├── bezierLight.frag ├── bezierLight.vert ├── floorLTC.frag └── floorLTC.vert └── src ├── CMakeLists.txt ├── bezierLight.cpp ├── bezierLight.h ├── common.h ├── constants.h ├── glad ├── LICENSE └── gl.h ├── imgui ├── LICENSE.txt ├── imconfig.h ├── imgui.cpp ├── imgui.h ├── imgui_demo.cpp ├── imgui_draw.cpp ├── imgui_impl_glfw.cpp ├── imgui_impl_glfw.h ├── imgui_impl_opengl3.cpp ├── imgui_impl_opengl3.h ├── imgui_internal.h ├── imgui_tables.cpp ├── imgui_widgets.cpp ├── imstb_rectpack.h ├── imstb_textedit.h └── imstb_truetype.h ├── ltc.inc ├── ltc2.inc ├── ltcSurface.cpp ├── ltcSurface.h ├── main.cpp ├── openmp.h ├── render.cpp ├── render.h ├── stb ├── LICENSE ├── stb_image.h └── stb_image_write.h └── tinyobjloader ├── LICENSE └── tiny_obj_loader.h /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | # BasedOnStyle: LLVM 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Left 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: None 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: false 36 | SplitEmptyRecord: false 37 | SplitEmptyNamespace: false 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Custom 40 | BreakBeforeInheritanceComma: false 41 | BreakBeforeTernaryOperators: false 42 | BreakConstructorInitializersBeforeComma: false 43 | BreakConstructorInitializers: AfterColon 44 | BreakAfterJavaFieldAnnotations: false 45 | BreakStringLiterals: true 46 | ColumnLimit: 0 47 | CommentPragmas: '^ IWYU pragma:' 48 | CompactNamespaces: false 49 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 50 | ConstructorInitializerIndentWidth: 4 51 | ContinuationIndentWidth: 4 52 | Cpp11BracedListStyle: true 53 | DerivePointerAlignment: false 54 | DisableFormat: false 55 | ExperimentalAutoDetectBinPacking: false 56 | FixNamespaceComments: false 57 | ForEachMacros: 58 | - foreach 59 | - Q_FOREACH 60 | - BOOST_FOREACH 61 | IncludeBlocks: Preserve 62 | IncludeIsMainRegex: '(Test)?$' 63 | IndentCaseLabels: false 64 | IndentPPDirectives: AfterHash 65 | IndentWidth: 4 66 | IndentWrappedFunctionNames: true 67 | JavaScriptQuotes: Leave 68 | JavaScriptWrapImports: true 69 | KeepEmptyLinesAtTheStartOfBlocks: false 70 | MacroBlockBegin: '' 71 | MacroBlockEnd: '' 72 | MaxEmptyLinesToKeep: 1 73 | NamespaceIndentation: None 74 | ObjCBinPackProtocolList: Auto 75 | ObjCBlockIndentWidth: 2 76 | ObjCSpaceAfterProperty: false 77 | ObjCSpaceBeforeProtocolList: true 78 | PenaltyBreakAssignment: 2 79 | PenaltyBreakBeforeFirstCallParameter: 19 80 | PenaltyBreakComment: 300 81 | PenaltyBreakFirstLessLess: 120 82 | PenaltyBreakString: 1000 83 | PenaltyExcessCharacter: 1000000 84 | PenaltyReturnTypeOnItsOwnLine: 60 85 | PointerAlignment: Right 86 | ReflowComments: true 87 | SortIncludes: true 88 | SortUsingDeclarations: true 89 | SpaceAfterCStyleCast: true 90 | SpaceAfterTemplateKeyword: true 91 | SpaceBeforeAssignmentOperators: true 92 | SpaceBeforeCtorInitializerColon: true 93 | SpaceBeforeInheritanceColon: true 94 | SpaceBeforeParens: ControlStatements 95 | SpaceBeforeRangeBasedForLoopColon: true 96 | SpaceInEmptyParentheses: false 97 | SpacesBeforeTrailingComments: 2 98 | SpacesInAngles: false 99 | SpacesInContainerLiterals: false 100 | SpacesInCStyleCastParentheses: false 101 | SpacesInParentheses: false 102 | SpacesInSquareBrackets: false 103 | Standard: Cpp11 104 | TabWidth: 4 105 | UseTab: Never 106 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: "-*,\ 2 | readability-*,\ 3 | -readability-convert-member-functions-to-static,\ 4 | -readability-uppercase-literal-suffix,\ 5 | -readability-static-accessed-through-instance,\ 6 | " 7 | WarningsAsErrors: '' 8 | HeaderFilterRegex: 'source.*\.(hpp|h)' 9 | AnalyzeTemporaryDtors: false 10 | FormatStyle: file 11 | CheckOptions: 12 | - key: readability-identifier-naming.ClassCase 13 | value: CamelCase 14 | - key: readability-identifier-naming.StructCase 15 | value: CamelCase 16 | -------------------------------------------------------------------------------- /.github/workflows/macos.yaml: -------------------------------------------------------------------------------- 1 | name: MacOS CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - dev 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | GNU_CC: /usr/local/bin/gcc-9 12 | GNU_CXX: /usr/local/bin/g++-9 13 | LLVM_CC: /usr/local/opt/llvm/bin/clang 14 | LLVM_CXX: /usr/local/opt/llvm/bin/clang++ 15 | 16 | jobs: 17 | macos: 18 | runs-on: macos-latest 19 | 20 | strategy: 21 | matrix: 22 | compiler: 23 | - gcc 24 | - clang 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | - name: Install LLVM Clang 30 | if: ${{ matrix.compiler == 'clang' }} 31 | run: | 32 | brew install llvm 33 | - name: Install GLFW3 34 | run: | 35 | brew install glfw3 36 | - name: Install GLM 37 | run: | 38 | brew install glm 39 | - name: CMake Build 40 | env: 41 | C_COMPILER: ${{ matrix.compiler }} 42 | run: | 43 | cmake -E make_directory ${{runner.workspace}}/build 44 | cd ${{runner.workspace}}/build 45 | if [ "$C_COMPILER" = "gcc" ]; then 46 | cmake $GITHUB_WORKSPACE \ 47 | -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ 48 | -DCMAKE_C_COMPILER=$GNU_CC \ 49 | -DCMAKE_CXX_COMPILER=$GNU_CXX 50 | fi 51 | if [ "$C_COMPILER" = "clang" ]; then 52 | cmake $GITHUB_WORKSPACE \ 53 | -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ 54 | -DCMAKE_C_COMPILER=$LLVM_CC \ 55 | -DCMAKE_CXX_COMPILER=$LLVM_CXX 56 | fi 57 | cmake --build . --config $BUILD_TYPE --parallel 2 58 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yaml: -------------------------------------------------------------------------------- 1 | name: Ubuntu CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - dev 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | GNU_CC: gcc-9 12 | GNU_CXX: g++-9 13 | LLVM_CC: clang-10 14 | LLVM_CXX: clang++-10 15 | 16 | jobs: 17 | ubuntu: 18 | runs-on: ubuntu-latest 19 | 20 | strategy: 21 | matrix: 22 | compiler: [gcc, clang] 23 | 24 | steps: 25 | - uses: actions/checkout@v2 26 | 27 | - name: Install GNU C/C++ Compiler 28 | if: ${{ matrix.compiler == 'gcc' }} 29 | run: | 30 | sudo apt-get update -y 31 | sudo apt-get install -y gcc-9 g++-9 32 | - name: Install LLVM Clang 33 | if: ${{ matrix.compiler == 'clang' }} 34 | run: | 35 | sudo apt-get update -y 36 | sudo apt-get install -y clang-10 clang++-10 37 | - name: Install OpenGL envs 38 | run: | 39 | sudo apt-get update -y 40 | sudo apt-get install -y libglu1-mesa-dev mesa-common-dev 41 | sudo apt-get install -y libxmu-dev libxi-dev libxrandr-dev libxinerama-dev libxcursor-dev 42 | - name: Install GLFW3 43 | run: | 44 | git clone https://github.com/glfw/glfw.git 45 | cd glfw && git checkout 3.3.4 46 | mkdir build && cd build 47 | cmake -DCMAKE_BUILD_TYPE=Release .. 48 | make -j2 && sudo make install 49 | - name: Install GLM 50 | run: | 51 | git clone https://github.com/g-truc/glm.git 52 | cd glm && git checkout 0.9.9.8 53 | sudo cp -R glm /usr/local/include 54 | - name: CMake Build 55 | env: 56 | C_COMPILER: ${{ matrix.compiler }} 57 | run: | 58 | cmake -E make_directory ${{runner.workspace}}/build 59 | cd ${{runner.workspace}}/build 60 | if [ "$C_COMPILER" = "gcc" ]; then 61 | cmake $GITHUB_WORKSPACE \ 62 | -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ 63 | -DCMAKE_C_COMPILER=$GNU_CC \ 64 | -DCMAKE_CXX_COMPILER=$GNU_CXX 65 | fi 66 | if [ "$C_COMPILER" = "clang" ]; then 67 | cmake $GITHUB_WORKSPACE \ 68 | -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ 69 | -DCMAKE_C_COMPILER=$LLVM_CC \ 70 | -DCMAKE_CXX_COMPILER=$LLVM_CXX 71 | fi 72 | cmake --build . --config $BUILD_TYPE --parallel 2 73 | -------------------------------------------------------------------------------- /.github/workflows/windows.yaml: -------------------------------------------------------------------------------- 1 | name: Windows CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - dev 8 | 9 | env: 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | windows: 14 | runs-on: windows-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: Install GLFW3 19 | run: | 20 | git clone https://github.com/glfw/glfw.git 21 | cd glfw && git checkout 3.3.4 22 | mkdir build && cd build 23 | cmake .. ` 24 | -G "Visual Studio 16 2019" -A x64 ` 25 | -DCMAKE_INSTALL_PREFIX="C:/Program Files/glfw3/" 26 | cmake --build . --config "$env:BUILD_TYPE" 27 | cmake --build . --config "$env:BUILD_TYPE" --target INSTALL 28 | - name: Install GLM 29 | run: | 30 | git clone https://github.com/g-truc/glm.git 31 | cd glm && git checkout 0.9.9.8 32 | cd .. && Copy-Item glm "C:/Program Files/glm/" -Recurse 33 | 34 | - name: CMake Build 35 | run: | 36 | cmake -E make_directory ${{ runner.workspace }}/build 37 | cd ${{ runner.workspace }}/build 38 | cmake "$env:GITHUB_WORKSPACE" ` 39 | -G "Visual Studio 16 2019" -A x64 ` 40 | -DGLFW3_DIR="C:/Program Files/glfw3" ` 41 | -DGLM_DIR="C:/Program Files/glm" 42 | cmake --build . --config "$env:BUILD_TYPE" 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | .vscode/ 3 | Debug/ 4 | Release/ 5 | build/ 6 | x64/ 7 | *.vcxproj.user 8 | *.png 9 | imgui.ini 10 | 11 | !images/* 12 | !data/* 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8.0) 2 | project(BezierLightLTC) 3 | 4 | if (WIN32) 5 | set(GLFW3_DIR "GLFW3_DIR" CACHE PATH "") 6 | set(GLM_DIR "GLM_DIR" CACHE PATH "") 7 | endif() 8 | 9 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") 10 | 11 | if (UNIX AND NOT APPLE) 12 | set(LINUX TRUE) 13 | endif() 14 | 15 | set(CMAKE_CXX_STANDARD 14) 16 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 17 | if (UNIX) 18 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -pthread") 19 | endif() 20 | 21 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 22 | add_compile_options(-fdiagnostics-color=always) 23 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 24 | add_compile_options(-fcolor-diagnostics) 25 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") 26 | add_compile_options(-fcolor-diagnostics) 27 | endif () 28 | 29 | # ---------- 30 | # Packages 31 | # ---------- 32 | find_package(OpenGL REQUIRED) 33 | find_package(GLFW3 REQUIRED) 34 | find_package(GLM REQUIRED) 35 | 36 | # ---------- 37 | # Output paths 38 | # ---------- 39 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 40 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 41 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 42 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 43 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin) 44 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib) 45 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 46 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin) 47 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib) 48 | set(CMAKE_DEBUG_POSTFIX "-debug") 49 | 50 | # ---------- 51 | # OS specific settings 52 | # ---------- 53 | set(COMMON_INCLUDE_DIRS ${OPENGL_INCLUDE_DIRS} 54 | ${GLFW3_INCLUDE_DIRS} 55 | ${GLM_INCLUDE_DIRS}) 56 | set(COMMON_LIBRARIES ${OPENGL_LIBRARIES} 57 | ${GLFW3_LIBRARIES}) 58 | 59 | if (MSVC) 60 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 61 | endif() 62 | 63 | if (MINGW) 64 | set(COMMON_LIBRARIES ${COMMON_LIBRARIES} imm32 dwmapi) 65 | endif() 66 | 67 | if (APPLE) 68 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo") 69 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo") 70 | endif() 71 | 72 | if (LINUX) 73 | find_package(PkgConfig REQUIRED) 74 | pkg_search_module(GLFW3 REQUIRED glfw3) 75 | set(COMMON_LIBRARIES ${COMMON_LIBRARIES} ${CMAKE_DL_LIBS} ${GLFW3_STATIC_LIBRARIES}) 76 | endif() 77 | 78 | # ---------- 79 | # Traverse subdirectories 80 | # ---------- 81 | 82 | file(GLOB SHADER_FILES "shaders/*.vert" "shaders/*.frag") 83 | add_subdirectory(src) 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public 421 | licenses. Notwithstanding, Creative Commons may elect to apply one of 422 | its public licenses to material it publishes and in those instances 423 | will be considered the “Licensor.” The text of the Creative Commons 424 | public licenses is dedicated to the public domain under the CC0 Public 425 | Domain Dedication. Except for the limited purpose of indicating that 426 | material is shared under a Creative Commons public license or as 427 | otherwise permitted by the Creative Commons policies published at 428 | creativecommons.org/policies, Creative Commons does not authorize the 429 | use of the trademark "Creative Commons" or any other trademark or logo 430 | of Creative Commons without its prior written consent including, 431 | without limitation, in connection with any unauthorized modifications 432 | to any of its public licenses or any other arrangements, 433 | understandings, or agreements concerning use of licensed material. For 434 | the avoidance of doubt, this paragraph does not form part of the 435 | public licenses. 436 | 437 | Creative Commons may be contacted at creativecommons.org. 438 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Free-form area light rendering 2 | === 3 | 4 | Creative Commons License
5 | 6 | [![Windows CI](https://github.com/Paul180297/BezierLightLTC/actions/workflows/windows.yaml/badge.svg)](https://github.com/Paul180297/BezierLightLTC/actions/workflows/windows.yaml) 7 | [![MacOS CI](https://github.com/Paul180297/BezierLightLTC/actions/workflows/macos.yaml/badge.svg)](https://github.com/Paul180297/BezierLightLTC/actions/workflows/macos.yaml) 8 | [![Ubuntu CI](https://github.com/Paul180297/BezierLightLTC/actions/workflows/ubuntu.yaml/badge.svg)](https://github.com/Paul180297/BezierLightLTC/actions/workflows/ubuntu.yaml) 9 | 10 | This is an official implementation of the paper, 11 | 12 | > Kuge et al., "Real-Time Shading of Free-Form Area Lights using Linearly Transformed Cosines," Journal of Computer Graphics Techniques, No. 4, Vol. 10, 2021. 13 | 14 | Please see the [JCGT paper](#) and [our website](https://tatsy.github.io/projects/kuge2021bezlight/) for more details. 15 | 16 | ### Requirements 17 | 18 | * OpenGL 4.3 or higher 19 | * GLFW3 20 | * GLM 21 | 22 | ### Build 23 | 24 | ```shell 25 | git clone https://github.com/Paul180297/BezierLightLTC.git 26 | cd BezierLightLTC 27 | mkdir build && cd build 28 | cmake .. -D CMAKE_BUILD_TYPE=Release 29 | cmake --build . --config Release 30 | 31 | ``` 32 | 33 | ### Run 34 | 35 | ```shell 36 | # From project root 37 | ./build/bin/bezier_ltc 38 | ``` 39 | 40 | ### Screen shot 41 | 42 | demo 01 43 | 44 | ### Reference 45 | 46 | ```bibtex 47 | @article{kuge2021bezlight, 48 | author={Kuge, Takahiro and Yatagawa, Tatsuya and Morishima, Shigeo}, 49 | title={Real-time Shading with Free-form Planar Area Lights using Linearly Transformed Cosines}, 50 | journal={Journal of Computer Graphics Techniques}, 51 | number={4}, 52 | volume={10}, 53 | page={1--16}, 54 | year={2021} 55 | } 56 | 57 | ``` 58 | 59 | ### License 60 | 61 | [CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/), 2021 (c) Takahiro Kuge and Tatsuya Yatagawa 62 | 63 | This project has some third-party dependencies, each of which may have independent licensing: 64 | 65 | * [Dear ImGui](https://github.com/ocornut/imgui) - Bloat-free Graphical User interface for C++ with minimal dependencies 66 | * [stb](https://github.com/nothings/stb): single-file public domain libraries for C/C++ 67 | * [glad](https://github.com/Dav1dde/glad) - Multi-Language Vulkan/GL/GLES/EGL/GLX/WGL Loader-Generator based on the official specs. 68 | * [tinyobjloader](https://github.com/tinyobjloader/tinyobjloader) - Tiny but powerful single file wavefront obj loader 69 | -------------------------------------------------------------------------------- /cmake/FindGLFW3.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | set(GLFW3_DIR "" CACHE PATH "") 4 | 5 | find_path(GLFW3_INCLUDE_DIR 6 | NAMES GLFW/glfw3.h 7 | PATHS 8 | /usr/include 9 | /usr/local/include 10 | /usr/local/Cellar/glfw3/include 11 | ${GLFW3_DIR}/include) 12 | 13 | find_library(GLFW3_LIBRARY 14 | NAMES glfw3 glfw 15 | PATHS 16 | /usr/lib 17 | /usr/local/lib 18 | /usr/local/Cellar/glfw3/lib 19 | ${GLFW3_DIR}/lib) 20 | 21 | find_package_handle_standard_args( 22 | GLFW3 23 | DEFAULT_MSG 24 | GLFW3_INCLUDE_DIR 25 | GLFW3_LIBRARY 26 | ) 27 | 28 | mark_as_advanced(GLFW3_DIR GLFW3_INCLUDE_DIR GLFW3_LIBRARY) 29 | 30 | if (GLFW3_FOUND) 31 | message(STATUS "GLFW3 include: ${GLFW3_INCLUDE_DIR}") 32 | message(STATUS "GLFW3 library: ${GLFW3_LIBRARY}") 33 | set(GLFW3_INCLUDE_DIRS "${GLFW3_INCLUDE_DIR}" CACHE PATH "") 34 | set(GLFW3_LIBRARIES "${GLFW3_LIBRARY}" CACHE FILEPATH "") 35 | mark_as_advanced(GLFW3_INCLUDE_DIR GLFW3_LIBRARY) 36 | endif() 37 | -------------------------------------------------------------------------------- /cmake/FindGLM.cmake: -------------------------------------------------------------------------------- 1 | include(FindPackageHandleStandardArgs) 2 | 3 | set(GLM_DIR "" CACHE PATH "") 4 | 5 | find_path(GLM_INCLUDE_DIR 6 | NAMES glm/glm.hpp 7 | PATHS 8 | /usr/include 9 | /usr/local/include 10 | ${GLM_DIR} 11 | ${GLM_DIR}/include) 12 | 13 | find_package_handle_standard_args( 14 | GLM 15 | DEFAULT_MSG 16 | GLM_INCLUDE_DIR 17 | ) 18 | 19 | if (GLM_FOUND) 20 | message(STATUS "GLM include: ${GLM_INCLUDE_DIR}") 21 | set(GLM_INCLUDE_DIRS "${GLM_INCLUDE_DIR}" CACHE PATH "") 22 | mark_as_advanced(GLM_INCLUDE_DIR) 23 | endif() 24 | -------------------------------------------------------------------------------- /data/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paul180297/BezierLightLTC/b79f4562f70c3e6c9381c16a91e9f5354d6f580a/data/Roboto-Medium.ttf -------------------------------------------------------------------------------- /data/clipped_cavity_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paul180297/BezierLightLTC/b79f4562f70c3e6c9381c16a91e9f5354d6f580a/data/clipped_cavity_small.png -------------------------------------------------------------------------------- /data/gradation_squares.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paul180297/BezierLightLTC/b79f4562f70c3e6c9381c16a91e9f5354d6f580a/data/gradation_squares.png -------------------------------------------------------------------------------- /data/plane.obj: -------------------------------------------------------------------------------- 1 | v 20 0 -20 2 | v 20 0 20 3 | v -20 0 20 4 | v -20 0 -20 5 | 6 | vt 1 1 7 | vt 1 0 8 | vt 0 0 9 | vt 0 1 10 | 11 | vn 0 1 0 12 | f 1/1/1 4/4/1 2/2/1 13 | f 4/4/1 3/3/1 2/2/1 14 | -------------------------------------------------------------------------------- /data/roughness_teaser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paul180297/BezierLightLTC/b79f4562f70c3e6c9381c16a91e9f5354d6f580a/data/roughness_teaser.png -------------------------------------------------------------------------------- /data/small_plane.obj: -------------------------------------------------------------------------------- 1 | v 1 -1 0 2 | v 1 1 0 3 | v -1 1 0 4 | v -1 -1 0 5 | 6 | vt 1 1 7 | vt 1 0 8 | vt 0 0 9 | vt 0 1 10 | 11 | vn 0 0 1 12 | f 1/1/1 4/4/1 2/2/1 13 | f 4/4/1 3/3/1 2/2/1 14 | -------------------------------------------------------------------------------- /images/demo01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paul180297/BezierLightLTC/b79f4562f70c3e6c9381c16a91e9f5354d6f580a/images/demo01.png -------------------------------------------------------------------------------- /shaders/bezierLight.frag: -------------------------------------------------------------------------------- 1 | #version 410 2 | 3 | #define PI 3.141592653589793 4 | #define INV_PI 0.3183098861837907 5 | #define MAX_N_POINTS 48 6 | #define NUM_CPS_IN_CURVE 4 7 | 8 | in vec3 f_vertPosWorld; 9 | in vec2 f_texcoord; 10 | 11 | out vec4 out_color; 12 | 13 | uniform vec3 u_lightLe; 14 | uniform vec3 u_cpsWorld[MAX_N_POINTS]; 15 | 16 | uniform int u_numCurves; 17 | uniform int u_texWidth; 18 | uniform int u_texHeight; 19 | uniform int u_marginSize; 20 | 21 | uniform bool u_isTwoSided; 22 | uniform bool u_isBezTexed; 23 | 24 | uniform sampler2D u_bezLightTex; 25 | 26 | void correctUV(inout vec2 uv) { 27 | vec2 texSize = vec2(u_texWidth, u_texHeight); 28 | vec2 marginTexSize = texSize + 2 * vec2(u_marginSize); 29 | uv *= texSize / marginTexSize; 30 | uv += vec2(u_marginSize) / marginTexSize; 31 | } 32 | 33 | // ---------------------------------------------- 34 | // sRGB tone mapping 35 | // ---------------------------------------------- 36 | const float gamma = 2.2; 37 | vec3 toLinear(vec3 v) { return pow(v, vec3(gamma)); } 38 | vec3 toSRGB(vec3 v) { return pow(v, vec3(1.0 / gamma)); } 39 | 40 | // ---------------------------------------------- 41 | // ACES tone mapper (borrwed from LTC program) 42 | // https://eheitzresearch.wordpress.com/415-2/ 43 | // ---------------------------------------------- 44 | vec3 rrt_odt_fit(vec3 v) 45 | { 46 | vec3 a = v*( v + 0.0245786) - 0.000090537; 47 | vec3 b = v*(0.983729*v + 0.4329510) + 0.238081; 48 | return a/b; 49 | } 50 | 51 | mat3 mat3_from_rows(vec3 c0, vec3 c1, vec3 c2) 52 | { 53 | mat3 m = mat3(c0, c1, c2); 54 | m = transpose(m); 55 | 56 | return m; 57 | } 58 | 59 | vec3 aces_fitted(vec3 color) 60 | { 61 | mat3 ACES_INPUT_MAT = mat3_from_rows( 62 | vec3( 0.59719, 0.35458, 0.04823), 63 | vec3( 0.07600, 0.90834, 0.01566), 64 | vec3( 0.02840, 0.13383, 0.83777)); 65 | 66 | mat3 ACES_OUTPUT_MAT = mat3_from_rows( 67 | vec3( 1.60475,-0.53108,-0.07367), 68 | vec3(-0.10208, 1.10813,-0.00605), 69 | vec3(-0.00327,-0.07276, 1.07602)); 70 | 71 | color = ACES_INPUT_MAT * color; 72 | 73 | // Apply RRT and ODT 74 | color = rrt_odt_fit(color); 75 | 76 | color = ACES_OUTPUT_MAT * color; 77 | 78 | // Clamp to [0, 1] 79 | color = clamp(color, 0.0, 1.0); 80 | 81 | return color; 82 | } 83 | 84 | // ---------------------------------------------- 85 | // main 86 | // ---------------------------------------------- 87 | void main(void) { 88 | vec3 color = vec3(1.0); 89 | 90 | if (u_isBezTexed) { 91 | vec2 uv = f_texcoord; 92 | correctUV(uv); 93 | color = textureLod(u_bezLightTex, uv, 0.0).rgb; 94 | } 95 | 96 | // color = aces_fitted(color); 97 | color = clamp(color, vec3(0.0), vec3(1.0)); 98 | color = toSRGB(color); 99 | 100 | out_color = vec4(color, 1.0); 101 | } 102 | -------------------------------------------------------------------------------- /shaders/bezierLight.vert: -------------------------------------------------------------------------------- 1 | #version 410 2 | 3 | layout(location = 0) in vec3 in_position; 4 | layout(location = 1) in vec3 in_normal; 5 | layout(location = 2) in vec2 in_texcoord; 6 | 7 | out vec3 f_vertPosWorld; 8 | out vec2 f_texcoord; 9 | 10 | uniform mat4 u_mMat; 11 | uniform mat4 u_mvpMat; 12 | 13 | void main(){ 14 | gl_Position = u_mvpMat * vec4(in_position, 1.0); 15 | f_vertPosWorld = (u_mMat * vec4(in_position, 1.0)).xyz; 16 | f_texcoord = vec2(in_texcoord.x, 1.0 - in_texcoord.y); 17 | } 18 | -------------------------------------------------------------------------------- /shaders/floorLTC.vert: -------------------------------------------------------------------------------- 1 | #version 410 2 | 3 | layout(location = 0) in vec3 in_position; 4 | layout(location = 1) in vec3 in_normal; 5 | layout(location = 2) in vec2 in_texcoord; 6 | 7 | out vec3 f_normalWorld; 8 | out vec3 f_vertPosWorld; 9 | out vec2 f_texcoord; 10 | 11 | uniform mat4 u_mMat; 12 | uniform mat4 u_mvMat; 13 | uniform mat4 u_mvpMat; 14 | uniform mat4 u_normMat; 15 | 16 | void main(){ 17 | gl_Position = u_mvpMat * vec4(in_position, 1.0); 18 | f_normalWorld = (u_mMat * vec4(in_normal, 0.0)).xyz; 19 | f_vertPosWorld = (u_mMat * vec4(in_position, 1.0)).xyz; 20 | f_texcoord = vec2(in_texcoord.x, in_texcoord.y); 21 | } 22 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ---------------------------------- 2 | # Define ImGui interface 3 | # ---------------------------------- 4 | set(IMGUI_INTERFACE imgui) 5 | 6 | add_definitions(-DIMGUI_IMPL_OPENGL_LOADER_GLAD2) 7 | add_library(${IMGUI_INTERFACE} INTERFACE) 8 | file(GLOB 9 | IMGUI_FILES 10 | ${CMAKE_CURRENT_SOURCE_DIR}/imgui/*.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/imgui/*.h 12 | ) 13 | target_sources(${IMGUI_INTERFACE} INTERFACE ${IMGUI_FILES}) 14 | source_group("Imgui" FILES ${IMGUI_FILES}) 15 | 16 | # ---------------------------------- 17 | # Define main build target 18 | # ---------------------------------- 19 | set(BUILD_TARGET bezier_ltc) 20 | file(GLOB 21 | SOURCE_FILES 22 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp 23 | ${CMAKE_CURRENT_SOURCE_DIR}/*.h 24 | ) 25 | 26 | add_executable(${BUILD_TARGET}) 27 | 28 | target_sources( 29 | ${BUILD_TARGET} 30 | PRIVATE 31 | ${SOURCE_FILES} 32 | ${SHADER_FILES} 33 | ) 34 | 35 | source_group("Source Files" FILES ${SOURCE_FILES}) 36 | source_group("Shader Files" FILES ${SHADER_FILES}) 37 | 38 | include_directories( 39 | ${CMAKE_CURRENT_SOURCE_DIR} 40 | ${CMAKE_CURRENT_SOURCE_DIR}/stb 41 | ${CMAKE_CURRENT_SOURCE_DIR}/tinyobjloader 42 | ${CMAKE_CURRENT_SOURCE_DIR}/imgui 43 | ${COMMON_INCLUDE_DIRS} 44 | ) 45 | 46 | target_link_libraries( 47 | ${BUILD_TARGET} 48 | PRIVATE 49 | ${COMMON_LIBRARIES} 50 | ${IMGUI_INTERFACE} 51 | ) 52 | 53 | if (MSVC) 54 | target_compile_options(${BUILD_TARGET} PUBLIC "/Zi") 55 | set_property(TARGET ${BUILD_TARGET} APPEND PROPERTY LINK_FLAGS "/ignore:4099 /DEBUG /PROFILE") 56 | endif() 57 | -------------------------------------------------------------------------------- /src/bezierLight.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bezierLight.h" 9 | #include "common.h" 10 | #include "openmp.h" 11 | 12 | static constexpr int MARGIN_SIZE = 0; 13 | static constexpr int MAXDIST = 55; 14 | static constexpr int OVERLAP = 7; 15 | 16 | namespace { 17 | 18 | int C(int n, int m) { 19 | m = std::min(m, n - m); 20 | if (m == 0) { 21 | return 1; 22 | } 23 | if (m == 1) { 24 | return n; 25 | } 26 | 27 | int ret = 1; 28 | for (int i = 0; i < m; i++) { 29 | ret *= (n - i) / (i + 1); 30 | } 31 | 32 | return ret; 33 | } 34 | 35 | float bernstein(int n, int i, double t) { 36 | return C(n, i) * std::pow(t, i) * std::pow(1.0 - t, n - i); 37 | } 38 | 39 | } // anonymous namespace 40 | 41 | void BezierLight::initialize() { 42 | numPoints = 0; // initialize with minumum value 43 | numCurves = 0; 44 | Le = glm::vec3(1.0f); 45 | center = glm::vec3(0.0f); 46 | size = glm::vec2(1.0f); 47 | rotAngle = glm::vec3(0.0f); 48 | translate = glm::vec3(0.0f); 49 | 50 | texHeight = 0; 51 | texWidth = 0; 52 | marginSize = 0; 53 | maxLOD = 0; 54 | 55 | isTwoSided = false; 56 | isBezTexed = false; 57 | 58 | bezLightTexId = -1; 59 | bernCoeffTexId = -1; 60 | } 61 | 62 | void BezierLight::createCPSmodel(LightType type) { 63 | // create cps in normalize model space [-1, 1] 64 | cpsModel.clear(); 65 | cpsModel.shrink_to_fit(); 66 | 67 | switch (type) { 68 | case 0: { 69 | cpsModel.push_back(glm::vec3(0.0f, 0.65f, 0.0f)); 70 | cpsModel.push_back(glm::vec3(1.6f, -0.95f, 0.0f)); 71 | cpsModel.push_back(glm::vec3(-1.6f, -0.95f, 0.0f)); 72 | cpsModel.push_back(cpsModel[0]); 73 | } break; 74 | 75 | case 1: { 76 | const float scale = 0.9f; 77 | cpsModel.push_back(glm::vec3(-0.8f, 0.6f, 0.0f) * scale); 78 | cpsModel.push_back(glm::vec3(-0.4375f, 1.4f, 0.0f) * scale); 79 | cpsModel.push_back(glm::vec3(0.4375f, -0.0f, 0.0f) * scale); 80 | cpsModel.push_back(glm::vec3(0.8f, 0.6f, 0.0f) * scale); 81 | cpsModel.push_back(cpsModel[3]); 82 | cpsModel.push_back(glm::vec3(0.125f, -1.85f, 0.0f) * scale); 83 | cpsModel.push_back(glm::vec3(-0.125f, 0.30f, 0.0f) * scale); 84 | cpsModel.push_back(cpsModel[0]); 85 | } break; 86 | 87 | case 2: { 88 | const float scale = 0.85f; 89 | cpsModel.push_back(glm::vec3(-0.1f, 0.10f, 0.0f) * scale); 90 | cpsModel.push_back(glm::vec3(-0.8f, 1.1f, 0.0f) * scale); 91 | cpsModel.push_back(glm::vec3(0.8f, 1.1f, 0.0f) * scale); 92 | cpsModel.push_back(glm::vec3(0.1f, 0.1f, 0.0f) * scale); 93 | cpsModel.push_back(cpsModel[3]); 94 | cpsModel.push_back(glm::vec3(0.8f, 0.1f, 0.0f) * scale); 95 | cpsModel.push_back(glm::vec3(0.8f, -0.90f, 0.0f) * scale); 96 | cpsModel.push_back(glm::vec3(0.0f, -0.40f, 0.0f) * scale); 97 | cpsModel.push_back(cpsModel[7]); 98 | cpsModel.push_back(glm::vec3(-0.8f, -0.90f, 0.0f) * scale); 99 | cpsModel.push_back(glm::vec3(-0.8f, 0.10f, 0.0f) * scale); 100 | cpsModel.push_back(cpsModel[0]); 101 | } break; 102 | 103 | case 3: { 104 | cpsModel.push_back(glm::vec3(-0.5f, 0.5f, 0.0f)); 105 | cpsModel.push_back(glm::vec3(-0.16666f, 1.0f, 0.0f)); 106 | cpsModel.push_back(glm::vec3(0.16666f, 0.2f, 0.0f)); 107 | cpsModel.push_back(glm::vec3(0.5f, 0.5f, 0.0f)); 108 | cpsModel.push_back(cpsModel[3]); 109 | cpsModel.push_back(glm::vec3(1.0f, 0.19f, 0.0f)); 110 | cpsModel.push_back(glm::vec3(0.4f, -0.3f, 0.0f)); 111 | cpsModel.push_back(glm::vec3(0.5f, -0.5f, 0.0f)); 112 | cpsModel.push_back(cpsModel[7]); 113 | cpsModel.push_back(glm::vec3(0.0f, 0.2f, 0.0f)); 114 | cpsModel.push_back(glm::vec3(-0.3f, -0.4f, 0.0f)); 115 | cpsModel.push_back(glm::vec3(-0.5f, -0.5f, 0.0f)); 116 | cpsModel.push_back(cpsModel[11]); 117 | cpsModel.push_back(glm::vec3(-1.2f, -0.2f, 0.0f)); 118 | cpsModel.push_back(glm::vec3(-0.1f, 0.15f, 0.0f)); 119 | cpsModel.push_back(cpsModel[0]); 120 | } break; 121 | 122 | case 4: { 123 | cpsModel.push_back(glm::vec3(0.0f, 0.65f, 0.0f)); 124 | cpsModel.push_back(glm::vec3(0.7f, 0.3f, 0.0f)); 125 | cpsModel.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); 126 | cpsModel.push_back(glm::vec3(0.2f, -0.55f, 0.0f)); 127 | cpsModel.push_back(cpsModel[3]); 128 | cpsModel.push_back(glm::vec3(0.4f, -0.4f, 0.0f)); 129 | cpsModel.push_back(glm::vec3(-0.0f, -0.3f, 0.0f)); 130 | cpsModel.push_back(glm::vec3(0.0f, -0.1f, 0.0f)); 131 | cpsModel.push_back(cpsModel[7]); 132 | cpsModel.push_back(glm::vec3(0.0f, -0.3f, 0.0f)); 133 | cpsModel.push_back(glm::vec3(-0.4f, -0.4f, 0.0f)); 134 | cpsModel.push_back(glm::vec3(-0.2f, -0.55f, 0.0f)); 135 | cpsModel.push_back(cpsModel[11]); 136 | cpsModel.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); 137 | cpsModel.push_back(glm::vec3(-0.7f, 0.3f, 0.0f)); 138 | cpsModel.push_back(glm::vec3(0.0f, 0.65f, 0.0f)); 139 | cpsModel.push_back(cpsModel[15]); 140 | cpsModel.push_back(glm::vec3(0.0f, 0.5f, 0.0f)); 141 | cpsModel.push_back(glm::vec3(0.0f, 0.45f, 0.0f)); 142 | cpsModel.push_back(glm::vec3(0.0f, 0.4f, 0.0f)); 143 | cpsModel.push_back(cpsModel[19]); 144 | cpsModel.push_back(glm::vec3(-0.5f, 0.0f, 0.0f)); 145 | cpsModel.push_back(glm::vec3(0.5f, 0.0f, 0.0f)); 146 | cpsModel.push_back(glm::vec3(0.0f, 0.4f, 0.0f)); 147 | cpsModel.push_back(cpsModel[23]); 148 | cpsModel.push_back(glm::vec3(0.0f, 0.45f, 0.0f)); 149 | cpsModel.push_back(glm::vec3(0.0f, 0.5f, 0.0f)); 150 | cpsModel.push_back(cpsModel[0]); 151 | } break; 152 | 153 | case 5: { 154 | cpsModel.push_back(glm::vec3(-0.8f, -0.64f, 0.0f)); 155 | cpsModel.push_back(glm::vec3(-0.4f, 0.8f, 0.0f)); 156 | cpsModel.push_back(glm::vec3(-0.2f, -0.4f, 0.0f)); 157 | cpsModel.push_back(glm::vec3(0.0f, -0.4f, 0.0f)); 158 | cpsModel.push_back(cpsModel[3]); 159 | cpsModel.push_back(glm::vec3(0.2f, -0.4f, 0.0f)); 160 | cpsModel.push_back(glm::vec3(0.4f, 0.8f, 0.0f)); 161 | cpsModel.push_back(glm::vec3(0.8f, -0.64f, 0.0f)); 162 | cpsModel.push_back(cpsModel[7]); 163 | cpsModel.push_back(glm::vec3(0.0f, -0.64f, 0.0f)); 164 | cpsModel.push_back(glm::vec3(0.0f, -0.64f, 0.0f)); 165 | cpsModel.push_back(cpsModel[0]); 166 | } break; 167 | 168 | case 6: { 169 | cpsModel.push_back(glm::vec3(-1.0f, 1.0f, 0.0f)); 170 | cpsModel.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); 171 | cpsModel.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); 172 | cpsModel.push_back(glm::vec3(1.0f, 1.0f, 0.0f)); 173 | cpsModel.push_back(cpsModel[3]); 174 | cpsModel.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); 175 | cpsModel.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); 176 | cpsModel.push_back(glm::vec3(1.0f, -1.0f, 0.0f)); 177 | cpsModel.push_back(cpsModel[7]); 178 | cpsModel.push_back(glm::vec3(0.0f, -1.0f, 0.0f)); 179 | cpsModel.push_back(glm::vec3(0.0f, -1.0f, 0.0f)); 180 | cpsModel.push_back(glm::vec3(-1.0f, -1.0f, 0.0f)); 181 | cpsModel.push_back(cpsModel[11]); 182 | cpsModel.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); 183 | cpsModel.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); 184 | cpsModel.push_back(cpsModel[0]); 185 | } break; 186 | 187 | case 7: { 188 | // Line #0: 189 | cpsModel.push_back(glm::vec3(-0.3855f, -0.5181f, 0.0f)); 190 | cpsModel.push_back(glm::vec3(-0.1042f, -0.0200f, 0.0f)); 191 | cpsModel.push_back(glm::vec3(-0.1042f, -0.0200f, 0.0f)); 192 | cpsModel.push_back(glm::vec3(0.1770f, 0.4780f, 0.0f)); 193 | // Line #1: 194 | cpsModel.push_back(glm::vec3(0.1770f, 0.4780f, 0.0f)); 195 | cpsModel.push_back(glm::vec3(0.0142f, 0.4780f, 0.0f)); 196 | cpsModel.push_back(glm::vec3(0.0142f, 0.4780f, 0.0f)); 197 | cpsModel.push_back(glm::vec3(-0.1487f, 0.4780f, 0.0f)); 198 | // CubicBezier #2: 199 | cpsModel.push_back(glm::vec3(-0.1487f, 0.4780f, 0.0f)); 200 | cpsModel.push_back(glm::vec3(-0.1834f, 0.4820f, 0.0f)); 201 | cpsModel.push_back(glm::vec3(-0.2177f, 0.4683f, 0.0f)); 202 | cpsModel.push_back(glm::vec3(-0.2400f, 0.4414f, 0.0f)); 203 | // CubicBezier #3: 204 | cpsModel.push_back(glm::vec3(-0.2400f, 0.4414f, 0.0f)); 205 | cpsModel.push_back(glm::vec3(-0.2571f, 0.4165f, 0.0f)); 206 | cpsModel.push_back(glm::vec3(-0.3572f, 0.2905f, 0.0f)); 207 | cpsModel.push_back(glm::vec3(-0.3572f, 0.2905f, 0.0f)); 208 | // Line #4: 209 | cpsModel.push_back(glm::vec3(-0.3572f, 0.2905f, 0.0f)); 210 | cpsModel.push_back(glm::vec3(-0.3535f, 0.4216f, 0.0f)); 211 | cpsModel.push_back(glm::vec3(-0.3535f, 0.4216f, 0.0f)); 212 | cpsModel.push_back(glm::vec3(-0.3499f, 0.5527f, 0.0f)); 213 | // Line #5: 214 | cpsModel.push_back(glm::vec3(-0.3499f, 0.5527f, 0.0f)); 215 | cpsModel.push_back(glm::vec3(0.0056f, 0.5527f, 0.0f)); 216 | cpsModel.push_back(glm::vec3(0.0056f, 0.5527f, 0.0f)); 217 | cpsModel.push_back(glm::vec3(0.3611f, 0.5527f, 0.0f)); 218 | // Line #6: 219 | cpsModel.push_back(glm::vec3(0.3611f, 0.5527f, 0.0f)); 220 | cpsModel.push_back(glm::vec3(0.0791f, 0.0549f, 0.0f)); 221 | cpsModel.push_back(glm::vec3(0.0791f, 0.0549f, 0.0f)); 222 | cpsModel.push_back(glm::vec3(-0.2029f, -0.4429f, 0.0f)); 223 | // Line #7: 224 | cpsModel.push_back(glm::vec3(-0.2029f, -0.4429f, 0.0f)); 225 | cpsModel.push_back(glm::vec3(-0.0137f, -0.4429f, 0.0f)); 226 | cpsModel.push_back(glm::vec3(-0.0137f, -0.4429f, 0.0f)); 227 | cpsModel.push_back(glm::vec3(0.1755f, -0.4429f, 0.0f)); 228 | // CubicBezier #8: 229 | cpsModel.push_back(glm::vec3(0.1755f, -0.4429f, 0.0f)); 230 | cpsModel.push_back(glm::vec3(0.2106f, -0.4470f, 0.0f)); 231 | cpsModel.push_back(glm::vec3(0.2454f, -0.4333f, 0.0f)); 232 | cpsModel.push_back(glm::vec3(0.2683f, -0.4063f, 0.0f)); 233 | // CubicBezier #9: 234 | cpsModel.push_back(glm::vec3(0.2683f, -0.4063f, 0.0f)); 235 | cpsModel.push_back(glm::vec3(0.2854f, -0.3819f, 0.0f)); 236 | cpsModel.push_back(glm::vec3(0.3855f, -0.2554f, 0.0f)); 237 | cpsModel.push_back(glm::vec3(0.3855f, -0.2554f, 0.0f)); 238 | // Line #10: 239 | cpsModel.push_back(glm::vec3(0.3855f, -0.2554f, 0.0f)); 240 | cpsModel.push_back(glm::vec3(0.3816f, -0.3867f, 0.0f)); 241 | cpsModel.push_back(glm::vec3(0.3816f, -0.3867f, 0.0f)); 242 | cpsModel.push_back(glm::vec3(0.3777f, -0.5181f, 0.0f)); 243 | // Line #11: 244 | cpsModel.push_back(glm::vec3(0.3777f, -0.5181f, 0.0f)); 245 | cpsModel.push_back(glm::vec3(-0.0039f, -0.5181f, 0.0f)); 246 | cpsModel.push_back(glm::vec3(-0.0039f, -0.5181f, 0.0f)); 247 | cpsModel.push_back(glm::vec3(-0.3855f, -0.5181f, 0.0f)); 248 | } break; 249 | } 250 | 251 | this->numPoints = (int) cpsModel.size(); 252 | this->numCurves = numPoints / NUM_CPS_IN_CURVE; 253 | for (auto v : cpsModel) { 254 | center += v / (float) numPoints; 255 | } 256 | 257 | // compute sample points on boundary curve 258 | samplePoints.clear(); 259 | samplePoints.push_back(glm::vec3(0.0f)); 260 | const int nSplit = 32; 261 | for (int i = 0; i < this->numCurves; i++) { 262 | for (int j = 0; j < nSplit; j++) { 263 | glm::vec3 p(0.0f); 264 | const float t = (float) j / (float) nSplit; 265 | for (int d = 0; d < NUM_CPS_IN_CURVE; d++) { 266 | p += bernstein(NUM_CPS_IN_CURVE - 1, d, t) * cpsModel[i * NUM_CPS_IN_CURVE + d]; 267 | } 268 | samplePoints.push_back(p); 269 | } 270 | } 271 | samplePoints.push_back(samplePoints[1]); 272 | 273 | // create VAO for sample points 274 | glGenVertexArrays(1, &ptsVaoId); 275 | glBindVertexArray(ptsVaoId); 276 | 277 | glGenBuffers(1, &ptsVboId); 278 | glBindBuffer(GL_ARRAY_BUFFER, ptsVboId); 279 | glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * samplePoints.size(), samplePoints.data(), GL_STATIC_DRAW); 280 | 281 | glEnableVertexAttribArray(0); 282 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), 0); 283 | 284 | glBindVertexArray(0); 285 | 286 | // default translation parameters 287 | size = glm::vec2(2.0f); 288 | rotAngle = 90.0f * glm::vec3(0.0f, 0.0f, 0.0f); 289 | translate = glm::vec3(0.0f, 1.30f, 0.0f); 290 | } 291 | 292 | void BezierLight::calcCPSworld() { 293 | // update transformation 294 | modelMat = glm::translate(translate) * 295 | rotateXYZ(degToRad(rotAngle)) * 296 | glm::scale(glm::vec3(size, 1.0f)); 297 | 298 | // compute control points in world space 299 | cpsWorld.resize(numPoints); 300 | for (int i = 0; i < numPoints; i++) { 301 | glm::vec4 p = modelMat * glm::vec4(cpsModel[i], 1.0f); 302 | // Avoid numerical unstability in algebraic clipping 303 | p.y = p.y > 0.0 ? p.y + 1.0e-3 : p.y - 1.0e-3; 304 | cpsWorld[i] = glm::vec3(p.x, p.y, p.z); 305 | } 306 | 307 | // compute barycenter of area light 308 | glm::vec4 p = modelMat * glm::vec4(center, 1.0f); 309 | center = glm::vec3(p.x, p.y, p.z); 310 | } 311 | 312 | glm::mat4 BezierLight::rotateX(float ax) { 313 | glm::mat4 xRotMat = glm::rotate(ax, glm::vec3(1.0f, 0.0f, 0.0f)); 314 | return xRotMat; 315 | } 316 | 317 | glm::mat4 BezierLight::rotateY(float ay) { 318 | glm::mat4 yRotMat = glm::rotate(ay, glm::vec3(0.0f, 1.0f, 0.0f)); 319 | return yRotMat; 320 | } 321 | 322 | glm::mat4 BezierLight::rotateZ(float az) { 323 | glm::mat4 zRotMat = glm::rotate(az, glm::vec3(0.0f, 0.0f, 1.0f)); 324 | return zRotMat; 325 | } 326 | 327 | glm::mat4 BezierLight::rotateXYZ(glm::vec3 rotAngle) { 328 | glm::mat4 rotMat = rotateZ(rotAngle.z) * rotateY(rotAngle.y) * rotateX(rotAngle.x); 329 | return rotMat; 330 | } 331 | 332 | glm::vec3 BezierLight::degToRad(glm::vec3 rotAngle) { 333 | return (float) (Pi / 180.0) * rotAngle; 334 | } 335 | 336 | glm::vec3 BezierLight::bezierCurve(const int curve, const float t) { 337 | glm::vec3 vs[NUM_CPS_IN_CURVE]; 338 | for (int i = 0; i < NUM_CPS_IN_CURVE; i++) { 339 | vs[i] = cpsModel[curve * NUM_CPS_IN_CURVE + i]; 340 | } 341 | 342 | for (int i = NUM_CPS_IN_CURVE - 1; i >= 1; i--) { 343 | for (int j = 0; j < i; j++) { 344 | vs[j] = glm::mix(vs[j], vs[j + 1], t); 345 | } 346 | } 347 | 348 | return vs[0]; 349 | } 350 | 351 | void BezierLight::gaussianFilter(std::vector> &kernel, int kernelSize, float sigma) { 352 | const float twicedSigmaSquared = 2.0f * sigma * sigma; 353 | 354 | kernel = std::vector>(kernelSize, std::vector(kernelSize, 0.0f)); // deep copy 355 | 356 | if (kernelSize % 2 == 1) { 357 | // generating kernel for odd kernelSize 358 | // left up to right down 359 | const int offset = kernelSize / 2; 360 | float sum = 0.0f; 361 | for (int kernelY = -offset; kernelY <= offset; kernelY++) { // 10 11 12 13 14 362 | for (int kernelX = -offset; kernelX <= offset; kernelX++) { 363 | const float rSquared = kernelX * kernelX + kernelY * kernelY; 364 | const float weight = exp(-rSquared / twicedSigmaSquared); 365 | kernel[kernelY + offset][kernelX + offset] = weight; 366 | sum += weight; 367 | } 368 | } 369 | 370 | // normalizing 371 | for (int kernelY = 0; kernelY < kernelSize; kernelY++) { 372 | for (int kernelX = 0; kernelX < kernelSize; kernelX++) { 373 | kernel[kernelY][kernelX] /= sum; 374 | } 375 | } 376 | } else { 377 | Error("kernelSize is even number, invalid size!!"); 378 | } 379 | } 380 | 381 | void BezierLight::createBezLightTex(const std::string &filename) { 382 | // load image file 383 | int channels; 384 | unsigned char *bytes = stbi_load(filename.c_str(), &this->texWidth, &this->texHeight, &channels, STBI_rgb_alpha); 385 | 386 | if (!bytes) { 387 | fprintf(stderr, "Failed to load image file: %s\n", filename.c_str()); 388 | exit(1); 389 | } 390 | this->marginSize = MARGIN_SIZE; 391 | this->maxLOD = int(std::log2(texWidth + 2 * marginSize)); 392 | 393 | // prepare texture storage 394 | GLenum target = GL_TEXTURE_2D; 395 | GLenum filter = GL_LINEAR_MIPMAP_LINEAR; 396 | GLenum address = GL_CLAMP_TO_EDGE; 397 | 398 | glGenTextures(1, &bezLightTexId); 399 | glBindTexture(target, bezLightTexId); 400 | glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); 401 | glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); 402 | glTexParameteri(target, GL_TEXTURE_WRAP_S, address); 403 | glTexParameteri(target, GL_TEXTURE_WRAP_T, address); 404 | glTexImage2D(target, 0, GL_RGBA8, this->texWidth, this->texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); 405 | glGenerateMipmap(target); 406 | 407 | // generate prefiltered LOD texture 408 | if (texWidth != texHeight || pow(2, std::log2(texHeight)) != texHeight) { 409 | Error("invalid texture size, cannot compute maxLOD"); 410 | } 411 | 412 | float *Pbytes = new float[4 * texWidth * texHeight]; 413 | for (int i = 0; i < 4 * texWidth * texHeight; i++) { 414 | Pbytes[i] = float(bytes[i]) / 255.0f; 415 | } 416 | 417 | // clip texture by bezier-curve shape and generate distance map 418 | for (int row = 0; row < texHeight; row++) { 419 | for (int col = 0; col < texWidth; col++) { 420 | const int texelIndex = 4 * (row * texWidth + col); 421 | glm::vec3 texelUV = glm::vec3(float(col) / (texWidth - 1), 1.0f - float(row) / (texHeight - 1), 0.0); 422 | 423 | const int NDIV = 32; 424 | const float dt = 1.0f / NDIV; 425 | glm::vec3 dir = glm::vec3(0.0f); 426 | float sumAngle = 0.0f; 427 | for (int curve = 0; curve < numCurves; curve++) { 428 | // change [-1, 1] space to [0, 1] space 429 | glm::vec3 v0 = 0.5f * bezierCurve(curve, 0.0) + glm::vec3(0.5f, 0.5f, 0.0); 430 | glm::vec3 e0 = v0 - texelUV; 431 | for (int div = 0; div < NDIV; div++) { 432 | const float t1 = (div + 1) * dt; 433 | 434 | const glm::vec3 v1 = 0.5f * bezierCurve(curve, t1) + glm::vec3(0.5f, 0.5f, 0.0); 435 | const glm::vec3 e1 = v1 - texelUV; 436 | 437 | // check cross direction 438 | float tmp; 439 | glm::vec3 crs = glm::cross(e0, e1); 440 | if (curve == 0 && div == 0) { 441 | tmp = 1.0f; 442 | dir = crs; 443 | } else { 444 | float inner = dot(crs, dir); 445 | tmp = glm::sign(inner); 446 | } 447 | 448 | float l0l1 = length(e0) * length(e1) + 0.00001; 449 | float angle = acos(dot(e0, e1) / l0l1); 450 | sumAngle += tmp * angle; 451 | 452 | v0 = v1; 453 | e0 = e1; 454 | } 455 | } 456 | 457 | if (glm::abs(sumAngle) < Pi) { 458 | Pbytes[texelIndex + 0] = 0.0f; 459 | Pbytes[texelIndex + 1] = 0.0f; 460 | Pbytes[texelIndex + 2] = 0.0f; 461 | Pbytes[texelIndex + 3] = 0.0f; // sign of outside of texture 462 | } 463 | } 464 | } 465 | 466 | // Save 467 | for (int i = 0; i < texWidth * texHeight; i++) { 468 | if (Pbytes[4 * i + 3] == 0.0f) { 469 | bytes[4 * i + 0] = 0; 470 | bytes[4 * i + 1] = 0; 471 | bytes[4 * i + 2] = 0; 472 | bytes[4 * i + 3] = 0; 473 | } 474 | } 475 | 476 | // id needed, export png for checking clipped texture 477 | // stbi_write_png(std::string("clipped_texture.png").c_str(), texWidth, texHeight, 4, bytes, 0); 478 | stbi_image_free(bytes); 479 | 480 | // create Gaussian filter for inside of Bezier curve 481 | std::vector> kernel; 482 | unsigned int kernelSize = 15; 483 | const float sigma = 9.0f; 484 | if (kernelSize % 2 == 0) { 485 | kernelSize++; 486 | } 487 | gaussianFilter(kernel, kernelSize, sigma); 488 | 489 | // create Gaussian filters for outside of Bezier curve 490 | std::vector>> outKernels; 491 | std::vector outKernelSizes; 492 | for (int i = OVERLAP; i <= MAXDIST; i++) { 493 | std::vector> outKernel; 494 | const float outSigma = i; 495 | const uint32_t outKernelSize = 2 * i + 1; 496 | 497 | gaussianFilter(outKernel, outKernelSize, outSigma); 498 | 499 | outKernels.emplace_back(outKernel); 500 | outKernelSizes.emplace_back(outKernelSize); 501 | } 502 | 503 | int LODfactor = 1; 504 | for (int LOD = 0; LOD <= maxLOD; LOD++) { 505 | const int LODwidth = texWidth / LODfactor; 506 | const int LODheight = texHeight / LODfactor; 507 | float *Sbytes = new float[4 * LODwidth * LODheight]; 508 | if (LOD == 0) { 509 | memcpy(Sbytes, Pbytes, sizeof(float) * 4 * texWidth * texHeight); 510 | } else if (LOD > 0) { 511 | // texture size change 512 | unsigned int SpixelPos = 0; 513 | const int Pwidth = LODwidth * 2; 514 | const int Pheight = LODheight * 2; 515 | for (int row = 0; row < Pheight; row += 2) { 516 | for (int col = 0; col < Pwidth; col += 2) { 517 | // calculate pixelPos 518 | int PpixelPos = row * Pwidth + col; 519 | 520 | // take average for RGBA 521 | for (int RGB = 0; RGB < 3; RGB++) { 522 | int dataPos[4]; 523 | dataPos[0] = 4 * PpixelPos + RGB; // left up 524 | dataPos[1] = 4 * (PpixelPos + 1) + RGB; // right up 525 | dataPos[2] = 4 * (PpixelPos + Pwidth) + RGB; // left down 526 | dataPos[3] = 4 * (PpixelPos + Pwidth + 1) + RGB; // right down 527 | 528 | float ave = 0.25f * (Pbytes[dataPos[0]] + Pbytes[dataPos[1]] + Pbytes[dataPos[2]] + Pbytes[dataPos[3]]); 529 | Sbytes[4 * SpixelPos + RGB] = ave; 530 | } 531 | Sbytes[4 * SpixelPos + 3] = 1.0f; 532 | 533 | SpixelPos++; 534 | } 535 | } 536 | } 537 | 538 | // for next loop 539 | memcpy(Pbytes, Sbytes, sizeof(float) * 4 * LODwidth * LODheight); 540 | 541 | float *Gbytes = new float[4 * LODwidth * LODheight]; 542 | omp_parallel_for(int row = 0; row < LODheight; row++) { 543 | for (int col = 0; col < LODwidth; col++) { 544 | // calculate SpixelPos from row and col 545 | unsigned int SpixelPos = row * LODwidth + col; 546 | 547 | if (LOD == 0) { 548 | // inside of Bezier curve 549 | if (Sbytes[4 * SpixelPos + 3] != 0.0f) { // just copy 550 | Gbytes[4 * SpixelPos + 0] = Sbytes[4 * SpixelPos + 0]; 551 | Gbytes[4 * SpixelPos + 1] = Sbytes[4 * SpixelPos + 1]; 552 | Gbytes[4 * SpixelPos + 2] = Sbytes[4 * SpixelPos + 2]; 553 | Gbytes[4 * SpixelPos + 3] = Sbytes[4 * SpixelPos + 3]; 554 | } else { // outside of Bezier curve 555 | const int NDIV = 64; 556 | const float dt = 1.0f / NDIV; 557 | const glm::vec3 texelUV = glm::vec3(float(col) / (LODwidth - 1), 1.0f - float(row) / (LODheight - 1), 0.0); 558 | int dist = INT_MAX; 559 | for (int curve = 0; curve < numCurves; curve++) { 560 | for (int div = 0; div < NDIV; div++) { 561 | const float t = div * dt; 562 | 563 | // change [-1, 1] space to [0, texWidth] space 564 | glm::vec3 point = 0.5f * bezierCurve(curve, t) + glm::vec3(0.5f, 0.5f, 0.0f); 565 | 566 | const int tmpDist = int(texWidth * glm::length(texelUV - point)) + 1; 567 | dist = glm::min(dist, tmpDist); 568 | } 569 | } 570 | 571 | // filter never intersects the curve, skip 572 | if (dist > (MAXDIST - OVERLAP)) { 573 | Gbytes[4 * SpixelPos + 0] = Sbytes[4 * SpixelPos + 0]; 574 | Gbytes[4 * SpixelPos + 1] = Sbytes[4 * SpixelPos + 1]; 575 | Gbytes[4 * SpixelPos + 2] = Sbytes[4 * SpixelPos + 2]; 576 | Gbytes[4 * SpixelPos + 3] = Sbytes[4 * SpixelPos + 3]; 577 | continue; 578 | } 579 | 580 | const int kernelIndex = dist - 1; 581 | std::vector> outKernel = outKernels[kernelIndex]; 582 | const int outKernelSize = outKernelSizes[kernelIndex]; 583 | 584 | const int offset = outKernelSize / 2; 585 | 586 | for (int RGB = 0; RGB < 3; RGB++) { 587 | float weightedBytes = 0.0f; 588 | float weightLoss = 1.0f; 589 | 590 | for (int kernelY = -offset; kernelY <= offset; kernelY++) { 591 | for (int kernelX = -offset; kernelX <= offset; kernelX++) { 592 | const int kernelRow = row + kernelY; 593 | const int kernelCol = col + kernelX; 594 | 595 | // operation for pixels out of original texture 596 | if (kernelRow < 0 || 597 | kernelRow > LODheight - 1 || 598 | kernelCol < 0 || 599 | kernelCol > LODwidth - 1) { 600 | weightLoss -= outKernel[kernelY + offset][kernelX + offset]; 601 | continue; 602 | } 603 | 604 | const int kernelSpixelPos = kernelRow * LODwidth + kernelCol; 605 | if (Sbytes[4 * kernelSpixelPos + RGB] == 0.0f) { 606 | weightLoss -= outKernel[kernelY + offset][kernelX + offset]; 607 | continue; 608 | } 609 | weightedBytes += outKernel[kernelY + offset][kernelX + offset] * Sbytes[4 * kernelSpixelPos + RGB]; 610 | } 611 | } 612 | Gbytes[4 * SpixelPos + RGB] = (1.0f / weightLoss) * weightedBytes; 613 | } 614 | Gbytes[4 * SpixelPos + 3] = 1.0f; 615 | } 616 | 617 | // for following loops LOD > 0 618 | memcpy(Pbytes, Gbytes, sizeof(float) * 4 * LODwidth * LODheight); 619 | } else { // LOD > 0, simply apply gaussian filter 620 | const int offset = kernelSize / 2; 621 | for (int RGB = 0; RGB < 3; RGB++) { 622 | // calculate weighted value using kernel for RGBA 623 | float weightedBytes = 0.0f; 624 | float weightLoss = 1.0f; 625 | for (int kernelY = -offset; kernelY <= offset; kernelY++) { 626 | for (int kernelX = -offset; kernelX <= offset; kernelX++) { 627 | // operation for pixels beyond kernel border 628 | if (col + kernelX < 0 || 629 | col + kernelX > LODwidth - 1 || 630 | row + kernelY < 0 || 631 | row + kernelY > LODheight - 1) { 632 | weightLoss -= kernel[kernelY + offset][kernelX + offset]; 633 | continue; 634 | } 635 | 636 | const int weightedPixelPos = SpixelPos + LODwidth * kernelY + kernelX; 637 | if (Sbytes[4 * weightedPixelPos + RGB] == 0.0f) { 638 | weightLoss -= kernel[kernelY + offset][kernelX + offset]; 639 | } else { 640 | weightedBytes += kernel[kernelY + offset][kernelX + offset] * Sbytes[4 * weightedPixelPos + RGB]; 641 | } 642 | } 643 | } 644 | Gbytes[4 * SpixelPos + RGB] = (1.0f / weightLoss) * weightedBytes; 645 | } 646 | Gbytes[4 * SpixelPos + 3] = 1.0f; 647 | } 648 | } 649 | } 650 | 651 | glTexSubImage2D(target, LOD, 0, 0, LODwidth, LODheight, GL_RGBA, GL_FLOAT, Gbytes); 652 | LODfactor *= 2; 653 | 654 | delete[] Sbytes; 655 | delete[] Gbytes; 656 | } 657 | 658 | glBindTexture(target, 0); 659 | 660 | delete[] Pbytes; 661 | } 662 | 663 | void BezierLight::compBernCoeffs() { 664 | int n = NUM_CPS_IN_CURVE - 1; 665 | for (int i = 0; i <= COEFF_DIV; i++) { 666 | double t = 1.0 / COEFF_DIV * i; 667 | 668 | bernCoeffs[i] = glm::vec4(bernstein(n, 0, t), 669 | bernstein(n, 1, t), 670 | bernstein(n, 2, t), 671 | bernstein(n, 3, t)); 672 | } 673 | } 674 | 675 | void BezierLight::createBernCoeffTex() { 676 | // compute bernstein coeffs 677 | compBernCoeffs(); 678 | 679 | glGenTextures(1, &bernCoeffTexId); 680 | 681 | GLenum target = GL_TEXTURE_1D; 682 | GLenum filter = GL_LINEAR; 683 | GLenum address = GL_CLAMP_TO_EDGE; 684 | 685 | glBindTexture(target, bernCoeffTexId); 686 | 687 | glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); 688 | glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); 689 | glTexParameteri(target, GL_TEXTURE_WRAP_S, address); 690 | glTexParameteri(target, GL_TEXTURE_WRAP_T, address); 691 | 692 | // upload 693 | glTexImage1D(target, 0, GL_RGBA32F, bernCoeffs.size(), 0, GL_RGBA, GL_FLOAT, bernCoeffs.data()); 694 | 695 | glBindTexture(target, 0); 696 | } 697 | 698 | void BezierLight::drawBez(const Camera &camera) { 699 | glUseProgram(programId); 700 | 701 | GLuint location = glGetUniformLocation(programId, "u_cpsWorld"); 702 | glUniform3fv(location, numPoints, glm::value_ptr(cpsWorld[0])); 703 | 704 | location = glGetUniformLocation(programId, "u_numCurves"); 705 | glUniform1i(location, numCurves); 706 | 707 | location = glGetUniformLocation(programId, "u_isTwoSided"); 708 | glUniform1i(location, isTwoSided); 709 | 710 | location = glGetUniformLocation(programId, "u_isBezTexed"); 711 | glUniform1i(location, isBezTexed); 712 | 713 | if (isBezTexed) { 714 | location = glGetUniformLocation(programId, "u_texWidth"); 715 | glUniform1i(location, texWidth); 716 | 717 | location = glGetUniformLocation(programId, "u_texHeight"); 718 | glUniform1i(location, texHeight); 719 | 720 | location = glGetUniformLocation(programId, "u_marginSize"); 721 | glUniform1i(location, marginSize); 722 | 723 | location = glGetUniformLocation(programId, "u_bezLightTex"); 724 | glActiveTexture(GL_TEXTURE0); 725 | glBindTexture(GL_TEXTURE_2D, bezLightTexId); 726 | glUniform1i(location, 0); 727 | } 728 | 729 | //location = glGetUniformLocation(programId, "u_bernCoeffTex"); 730 | //glActiveTexture(GL_TEXTURE1); 731 | //glBindTexture(GL_TEXTURE_1D, bernCoeffTexId); 732 | //glUniform1i(location, 1); 733 | 734 | location = glGetUniformLocation(programId, "u_diffColor"); 735 | glUniform3fv(location, 1, glm::value_ptr(diffColor)); 736 | location = glGetUniformLocation(programId, "u_specColor"); 737 | glUniform3fv(location, 1, glm::value_ptr(specColor)); 738 | location = glGetUniformLocation(programId, "u_ambiColor"); 739 | glUniform3fv(location, 1, glm::value_ptr(ambiColor)); 740 | location = glGetUniformLocation(programId, "u_shininess"); 741 | glUniform1f(location, shininess); 742 | 743 | glm::mat4 mMat, mvMat, mvpMat, normMat; 744 | mMat = modelMat; 745 | mvMat = camera.viewMat * mMat; 746 | mvpMat = camera.projMat * mvMat; 747 | normMat = glm::transpose(glm::inverse(mvMat)); 748 | 749 | location = glGetUniformLocation(programId, "u_lightPos"); 750 | glUniform3fv(location, 1, glm::value_ptr(center)); 751 | location = glGetUniformLocation(programId, "u_lightLe"); 752 | glUniform3fv(location, 1, glm::value_ptr(Le)); 753 | location = glGetUniformLocation(programId, "u_lightMat"); 754 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(camera.viewMat)); 755 | location = glGetUniformLocation(programId, "u_mMat"); 756 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(mMat)); 757 | location = glGetUniformLocation(programId, "u_mvMat"); 758 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(mvMat)); 759 | location = glGetUniformLocation(programId, "u_mvpMat"); 760 | glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(mvpMat)); 761 | location = glGetUniformLocation(programId, "u_normMat"); 762 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(normMat)); 763 | 764 | if (textureId != 0) { 765 | glActiveTexture(GL_TEXTURE0); 766 | glBindTexture(GL_TEXTURE_2D, textureId); 767 | location = glGetUniformLocation(programId, "u_isTextured"); 768 | glUniform1i(location, true); 769 | location = glGetUniformLocation(programId, "u_texture"); 770 | glUniform1i(location, 0); 771 | } else { 772 | location = glGetUniformLocation(programId, "u_isTextured"); 773 | glUniform1i(location, 0); 774 | } 775 | 776 | glEnable(GL_STENCIL_TEST); 777 | { 778 | glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 779 | glStencilFunc(GL_ALWAYS, 0, 1); 780 | glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); 781 | glStencilMask(1); 782 | 783 | glDisable(GL_DEPTH_TEST); 784 | glBindVertexArray(ptsVaoId); 785 | glDrawArrays(GL_TRIANGLE_FAN, 0, samplePoints.size()); 786 | glBindVertexArray(0); 787 | glEnable(GL_DEPTH_TEST); 788 | 789 | glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); 790 | glStencilFunc(GL_EQUAL, 1, 1); 791 | glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); 792 | 793 | glBindVertexArray(vaoId); 794 | glDrawElements(GL_TRIANGLES, bufferSize, GL_UNSIGNED_INT, 0); 795 | glBindVertexArray(0); 796 | } 797 | glDisable(GL_STENCIL_TEST); 798 | 799 | glUseProgram(0); 800 | } 801 | -------------------------------------------------------------------------------- /src/bezierLight.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "render.h" 7 | 8 | static constexpr int NUM_CPS_IN_CURVE = 4; 9 | static constexpr int COEFF_DIV = 1024; 10 | 11 | enum LightType { 12 | ONE = 0, 13 | TWO = 1, 14 | THREE = 2, 15 | FOUR = 3, 16 | CAVITY = 4, 17 | CLIP = 5, 18 | QUAD = 6, 19 | CHAR = 7, 20 | }; 21 | 22 | struct BezierLight : public RenderObject { 23 | void initialize(); 24 | void createCPSmodel(LightType); 25 | void calcCPSworld(); 26 | glm::vec3 bezierCurve(const int curve, const float t); 27 | 28 | void gaussianFilter(std::vector> &kernel, int kernelSize, float sigma); 29 | void createBezLightTex(const std::string &filename); 30 | 31 | void compBernCoeffs(); 32 | void createBernCoeffTex(); 33 | void drawBez(const Camera &camera); 34 | 35 | int numPoints; 36 | int numCurves; 37 | std::vector cpsModel; 38 | std::vector cpsWorld; 39 | std::vector samplePoints; 40 | std::array bernCoeffs; 41 | 42 | GLuint ptsVaoId; 43 | GLuint ptsVboId; 44 | 45 | glm::vec3 Le; 46 | glm::vec3 center; 47 | glm::vec2 size; 48 | glm::vec3 rotAngle; 49 | glm::vec3 translate; 50 | bool isTwoSided; 51 | bool isMove; 52 | bool isBezTexed; 53 | 54 | int texHeight; 55 | int texWidth; 56 | int marginSize; 57 | int maxLOD; 58 | 59 | GLuint bezLightTexId; 60 | GLuint bernCoeffTexId; 61 | 62 | glm::mat4 rotateX(float ax); 63 | glm::mat4 rotateY(float ay); 64 | glm::mat4 rotateZ(float az); 65 | glm::mat4 rotateXYZ(glm::vec3 rotAngle); 66 | glm::vec3 degToRad(glm::vec3 rotAngle); 67 | }; 68 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // ---------------------------------------------------------------------------- 4 | // Parameter constants 5 | // ---------------------------------------------------------------------------- 6 | 7 | static constexpr double Pi = 3.141592653589793; 8 | static constexpr double InvPi = 0.3183098861837907; 9 | 10 | // ----------------------------------------------------------------------------- 11 | // Assertion with message 12 | // ----------------------------------------------------------------------------- 13 | 14 | #ifndef __FUNCTION_NAME__ 15 | # if defined(_WIN32) || defined(__WIN32__) 16 | # define __FUNCTION_NAME__ __FUNCTION__ 17 | # else 18 | # define __FUNCTION_NAME__ __func__ 19 | # endif 20 | #endif 21 | 22 | #undef NDEBUG 23 | #ifdef PY_VERSION_HEX 24 | # define Assertion(PREDICATE, ...) \ 25 | do { \ 26 | if (!(PREDICATE)) { \ 27 | std::stringstream ss; \ 28 | ss << "Asssertion \"" \ 29 | << #PREDICATE << "\" failed in " << __FILE__ \ 30 | << " line " << __LINE__ \ 31 | << " in function \"" << (__FUNCTION_NAME__) << "\"" \ 32 | << " : "; \ 33 | throw std::runtime_error(ss.str()); \ 34 | } \ 35 | } while (false) 36 | #elif !defined(NDEBUG) 37 | # define Assertion(PREDICATE, ...) \ 38 | do { \ 39 | if (!(PREDICATE)) { \ 40 | std::cerr << "Asssertion \"" \ 41 | << #PREDICATE << "\" failed in " << __FILE__ \ 42 | << " line " << __LINE__ \ 43 | << " in function \"" << (__FUNCTION_NAME__) << "\"" \ 44 | << " : "; \ 45 | fprintf(stderr, __VA_ARGS__); \ 46 | std::cerr << std::endl; \ 47 | std::abort(); \ 48 | } \ 49 | } while (false) 50 | #else // NDEBUG 51 | # define Assertion(PREDICATE, ...) \ 52 | do { \ 53 | } while (false) 54 | #endif // NDEBUG 55 | 56 | // ----------------------------------------------------------------------------- 57 | // Message handlers 58 | // ----------------------------------------------------------------------------- 59 | 60 | #ifndef NDEBUG 61 | # define Info(...) \ 62 | do { \ 63 | std::cout << "[ INFO ] "; \ 64 | fprintf(stdout, __VA_ARGS__); \ 65 | std::cerr << std::endl; \ 66 | } while (false); 67 | # define Warning(...) \ 68 | do { \ 69 | std::cerr << "[WARNING] "; \ 70 | fprintf(stdout, __VA_ARGS__); \ 71 | std::cerr << std::endl; \ 72 | } while (false); 73 | #else 74 | # define Info(...) 75 | # define Warning(...) 76 | #endif 77 | 78 | #define Error(...) \ 79 | do { \ 80 | std::cerr << "[ ERROR ] "; \ 81 | fprintf(stderr, __VA_ARGS__); \ 82 | std::cerr << std::endl; \ 83 | std::abort(); \ 84 | } while (false); 85 | -------------------------------------------------------------------------------- /src/constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static const int WIN_WIDTH = 1024; 6 | static const int WIN_HEIGHT = 1024; 7 | static const char *WIN_TITLE = "Bezier Area Light"; 8 | 9 | static const std::string PLANE_OBJ = "data/plane.obj"; 10 | static const std::string SMALLPLANE_OBJ = "data/small_plane.obj"; 11 | 12 | static const std::string FLOORLTC_SHADER = "shaders/floorLTC"; 13 | static const std::string BEZLIGHT_SHADER = "shaders/bezierLight"; 14 | 15 | static const std::string GRADATION_PNG = "data/gradation_squares.png"; 16 | static const std::string CAVITY_PNG = "data/clipped_cavity_small.png"; // for debugging only, apply to square (QUAD) light 17 | static const std::string ROUGHNESS_TEASER_PNG = "data/roughness_teaser.png"; 18 | -------------------------------------------------------------------------------- /src/glad/LICENSE: -------------------------------------------------------------------------------- 1 | The glad source code: 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2013-2021 David Herberth 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | 25 | The Khronos Specifications: 26 | 27 | Copyright (c) 2013-2020 The Khronos Group Inc. 28 | 29 | Licensed under the Apache License, Version 2.0 (the "License"); 30 | you may not use this file except in compliance with the License. 31 | You may obtain a copy of the License at 32 | 33 | http://www.apache.org/licenses/LICENSE-2.0 34 | 35 | Unless required by applicable law or agreed to in writing, software 36 | distributed under the License is distributed on an "AS IS" BASIS, 37 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 38 | See the License for the specific language governing permissions and 39 | limitations under the License. 40 | 41 | 42 | The EGL Specification and various headers: 43 | 44 | Copyright (c) 2007-2016 The Khronos Group Inc. 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining a 47 | copy of this software and/or associated documentation files (the 48 | "Materials"), to deal in the Materials without restriction, including 49 | without limitation the rights to use, copy, modify, merge, publish, 50 | distribute, sublicense, and/or sell copies of the Materials, and to 51 | permit persons to whom the Materials are furnished to do so, subject to 52 | the following conditions: 53 | 54 | The above copyright notice and this permission notice shall be included 55 | in all copies or substantial portions of the Materials. 56 | 57 | THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 58 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 59 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 60 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 61 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 62 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 63 | MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 64 | -------------------------------------------------------------------------------- /src/imgui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2021 Omar Cornut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec( dllexport ) 27 | //#define IMGUI_API __declspec( dllimport ) 28 | 29 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 30 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 31 | 32 | //---- Disable all of Dear ImGui or don't implement standard windows. 33 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 34 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 35 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 36 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger window: ShowMetricsWindow() will be empty. 37 | 38 | //---- Don't implement some functions to reduce linkage requirements. 39 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. (imm32.lib/.a) 41 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 42 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 43 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 44 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 45 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 46 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 47 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 48 | 49 | //---- Include imgui_user.h at the end of imgui.h as a convenience 50 | //#define IMGUI_INCLUDE_IMGUI_USER_H 51 | 52 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 53 | //#define IMGUI_USE_BGRA_PACKED_COLOR 54 | 55 | //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 56 | //#define IMGUI_USE_WCHAR32 57 | 58 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 59 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 60 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 61 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 62 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 63 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 64 | 65 | //---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 66 | // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 67 | // #define IMGUI_USE_STB_SPRINTF 68 | 69 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 70 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 71 | // On Windows you may use vcpkg with 'vcpkg install freetype' + 'vcpkg integrate install'. 72 | //#define IMGUI_ENABLE_FREETYPE 73 | 74 | //---- Use stb_truetype to build and rasterize the font atlas (default) 75 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 76 | //#define IMGUI_ENABLE_STB_TRUETYPE 77 | 78 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 79 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 80 | /* 81 | #define IM_VEC2_CLASS_EXTRA \ 82 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 83 | operator MyVec2() const { return MyVec2(x,y); } 84 | 85 | #define IM_VEC4_CLASS_EXTRA \ 86 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 87 | operator MyVec4() const { return MyVec4(x,y,z,w); } 88 | */ 89 | 90 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 91 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 92 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 93 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 94 | //#define ImDrawIdx unsigned int 95 | 96 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 97 | //struct ImDrawList; 98 | //struct ImDrawCmd; 99 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 100 | //#define ImDrawCallback MyImDrawCallback 101 | 102 | //---- Debug Tools: Macro to break in Debugger 103 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 104 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 105 | //#define IM_DEBUG_BREAK __debugbreak() 106 | 107 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 108 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 109 | // This adds a small runtime cost which is why it is not enabled by default. 110 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 111 | 112 | //---- Debug Tools: Enable slower asserts 113 | //#define IMGUI_DEBUG_PARANOID 114 | 115 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 116 | /* 117 | namespace ImGui 118 | { 119 | void MyFunction(const char* name, const MyMatrix44& v); 120 | } 121 | */ 122 | -------------------------------------------------------------------------------- /src/imgui/imgui_impl_glfw.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | // (Requires: GLFW 3.1+) 5 | 6 | // Implemented features: 7 | // [X] Platform: Clipboard support. 8 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+). 10 | // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). 11 | 12 | // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 13 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 14 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 15 | 16 | // CHANGELOG 17 | // (minor and older changes stripped away, please see git history for details) 18 | // 2020-01-17: Inputs: Disable error callback while assigning mouse cursors because some X11 setup don't have them and it generates errors. 19 | // 2019-12-05: Inputs: Added support for new mouse cursors added in GLFW 3.4+ (resizing cursors, not allowed cursor). 20 | // 2019-10-18: Misc: Previously installed user callbacks are now restored on shutdown. 21 | // 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter. 22 | // 2019-05-11: Inputs: Don't filter value from character callback before calling AddInputCharacter(). 23 | // 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized. 24 | // 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window. 25 | // 2018-11-07: Inputs: When installing our GLFW callbacks, we save user's previously installed ones - if any - and chain call them. 26 | // 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls. 27 | // 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor. 28 | // 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. 29 | // 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. 30 | // 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). 31 | // 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. 32 | // 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. 33 | // 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. 34 | // 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). 35 | // 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. 36 | // 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. 37 | // 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). 38 | // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. 39 | 40 | #include "imgui.h" 41 | #include "imgui_impl_glfw.h" 42 | 43 | // GLFW 44 | #include 45 | #ifdef _WIN32 46 | #undef APIENTRY 47 | #define GLFW_EXPOSE_NATIVE_WIN32 48 | #include // for glfwGetWin32Window 49 | #endif 50 | #define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING 51 | #define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED 52 | #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity 53 | #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale 54 | #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface 55 | #ifdef GLFW_RESIZE_NESW_CURSOR // let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? 56 | #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR 57 | #else 58 | #define GLFW_HAS_NEW_CURSORS (0) 59 | #endif 60 | 61 | // Data 62 | enum GlfwClientApi 63 | { 64 | GlfwClientApi_Unknown, 65 | GlfwClientApi_OpenGL, 66 | GlfwClientApi_Vulkan 67 | }; 68 | static GLFWwindow* g_Window = NULL; // Main window 69 | static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; 70 | static double g_Time = 0.0; 71 | static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; 72 | static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; 73 | static bool g_InstalledCallbacks = false; 74 | 75 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 76 | static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; 77 | static GLFWscrollfun g_PrevUserCallbackScroll = NULL; 78 | static GLFWkeyfun g_PrevUserCallbackKey = NULL; 79 | static GLFWcharfun g_PrevUserCallbackChar = NULL; 80 | 81 | static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) 82 | { 83 | return glfwGetClipboardString((GLFWwindow*)user_data); 84 | } 85 | 86 | static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) 87 | { 88 | glfwSetClipboardString((GLFWwindow*)user_data, text); 89 | } 90 | 91 | void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) 92 | { 93 | if (g_PrevUserCallbackMousebutton != NULL) 94 | g_PrevUserCallbackMousebutton(window, button, action, mods); 95 | 96 | if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) 97 | g_MouseJustPressed[button] = true; 98 | } 99 | 100 | void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) 101 | { 102 | if (g_PrevUserCallbackScroll != NULL) 103 | g_PrevUserCallbackScroll(window, xoffset, yoffset); 104 | 105 | ImGuiIO& io = ImGui::GetIO(); 106 | io.MouseWheelH += (float)xoffset; 107 | io.MouseWheel += (float)yoffset; 108 | } 109 | 110 | void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) 111 | { 112 | if (g_PrevUserCallbackKey != NULL) 113 | g_PrevUserCallbackKey(window, key, scancode, action, mods); 114 | 115 | ImGuiIO& io = ImGui::GetIO(); 116 | if (key >= 0 && key < IM_ARRAYSIZE(io.KeysDown)) 117 | { 118 | if (action == GLFW_PRESS) 119 | io.KeysDown[key] = true; 120 | if (action == GLFW_RELEASE) 121 | io.KeysDown[key] = false; 122 | } 123 | 124 | // Modifiers are not reliable across systems 125 | io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; 126 | io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; 127 | io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; 128 | #ifdef _WIN32 129 | io.KeySuper = false; 130 | #else 131 | io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; 132 | #endif 133 | } 134 | 135 | void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) 136 | { 137 | if (g_PrevUserCallbackChar != NULL) 138 | g_PrevUserCallbackChar(window, c); 139 | 140 | ImGuiIO& io = ImGui::GetIO(); 141 | io.AddInputCharacter(c); 142 | } 143 | 144 | static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) 145 | { 146 | g_Window = window; 147 | g_Time = 0.0; 148 | 149 | // Setup backend capabilities flags 150 | ImGuiIO& io = ImGui::GetIO(); 151 | io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) 152 | io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) 153 | io.BackendPlatformName = "imgui_impl_glfw"; 154 | 155 | // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. 156 | io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; 157 | io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; 158 | io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; 159 | io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; 160 | io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; 161 | io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; 162 | io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; 163 | io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; 164 | io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; 165 | io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; 166 | io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; 167 | io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; 168 | io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; 169 | io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; 170 | io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; 171 | io.KeyMap[ImGuiKey_KeyPadEnter] = GLFW_KEY_KP_ENTER; 172 | io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; 173 | io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; 174 | io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; 175 | io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; 176 | io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; 177 | io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; 178 | 179 | io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; 180 | io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; 181 | io.ClipboardUserData = g_Window; 182 | #if defined(_WIN32) 183 | io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); 184 | #endif 185 | 186 | // Create mouse cursors 187 | // (By design, on X11 cursors are user configurable and some cursors may be missing. When a cursor doesn't exist, 188 | // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. 189 | // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) 190 | GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL); 191 | g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 192 | g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); 193 | g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); 194 | g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); 195 | g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); 196 | #if GLFW_HAS_NEW_CURSORS 197 | g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); 198 | g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); 199 | g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); 200 | g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); 201 | #else 202 | g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 203 | g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 204 | g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 205 | g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); 206 | #endif 207 | glfwSetErrorCallback(prev_error_callback); 208 | 209 | // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. 210 | g_PrevUserCallbackMousebutton = NULL; 211 | g_PrevUserCallbackScroll = NULL; 212 | g_PrevUserCallbackKey = NULL; 213 | g_PrevUserCallbackChar = NULL; 214 | if (install_callbacks) 215 | { 216 | g_InstalledCallbacks = true; 217 | g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); 218 | g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); 219 | g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); 220 | g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); 221 | } 222 | 223 | g_ClientApi = client_api; 224 | return true; 225 | } 226 | 227 | bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) 228 | { 229 | return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); 230 | } 231 | 232 | bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) 233 | { 234 | return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); 235 | } 236 | 237 | bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) 238 | { 239 | return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); 240 | } 241 | 242 | void ImGui_ImplGlfw_Shutdown() 243 | { 244 | if (g_InstalledCallbacks) 245 | { 246 | glfwSetMouseButtonCallback(g_Window, g_PrevUserCallbackMousebutton); 247 | glfwSetScrollCallback(g_Window, g_PrevUserCallbackScroll); 248 | glfwSetKeyCallback(g_Window, g_PrevUserCallbackKey); 249 | glfwSetCharCallback(g_Window, g_PrevUserCallbackChar); 250 | g_InstalledCallbacks = false; 251 | } 252 | 253 | for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) 254 | { 255 | glfwDestroyCursor(g_MouseCursors[cursor_n]); 256 | g_MouseCursors[cursor_n] = NULL; 257 | } 258 | g_ClientApi = GlfwClientApi_Unknown; 259 | } 260 | 261 | static void ImGui_ImplGlfw_UpdateMousePosAndButtons() 262 | { 263 | // Update buttons 264 | ImGuiIO& io = ImGui::GetIO(); 265 | for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) 266 | { 267 | // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. 268 | io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; 269 | g_MouseJustPressed[i] = false; 270 | } 271 | 272 | // Update mouse position 273 | const ImVec2 mouse_pos_backup = io.MousePos; 274 | io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); 275 | #ifdef __EMSCRIPTEN__ 276 | const bool focused = true; // Emscripten 277 | #else 278 | const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; 279 | #endif 280 | if (focused) 281 | { 282 | if (io.WantSetMousePos) 283 | { 284 | glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); 285 | } 286 | else 287 | { 288 | double mouse_x, mouse_y; 289 | glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); 290 | io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); 291 | } 292 | } 293 | } 294 | 295 | static void ImGui_ImplGlfw_UpdateMouseCursor() 296 | { 297 | ImGuiIO& io = ImGui::GetIO(); 298 | if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) 299 | return; 300 | 301 | ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); 302 | if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) 303 | { 304 | // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor 305 | glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); 306 | } 307 | else 308 | { 309 | // Show OS mouse cursor 310 | // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. 311 | glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); 312 | glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); 313 | } 314 | } 315 | 316 | static void ImGui_ImplGlfw_UpdateGamepads() 317 | { 318 | ImGuiIO& io = ImGui::GetIO(); 319 | memset(io.NavInputs, 0, sizeof(io.NavInputs)); 320 | if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) 321 | return; 322 | 323 | // Update gamepad inputs 324 | #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } 325 | #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } 326 | int axes_count = 0, buttons_count = 0; 327 | const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); 328 | const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); 329 | MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A 330 | MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B 331 | MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X 332 | MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y 333 | MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left 334 | MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right 335 | MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up 336 | MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down 337 | MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB 338 | MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB 339 | MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB 340 | MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB 341 | MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); 342 | MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); 343 | MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); 344 | MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); 345 | #undef MAP_BUTTON 346 | #undef MAP_ANALOG 347 | if (axes_count > 0 && buttons_count > 0) 348 | io.BackendFlags |= ImGuiBackendFlags_HasGamepad; 349 | else 350 | io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; 351 | } 352 | 353 | void ImGui_ImplGlfw_NewFrame() 354 | { 355 | ImGuiIO& io = ImGui::GetIO(); 356 | IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); 357 | 358 | // Setup display size (every frame to accommodate for window resizing) 359 | int w, h; 360 | int display_w, display_h; 361 | glfwGetWindowSize(g_Window, &w, &h); 362 | glfwGetFramebufferSize(g_Window, &display_w, &display_h); 363 | io.DisplaySize = ImVec2((float)w, (float)h); 364 | if (w > 0 && h > 0) 365 | io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); 366 | 367 | // Setup time step 368 | double current_time = glfwGetTime(); 369 | io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); 370 | g_Time = current_time; 371 | 372 | ImGui_ImplGlfw_UpdateMousePosAndButtons(); 373 | ImGui_ImplGlfw_UpdateMouseCursor(); 374 | 375 | // Update game controllers (if enabled and available) 376 | ImGui_ImplGlfw_UpdateGamepads(); 377 | } 378 | -------------------------------------------------------------------------------- /src/imgui/imgui_impl_glfw.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for GLFW 2 | // This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..) 3 | // (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) 4 | 5 | // Implemented features: 6 | // [X] Platform: Clipboard support. 7 | // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 8 | // [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. 9 | // [X] Platform: Keyboard arrays indexed using GLFW_KEY_* codes, e.g. ImGui::IsKeyPressed(GLFW_KEY_SPACE). 10 | 11 | // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 12 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 13 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 14 | 15 | // About GLSL version: 16 | // The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. 17 | // Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! 18 | 19 | #pragma once 20 | #include "imgui.h" // IMGUI_IMPL_API 21 | 22 | struct GLFWwindow; 23 | 24 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); 25 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); 26 | IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); 27 | IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); 28 | IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); 29 | 30 | // GLFW callbacks 31 | // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any. 32 | // - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call those function yourself from your own GLFW callbacks. 33 | IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); 34 | IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); 35 | IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); 36 | IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); 37 | -------------------------------------------------------------------------------- /src/imgui/imgui_impl_opengl3.cpp: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline 2 | // - Desktop GL: 2.x 3.x 4.x 3 | // - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) 4 | // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) 5 | 6 | // Implemented features: 7 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! 8 | // [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. 9 | 10 | // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 11 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 12 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 13 | 14 | // CHANGELOG 15 | // (minor and older changes stripped away, please see git history for details) 16 | // 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version. 17 | // 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) 18 | // 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater. 19 | // 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer. 20 | // 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state. 21 | // 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state. 22 | // 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x) 23 | // 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader. 24 | // 2020-07-10: OpenGL: Added support for glad2 OpenGL loader. 25 | // 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX. 26 | // 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix. 27 | // 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset. 28 | // 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader. 29 | // 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader. 30 | // 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders. 31 | // 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility. 32 | // 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call. 33 | // 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. 34 | // 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. 35 | // 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop. 36 | // 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early. 37 | // 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0). 38 | // 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader. 39 | // 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display. 40 | // 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450). 41 | // 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. 42 | // 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN. 43 | // 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used. 44 | // 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES". 45 | // 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation. 46 | // 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link. 47 | // 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. 48 | // 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. 49 | // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. 50 | // 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. 51 | // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". 52 | // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. 53 | // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. 54 | // 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. 55 | // 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. 56 | // 2017-05-01: OpenGL: Fixed save and restore of current blend func state. 57 | // 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. 58 | // 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. 59 | // 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) 60 | 61 | //---------------------------------------- 62 | // OpenGL GLSL GLSL 63 | // version version string 64 | //---------------------------------------- 65 | // 2.0 110 "#version 110" 66 | // 2.1 120 "#version 120" 67 | // 3.0 130 "#version 130" 68 | // 3.1 140 "#version 140" 69 | // 3.2 150 "#version 150" 70 | // 3.3 330 "#version 330 core" 71 | // 4.0 400 "#version 400 core" 72 | // 4.1 410 "#version 410 core" 73 | // 4.2 420 "#version 410 core" 74 | // 4.3 430 "#version 430 core" 75 | // ES 2.0 100 "#version 100" = WebGL 1.0 76 | // ES 3.0 300 "#version 300 es" = WebGL 2.0 77 | //---------------------------------------- 78 | 79 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 80 | #define _CRT_SECURE_NO_WARNINGS 81 | #endif 82 | 83 | #include "imgui.h" 84 | #include "imgui_impl_opengl3.h" 85 | #include 86 | #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier 87 | #include // intptr_t 88 | #else 89 | #include // intptr_t 90 | #endif 91 | 92 | // GL includes 93 | #if defined(IMGUI_IMPL_OPENGL_ES2) 94 | #include 95 | #elif defined(IMGUI_IMPL_OPENGL_ES3) 96 | #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) 97 | #include // Use GL ES 3 98 | #else 99 | #include // Use GL ES 3 100 | #endif 101 | #else 102 | // About Desktop OpenGL function loaders: 103 | // Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. 104 | // Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). 105 | // You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. 106 | #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) 107 | #include // Needs to be initialized with gl3wInit() in user's code 108 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) 109 | #include // Needs to be initialized with glewInit() in user's code. 110 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) 111 | #include // Needs to be initialized with gladLoadGL() in user's code. 112 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) 113 | #include // Needs to be initialized with gladLoadGL(...) or gladLoaderLoadGL() in user's code. 114 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) 115 | #ifndef GLFW_INCLUDE_NONE 116 | #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. 117 | #endif 118 | #include // Needs to be initialized with glbinding::Binding::initialize() in user's code. 119 | #include 120 | using namespace gl; 121 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) 122 | #ifndef GLFW_INCLUDE_NONE 123 | #define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors. 124 | #endif 125 | #include // Needs to be initialized with glbinding::initialize() in user's code. 126 | #include 127 | using namespace gl; 128 | #else 129 | #include IMGUI_IMPL_OPENGL_LOADER_CUSTOM 130 | #endif 131 | #endif 132 | 133 | // Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have. 134 | #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2) 135 | #define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 136 | #endif 137 | 138 | // Desktop GL 3.3+ has glBindSampler() 139 | #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_3) 140 | #define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER 141 | #endif 142 | 143 | // Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state 144 | #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1) 145 | #define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART 146 | #endif 147 | 148 | // Desktop GL use extension detection 149 | #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) 150 | #define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS 151 | #endif 152 | 153 | // OpenGL Data 154 | static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2) 155 | static char g_GlslVersionString[32] = ""; // Specified by user or detected based on compile time GL settings. 156 | static GLuint g_FontTexture = 0; 157 | static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; 158 | static GLint g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location 159 | static GLuint g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location 160 | static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; 161 | static bool g_HasClipOrigin = false; 162 | 163 | // Functions 164 | bool ImGui_ImplOpenGL3_Init(const char* glsl_version) 165 | { 166 | // Query for GL version (e.g. 320 for GL 3.2) 167 | #if !defined(IMGUI_IMPL_OPENGL_ES2) 168 | GLint major = 0; 169 | GLint minor = 0; 170 | glGetIntegerv(GL_MAJOR_VERSION, &major); 171 | glGetIntegerv(GL_MINOR_VERSION, &minor); 172 | if (major == 0 && minor == 0) 173 | { 174 | // Query GL_VERSION in desktop GL 2.x, the string will start with "." 175 | const char* gl_version = (const char*)glGetString(GL_VERSION); 176 | sscanf(gl_version, "%d.%d", &major, &minor); 177 | } 178 | g_GlVersion = (GLuint)(major * 100 + minor * 10); 179 | #else 180 | g_GlVersion = 200; // GLES 2 181 | #endif 182 | 183 | // Setup backend capabilities flags 184 | ImGuiIO& io = ImGui::GetIO(); 185 | io.BackendRendererName = "imgui_impl_opengl3"; 186 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 187 | if (g_GlVersion >= 320) 188 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. 189 | #endif 190 | 191 | // Store GLSL version string so we can refer to it later in case we recreate shaders. 192 | // Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. 193 | #if defined(IMGUI_IMPL_OPENGL_ES2) 194 | if (glsl_version == NULL) 195 | glsl_version = "#version 100"; 196 | #elif defined(IMGUI_IMPL_OPENGL_ES3) 197 | if (glsl_version == NULL) 198 | glsl_version = "#version 300 es"; 199 | #elif defined(__APPLE__) 200 | if (glsl_version == NULL) 201 | glsl_version = "#version 150"; 202 | #else 203 | if (glsl_version == NULL) 204 | glsl_version = "#version 130"; 205 | #endif 206 | IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString)); 207 | strcpy(g_GlslVersionString, glsl_version); 208 | strcat(g_GlslVersionString, "\n"); 209 | 210 | // Debugging construct to make it easily visible in the IDE and debugger which GL loader has been selected. 211 | // The code actually never uses the 'gl_loader' variable! It is only here so you can read it! 212 | // If auto-detection fails or doesn't select the same GL loader file as used by your application, 213 | // you are likely to get a crash below. 214 | // You can explicitly select a loader by using '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. 215 | const char* gl_loader = "Unknown"; 216 | IM_UNUSED(gl_loader); 217 | #if defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) 218 | gl_loader = "GL3W"; 219 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) 220 | gl_loader = "GLEW"; 221 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) 222 | gl_loader = "GLAD"; 223 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) 224 | gl_loader = "GLAD2"; 225 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) 226 | gl_loader = "glbinding2"; 227 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) 228 | gl_loader = "glbinding3"; 229 | #elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) 230 | gl_loader = "custom"; 231 | #else 232 | gl_loader = "none"; 233 | #endif 234 | 235 | // Make an arbitrary GL call (we don't actually need the result) 236 | // IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code. 237 | // Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above. 238 | GLint current_texture; 239 | glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_texture); 240 | 241 | // Detect extensions we support 242 | g_HasClipOrigin = (g_GlVersion >= 450); 243 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS 244 | GLint num_extensions = 0; 245 | glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions); 246 | for (GLint i = 0; i < num_extensions; i++) 247 | { 248 | const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); 249 | if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0) 250 | g_HasClipOrigin = true; 251 | } 252 | #endif 253 | 254 | return true; 255 | } 256 | 257 | void ImGui_ImplOpenGL3_Shutdown() 258 | { 259 | ImGui_ImplOpenGL3_DestroyDeviceObjects(); 260 | } 261 | 262 | void ImGui_ImplOpenGL3_NewFrame() 263 | { 264 | if (!g_ShaderHandle) 265 | ImGui_ImplOpenGL3_CreateDeviceObjects(); 266 | } 267 | 268 | static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object) 269 | { 270 | // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill 271 | glEnable(GL_BLEND); 272 | glBlendEquation(GL_FUNC_ADD); 273 | glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); 274 | glDisable(GL_CULL_FACE); 275 | glDisable(GL_DEPTH_TEST); 276 | glDisable(GL_STENCIL_TEST); 277 | glEnable(GL_SCISSOR_TEST); 278 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART 279 | if (g_GlVersion >= 310) 280 | glDisable(GL_PRIMITIVE_RESTART); 281 | #endif 282 | #ifdef GL_POLYGON_MODE 283 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 284 | #endif 285 | 286 | // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT) 287 | #if defined(GL_CLIP_ORIGIN) 288 | bool clip_origin_lower_left = true; 289 | if (g_HasClipOrigin) 290 | { 291 | GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)¤t_clip_origin); 292 | if (current_clip_origin == GL_UPPER_LEFT) 293 | clip_origin_lower_left = false; 294 | } 295 | #endif 296 | 297 | // Setup viewport, orthographic projection matrix 298 | // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. 299 | glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); 300 | float L = draw_data->DisplayPos.x; 301 | float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; 302 | float T = draw_data->DisplayPos.y; 303 | float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; 304 | #if defined(GL_CLIP_ORIGIN) 305 | if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left 306 | #endif 307 | const float ortho_projection[4][4] = 308 | { 309 | { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, 310 | { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, 311 | { 0.0f, 0.0f, -1.0f, 0.0f }, 312 | { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, 313 | }; 314 | glUseProgram(g_ShaderHandle); 315 | glUniform1i(g_AttribLocationTex, 0); 316 | glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); 317 | 318 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER 319 | if (g_GlVersion >= 330) 320 | glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. 321 | #endif 322 | 323 | (void)vertex_array_object; 324 | #ifndef IMGUI_IMPL_OPENGL_ES2 325 | glBindVertexArray(vertex_array_object); 326 | #endif 327 | 328 | // Bind vertex/index buffers and setup attributes for ImDrawVert 329 | glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); 330 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); 331 | glEnableVertexAttribArray(g_AttribLocationVtxPos); 332 | glEnableVertexAttribArray(g_AttribLocationVtxUV); 333 | glEnableVertexAttribArray(g_AttribLocationVtxColor); 334 | glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); 335 | glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); 336 | glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); 337 | } 338 | 339 | // OpenGL3 Render function. 340 | // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly. 341 | // This is in order to be able to run within an OpenGL engine that doesn't do so. 342 | void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) 343 | { 344 | // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) 345 | int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); 346 | int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); 347 | if (fb_width <= 0 || fb_height <= 0) 348 | return; 349 | 350 | // Backup GL state 351 | GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); 352 | glActiveTexture(GL_TEXTURE0); 353 | GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program); 354 | GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture); 355 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER 356 | GLuint last_sampler; if (g_GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; } 357 | #endif 358 | GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer); 359 | #ifndef IMGUI_IMPL_OPENGL_ES2 360 | GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object); 361 | #endif 362 | #ifdef GL_POLYGON_MODE 363 | GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); 364 | #endif 365 | GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); 366 | GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); 367 | GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); 368 | GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); 369 | GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); 370 | GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); 371 | GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); 372 | GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); 373 | GLboolean last_enable_blend = glIsEnabled(GL_BLEND); 374 | GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); 375 | GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); 376 | GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST); 377 | GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); 378 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART 379 | GLboolean last_enable_primitive_restart = (g_GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE; 380 | #endif 381 | 382 | // Setup desired GL state 383 | // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts) 384 | // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. 385 | GLuint vertex_array_object = 0; 386 | #ifndef IMGUI_IMPL_OPENGL_ES2 387 | glGenVertexArrays(1, &vertex_array_object); 388 | #endif 389 | ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); 390 | 391 | // Will project scissor/clipping rectangles into framebuffer space 392 | ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports 393 | ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) 394 | 395 | // Render command lists 396 | for (int n = 0; n < draw_data->CmdListsCount; n++) 397 | { 398 | const ImDrawList* cmd_list = draw_data->CmdLists[n]; 399 | 400 | // Upload vertex/index buffers 401 | glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); 402 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); 403 | 404 | for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) 405 | { 406 | const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; 407 | if (pcmd->UserCallback != NULL) 408 | { 409 | // User callback, registered via ImDrawList::AddCallback() 410 | // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) 411 | if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) 412 | ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); 413 | else 414 | pcmd->UserCallback(cmd_list, pcmd); 415 | } 416 | else 417 | { 418 | // Project scissor/clipping rectangles into framebuffer space 419 | ImVec4 clip_rect; 420 | clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x; 421 | clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y; 422 | clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x; 423 | clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y; 424 | 425 | if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) 426 | { 427 | // Apply scissor/clipping rectangle 428 | glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); 429 | 430 | // Bind texture, Draw 431 | glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); 432 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 433 | if (g_GlVersion >= 320) 434 | glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); 435 | else 436 | #endif 437 | glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); 438 | } 439 | } 440 | } 441 | } 442 | 443 | // Destroy the temporary VAO 444 | #ifndef IMGUI_IMPL_OPENGL_ES2 445 | glDeleteVertexArrays(1, &vertex_array_object); 446 | #endif 447 | 448 | // Restore modified GL state 449 | glUseProgram(last_program); 450 | glBindTexture(GL_TEXTURE_2D, last_texture); 451 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER 452 | if (g_GlVersion >= 330) 453 | glBindSampler(0, last_sampler); 454 | #endif 455 | glActiveTexture(last_active_texture); 456 | #ifndef IMGUI_IMPL_OPENGL_ES2 457 | glBindVertexArray(last_vertex_array_object); 458 | #endif 459 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 460 | glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); 461 | glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); 462 | if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); 463 | if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); 464 | if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); 465 | if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST); 466 | if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); 467 | #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART 468 | if (g_GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); } 469 | #endif 470 | 471 | #ifdef GL_POLYGON_MODE 472 | glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); 473 | #endif 474 | glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); 475 | glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); 476 | } 477 | 478 | bool ImGui_ImplOpenGL3_CreateFontsTexture() 479 | { 480 | // Build texture atlas 481 | ImGuiIO& io = ImGui::GetIO(); 482 | unsigned char* pixels; 483 | int width, height; 484 | io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. 485 | 486 | // Upload texture to graphics system 487 | GLint last_texture; 488 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 489 | glGenTextures(1, &g_FontTexture); 490 | glBindTexture(GL_TEXTURE_2D, g_FontTexture); 491 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 492 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 493 | #ifdef GL_UNPACK_ROW_LENGTH 494 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 495 | #endif 496 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); 497 | 498 | // Store our identifier 499 | io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture); 500 | 501 | // Restore state 502 | glBindTexture(GL_TEXTURE_2D, last_texture); 503 | 504 | return true; 505 | } 506 | 507 | void ImGui_ImplOpenGL3_DestroyFontsTexture() 508 | { 509 | if (g_FontTexture) 510 | { 511 | ImGuiIO& io = ImGui::GetIO(); 512 | glDeleteTextures(1, &g_FontTexture); 513 | io.Fonts->SetTexID(0); 514 | g_FontTexture = 0; 515 | } 516 | } 517 | 518 | // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file. 519 | static bool CheckShader(GLuint handle, const char* desc) 520 | { 521 | GLint status = 0, log_length = 0; 522 | glGetShaderiv(handle, GL_COMPILE_STATUS, &status); 523 | glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length); 524 | if ((GLboolean)status == GL_FALSE) 525 | fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc); 526 | if (log_length > 1) 527 | { 528 | ImVector buf; 529 | buf.resize((int)(log_length + 1)); 530 | glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); 531 | fprintf(stderr, "%s\n", buf.begin()); 532 | } 533 | return (GLboolean)status == GL_TRUE; 534 | } 535 | 536 | // If you get an error please report on GitHub. You may try different GL context version or GLSL version. 537 | static bool CheckProgram(GLuint handle, const char* desc) 538 | { 539 | GLint status = 0, log_length = 0; 540 | glGetProgramiv(handle, GL_LINK_STATUS, &status); 541 | glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length); 542 | if ((GLboolean)status == GL_FALSE) 543 | fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString); 544 | if (log_length > 1) 545 | { 546 | ImVector buf; 547 | buf.resize((int)(log_length + 1)); 548 | glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); 549 | fprintf(stderr, "%s\n", buf.begin()); 550 | } 551 | return (GLboolean)status == GL_TRUE; 552 | } 553 | 554 | bool ImGui_ImplOpenGL3_CreateDeviceObjects() 555 | { 556 | // Backup GL state 557 | GLint last_texture, last_array_buffer; 558 | glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); 559 | glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); 560 | #ifndef IMGUI_IMPL_OPENGL_ES2 561 | GLint last_vertex_array; 562 | glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); 563 | #endif 564 | 565 | // Parse GLSL version string 566 | int glsl_version = 130; 567 | sscanf(g_GlslVersionString, "#version %d", &glsl_version); 568 | 569 | const GLchar* vertex_shader_glsl_120 = 570 | "uniform mat4 ProjMtx;\n" 571 | "attribute vec2 Position;\n" 572 | "attribute vec2 UV;\n" 573 | "attribute vec4 Color;\n" 574 | "varying vec2 Frag_UV;\n" 575 | "varying vec4 Frag_Color;\n" 576 | "void main()\n" 577 | "{\n" 578 | " Frag_UV = UV;\n" 579 | " Frag_Color = Color;\n" 580 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 581 | "}\n"; 582 | 583 | const GLchar* vertex_shader_glsl_130 = 584 | "uniform mat4 ProjMtx;\n" 585 | "in vec2 Position;\n" 586 | "in vec2 UV;\n" 587 | "in vec4 Color;\n" 588 | "out vec2 Frag_UV;\n" 589 | "out vec4 Frag_Color;\n" 590 | "void main()\n" 591 | "{\n" 592 | " Frag_UV = UV;\n" 593 | " Frag_Color = Color;\n" 594 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 595 | "}\n"; 596 | 597 | const GLchar* vertex_shader_glsl_300_es = 598 | "precision mediump float;\n" 599 | "layout (location = 0) in vec2 Position;\n" 600 | "layout (location = 1) in vec2 UV;\n" 601 | "layout (location = 2) in vec4 Color;\n" 602 | "uniform mat4 ProjMtx;\n" 603 | "out vec2 Frag_UV;\n" 604 | "out vec4 Frag_Color;\n" 605 | "void main()\n" 606 | "{\n" 607 | " Frag_UV = UV;\n" 608 | " Frag_Color = Color;\n" 609 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 610 | "}\n"; 611 | 612 | const GLchar* vertex_shader_glsl_410_core = 613 | "layout (location = 0) in vec2 Position;\n" 614 | "layout (location = 1) in vec2 UV;\n" 615 | "layout (location = 2) in vec4 Color;\n" 616 | "uniform mat4 ProjMtx;\n" 617 | "out vec2 Frag_UV;\n" 618 | "out vec4 Frag_Color;\n" 619 | "void main()\n" 620 | "{\n" 621 | " Frag_UV = UV;\n" 622 | " Frag_Color = Color;\n" 623 | " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" 624 | "}\n"; 625 | 626 | const GLchar* fragment_shader_glsl_120 = 627 | "#ifdef GL_ES\n" 628 | " precision mediump float;\n" 629 | "#endif\n" 630 | "uniform sampler2D Texture;\n" 631 | "varying vec2 Frag_UV;\n" 632 | "varying vec4 Frag_Color;\n" 633 | "void main()\n" 634 | "{\n" 635 | " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n" 636 | "}\n"; 637 | 638 | const GLchar* fragment_shader_glsl_130 = 639 | "uniform sampler2D Texture;\n" 640 | "in vec2 Frag_UV;\n" 641 | "in vec4 Frag_Color;\n" 642 | "out vec4 Out_Color;\n" 643 | "void main()\n" 644 | "{\n" 645 | " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 646 | "}\n"; 647 | 648 | const GLchar* fragment_shader_glsl_300_es = 649 | "precision mediump float;\n" 650 | "uniform sampler2D Texture;\n" 651 | "in vec2 Frag_UV;\n" 652 | "in vec4 Frag_Color;\n" 653 | "layout (location = 0) out vec4 Out_Color;\n" 654 | "void main()\n" 655 | "{\n" 656 | " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 657 | "}\n"; 658 | 659 | const GLchar* fragment_shader_glsl_410_core = 660 | "in vec2 Frag_UV;\n" 661 | "in vec4 Frag_Color;\n" 662 | "uniform sampler2D Texture;\n" 663 | "layout (location = 0) out vec4 Out_Color;\n" 664 | "void main()\n" 665 | "{\n" 666 | " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" 667 | "}\n"; 668 | 669 | // Select shaders matching our GLSL versions 670 | const GLchar* vertex_shader = NULL; 671 | const GLchar* fragment_shader = NULL; 672 | if (glsl_version < 130) 673 | { 674 | vertex_shader = vertex_shader_glsl_120; 675 | fragment_shader = fragment_shader_glsl_120; 676 | } 677 | else if (glsl_version >= 410) 678 | { 679 | vertex_shader = vertex_shader_glsl_410_core; 680 | fragment_shader = fragment_shader_glsl_410_core; 681 | } 682 | else if (glsl_version == 300) 683 | { 684 | vertex_shader = vertex_shader_glsl_300_es; 685 | fragment_shader = fragment_shader_glsl_300_es; 686 | } 687 | else 688 | { 689 | vertex_shader = vertex_shader_glsl_130; 690 | fragment_shader = fragment_shader_glsl_130; 691 | } 692 | 693 | // Create shaders 694 | const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader }; 695 | g_VertHandle = glCreateShader(GL_VERTEX_SHADER); 696 | glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL); 697 | glCompileShader(g_VertHandle); 698 | CheckShader(g_VertHandle, "vertex shader"); 699 | 700 | const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader }; 701 | g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER); 702 | glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL); 703 | glCompileShader(g_FragHandle); 704 | CheckShader(g_FragHandle, "fragment shader"); 705 | 706 | g_ShaderHandle = glCreateProgram(); 707 | glAttachShader(g_ShaderHandle, g_VertHandle); 708 | glAttachShader(g_ShaderHandle, g_FragHandle); 709 | glLinkProgram(g_ShaderHandle); 710 | CheckProgram(g_ShaderHandle, "shader program"); 711 | 712 | g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); 713 | g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); 714 | g_AttribLocationVtxPos = (GLuint)glGetAttribLocation(g_ShaderHandle, "Position"); 715 | g_AttribLocationVtxUV = (GLuint)glGetAttribLocation(g_ShaderHandle, "UV"); 716 | g_AttribLocationVtxColor = (GLuint)glGetAttribLocation(g_ShaderHandle, "Color"); 717 | 718 | // Create buffers 719 | glGenBuffers(1, &g_VboHandle); 720 | glGenBuffers(1, &g_ElementsHandle); 721 | 722 | ImGui_ImplOpenGL3_CreateFontsTexture(); 723 | 724 | // Restore modified GL state 725 | glBindTexture(GL_TEXTURE_2D, last_texture); 726 | glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); 727 | #ifndef IMGUI_IMPL_OPENGL_ES2 728 | glBindVertexArray(last_vertex_array); 729 | #endif 730 | 731 | return true; 732 | } 733 | 734 | void ImGui_ImplOpenGL3_DestroyDeviceObjects() 735 | { 736 | if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; } 737 | if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; } 738 | if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); } 739 | if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); } 740 | if (g_VertHandle) { glDeleteShader(g_VertHandle); g_VertHandle = 0; } 741 | if (g_FragHandle) { glDeleteShader(g_FragHandle); g_FragHandle = 0; } 742 | if (g_ShaderHandle) { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; } 743 | 744 | ImGui_ImplOpenGL3_DestroyFontsTexture(); 745 | } 746 | -------------------------------------------------------------------------------- /src/imgui/imgui_impl_opengl3.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline 2 | // - Desktop GL: 2.x 3.x 4.x 3 | // - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) 4 | // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) 5 | 6 | // Implemented features: 7 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! 8 | // [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. 9 | 10 | // You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 11 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 12 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 13 | 14 | // About Desktop OpenGL function loaders: 15 | // Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers. 16 | // Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad). 17 | // You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own. 18 | 19 | // About GLSL version: 20 | // The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. 21 | // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" 22 | // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. 23 | 24 | #pragma once 25 | #include "imgui.h" // IMGUI_IMPL_API 26 | 27 | // Backend API 28 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); 29 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); 30 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); 31 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); 32 | 33 | // (Optional) Called by Init/NewFrame/Shutdown 34 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); 35 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); 36 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); 37 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); 38 | 39 | // Specific OpenGL ES versions 40 | //#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten 41 | //#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android 42 | 43 | // Attempt to auto-detect the default Desktop GL loader based on available header files. 44 | // If auto-detection fails or doesn't select the same GL loader file as used by your application, 45 | // you are likely to get a crash in ImGui_ImplOpenGL3_Init(). 46 | // You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. 47 | #if !defined(IMGUI_IMPL_OPENGL_ES2) \ 48 | && !defined(IMGUI_IMPL_OPENGL_ES3) \ 49 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \ 50 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \ 51 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \ 52 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) \ 53 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \ 54 | && !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \ 55 | && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) 56 | 57 | // Try to detect GLES on matching platforms 58 | #if defined(__APPLE__) 59 | #include "TargetConditionals.h" 60 | #endif 61 | #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) 62 | #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" 63 | #elif defined(__EMSCRIPTEN__) 64 | #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" 65 | 66 | // Otherwise try to detect supported Desktop OpenGL loaders.. 67 | #elif defined(__has_include) 68 | #if __has_include() 69 | #define IMGUI_IMPL_OPENGL_LOADER_GLEW 70 | #elif __has_include() 71 | #define IMGUI_IMPL_OPENGL_LOADER_GLAD 72 | #elif __has_include() 73 | #define IMGUI_IMPL_OPENGL_LOADER_GLAD2 74 | #elif __has_include() 75 | #define IMGUI_IMPL_OPENGL_LOADER_GL3W 76 | #elif __has_include() 77 | #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3 78 | #elif __has_include() 79 | #define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2 80 | #else 81 | #error "Cannot detect OpenGL loader!" 82 | #endif 83 | #else 84 | #define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository 85 | #endif 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/imgui/imstb_rectpack.h: -------------------------------------------------------------------------------- 1 | // [DEAR IMGUI] 2 | // This is a slightly modified version of stb_rect_pack.h 1.00. 3 | // Those changes would need to be pushed into nothings/stb: 4 | // - Added STBRP__CDECL 5 | // Grep for [DEAR IMGUI] to find the changes. 6 | 7 | // stb_rect_pack.h - v1.00 - public domain - rectangle packing 8 | // Sean Barrett 2014 9 | // 10 | // Useful for e.g. packing rectangular textures into an atlas. 11 | // Does not do rotation. 12 | // 13 | // Not necessarily the awesomest packing method, but better than 14 | // the totally naive one in stb_truetype (which is primarily what 15 | // this is meant to replace). 16 | // 17 | // Has only had a few tests run, may have issues. 18 | // 19 | // More docs to come. 20 | // 21 | // No memory allocations; uses qsort() and assert() from stdlib. 22 | // Can override those by defining STBRP_SORT and STBRP_ASSERT. 23 | // 24 | // This library currently uses the Skyline Bottom-Left algorithm. 25 | // 26 | // Please note: better rectangle packers are welcome! Please 27 | // implement them to the same API, but with a different init 28 | // function. 29 | // 30 | // Credits 31 | // 32 | // Library 33 | // Sean Barrett 34 | // Minor features 35 | // Martins Mozeiko 36 | // github:IntellectualKitty 37 | // 38 | // Bugfixes / warning fixes 39 | // Jeremy Jaussaud 40 | // Fabian Giesen 41 | // 42 | // Version history: 43 | // 44 | // 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles 45 | // 0.99 (2019-02-07) warning fixes 46 | // 0.11 (2017-03-03) return packing success/fail result 47 | // 0.10 (2016-10-25) remove cast-away-const to avoid warnings 48 | // 0.09 (2016-08-27) fix compiler warnings 49 | // 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) 50 | // 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) 51 | // 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort 52 | // 0.05: added STBRP_ASSERT to allow replacing assert 53 | // 0.04: fixed minor bug in STBRP_LARGE_RECTS support 54 | // 0.01: initial release 55 | // 56 | // LICENSE 57 | // 58 | // See end of file for license information. 59 | 60 | ////////////////////////////////////////////////////////////////////////////// 61 | // 62 | // INCLUDE SECTION 63 | // 64 | 65 | #ifndef STB_INCLUDE_STB_RECT_PACK_H 66 | #define STB_INCLUDE_STB_RECT_PACK_H 67 | 68 | #define STB_RECT_PACK_VERSION 1 69 | 70 | #ifdef STBRP_STATIC 71 | #define STBRP_DEF static 72 | #else 73 | #define STBRP_DEF extern 74 | #endif 75 | 76 | #ifdef __cplusplus 77 | extern "C" { 78 | #endif 79 | 80 | typedef struct stbrp_context stbrp_context; 81 | typedef struct stbrp_node stbrp_node; 82 | typedef struct stbrp_rect stbrp_rect; 83 | 84 | #ifdef STBRP_LARGE_RECTS 85 | typedef int stbrp_coord; 86 | #else 87 | typedef unsigned short stbrp_coord; 88 | #endif 89 | 90 | STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); 91 | // Assign packed locations to rectangles. The rectangles are of type 92 | // 'stbrp_rect' defined below, stored in the array 'rects', and there 93 | // are 'num_rects' many of them. 94 | // 95 | // Rectangles which are successfully packed have the 'was_packed' flag 96 | // set to a non-zero value and 'x' and 'y' store the minimum location 97 | // on each axis (i.e. bottom-left in cartesian coordinates, top-left 98 | // if you imagine y increasing downwards). Rectangles which do not fit 99 | // have the 'was_packed' flag set to 0. 100 | // 101 | // You should not try to access the 'rects' array from another thread 102 | // while this function is running, as the function temporarily reorders 103 | // the array while it executes. 104 | // 105 | // To pack into another rectangle, you need to call stbrp_init_target 106 | // again. To continue packing into the same rectangle, you can call 107 | // this function again. Calling this multiple times with multiple rect 108 | // arrays will probably produce worse packing results than calling it 109 | // a single time with the full rectangle array, but the option is 110 | // available. 111 | // 112 | // The function returns 1 if all of the rectangles were successfully 113 | // packed and 0 otherwise. 114 | 115 | struct stbrp_rect 116 | { 117 | // reserved for your use: 118 | int id; 119 | 120 | // input: 121 | stbrp_coord w, h; 122 | 123 | // output: 124 | stbrp_coord x, y; 125 | int was_packed; // non-zero if valid packing 126 | 127 | }; // 16 bytes, nominally 128 | 129 | 130 | STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); 131 | // Initialize a rectangle packer to: 132 | // pack a rectangle that is 'width' by 'height' in dimensions 133 | // using temporary storage provided by the array 'nodes', which is 'num_nodes' long 134 | // 135 | // You must call this function every time you start packing into a new target. 136 | // 137 | // There is no "shutdown" function. The 'nodes' memory must stay valid for 138 | // the following stbrp_pack_rects() call (or calls), but can be freed after 139 | // the call (or calls) finish. 140 | // 141 | // Note: to guarantee best results, either: 142 | // 1. make sure 'num_nodes' >= 'width' 143 | // or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' 144 | // 145 | // If you don't do either of the above things, widths will be quantized to multiples 146 | // of small integers to guarantee the algorithm doesn't run out of temporary storage. 147 | // 148 | // If you do #2, then the non-quantized algorithm will be used, but the algorithm 149 | // may run out of temporary storage and be unable to pack some rectangles. 150 | 151 | STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); 152 | // Optionally call this function after init but before doing any packing to 153 | // change the handling of the out-of-temp-memory scenario, described above. 154 | // If you call init again, this will be reset to the default (false). 155 | 156 | 157 | STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); 158 | // Optionally select which packing heuristic the library should use. Different 159 | // heuristics will produce better/worse results for different data sets. 160 | // If you call init again, this will be reset to the default. 161 | 162 | enum 163 | { 164 | STBRP_HEURISTIC_Skyline_default=0, 165 | STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, 166 | STBRP_HEURISTIC_Skyline_BF_sortHeight 167 | }; 168 | 169 | 170 | ////////////////////////////////////////////////////////////////////////////// 171 | // 172 | // the details of the following structures don't matter to you, but they must 173 | // be visible so you can handle the memory allocations for them 174 | 175 | struct stbrp_node 176 | { 177 | stbrp_coord x,y; 178 | stbrp_node *next; 179 | }; 180 | 181 | struct stbrp_context 182 | { 183 | int width; 184 | int height; 185 | int align; 186 | int init_mode; 187 | int heuristic; 188 | int num_nodes; 189 | stbrp_node *active_head; 190 | stbrp_node *free_head; 191 | stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' 192 | }; 193 | 194 | #ifdef __cplusplus 195 | } 196 | #endif 197 | 198 | #endif 199 | 200 | ////////////////////////////////////////////////////////////////////////////// 201 | // 202 | // IMPLEMENTATION SECTION 203 | // 204 | 205 | #ifdef STB_RECT_PACK_IMPLEMENTATION 206 | #ifndef STBRP_SORT 207 | #include 208 | #define STBRP_SORT qsort 209 | #endif 210 | 211 | #ifndef STBRP_ASSERT 212 | #include 213 | #define STBRP_ASSERT assert 214 | #endif 215 | 216 | // [DEAR IMGUI] Added STBRP__CDECL 217 | #ifdef _MSC_VER 218 | #define STBRP__NOTUSED(v) (void)(v) 219 | #define STBRP__CDECL __cdecl 220 | #else 221 | #define STBRP__NOTUSED(v) (void)sizeof(v) 222 | #define STBRP__CDECL 223 | #endif 224 | 225 | enum 226 | { 227 | STBRP__INIT_skyline = 1 228 | }; 229 | 230 | STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) 231 | { 232 | switch (context->init_mode) { 233 | case STBRP__INIT_skyline: 234 | STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); 235 | context->heuristic = heuristic; 236 | break; 237 | default: 238 | STBRP_ASSERT(0); 239 | } 240 | } 241 | 242 | STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) 243 | { 244 | if (allow_out_of_mem) 245 | // if it's ok to run out of memory, then don't bother aligning them; 246 | // this gives better packing, but may fail due to OOM (even though 247 | // the rectangles easily fit). @TODO a smarter approach would be to only 248 | // quantize once we've hit OOM, then we could get rid of this parameter. 249 | context->align = 1; 250 | else { 251 | // if it's not ok to run out of memory, then quantize the widths 252 | // so that num_nodes is always enough nodes. 253 | // 254 | // I.e. num_nodes * align >= width 255 | // align >= width / num_nodes 256 | // align = ceil(width/num_nodes) 257 | 258 | context->align = (context->width + context->num_nodes-1) / context->num_nodes; 259 | } 260 | } 261 | 262 | STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) 263 | { 264 | int i; 265 | #ifndef STBRP_LARGE_RECTS 266 | STBRP_ASSERT(width <= 0xffff && height <= 0xffff); 267 | #endif 268 | 269 | for (i=0; i < num_nodes-1; ++i) 270 | nodes[i].next = &nodes[i+1]; 271 | nodes[i].next = NULL; 272 | context->init_mode = STBRP__INIT_skyline; 273 | context->heuristic = STBRP_HEURISTIC_Skyline_default; 274 | context->free_head = &nodes[0]; 275 | context->active_head = &context->extra[0]; 276 | context->width = width; 277 | context->height = height; 278 | context->num_nodes = num_nodes; 279 | stbrp_setup_allow_out_of_mem(context, 0); 280 | 281 | // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) 282 | context->extra[0].x = 0; 283 | context->extra[0].y = 0; 284 | context->extra[0].next = &context->extra[1]; 285 | context->extra[1].x = (stbrp_coord) width; 286 | #ifdef STBRP_LARGE_RECTS 287 | context->extra[1].y = (1<<30); 288 | #else 289 | context->extra[1].y = 65535; 290 | #endif 291 | context->extra[1].next = NULL; 292 | } 293 | 294 | // find minimum y position if it starts at x1 295 | static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) 296 | { 297 | stbrp_node *node = first; 298 | int x1 = x0 + width; 299 | int min_y, visited_width, waste_area; 300 | 301 | STBRP__NOTUSED(c); 302 | 303 | STBRP_ASSERT(first->x <= x0); 304 | 305 | #if 0 306 | // skip in case we're past the node 307 | while (node->next->x <= x0) 308 | ++node; 309 | #else 310 | STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency 311 | #endif 312 | 313 | STBRP_ASSERT(node->x <= x0); 314 | 315 | min_y = 0; 316 | waste_area = 0; 317 | visited_width = 0; 318 | while (node->x < x1) { 319 | if (node->y > min_y) { 320 | // raise min_y higher. 321 | // we've accounted for all waste up to min_y, 322 | // but we'll now add more waste for everything we've visted 323 | waste_area += visited_width * (node->y - min_y); 324 | min_y = node->y; 325 | // the first time through, visited_width might be reduced 326 | if (node->x < x0) 327 | visited_width += node->next->x - x0; 328 | else 329 | visited_width += node->next->x - node->x; 330 | } else { 331 | // add waste area 332 | int under_width = node->next->x - node->x; 333 | if (under_width + visited_width > width) 334 | under_width = width - visited_width; 335 | waste_area += under_width * (min_y - node->y); 336 | visited_width += under_width; 337 | } 338 | node = node->next; 339 | } 340 | 341 | *pwaste = waste_area; 342 | return min_y; 343 | } 344 | 345 | typedef struct 346 | { 347 | int x,y; 348 | stbrp_node **prev_link; 349 | } stbrp__findresult; 350 | 351 | static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) 352 | { 353 | int best_waste = (1<<30), best_x, best_y = (1 << 30); 354 | stbrp__findresult fr; 355 | stbrp_node **prev, *node, *tail, **best = NULL; 356 | 357 | // align to multiple of c->align 358 | width = (width + c->align - 1); 359 | width -= width % c->align; 360 | STBRP_ASSERT(width % c->align == 0); 361 | 362 | // if it can't possibly fit, bail immediately 363 | if (width > c->width || height > c->height) { 364 | fr.prev_link = NULL; 365 | fr.x = fr.y = 0; 366 | return fr; 367 | } 368 | 369 | node = c->active_head; 370 | prev = &c->active_head; 371 | while (node->x + width <= c->width) { 372 | int y,waste; 373 | y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); 374 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL 375 | // bottom left 376 | if (y < best_y) { 377 | best_y = y; 378 | best = prev; 379 | } 380 | } else { 381 | // best-fit 382 | if (y + height <= c->height) { 383 | // can only use it if it first vertically 384 | if (y < best_y || (y == best_y && waste < best_waste)) { 385 | best_y = y; 386 | best_waste = waste; 387 | best = prev; 388 | } 389 | } 390 | } 391 | prev = &node->next; 392 | node = node->next; 393 | } 394 | 395 | best_x = (best == NULL) ? 0 : (*best)->x; 396 | 397 | // if doing best-fit (BF), we also have to try aligning right edge to each node position 398 | // 399 | // e.g, if fitting 400 | // 401 | // ____________________ 402 | // |____________________| 403 | // 404 | // into 405 | // 406 | // | | 407 | // | ____________| 408 | // |____________| 409 | // 410 | // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned 411 | // 412 | // This makes BF take about 2x the time 413 | 414 | if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { 415 | tail = c->active_head; 416 | node = c->active_head; 417 | prev = &c->active_head; 418 | // find first node that's admissible 419 | while (tail->x < width) 420 | tail = tail->next; 421 | while (tail) { 422 | int xpos = tail->x - width; 423 | int y,waste; 424 | STBRP_ASSERT(xpos >= 0); 425 | // find the left position that matches this 426 | while (node->next->x <= xpos) { 427 | prev = &node->next; 428 | node = node->next; 429 | } 430 | STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); 431 | y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); 432 | if (y + height <= c->height) { 433 | if (y <= best_y) { 434 | if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { 435 | best_x = xpos; 436 | STBRP_ASSERT(y <= best_y); 437 | best_y = y; 438 | best_waste = waste; 439 | best = prev; 440 | } 441 | } 442 | } 443 | tail = tail->next; 444 | } 445 | } 446 | 447 | fr.prev_link = best; 448 | fr.x = best_x; 449 | fr.y = best_y; 450 | return fr; 451 | } 452 | 453 | static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) 454 | { 455 | // find best position according to heuristic 456 | stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); 457 | stbrp_node *node, *cur; 458 | 459 | // bail if: 460 | // 1. it failed 461 | // 2. the best node doesn't fit (we don't always check this) 462 | // 3. we're out of memory 463 | if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { 464 | res.prev_link = NULL; 465 | return res; 466 | } 467 | 468 | // on success, create new node 469 | node = context->free_head; 470 | node->x = (stbrp_coord) res.x; 471 | node->y = (stbrp_coord) (res.y + height); 472 | 473 | context->free_head = node->next; 474 | 475 | // insert the new node into the right starting point, and 476 | // let 'cur' point to the remaining nodes needing to be 477 | // stiched back in 478 | 479 | cur = *res.prev_link; 480 | if (cur->x < res.x) { 481 | // preserve the existing one, so start testing with the next one 482 | stbrp_node *next = cur->next; 483 | cur->next = node; 484 | cur = next; 485 | } else { 486 | *res.prev_link = node; 487 | } 488 | 489 | // from here, traverse cur and free the nodes, until we get to one 490 | // that shouldn't be freed 491 | while (cur->next && cur->next->x <= res.x + width) { 492 | stbrp_node *next = cur->next; 493 | // move the current node to the free list 494 | cur->next = context->free_head; 495 | context->free_head = cur; 496 | cur = next; 497 | } 498 | 499 | // stitch the list back in 500 | node->next = cur; 501 | 502 | if (cur->x < res.x + width) 503 | cur->x = (stbrp_coord) (res.x + width); 504 | 505 | #ifdef _DEBUG 506 | cur = context->active_head; 507 | while (cur->x < context->width) { 508 | STBRP_ASSERT(cur->x < cur->next->x); 509 | cur = cur->next; 510 | } 511 | STBRP_ASSERT(cur->next == NULL); 512 | 513 | { 514 | int count=0; 515 | cur = context->active_head; 516 | while (cur) { 517 | cur = cur->next; 518 | ++count; 519 | } 520 | cur = context->free_head; 521 | while (cur) { 522 | cur = cur->next; 523 | ++count; 524 | } 525 | STBRP_ASSERT(count == context->num_nodes+2); 526 | } 527 | #endif 528 | 529 | return res; 530 | } 531 | 532 | // [DEAR IMGUI] Added STBRP__CDECL 533 | static int STBRP__CDECL rect_height_compare(const void *a, const void *b) 534 | { 535 | const stbrp_rect *p = (const stbrp_rect *) a; 536 | const stbrp_rect *q = (const stbrp_rect *) b; 537 | if (p->h > q->h) 538 | return -1; 539 | if (p->h < q->h) 540 | return 1; 541 | return (p->w > q->w) ? -1 : (p->w < q->w); 542 | } 543 | 544 | // [DEAR IMGUI] Added STBRP__CDECL 545 | static int STBRP__CDECL rect_original_order(const void *a, const void *b) 546 | { 547 | const stbrp_rect *p = (const stbrp_rect *) a; 548 | const stbrp_rect *q = (const stbrp_rect *) b; 549 | return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); 550 | } 551 | 552 | #ifdef STBRP_LARGE_RECTS 553 | #define STBRP__MAXVAL 0xffffffff 554 | #else 555 | #define STBRP__MAXVAL 0xffff 556 | #endif 557 | 558 | STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) 559 | { 560 | int i, all_rects_packed = 1; 561 | 562 | // we use the 'was_packed' field internally to allow sorting/unsorting 563 | for (i=0; i < num_rects; ++i) { 564 | rects[i].was_packed = i; 565 | } 566 | 567 | // sort according to heuristic 568 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); 569 | 570 | for (i=0; i < num_rects; ++i) { 571 | if (rects[i].w == 0 || rects[i].h == 0) { 572 | rects[i].x = rects[i].y = 0; // empty rect needs no space 573 | } else { 574 | stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); 575 | if (fr.prev_link) { 576 | rects[i].x = (stbrp_coord) fr.x; 577 | rects[i].y = (stbrp_coord) fr.y; 578 | } else { 579 | rects[i].x = rects[i].y = STBRP__MAXVAL; 580 | } 581 | } 582 | } 583 | 584 | // unsort 585 | STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); 586 | 587 | // set was_packed flags and all_rects_packed status 588 | for (i=0; i < num_rects; ++i) { 589 | rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); 590 | if (!rects[i].was_packed) 591 | all_rects_packed = 0; 592 | } 593 | 594 | // return the all_rects_packed status 595 | return all_rects_packed; 596 | } 597 | #endif 598 | 599 | /* 600 | ------------------------------------------------------------------------------ 601 | This software is available under 2 licenses -- choose whichever you prefer. 602 | ------------------------------------------------------------------------------ 603 | ALTERNATIVE A - MIT License 604 | Copyright (c) 2017 Sean Barrett 605 | Permission is hereby granted, free of charge, to any person obtaining a copy of 606 | this software and associated documentation files (the "Software"), to deal in 607 | the Software without restriction, including without limitation the rights to 608 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 609 | of the Software, and to permit persons to whom the Software is furnished to do 610 | so, subject to the following conditions: 611 | The above copyright notice and this permission notice shall be included in all 612 | copies or substantial portions of the Software. 613 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 614 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 615 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 616 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 617 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 618 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 619 | SOFTWARE. 620 | ------------------------------------------------------------------------------ 621 | ALTERNATIVE B - Public Domain (www.unlicense.org) 622 | This is free and unencumbered software released into the public domain. 623 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 624 | software, either in source code form or as a compiled binary, for any purpose, 625 | commercial or non-commercial, and by any means. 626 | In jurisdictions that recognize copyright laws, the author or authors of this 627 | software dedicate any and all copyright interest in the software to the public 628 | domain. We make this dedication for the benefit of the public at large and to 629 | the detriment of our heirs and successors. We intend this dedication to be an 630 | overt act of relinquishment in perpetuity of all present and future rights to 631 | this software under copyright law. 632 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 633 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 634 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 635 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 636 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 637 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 638 | ------------------------------------------------------------------------------ 639 | */ 640 | -------------------------------------------------------------------------------- /src/ltcSurface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ltc2.inc" 5 | #include "ltcSurface.h" 6 | 7 | void LtcSurface::initialize() { 8 | alpha = 0.01; 9 | 10 | ltcMatTexId = -1; 11 | ltcMagTexId = -1; 12 | 13 | isRoughTexed = false; 14 | roughnessTexId = -1; 15 | } 16 | 17 | void LtcSurface::createLTCmatTex() { 18 | glGenTextures(1, <cMatTexId); 19 | 20 | GLenum target = GL_TEXTURE_2D; 21 | GLenum filter = GL_LINEAR; 22 | GLenum address = GL_CLAMP_TO_EDGE; 23 | 24 | glBindTexture(target, ltcMatTexId); 25 | 26 | glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); 27 | glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); 28 | 29 | glTexParameteri(target, GL_TEXTURE_WRAP_S, address); 30 | glTexParameteri(target, GL_TEXTURE_WRAP_T, address); 31 | 32 | //glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 33 | 34 | float data[size][size][4]; 35 | 36 | for (int v = 0; v < size; ++v) { 37 | for (int u = 0; u < size; ++u) { 38 | // ltc2.inc 39 | float a = tabMinv[u * size + v % size][0] / (tabMinv[u * size + v % size][4]); 40 | float b = tabMinv[u * size + v % size][2] / (tabMinv[u * size + v % size][4]); 41 | float c = tabMinv[u * size + v % size][6] / (tabMinv[u * size + v % size][4]); 42 | float d = tabMinv[u * size + v % size][8] / (tabMinv[u * size + v % size][4]); 43 | 44 | data[u][v][0] = a; 45 | data[u][v][1] = b; 46 | data[u][v][2] = c; 47 | data[u][v][3] = d; 48 | } 49 | } 50 | 51 | // upload 52 | glTexImage2D(target, 0, GL_RGBA32F, size, size, 0, GL_RGBA, GL_FLOAT, data); 53 | 54 | glBindTexture(target, 0); 55 | } 56 | 57 | void LtcSurface::createLTCmagTex() { 58 | glGenTextures(1, <cMagTexId); 59 | 60 | GLenum target = GL_TEXTURE_2D; 61 | GLenum filter = GL_LINEAR; 62 | GLenum address = GL_CLAMP_TO_EDGE; 63 | 64 | glBindTexture(target, ltcMagTexId); 65 | 66 | glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); 67 | glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); 68 | 69 | glTexParameteri(target, GL_TEXTURE_WRAP_S, address); 70 | glTexParameteri(target, GL_TEXTURE_WRAP_T, address); 71 | 72 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 73 | 74 | float data[size * size]; 75 | 76 | for (int u = 0; u < size; ++u) { 77 | for (int v = 0; v < size; ++v) { 78 | float a = tabAmplitude[u * size + v % size]; 79 | 80 | data[u * size + v % size] = a; 81 | } 82 | } 83 | 84 | // upload 85 | glTexImage2D(target, 0, GL_R32F, size, size, 0, GL_RED, GL_FLOAT, data); 86 | 87 | glBindTexture(target, 0); 88 | } 89 | 90 | void LtcSurface::createRoughnessTex(const std::string &filename) { 91 | GLenum target = GL_TEXTURE_2D; 92 | GLenum filter = GL_LINEAR_MIPMAP_LINEAR; 93 | GLenum address = GL_CLAMP_TO_EDGE; 94 | 95 | glGenTextures(1, &roughnessTexId); 96 | glBindTexture(target, roughnessTexId); 97 | 98 | glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); 99 | glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); 100 | glTexParameteri(target, GL_TEXTURE_WRAP_S, address); 101 | glTexParameteri(target, GL_TEXTURE_WRAP_T, address); 102 | 103 | int channels, texWidth, texHeight; 104 | unsigned char *bytes = stbi_load(filename.c_str(), &texWidth, &texHeight, &channels, STBI_rgb_alpha); 105 | 106 | if (!bytes) { 107 | fprintf(stderr, "Failed to load image file: %s\n", filename.c_str()); 108 | exit(1); 109 | } 110 | 111 | glTexImage2D(target, 0, GL_RGBA32F, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes); 112 | glGenerateMipmap(target); 113 | 114 | glBindTexture(target, 0); 115 | } 116 | 117 | void LtcSurface::drawSurface(const Camera &camera, const BezierLight bezLight) { 118 | glUseProgram(programId); 119 | 120 | GLuint location = glGetUniformLocation(programId, "u_alpha"); 121 | glUniform1f(location, alpha); 122 | 123 | location = glGetUniformLocation(programId, "u_isRoughTexed"); 124 | glUniform1i(location, isRoughTexed); 125 | 126 | location = glGetUniformLocation(programId, "u_cameraPos"); 127 | glUniform3fv(location, 1, glm::value_ptr(camera.cameraPos)); 128 | 129 | location = glGetUniformLocation(programId, "u_cpsWorld"); 130 | glUniform3fv(location, bezLight.numPoints, glm::value_ptr(bezLight.cpsWorld[0])); 131 | 132 | location = glGetUniformLocation(programId, "u_numCurves"); 133 | glUniform1i(location, bezLight.numCurves); 134 | 135 | location = glGetUniformLocation(programId, "u_lightCenter"); 136 | glUniform3fv(location, 1, glm::value_ptr(bezLight.center)); 137 | 138 | location = glGetUniformLocation(programId, "u_isLightMove"); 139 | glUniform1i(location, bezLight.isMove); 140 | 141 | location = glGetUniformLocation(programId, "u_isTwoSided"); 142 | glUniform1i(location, bezLight.isTwoSided); 143 | 144 | location = glGetUniformLocation(programId, "u_isBezTexed"); 145 | glUniform1i(location, bezLight.isBezTexed); 146 | 147 | location = glGetUniformLocation(programId, "u_ltcMatTex"); 148 | glActiveTexture(GL_TEXTURE0); 149 | glBindTexture(GL_TEXTURE_2D, ltcMatTexId); 150 | glUniform1i(location, 0); 151 | 152 | location = glGetUniformLocation(programId, "u_ltcMagTex"); 153 | glActiveTexture(GL_TEXTURE1); 154 | glBindTexture(GL_TEXTURE_2D, ltcMagTexId); 155 | glUniform1i(location, 1); 156 | 157 | if (isRoughTexed) { 158 | location = glGetUniformLocation(programId, "u_roughnessTex"); 159 | glActiveTexture(GL_TEXTURE2); 160 | glBindTexture(GL_TEXTURE_2D, roughnessTexId); 161 | glUniform1i(location, 2); 162 | } 163 | 164 | if (bezLight.isBezTexed) { 165 | location = glGetUniformLocation(programId, "u_bezLightMmat"); 166 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(bezLight.modelMat)); 167 | 168 | location = glGetUniformLocation(programId, "u_texWidth"); 169 | glUniform1i(location, bezLight.texWidth); 170 | 171 | location = glGetUniformLocation(programId, "u_texHeight"); 172 | glUniform1i(location, bezLight.texHeight); 173 | 174 | location = glGetUniformLocation(programId, "u_marginSize"); 175 | glUniform1i(location, bezLight.marginSize); 176 | 177 | location = glGetUniformLocation(programId, "u_maxLOD"); 178 | glUniform1i(location, bezLight.maxLOD); 179 | 180 | location = glGetUniformLocation(programId, "u_bezLightTex"); 181 | glActiveTexture(GL_TEXTURE3); 182 | glBindTexture(GL_TEXTURE_2D, bezLight.bezLightTexId); 183 | glUniform1i(location, 3); 184 | } 185 | 186 | //location = glGetUniformLocation(programId, "u_bernCoeffTex"); 187 | //glActiveTexture(GL_TEXTURE4); 188 | //glBindTexture(GL_TEXTURE_1D, bezLight.bernCoeffTexId); 189 | //glUniform1i(location, 4); 190 | 191 | draw(camera, bezLight.center, bezLight.Le); 192 | } 193 | -------------------------------------------------------------------------------- /src/ltcSurface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bezierLight.h" 4 | #include "render.h" 5 | 6 | struct LtcSurface : public RenderObject { 7 | void initialize(); 8 | void createLTCmatTex(); 9 | void createLTCmagTex(); 10 | void createRoughnessTex(const std::string &filename); 11 | 12 | void drawSurface(const Camera &camera, const BezierLight bezLight); 13 | 14 | float alpha; 15 | GLuint ltcMatTexId; 16 | GLuint ltcMagTexId; 17 | GLuint roughnessTexId; 18 | bool isRoughTexed; 19 | }; -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define GLAD_GL_IMPLEMENTATION 11 | #include 12 | 13 | #define GLFW_INCLUDE_GLU 14 | #include 15 | 16 | #define GLM_ENABLE_EXPERIMENTAL 17 | #include 18 | #include 19 | #include 20 | 21 | #define STB_IMAGE_IMPLEMENTATION 22 | #include 23 | 24 | #define STB_IMAGE_WRITE_IMPLEMENTATION 25 | #include 26 | 27 | #define TINYOBJLOADER_IMPLEMENTATION 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #include "bezierLight.h" 35 | #include "constants.h" 36 | #include "ltcSurface.h" 37 | #include "render.h" 38 | 39 | static LtcSurface ltcFloor; 40 | static BezierLight bezLight; 41 | static Camera camera; 42 | static bool isAnim = false; 43 | 44 | static constexpr double Pi = 3.14159265358979; 45 | 46 | #define SAVE_MOVIE 0 47 | 48 | void initializeGL() { 49 | glEnable(GL_DEPTH_TEST); 50 | glDisable(GL_CULL_FACE); 51 | 52 | glBlendEquation(GL_FUNC_ADD); 53 | 54 | glClearColor(0.3f, 0.3f, 0.3f, 1.0f); 55 | glClearStencil(0); 56 | 57 | // ltcFloor 58 | { 59 | ltcFloor.initialize(); 60 | ltcFloor.loadOBJ(PLANE_OBJ); 61 | ltcFloor.buildShader(FLOORLTC_SHADER); 62 | ltcFloor.modelMat = glm::scale(glm::vec3(1.0f, 1.0f, 1.0f)); 63 | ltcFloor.diffColor = glm::vec3(1.0f); 64 | ltcFloor.specColor = glm::vec3(1.0f); 65 | 66 | ltcFloor.alpha = 0.01f; 67 | ltcFloor.createLTCmatTex(); 68 | ltcFloor.createLTCmagTex(); 69 | 70 | ltcFloor.isRoughTexed = true; 71 | if (ltcFloor.isRoughTexed) { 72 | ltcFloor.createRoughnessTex(ROUGHNESS_TEASER_PNG); 73 | } 74 | } 75 | 76 | // Bezier-curve light 77 | { 78 | bezLight.initialize(); 79 | bezLight.loadOBJ(SMALLPLANE_OBJ); 80 | bezLight.buildShader(BEZLIGHT_SHADER); 81 | bezLight.Le = glm::vec3(1.0f); 82 | 83 | // create bezier curve points in normalized model space, then transform to world space 84 | bezLight.createCPSmodel(FOUR); 85 | bezLight.calcCPSworld(); 86 | 87 | // create texture for bernstein coefficients, only for debugging 88 | //bezLight.createBernCoeffTex(); 89 | 90 | // other settings 91 | bezLight.isBezTexed = true; 92 | if (bezLight.isBezTexed) { 93 | bezLight.createBezLightTex(GRADATION_PNG); 94 | } 95 | } 96 | 97 | // Camera parameters 98 | { 99 | camera.cameraPos = glm::vec3(0.0f, 6.0f, 32.5f); 100 | camera.cameraDir = glm::vec3(0.0f, 0.0f, 0.0f); 101 | camera.cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); 102 | camera.viewMat = glm::lookAt(camera.cameraPos, camera.cameraDir, camera.cameraUp); 103 | camera.projMat = glm::perspective(glm::radians(50.0f), float(WIN_WIDTH) / float(WIN_HEIGHT), 1.0f, 100.0f); 104 | } 105 | } 106 | 107 | void draw(bool isShowGui = true) { 108 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 109 | 110 | // bezierLight 111 | { 112 | GLuint programId = bezLight.programId; 113 | glUseProgram(programId); 114 | bezLight.drawBez(camera); 115 | glUseProgram(0); 116 | } 117 | 118 | // ltcFloor 119 | { 120 | GLuint programId = ltcFloor.programId; 121 | glUseProgram(programId); 122 | ltcFloor.drawSurface(camera, bezLight); 123 | glUseProgram(0); 124 | } 125 | 126 | // ImGui 127 | if (isShowGui) { 128 | ImGui_ImplOpenGL3_NewFrame(); 129 | ImGui_ImplGlfw_NewFrame(); 130 | ImGui::NewFrame(); 131 | ImGui::Begin("Status"); 132 | ImGui::Text("Vendor: %s", glGetString(GL_VENDOR)); 133 | ImGui::Text("Renderer: %s", glGetString(GL_RENDERER)); 134 | ImGui::Text("OpenGL: %s", glGetString(GL_VERSION)); 135 | ImGui::Text("GLSL: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); 136 | 137 | ImGui::Separator(); 138 | 139 | static int s = 3; 140 | int prev_s = s; 141 | ImGui::Text("Scene:"); 142 | const char *scene_chars[] = {"teardrop", "torch", "tripod", "rainbow", "cavity", "char", "camel"}; 143 | ImGui::Combo(" ", &s, scene_chars, IM_ARRAYSIZE(scene_chars)); 144 | 145 | static int a = 0; 146 | ImGui::Text("Roughness:"); 147 | const char *alpha_chars[] = {"checker", "0.01", "0.1", "0.25", "0.4"}; 148 | ImGui::Combo(" ", &a, alpha_chars, IM_ARRAYSIZE(alpha_chars)); 149 | 150 | ImGui::Checkbox("Animate", &isAnim); 151 | ImGui::Checkbox("Light move", &bezLight.isMove); 152 | ImGui::Checkbox("Two-side", &bezLight.isTwoSided); 153 | 154 | static bool isVsync = true; 155 | ImGui::Checkbox("Vsync", &isVsync); 156 | glfwSwapInterval(isVsync ? 1 : 0); 157 | 158 | ImGui::End(); 159 | 160 | switch (a) { 161 | case 0: 162 | ltcFloor.isRoughTexed = true; 163 | break; 164 | case 1: 165 | ltcFloor.isRoughTexed = false; 166 | ltcFloor.alpha = 0.01; 167 | break; 168 | case 2: 169 | ltcFloor.isRoughTexed = false; 170 | ltcFloor.alpha = 0.1; 171 | break; 172 | case 3: 173 | ltcFloor.isRoughTexed = false; 174 | ltcFloor.alpha = 0.25; 175 | break; 176 | case 4: 177 | ltcFloor.isRoughTexed = false; 178 | ltcFloor.alpha = 0.4; 179 | break; 180 | } 181 | 182 | // ONE, TWO, THREE, FOUR, CAVITYLEAF, CLIP, QUAD, CHAR 183 | if (s != prev_s) { 184 | switch (s) { 185 | case 0: 186 | bezLight.isBezTexed = false; 187 | bezLight.createCPSmodel(ONE); 188 | bezLight.calcCPSworld(); 189 | break; 190 | case 1: 191 | bezLight.isBezTexed = false; 192 | bezLight.createCPSmodel(TWO); 193 | bezLight.calcCPSworld(); 194 | break; 195 | case 2: 196 | bezLight.isBezTexed = false; 197 | bezLight.createCPSmodel(THREE); 198 | bezLight.calcCPSworld(); 199 | break; 200 | case 3: 201 | bezLight.isBezTexed = true; 202 | bezLight.createCPSmodel(FOUR); 203 | bezLight.calcCPSworld(); 204 | bezLight.createBezLightTex(GRADATION_PNG); 205 | break; 206 | case 4: 207 | bezLight.isBezTexed = false; 208 | bezLight.createCPSmodel(CAVITY); 209 | bezLight.calcCPSworld(); 210 | break; 211 | case 5: 212 | bezLight.isBezTexed = false; 213 | bezLight.createCPSmodel(CHAR); 214 | bezLight.calcCPSworld(); 215 | break; 216 | case 6: 217 | bezLight.isBezTexed = false; 218 | bezLight.createCPSmodel(CLIP); 219 | bezLight.calcCPSworld(); 220 | break; 221 | } 222 | } 223 | 224 | ImGui::Render(); 225 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 226 | } 227 | } 228 | 229 | void saveCurrentBuffer(GLFWwindow *window, const std::string &filename = "") { 230 | // Draw without GUI 231 | draw(false); 232 | 233 | // Capture image 234 | int width, height; 235 | glfwGetWindowSize(window, &width, &height); 236 | 237 | auto bytes = std::make_unique(width * height * 4); 238 | glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (void *) bytes.get()); 239 | 240 | // Invert vertically 241 | for (int y = 0; y < height / 2; y++) { 242 | for (int x = 0; x < width; x++) { 243 | const int iy = height - y - 1; 244 | for (int c = 0; c < 4; c++) { 245 | std::swap(bytes[(y * width + x) * 4 + c], bytes[(iy * width + x) * 4 + c]); 246 | } 247 | } 248 | } 249 | 250 | // Check file existence 251 | time_t now = time(0); 252 | tm *ts = localtime(&now); 253 | 254 | std::string outname = filename; 255 | if (filename.empty()) { 256 | char buf[128]; 257 | strftime(buf, sizeof(buf), "%Y%m%d_%H%M%S.png", ts); 258 | outname = std::string(buf); 259 | } 260 | 261 | // Save 262 | stbi_write_png(outname.c_str(), width, height, 4, bytes.get(), 0); 263 | printf("Buffer saved: %s\n", outname.c_str()); 264 | } 265 | 266 | void update(GLFWwindow *window) { 267 | static int frameCount = 260; // 180, 260, 320 268 | 269 | #if SAVE_MOVIE 270 | { 271 | char filename[256]; 272 | sprintf(filename, "%04d.png", frameCount); 273 | saveCurrentBuffer(window, filename); 274 | } 275 | #endif 276 | 277 | camera.cameraPos.y = 1.0f; 278 | camera.cameraPos.x = 7.0f * sin(frameCount * Pi / 360 - 0.5f * Pi); 279 | camera.cameraPos.z = std::abs(7.0f * cos(frameCount * Pi / 360 - 0.5f * Pi)); 280 | camera.viewMat = glm::lookAt(camera.cameraPos, camera.cameraDir, camera.cameraUp); 281 | 282 | // Move light 283 | if (bezLight.isMove) { 284 | bezLight.translate.y = 1.5f * std::cos(Pi * frameCount / 120.0f); 285 | bezLight.rotAngle.z = -frameCount * 0.5f; 286 | bezLight.calcCPSworld(); 287 | } 288 | 289 | if (isAnim) { 290 | frameCount++; 291 | } 292 | } 293 | 294 | void keyboard(GLFWwindow *window, int key, int scancode, int action, int mods) { 295 | if (action == GLFW_PRESS) { 296 | if (key == GLFW_KEY_ESCAPE) { 297 | glfwSetWindowShouldClose(window, true); 298 | } 299 | 300 | if (key == GLFW_KEY_S && mods == GLFW_MOD_CONTROL) { 301 | saveCurrentBuffer(window); 302 | } 303 | } 304 | } 305 | 306 | void resize(GLFWwindow *window, int width, int height) { 307 | // Update window size 308 | glfwSetWindowSize(window, width, height); 309 | 310 | // Update viewport following actual window size 311 | int renderBufferWidth, renderBufferHeight; 312 | glfwGetFramebufferSize(window, &renderBufferWidth, &renderBufferHeight); 313 | glViewport(0, 0, renderBufferWidth, renderBufferHeight); 314 | 315 | // Upcate projection matrix 316 | const float aspect = (float) renderBufferWidth / (float) renderBufferHeight; 317 | camera.projMat = glm::perspective(glm::radians(50.0f), aspect, 1.0f, 100.0f); 318 | } 319 | 320 | int main(int argc, char **argv) { 321 | if (glfwInit() == GL_FALSE) { 322 | fprintf(stderr, "Initialization failed!\n"); 323 | return 1; 324 | } 325 | 326 | #if !defined(__APPLE__) 327 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 328 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); 329 | #else 330 | glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 331 | glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); 332 | #endif 333 | glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 334 | glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 335 | glfwWindowHint(GLFW_SRGB_CAPABLE, GL_FALSE); 336 | 337 | GLFWwindow *window = glfwCreateWindow(WIN_WIDTH, WIN_HEIGHT, WIN_TITLE, NULL, NULL); 338 | if (window == NULL) { 339 | fprintf(stderr, "Window creation failed!\n"); 340 | glfwTerminate(); 341 | return 1; 342 | } 343 | 344 | glfwMakeContextCurrent(window); 345 | glfwSwapInterval(1); 346 | 347 | const int version = gladLoadGL(glfwGetProcAddress); 348 | if (version == 0) { 349 | fprintf(stderr, "Failed to load OpenGL 3.x/4.x libraries\n"); 350 | fprintf(stderr, "Make sure at least OpenGL 4.3 is supported on your system\n"); 351 | return 1; 352 | } 353 | printf("Load OpenGL %d.%d\n", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version)); 354 | 355 | // ImGui 356 | IMGUI_CHECKVERSION(); 357 | ImGui::CreateContext(); 358 | ImGuiIO &io = ImGui::GetIO(); 359 | (void) io; 360 | io.Fonts->AddFontFromFileTTF("data/Roboto-Medium.ttf", 16.0f); 361 | ImGui::StyleColorsDark(); 362 | 363 | ImGui_ImplGlfw_InitForOpenGL(window, true); 364 | ImGui_ImplOpenGL3_Init("#version 330"); 365 | 366 | // Other setups 367 | initializeGL(); 368 | glfwSetWindowSizeCallback(window, resize); 369 | glfwSetKeyCallback(window, keyboard); 370 | 371 | double totalTime = 0.0; 372 | uint32_t frameNum = 0; 373 | const uint32_t fpsUpdateSkip = 100; 374 | while (glfwWindowShouldClose(window) == GL_FALSE) { 375 | const double startTime = glfwGetTime(); 376 | 377 | update(window); 378 | draw(); 379 | 380 | frameNum++; 381 | if (frameNum % fpsUpdateSkip == 0) { 382 | const double timeSec = totalTime / fpsUpdateSkip; 383 | char title[256]; 384 | sprintf(title, "%s: %.4f ms (%.2f fps)", WIN_TITLE, timeSec * 1000.0, 1.0 / timeSec); 385 | glfwSetWindowTitle(window, title); 386 | totalTime = 0.0; 387 | frameNum = 0; 388 | } 389 | 390 | glfwSwapBuffers(window); 391 | glfwPollEvents(); 392 | 393 | totalTime += (glfwGetTime() - startTime); 394 | } 395 | 396 | ImGui_ImplOpenGL3_Shutdown(); 397 | ImGui_ImplGlfw_Shutdown(); 398 | ImGui::DestroyContext(); 399 | 400 | glfwDestroyWindow(window); 401 | glfwTerminate(); 402 | 403 | return 0; 404 | } 405 | -------------------------------------------------------------------------------- /src/openmp.h: -------------------------------------------------------------------------------- 1 | #ifdef _MSC_VER 2 | # pragma once 3 | #endif 4 | 5 | #ifndef COMMON_OPENMP_H 6 | # define COMMON_OPENMP_H 7 | 8 | # if defined(_OPENMP) 9 | # include 10 | # if defined(_MSC_VER) 11 | # define omp_pragma __pragma(omp parallel for) 12 | # define omp_critical __pragma(omp critical) 13 | # else 14 | # define omp_pragma _Pragma("omp parallel for") 15 | # define omp_critical _Pragma("omp parallel for") 16 | # endif 17 | # define omp_parallel_for omp_pragma for 18 | # define omp_lock_t omp_lock_t 19 | # define omp_init_lock(lock) omp_init_lock(lock) 20 | # define omp_destroy_lock(lock) omp_destroy_lock(lock) 21 | # define omp_lock(lock) omp_set_lock(lock) 22 | # define omp_unlock(lock) omp_unset_lock(lock) 23 | # else 24 | # define omp_set_num_threads(n) 25 | # define omp_get_thread_num() 0 26 | # define omp_get_max_threads() 1 27 | # define omp_get_num_threads() 1 28 | # define omp_parallel_for for 29 | # define omp_critical 30 | # define omp_lock_t int 31 | # define omp_init_lock(lock) 32 | # define omp_destroy_lock(lock) 33 | # define omp_lock(lock) 34 | # define omp_unlock(lock) 35 | # endif 36 | 37 | #endif // COMMON_OPENMP_H 38 | -------------------------------------------------------------------------------- /src/render.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "render.h" 11 | 12 | Vertex::Vertex() : 13 | position(0.0f, 0.0f, 0.0f), normal(0.0f, 0.0f, 0.0f), texcoord(0.0f, 0.0f) { 14 | } 15 | 16 | Vertex::Vertex(const glm::vec3 &pos, const glm::vec3 &norm, const glm::vec2 &uv) : 17 | position(pos), normal(norm), texcoord(uv) { 18 | } 19 | 20 | void RenderObject::initialize() { 21 | programId = 0u; 22 | vaoId = 0u; 23 | vboId = 0u; 24 | iboId = 0u; 25 | textureId = 0u; 26 | bufferSize = 0; 27 | 28 | modelMat = glm::mat4(1.0f); 29 | ambiColor = glm::vec3(0.0f, 0.0f, 0.0f); 30 | diffColor = glm::vec3(0.0f, 0.0f, 0.0f); 31 | specColor = glm::vec3(0.0f, 0.0f, 0.0f); 32 | } 33 | 34 | void RenderObject::buildShader(const std::string &basename) { 35 | const std::string vertShaderFile = basename + ".vert"; 36 | const std::string fragShaderFile = basename + ".frag"; 37 | 38 | // Create shaders 39 | GLuint vertShaderId = glCreateShader(GL_VERTEX_SHADER); 40 | GLuint fragShaderId = glCreateShader(GL_FRAGMENT_SHADER); 41 | 42 | // Load vertex shader source 43 | std::ifstream vertFileInput(vertShaderFile.c_str(), std::ios::in); 44 | if (!vertFileInput.is_open()) { 45 | fprintf(stderr, "Failed to load vertex shader: %s\n", vertShaderFile.c_str()); 46 | exit(1); 47 | } 48 | std::istreambuf_iterator vertDataBegin(vertFileInput); 49 | std::istreambuf_iterator vertDataEnd; 50 | const std::string vertFileData(vertDataBegin, vertDataEnd); 51 | const char *vertShaderCode = vertFileData.c_str(); 52 | 53 | // Load fragment shader source 54 | std::ifstream fragFileInput(fragShaderFile.c_str(), std::ios::in); 55 | if (!fragFileInput.is_open()) { 56 | fprintf(stderr, "Failed to load fragment shader: %s\n", fragShaderFile.c_str()); 57 | exit(1); 58 | } 59 | std::istreambuf_iterator fragDataBegin(fragFileInput); 60 | std::istreambuf_iterator fragDataEnd; 61 | const std::string fragFileData(fragDataBegin, fragDataEnd); 62 | const char *fragShaderCode = fragFileData.c_str(); 63 | 64 | // Compile 65 | GLint compileStatus; 66 | glShaderSource(vertShaderId, 1, &vertShaderCode, NULL); 67 | glCompileShader(vertShaderId); 68 | glGetShaderiv(vertShaderId, GL_COMPILE_STATUS, &compileStatus); 69 | printf("Compiling %s...\n", vertShaderFile.c_str()); 70 | if (compileStatus == GL_FALSE) { 71 | fprintf(stderr, "Failed to compile vertex shader!\n"); 72 | 73 | GLint logLength; 74 | glGetShaderiv(vertShaderId, GL_INFO_LOG_LENGTH, &logLength); 75 | if (logLength > 0) { 76 | GLsizei length; 77 | char *errmsg = new char[logLength + 1]; 78 | glGetShaderInfoLog(vertShaderId, logLength, &length, errmsg); 79 | 80 | std::cerr << errmsg << std::endl; 81 | fprintf(stderr, "%s", vertShaderCode); 82 | 83 | delete[] errmsg; 84 | } 85 | } 86 | 87 | glShaderSource(fragShaderId, 1, &fragShaderCode, NULL); 88 | glCompileShader(fragShaderId); 89 | glGetShaderiv(fragShaderId, GL_COMPILE_STATUS, &compileStatus); 90 | printf("Compiling %s...\n", fragShaderFile.c_str()); 91 | if (compileStatus == GL_FALSE) { 92 | fprintf(stderr, "Failed to compile fragment shader!\n"); 93 | 94 | GLint logLength; 95 | glGetShaderiv(fragShaderId, GL_INFO_LOG_LENGTH, &logLength); 96 | if (logLength > 0) { 97 | GLsizei length; 98 | char *errmsg = new char[logLength + 1]; 99 | glGetShaderInfoLog(fragShaderId, logLength, &length, errmsg); 100 | 101 | std::cerr << errmsg << std::endl; 102 | fprintf(stderr, "%s", vertShaderCode); 103 | 104 | delete[] errmsg; 105 | } 106 | } 107 | 108 | // Link to program 109 | programId = glCreateProgram(); 110 | glAttachShader(programId, vertShaderId); 111 | glAttachShader(programId, fragShaderId); 112 | 113 | GLint linkState; 114 | glLinkProgram(programId); 115 | glGetProgramiv(programId, GL_LINK_STATUS, &linkState); 116 | if (linkState == GL_FALSE) { 117 | fprintf(stderr, "Failed to link shaders!\n"); 118 | 119 | GLint logLength; 120 | glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLength); 121 | if (logLength > 0) { 122 | GLsizei length; 123 | char *errmsg = new char[logLength + 1]; 124 | glGetProgramInfoLog(programId, logLength, &length, errmsg); 125 | 126 | std::cerr << errmsg << std::endl; 127 | delete[] errmsg; 128 | } 129 | 130 | exit(1); 131 | } 132 | } 133 | 134 | void RenderObject::loadOBJ(const std::string &filename) { 135 | // Load OBJ file. 136 | tinyobj::attrib_t attrib; 137 | std::vector shapes; 138 | std::vector materials; 139 | std::string err; 140 | bool success = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename.c_str()); 141 | if (!err.empty()) { 142 | std::cerr << "[WARNING] " << err << std::endl; 143 | } 144 | 145 | if (!success) { 146 | std::cerr << "Failed to load OBJ file: " << filename << std::endl; 147 | exit(1); 148 | } 149 | 150 | std::vector vertices; 151 | std::vector indices; 152 | for (int s = 0; s < shapes.size(); s++) { 153 | const tinyobj::shape_t &shape = shapes[s]; 154 | for (int i = 0; i < shape.mesh.indices.size(); i++) { 155 | const tinyobj::index_t &index = shapes[s].mesh.indices[i]; 156 | 157 | Vertex vertex; 158 | if (index.vertex_index >= 0) { 159 | vertex.position = glm::vec3( 160 | attrib.vertices[index.vertex_index * 3 + 0], 161 | attrib.vertices[index.vertex_index * 3 + 1], 162 | attrib.vertices[index.vertex_index * 3 + 2]); 163 | } 164 | 165 | if (index.normal_index >= 0) { 166 | vertex.normal = glm::vec3( 167 | attrib.normals[index.normal_index * 3 + 0], 168 | attrib.normals[index.normal_index * 3 + 1], 169 | attrib.normals[index.normal_index * 3 + 2]); 170 | } 171 | 172 | if (index.texcoord_index >= 0) { 173 | vertex.texcoord = glm::vec2( 174 | attrib.texcoords[index.texcoord_index * 2 + 0], 175 | 1.0f - attrib.texcoords[index.texcoord_index * 2 + 1]); 176 | } 177 | 178 | indices.push_back((uint32_t) vertices.size()); 179 | vertices.push_back(vertex); 180 | } 181 | } 182 | 183 | // Prepare VAO. 184 | glGenVertexArrays(1, &vaoId); 185 | glBindVertexArray(vaoId); 186 | 187 | glGenBuffers(1, &vboId); 188 | glBindBuffer(GL_ARRAY_BUFFER, vboId); 189 | glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), 190 | vertices.data(), GL_DYNAMIC_DRAW); 191 | 192 | glEnableVertexAttribArray(0); 193 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, position)); 194 | glEnableVertexAttribArray(1); 195 | glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, normal)); 196 | glEnableVertexAttribArray(2); 197 | glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, texcoord)); 198 | 199 | glGenBuffers(1, &iboId); 200 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboId); 201 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), 202 | indices.data(), GL_STATIC_DRAW); 203 | bufferSize = (int) indices.size(); 204 | 205 | glBindVertexArray(0); 206 | } 207 | 208 | void RenderObject::loadTexture(const std::string &filename) { 209 | glGenTextures(1, &textureId); 210 | glBindTexture(GL_TEXTURE_2D, textureId); 211 | 212 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 213 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 214 | 215 | int texWidth, texHeight, channels; 216 | unsigned char *bytes = stbi_load(filename.c_str(), &texWidth, &texHeight, &channels, STBI_rgb_alpha); 217 | if (!bytes) { 218 | fprintf(stderr, "Failed to load image file: %s\n", filename.c_str()); 219 | exit(1); 220 | } 221 | 222 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, texWidth, texHeight, 223 | 0, GL_RGBA, GL_UNSIGNED_BYTE, bytes); 224 | 225 | glBindTexture(GL_TEXTURE_2D, 0); 226 | 227 | stbi_image_free(bytes); 228 | } 229 | 230 | void RenderObject::draw(const Camera &camera, const glm::vec3 &lightPos, const glm::vec3 &lightLe) { 231 | glUseProgram(programId); 232 | 233 | GLuint location; 234 | location = glGetUniformLocation(programId, "u_diffColor"); 235 | glUniform3fv(location, 1, glm::value_ptr(diffColor)); 236 | location = glGetUniformLocation(programId, "u_specColor"); 237 | glUniform3fv(location, 1, glm::value_ptr(specColor)); 238 | location = glGetUniformLocation(programId, "u_ambiColor"); 239 | glUniform3fv(location, 1, glm::value_ptr(ambiColor)); 240 | location = glGetUniformLocation(programId, "u_shininess"); 241 | glUniform1f(location, shininess); 242 | 243 | glm::mat4 mMat, mvMat, mvpMat, normMat; 244 | mMat = modelMat; 245 | mvMat = camera.viewMat * mMat; 246 | mvpMat = camera.projMat * mvMat; 247 | normMat = glm::transpose(glm::inverse(mvMat)); 248 | 249 | location = glGetUniformLocation(programId, "u_lightPos"); 250 | glUniform3fv(location, 1, glm::value_ptr(lightPos)); 251 | location = glGetUniformLocation(programId, "u_lightLe"); 252 | glUniform3fv(location, 1, glm::value_ptr(lightLe)); 253 | location = glGetUniformLocation(programId, "u_lightMat"); 254 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(camera.viewMat)); 255 | location = glGetUniformLocation(programId, "u_mMat"); 256 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(mMat)); 257 | location = glGetUniformLocation(programId, "u_mvMat"); 258 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(mvMat)); 259 | location = glGetUniformLocation(programId, "u_mvpMat"); 260 | glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(mvpMat)); 261 | location = glGetUniformLocation(programId, "u_normMat"); 262 | glUniformMatrix4fv(location, 1, false, glm::value_ptr(normMat)); 263 | 264 | if (textureId != 0) { 265 | glActiveTexture(GL_TEXTURE0); 266 | glBindTexture(GL_TEXTURE_2D, textureId); 267 | location = glGetUniformLocation(programId, "u_isTextured"); 268 | glUniform1i(location, true); 269 | location = glGetUniformLocation(programId, "u_texture"); 270 | glUniform1i(location, 0); 271 | } else { 272 | location = glGetUniformLocation(programId, "u_isTextured"); 273 | glUniform1i(location, 0); 274 | } 275 | 276 | glBindVertexArray(vaoId); 277 | glDrawElements(GL_TRIANGLES, bufferSize, GL_UNSIGNED_INT, 0); 278 | glBindVertexArray(0); 279 | 280 | glUseProgram(0); 281 | } 282 | -------------------------------------------------------------------------------- /src/render.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define GLFW_INCLUDE_GLU 8 | #include 9 | 10 | #define GLM_ENABLE_EXPERIMENTAL 11 | #include 12 | #include 13 | #include 14 | 15 | struct Vertex { 16 | Vertex(); 17 | Vertex(const glm::vec3 &pos, const glm::vec3 &norm, const glm::vec2 &uv); 18 | 19 | glm::vec3 position; 20 | glm::vec3 normal; 21 | glm::vec2 texcoord; 22 | }; 23 | 24 | struct Camera { 25 | glm::mat4 projMat; 26 | glm::mat4 viewMat; 27 | glm::vec3 cameraPos; 28 | glm::vec3 cameraDir; 29 | glm::vec3 cameraUp; 30 | }; 31 | 32 | struct RenderObject { 33 | void initialize(); 34 | void buildShader(const std::string &basename); 35 | void loadOBJ(const std::string &filename); 36 | void loadTexture(const std::string &filename); 37 | void draw(const Camera &camera, const glm::vec3 &lightPos, const glm::vec3 &lightLe); 38 | 39 | GLuint programId; 40 | GLuint vaoId; 41 | GLuint vboId; 42 | GLuint iboId; 43 | GLuint textureId; 44 | int bufferSize; 45 | 46 | glm::mat4 modelMat; 47 | glm::vec3 ambiColor; 48 | glm::vec3 diffColor; 49 | glm::vec3 specColor; 50 | float shininess; 51 | }; 52 | -------------------------------------------------------------------------------- /src/stb/LICENSE: -------------------------------------------------------------------------------- 1 | This software is available under 2 licenses -- choose whichever you prefer. 2 | ------------------------------------------------------------------------------ 3 | ALTERNATIVE A - MIT License 4 | Copyright (c) 2017 Sean Barrett 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | ------------------------------------------------------------------------------ 21 | ALTERNATIVE B - Public Domain (www.unlicense.org) 22 | This is free and unencumbered software released into the public domain. 23 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 24 | software, either in source code form or as a compiled binary, for any purpose, 25 | commercial or non-commercial, and by any means. 26 | In jurisdictions that recognize copyright laws, the author or authors of this 27 | software dedicate any and all copyright interest in the software to the public 28 | domain. We make this dedication for the benefit of the public at large and to 29 | the detriment of our heirs and successors. We intend this dedication to be an 30 | overt act of relinquishment in perpetuity of all present and future rights to 31 | this software under copyright law. 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 36 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /src/tinyobjloader/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2019 Syoyo Fujita and many contributors. 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 13 | all 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 21 | THE SOFTWARE. 22 | 23 | ---------------------------------- 24 | 25 | mapbox/earcut.hpp 26 | 27 | ISC License 28 | 29 | Copyright (c) 2015, Mapbox 30 | 31 | Permission to use, copy, modify, and/or distribute this software for any purpose 32 | with or without fee is hereby granted, provided that the above copyright notice 33 | and this permission notice appear in all copies. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 36 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 37 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 38 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 39 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 40 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 41 | THIS SOFTWARE. 42 | --------------------------------------------------------------------------------