├── .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 | 
5 |
6 | [](https://github.com/Paul180297/BezierLightLTC/actions/workflows/windows.yaml)
7 | [](https://github.com/Paul180297/BezierLightLTC/actions/workflows/macos.yaml)
8 | [](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 |
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 |
--------------------------------------------------------------------------------