├── .gitignore ├── LICENSE ├── README.md ├── crinkler-license.txt ├── crinkler-manual.txt ├── devour.png ├── leviathan.vcxproj ├── leviathan.vcxproj.filters ├── link.exe ├── pbr-leviathan-2.0.sln ├── shader_minifier.exe └── src ├── 4klang ├── 4klang.h └── 4klang.obj ├── debug.h ├── definitions.h ├── editor.cpp ├── editor.h ├── fp.h ├── glext.h ├── main.cpp ├── shaders ├── fragment.frag ├── fragment.inl ├── post.frag ├── post.inl ├── texture.frag └── texture.inl ├── song.cpp └── song.h /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Roope Mäkinen 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Devour by Prismbeings 2 | This is a 4 kilobyte executable procedural graphics entry for the Revision 2019 competition. It placed 2nd among other very high quality entries. It uses a modified version of my Leviathan framework (https://github.com/armak/Leviathan-2.0) with the guts replaced for still instead of realtime rendering. 3 | 4 | Comment and binary downloads: https://www.pouet.net/prod.php?which=81043 5 | 6 | The rendering method is a monte carlo path tracer by doing a one sample per pixel rendering via every draw call and blending it additively with the existing framebuffer until done. Nothing is shown while this is happening as it is denied by the compo rules (no animation or iterative rendering). There is also an "Accumulate" mode that shows the process. After a desired amount of samples has been rendered, it switches to a post processing pass that does averaging and tonemapping of the samples (and some other stuff). The post processing is static rendered once per frame from then on. The resulting executable should be about 3800 bytes (the shader code isn't optimized at all). 7 | 8 | Result: 9 | ![Resulting render with about 1000 samples, I think](http://noby.untergrund.net/img/devour.png) 10 | 11 | Acknowledgements: 12 | - compressed with Crinkler 2.1a by Blueberry and Mentor of Loonies (http://www.crinkler.net/) 13 | - shader minified (slightly) with Shader Minifier by LLB (https://github.com/laurentlb/Shader_Minifier) 14 | - thanks to Fizzer for all sorts of help along the way 💛 15 | - greetings to everyone who participated in the amazing compo! 16 | -------------------------------------------------------------------------------- /crinkler-license.txt: -------------------------------------------------------------------------------- 1 | 2 | Crinkler - compressing linker for Windows 3 | Copyright 2005-2019 Aske Simon Christensen and Rune L. H. Stubbe. 4 | 5 | 6 | DISTRIBUTION 7 | 8 | Crinkler is freeware. You may distribute it freely, as long as the 9 | Crinkler executable and all accompanying documentation files are kept 10 | together and intact. You may not distribute modified versions or any 11 | kind of derived work based on Crinkler or its documentation. 12 | 13 | USE AND AUTHOR RIGHTS 14 | 15 | The output of Crinkler (executable files, HTML compression reports 16 | and other textual output) is to be considered the original works of 17 | you as a user of Crinkler. You may use Crinkler and its output for 18 | any legal purpose with the following restrictions: 19 | - You may not use Crinkler or its output for any commercial purpose. 20 | - You may not use Crinkler or its output in any setting where security 21 | is essential. 22 | - You may not use Crinkler or its output for any safety critical 23 | purpose. 24 | - You may not use Crinkler or its output in the production of any 25 | virus, worm, trojan, spyware or other malicious software. 26 | 27 | NO WARRANTY 28 | 29 | Crinkler comes with no warranties of any kind. Crinkler is 30 | experimental software that relies on many undocumented features of the 31 | Windows PE loader. It has been created with the sole purpose of 32 | producing tiny executable files that may or may not work as intended 33 | on current and/or future versions of Windows. 34 | 35 | LICENSE OF DISTORM 36 | 37 | Crinkler uses the BSD-licensed diStorm disassembler library for 38 | producing disassembly output of the compressed code. The license for 39 | diStorm requires that the license below is included with derivative 40 | works. The license below applies only to diStorm and not to Crinkler 41 | as a whole. 42 | 43 | 44 | :[diStorm64}: 45 | The ultimate disassembler library. 46 | Copyright (c) 2003,2004,2005,2006,2007,2008, Gil Dabah 47 | All rights reserved. 48 | 49 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 50 | 51 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 52 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 53 | * Neither the name of the diStorm nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 54 | 55 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 | -------------------------------------------------------------------------------- /crinkler-manual.txt: -------------------------------------------------------------------------------- 1 | 2 | CRINKLER - Compressing linker for Windows specialized for 4k intros 3 | 4 | Aske Simon Christensen "Blueberry/Loonies" 5 | Rune L. H. Stubbe "Mentor/TBC" 6 | 7 | Version 2.1a (January 19, 2019) 8 | 9 | 10 | 11 | VERSION HISTORY 12 | --------------- 13 | 19.01.19: 2.1a: Fixed width of report to make room for the 32 hex columns. 14 | /REUSEMODE:WRITE to write the reuse file without reading it. 15 | 16 | 18.12.18: 2.1: Crinkler executable built for both 32 bit and 64 bit. 17 | New, slightly different model estimation. 8-12x speedup. 18 | Optimized section reordering. About 3-4x speedup. 19 | Optimized and multi-threaded hash size optimization. 20 | New /COMPMODE:VERYSLOW option for a few extra bytes. 21 | Changed default compression mode to SLOW. 22 | Changed default HASHSIZE to 500 and HASHTRIES to 100. 23 | /REUSE option: Use models and ordering from last run. 24 | /REUSEMODE:STABLE to quickly iterate when making changes. 25 | /REUSEMODE:IMPROVE to improve upon previous compression. 26 | Print output file size in report. 27 | More compact bits-per-byte color legend in report. 28 | Choose configuration instead of hiding/showing in report. 29 | 32 column hex view and other adjustments in report. 30 | Avoid crash if an existing file could not be opened. 31 | Updated internal function list to Windows version 1809. 32 | 33 | 28.03.18: 2.0a: Fixed Crinkler crash on recent Windows SDK versions. 34 | Fixed Crinkler crash on forwards from ole32.dll. 35 | Corrected horizontal alignment issue in HTML report. 36 | Support forwarded RVA imports with /TINYIMPORT. 37 | Fixed spurious import of MessageBox with /TINYIMPORT. 38 | Print compatibility warning when using /TINYIMPORT. 39 | Updated internal function list to Windows version 1803. 40 | Extended description in the manual of /TINYIMPORT. 41 | Updated download link for the lib file for msvcrt.dll. 42 | 43 | 28.07.15: 2.0: /TINYHEADER option: smaller decompressor for 1k intros. 44 | /TINYIMPORT option: smaller import code for 1k intros. 45 | /EXPORT option to export code and data symbols. 46 | /SATURATE option to saturate context counters. 47 | /FALLBACKDLL option for when a DLL is not available. 48 | /UNALIGNCODE option to set alignment of all code to 1. 49 | Support for /REPLACEDLL during recompression. 50 | Consistent size between model estimation and reordering. 51 | Header size reduced by 2 bytes. 52 | Print previous size of output file. 53 | Accept version specifier after /SUBSYSTEM value. 54 | Switched from Intel OpenMP to MSVC concurrency API. 55 | 56 | 19.01.13: 1.4: Output EXE files work with recent NVIDIA drivers. 57 | New zero-section header layout saving around 30-50 bytes. 58 | Forwarded RVA imports supported via link-time forwarding. 59 | Dynamic C++ initializers supported. 60 | Support for producing Large Address Aware executables. 61 | Crinkler is Large Address Aware, handling larger inputs. 62 | Report all unresolved symbols and the location of each. 63 | Better resolving of ambiguous label references in report. 64 | Various adjustments to textual output. 65 | /RECOMPRESS overwrites input file by default. 66 | 67 | 05.03.11: 1.3: Fixed Crinkler crash on some AMD systems. 68 | Header size reduced by 21 bytes. 69 | Slightly improved model hash function. 70 | /OVERRIDEALIGNMENTS option to specify label alignments. 71 | No limit on the number of calls in call transform. 72 | Import code and entry point movable by section reordering. 73 | Fixed bug in handling of files with absolute path. 74 | Fixed labels in report showing up in the wrong section. 75 | Crinkler writes .dmp files in case of a crash. 76 | 77 | 05.09.09: 1.2: Output EXE files are now Windows 7 compatible. 78 | Output EXE files are no longer Windows 2000 compatible. 79 | Header size reduced by 16 bytes. 80 | Non-range import code is (usually) slightly smaller. 81 | Slightly improved section ordering estimation. 82 | /RECOMPRESS option to recompress Crinkler-compressed 83 | executables, optionally with different parameters. 84 | /FIX removed, as it is subsumed by /RECOMPRESS. 85 | 86 | 14.01.09: 1.1a: Fixed /TRUNCATEFLOATS crashing in some cases. 87 | Improved /ORDERTRIES estimation when call transform is used. 88 | Sometimes sections were misplaced in the HTML report. 89 | Various improvements to the HTML report. 90 | The /FIX option can input and output to the same file. 91 | Helpful error messages for various unsupported features. 92 | Prefer a custom entry point to a standard library one. 93 | New section in the manual about runtime libraries. 94 | 95 | 12.01.08: 1.1: Support for weak externals (virtual C++ destructors). 96 | Fixed compatibility with Data Execution Prevention. 97 | /REPORT option for a colorful HTML compression report. 98 | /TRUNCATEFLOATS option to mutilate float constants. 99 | /SAFEIMPORT is now default, disabled with /UNSAFEIMPORT. 100 | Slightly smaller overhead if range importing is not used. 101 | Fixed some problems with compressing very small files. 102 | /VERBOSE:FUNCTIONS removed, as it is subsumed by /REPORT. 103 | Remaining /VERBOSE options renamed to /PRINT. 104 | Maximum number of ORDERTRIES increased to 100000. 105 | 106 | 07.01.07: 1.0a: New /VERBOSE:FUNCTIONS options to sort the functions. 107 | Various verbose output fixes. 108 | Various crash fixes. 109 | A fix to the /FIX Crinkler version recognizer. 110 | 111 | 27.12.06: 1.0: Output EXE files are now Windows Vista compatible. 112 | Compression tweak for greatly improved compression ratio. 113 | Much faster compression. 114 | Automatically takes advantage of multiple processors. 115 | Improved Visual Studio 2005 integration. 116 | /COMPMODE:INSTANT option for very quick compression. 117 | /ORDERTRIES option to try out different section orderings. 118 | /SAFEIMPORT option to insert a check for nonexistent DLLs. 119 | /PROGRESSGUI option for a graphical progress bar. 120 | /REPLACEDLL option to replace one DLL with another. 121 | /FIX option to fix compatibility problems of older versions. 122 | 123 | 09.02.06: 0.4a: Fixed linker crash problem with blank member entries 124 | in some library files (such as glut32). 125 | The /PRIORITY option was not mentioned in the 126 | commandline usage help. 127 | 128 | 18.12.05: 0.4: Changed header and import code to make output EXE files 129 | compatible with 64-bit versions of Windows. 130 | Fixed a bug in the ordinal range import mechanism. 131 | Added a switch to control the process priority. 132 | Added a warning for range import of an unused DLL. 133 | Some more header squeezing. 134 | 135 | 31.10.05: 0.3: Output EXE files are now Windows 2000 compatible. 136 | Added a number of verbose options to output useful 137 | information about the program being compressed. 138 | Added an option for transforming function calls to 139 | use absolute offsets to improve compression. 140 | Fixed a bug in the linker regarding identically named 141 | sections. 142 | Fixed a potential crash bug in the linker. 143 | Various small tweaks and optimizations. 144 | 145 | 23.07.05: 0.2: Fixed bug in the decompressor. 146 | Changed the behaviour of the /CRINKLER option. 147 | Added timing to the progress bars. 148 | Some updates to the manual and usage description. 149 | 150 | 21.07.05: 0.1: First release. 151 | 152 | 153 | 154 | BACKGROUND 155 | ---------- 156 | 157 | Ever since the concept of size-limited demo competitions was 158 | introduced in the early 1990's (and before that as well), people have 159 | been using executable file compressors to reduce the size of their 160 | final executables. An executable file compressor is a program that 161 | takes as input an executable file and produces a new executable file 162 | which has the same behaviour as the original one but is (hopefully) 163 | smaller. 164 | 165 | The usual technique employed by executable file compressors is to 166 | compress the contents of the executable file using some general 167 | purpose data compression method and prepend to this compressed data a 168 | small piece of code (the decompressor) which decompresses the contents 169 | into memory in such a way that it looks to the code as if the original 170 | executable file had been loaded into memory in the normal way. 171 | 172 | The size of the decompressor is usually around a few hundred bytes, 173 | depending on the complexity of the compression method. This 174 | constitutes an unavoidable overhead in the compressed file, which is 175 | particularly evident for small files, such as 4k intros. Furthermore, 176 | the header of the Windows EXE file format contains a lot of 177 | information that needs to be there at fixed offsets in order for 178 | Windows to be able to load the file. The presence of these overheads 179 | from the header and decompressor motivated people to look for other 180 | means of compressing their 4k intros. 181 | 182 | Until Crinkler came around, the most popular strategy for compressing 183 | 4k intros for Windows was CAB dropping: A few simple transformations 184 | are performed on the executable to make it compress better (such as 185 | merging sections and setting unused header fields to zero), and the 186 | result is compressed using the Cabinet Compression tool included with 187 | Windows. The resulting .CAB file is renamed to have .BAT extension, 188 | and some commands are inserted into the file such that when the .BAT 189 | file is executed, it decompresses the executable to disk (using the 190 | Cabinet decompression command), runs the executable and then deletes 191 | the executable again. This saves the size of the decompression code 192 | (since an external program is used to do the decompression) and some 193 | of the size of the header (since the header can be compressed). 194 | 195 | Various dropping strategies combined with other space-saving hacks 196 | people employed on their 4k intros (in particular import by ordinal) 197 | caused severe compatibility problems. More often than not, people 198 | who wanted to run a newly released 4k intro found that it did not 199 | work on their own machine. It became customary to include a 200 | 'compatible' version in the distribution which was larger than 4k 201 | but worked on all machines. For a time, it seemed that the term 202 | '4k intro' meant '4k on the compo machine' intro. 203 | 204 | The main motivation for starting the Crinkler project was the feeling 205 | that the existing means available for compressing 4k intros were 206 | unsatisfactory. We want 4k intros that are self-contained EXE 207 | files. We want 4k intros that are 4 kilobytes in size. Our aim for 208 | Crinkler is to be the cleanest, most effective and most compatible 209 | executable file compressor for Windows 4k intros. 210 | 211 | 212 | 213 | COMPATIBILITY 214 | ------------- 215 | 216 | The goal of Crinkler is for the produced EXE files to be compatible 217 | with all widely used Windows versions and configurations. As of 218 | version 2.0a, the EXE files produced by Crinkler are, to the best of 219 | our knowledge, compatible with Windows XP, Windows Vista, Windows 7, 220 | Windows 8 and Windows 10, both 32 bit and 64 bit versions. They are 221 | compatible with Data Execution Prevention and with execution hooks 222 | that inspect the import or export table of launched executables 223 | (graphics drivers are known to do this). 224 | 225 | It is not a primary goal of Crinkler to anticipate incompatibilities 226 | that may arise in the future as a consequence of new Windows versions, 227 | graphics drivers or other widespread system changes. Guaranteeing such 228 | compatibility would require Crinkler to follow the EXE file format 229 | specification to the letter, precluding most of the header hacks that 230 | Crinkler utilizes in order to reduce the size overhead of the EXE 231 | format as much as possible. Rather, we strive to continually monitor 232 | the compatibility situation and release a new, fixed version of 233 | Crinkler whenever a situation arises that affects the compatibility 234 | severely (such as a new, incompatible version of Windows). This has 235 | occurred several times already throughout the history of Crinkler. 236 | 237 | Each new version of Crinkler not only produces executables that are 238 | compatible with the current majority of targeted systems. It also 239 | includes a way of fixing old Crinkler executables to have the same 240 | level of compatibility. See the section on recompression for more 241 | details on this feature. 242 | 243 | This compatibility strategy ensures that intros made using Crinkler 244 | will continue to be accessible to their audience, even if the Windows 245 | EXE loader changes in an incompatible way that could not be 246 | anticipated at the time the intro was produced. 247 | 248 | 249 | 250 | INTRODUCTION 251 | ------------ 252 | 253 | Crinkler is a different approach to executable file compression. While 254 | an ordinary executable file compressor operates on the executable file 255 | produced by the linker from object files, Crinkler replaces the linker 256 | by a combined linker and compressor. The result is an EXE file which 257 | does not do any kind of dropping. It decompresses into memory like a 258 | traditional executable file compressor. 259 | 260 | Crinkler employs a range of techniques to reduce the size of the 261 | resulting EXE file beyond what is usually obtained by using CAB 262 | compression: 263 | 264 | - Having control over the linking step gives much more flexibility in 265 | the optimizations and transformations possible on the data before 266 | and after compression. 267 | 268 | - The compression technique used by Crinkler is based on context 269 | modelling, which is far superior in compression ratio to the LZ 270 | variants used by CAB and most other compressors. The disadvantage of 271 | context modelling is that it is extremely slow, but this is of 272 | little importance when only 4 kilobytes need to be compressed. It 273 | also needs quite a lot of memory for decompression, but this is 274 | again not a problem, since the typical 4k intro uses a lot of memory 275 | anyway. 276 | 277 | - The actual compression algorithm performs many passes over the data 278 | in order to optimize the internal parameters of the compressor. This 279 | results in slower compression, but this is usually a reasonable 280 | price to pay for the extra bytes gained on the file size. 281 | 282 | - The contents of the executable are split into two parts - a code 283 | part and a data part - and each of these are compressed 284 | individually. This leads to better compression, as code and data are 285 | usually very different in structure and so do not benefit from being 286 | compressed together. 287 | 288 | - DLL functions are imported by hash code. This is robust to 289 | structural changes to the DLL between different versions while being 290 | quite compact - only 4 bytes per imported function. For DLLs with 291 | fixed relative ordinals (such as opengl32), a special technique, 292 | ordinal range import, can be used to further reduce the number of 293 | hash codes needed. 294 | 295 | - Much of the data in the EXE header is actually ignored by the EXE 296 | loader. This space is used for some of the decompression code. 297 | 298 | Using Crinkler is somewhat different from using an ordinary executable 299 | file compressor because of the linking step. In the following 300 | sections, we describe its use in detail. 301 | 302 | 303 | 304 | INSTALLATION 305 | ------------ 306 | 307 | To use as a stand-alone linker, Crinkler does not need any 308 | installation. Simply run crinkler.exe from the commandline with 309 | appropriate arguments, as described in the next section. 310 | 311 | However, if you are using Microsoft Visual Studio to develop your 312 | intro, the easiest way to use Crinkler is to run it in place of the 313 | normal Visual Studio linker. Crinkler has been designed as a drop-in 314 | replacement of the Visual Studio linker, supporting the same basic 315 | options. All of the options can then be set using the Visual Studio 316 | configuration window. 317 | 318 | Unfortunately, Visual Studio does not (as of this writing) support 319 | replacing its linker by a different one. So what you have to do to 320 | make Visual Studio use Crinkler for linking is the following: 321 | 322 | - Copy crinkler.exe to your project directory or to some other 323 | directory of your choice and rename it to link.exe. If you are using 324 | some other linker with a different name, such as the one used with 325 | the Intel C++ compiler, call it whatever the name of the linker is. 326 | 327 | - For Visual Studio 2008 and older, select Tools/Options... and go to 328 | Projects and Solutions/VC++ Directories. For Visual Studio 2010 or 329 | newer, open a project, select View/Property Manager, expand a project 330 | and a configuration, double click on Microsoft.Cpp.Win32.user and go 331 | to Common Properties/VC++ Directories. 332 | 333 | - At the top of the list for Executable files, add the directory where 334 | you placed Crinkler named link.exe, or add $(SolutionDir) to make it 335 | search in the project directory. 336 | 337 | - In the Release configuration (or whichever configuration you want to 338 | enable compression), under Linker/Command Line/Additional Options, 339 | type in /CRINKLER, along with any other Crinkler options you want to 340 | set. See the next section for more details on options. Also set 341 | Linker/Manifest File/Generate Manifest to No and 342 | C/C++/Optimization/Whole Program Optimization to No. 343 | 344 | If you have Visual Studio installed but want to run Crinkler from the 345 | commandline, the easiest way is to use the Visual Studio Command 346 | Prompt (available from the Start menu), since this sets up the LIB 347 | environment variable correctly. You can read off the value of the 348 | environment variables by running the 'set' command in this command 349 | prompt. If you are using a different command prompt, you will have to 350 | set up the LIB environment variable manually, or use the /LIBPATH 351 | option. 352 | 353 | 354 | 355 | USAGE 356 | ----- 357 | 358 | The general form of the command line for Crinkler is: 359 | 360 | CRINKLER [options] [object files] [library files] [@commandfile] 361 | 362 | When running from within Visual Studio, the object files will be the 363 | ones generated from the sources in the project. The library files will 364 | be the standard set of Win32 libraries, plus any additional library 365 | files specified under Linker/Input/Additional Dependencies. If you are 366 | using a standard runtime library, such as msvcrt, you will have to 367 | specify this one manually. See the section on standard libraries for 368 | more information. 369 | 370 | 371 | The following options are compatible with the VS linker and can be set 372 | using switches in the Visual Studio configuration window: 373 | 374 | /SUBSYSTEM:CONSOLE 375 | /SUBSYSTEM:WINDOWS 376 | (Linker/System/SubSystem) 377 | 378 | Specify the Windows subsystem to use. If the subsystem is CONSOLE, 379 | a console window will be opened when the program starts. The 380 | subsystem also determines the name of the default entry point (see 381 | /ENTRY). The default subsystem is WINDOWS. 382 | 383 | /LARGEADDRESSAWARE 384 | /LARGEADDRESSAWARE:NO 385 | (Linker/System/Enable Large Addresses) 386 | 387 | Specify whether the executable is able to handle addresses above 388 | 2 gigabytes. If this option is enabled, the executable will be able 389 | to allocate close to 4 gigabytes of memory. 390 | 391 | /OUT:[file] 392 | (Linker/General/Output File) 393 | 394 | Specify the name of the resulting executable file. The default 395 | name is out.exe. 396 | 397 | /ENTRY:[symbol] 398 | (Linker/Advanced/Entry Point) 399 | 400 | Specify the entry label in the code. The default entry label is 401 | mainCRTStartup for CONSOLE subsystem applications and 402 | WinMainCRTStartup for WINDOWS subsystem applications. 403 | 404 | /LIBPATH:[path] 405 | (Linker/General/Additional Library Directories) 406 | 407 | Add a number of directories (separated by semicolons) to the ones 408 | searched for library files. If a library is not found in any of 409 | these, the directories mentioned in the LIB environment variable 410 | are searched. 411 | 412 | @commandfile 413 | 414 | Commandline arguments will be read from the given file, as if they 415 | were given directly on the commandline. 416 | 417 | 418 | In addition to the above options, a number of options can be given to 419 | control the compression process. These can be specified under 420 | Linker/Command Line/Additional Options: 421 | 422 | /CRINKLER 423 | 424 | Enable the Crinkler compressor. If this option is disabled, 425 | Crinkler will search through the path for a command with the same 426 | name as itself, skipping itself, and pass all arguments on to this 427 | command instead. This will normally invoke the Visual Studio 428 | linker. If the name of the Crinkler executable is crinkler.exe, 429 | this option is enabled by default, otherwise it is disabled by 430 | default. 431 | 432 | /RECOMPRESS 433 | 434 | Decompress a Crinkler-compressed executable and recompress it 435 | using the given options. The resulting executable will have the 436 | same level of compatibility as one produced directly by the 437 | current version of Crinkler. See the section on compatibility for 438 | more information on the compatibility of Crinkler-produced 439 | executables. 440 | 441 | When this option is specified, Crinkler takes a single file 442 | argument, which must be an EXE file produced by Crinkler 0.4 or 443 | newer. 444 | 445 | See the section on recompression below for a description of the 446 | options that can be given to control the decompression process. 447 | 448 | /PRIORITY:IDLE 449 | /PRIORITY:BELOWNORMAL 450 | /PRIORITY:NORMAL 451 | 452 | Select the process priority at which Crinkler will run while 453 | compressing. The default priority is BELOWNORMAL. Use IDLE if you 454 | want Crinkler to disturb you as little as possible. Use NORMAL if 455 | you don't need your machine for anything else while compressing. 456 | 457 | /COMPMODE:INSTANT 458 | /COMPMODE:FAST 459 | /COMPMODE:SLOW 460 | /COMPMODE:VERYSLOW 461 | 462 | Choose between four different algorithms for the model estimation. 463 | The FAST compression mode performs a very quick estimation, whereas 464 | the SLOW mode takes up to some tens of seconds for a typical 4k, 465 | but also compresses significantly better. VERYSLOW is about 5-10x 466 | slower than SLOW and typically a few bytes better. INSTANT skips 467 | model estimation entirely and just uses a fixed set of models and 468 | weights. It also skips section reordering and hash table size 469 | optimization. Use INSTANT if you just want to check that your 470 | program works in compressed form and don't care about the size. 471 | The default compression mode is SLOW. 472 | 473 | /SATURATE 474 | 475 | The compressor and decompressor use pairs of 8-bit counters to 476 | track the distributions of 0 and 1 bits for each context. If your 477 | data is very repetitive (contains large blocks of the same pattern 478 | of values repeated over and over again), these counters may wrap 479 | around, which can sometimes hurt compression of these repetitive 480 | areas. 481 | 482 | This option inserts extra code in the decompression header to keep 483 | these counters from wrapping. It is worth trying out if you have 484 | large, repetitive regions and see in the compression report that 485 | the data in these regions suddenly jumps up from lightest green to 486 | slightly darker green for no apparent reason. 487 | 488 | /HASHSIZE:[memory size] 489 | 490 | Specify the amount of memory the decompressor is allowed to use 491 | while decompressing, in megabytes. In general, the more memory the 492 | decompressor is allowed to use, the better the compression ratio 493 | will be, though only slightly. The memory requirements of the 494 | final executable (the size of the executable image when loaded 495 | into memory) will be the maximum of this value and the original 496 | image size. The memory will not be deallocated until the program 497 | terminates, and any heap allocation the program performs will add 498 | to this memory usage. The default value is 100, which is usually a 499 | good compromise. 500 | 501 | /HASHTRIES:[number of retries] 502 | 503 | Specify the number of different hash table sizes the compressor 504 | will try in order to find one with few collisions. More tries lead 505 | to longer compression time but slightly better compression. The 506 | default value is 20. Higher values rarely improve the size by more 507 | than a few bytes. 508 | 509 | /TINYHEADER 510 | Enables an alternative compression algorithm trading off some 511 | compression efficiency for an even smaller decompression overhead. 512 | This can be beneficial when targeting extremely small file sizes 513 | such as 1kb. The simpler decompressor gathers statistics by 514 | repeated linear searches instead of hashing. This results in 515 | an O(n^2) decompression time which can become prohibitively slow 516 | for files significantly larger than 1kb. 517 | 518 | The COMPMODE, HASHSIZE, HASHTRIES, REUSE, SATURATE and EXPORT 519 | options are ignored when TINYHEADER is enabled. 520 | 521 | /TINYIMPORT 522 | Enables a more compact, but less future-proof, function importing 523 | scheme which does not require the explicit storage of function 524 | name hashes. This is achieved by indiscriminately importing every 525 | function from the relevant DLLs. The imported functions are 526 | scattered in an import table based on their function name hashes. 527 | Intuitively, this embeds the hash code entropy directly into the 528 | call instruction. 529 | 530 | Crinkler ensures that the import table size and hash function are 531 | chosen such that there are no collisions between the functions 532 | used by the linked program and other functions which are imported 533 | later from the DLLs. This way, the desired function pointers will 534 | be intact in the import table. 535 | 536 | However, Crinkler can only ensure this for functions that it knows 537 | about. These include the functions present in the DLLs on the 538 | system on which Crinkler is run, plus an internal list consisting 539 | of functions from commonly imported DLLs covering most supported 540 | Windows versions available at the time of release (Spring Creators 541 | Update 2018 version 1803 as of Crinkler 2.0a). 542 | 543 | Thus, this import technique is less resilient to changes in 544 | future windows versions, since when functions are added in a 545 | future version of the DLL, they may collide with functions used by 546 | the program, in which case the program will cease to work. 547 | Programs broken this way cannot be fixed by recompression. 548 | 549 | When using this options, it is strongly recommended to also 550 | distribute safe versions using ths normal import mechanism. 551 | 552 | The UNSAFEIMPORT, FALLBACKDLL and RANGE options are ignored 553 | when TINYIMPORT is enabled. 554 | 555 | /ORDERTRIES:[number of retries] 556 | 557 | Specify the number of section reordering iterations that the 558 | linker will try out in search for the ordering that gives the best 559 | compression ratio. The default is not to do any reordering. 560 | Crinkler starts from a heuristic ordering (the one used when 561 | initially estimating models) and incrementally makes small, random 562 | changes to the ordering to see if it can find one that compresses 563 | better. 564 | 565 | Specifying this option drastically increases the compression time, 566 | since Crinkler has to calculate the compressed size anew on every 567 | reordering. Usually, the size does not improve noticeably after a 568 | few thousand iterations. 569 | 570 | /REUSE:[reuse parameter file name] 571 | /REUSEMODE:STABLE 572 | /REUSEMODE:IMPROVE 573 | /REUSEMODE:WRITE 574 | /REUSEMODE:OFF 575 | 576 | After compression, write information about the selected models, 577 | the ordering of sections and the optimized hash table size to a 578 | text file with the specified name. If the file exists already, 579 | use the parameters in the file as input to the compression in a 580 | manner dependent on the chosen REUSEMODE: 581 | 582 | With STABLE (the default), skip all model estimation, section 583 | reordering and hash table size optimization and simply use the 584 | parameters exactly as in the file. Keep the reuse file as is. 585 | This option can be used to try out small changes to the contents 586 | of the code and data with a stable compression. Thus, it gives 587 | a much more reliable estimation of whether the change was an 588 | improvement or not. It is also useful as a way to compress very 589 | quickly after the first time with a similar compression ratio. 590 | 591 | With IMPROVE, only the section ordering from the file is reused, 592 | and a normal compression procedure is performed. If section 593 | reordering is enabled, it starts from the ordering in the reuse 594 | file and tries to optimize the ordering based on that. The file is 595 | written back only if the final file size is smaller than what 596 | the parameters in the reuse file would have given (which is not 597 | necessarily the size of the existing file, depending on what 598 | changes and operations are performed in the meantime). 599 | The option can be used to check whether better parameters can be 600 | found than the ones cached in the reuse file. It is also a way to 601 | run some extra reordering iterations (if reordering is enabled) 602 | to see if this improves compression. 603 | 604 | For both modes, it can be useful to edit the reuse file by hand 605 | to try out parameters manually or to nudge Crinkler in some 606 | direction. 607 | 608 | With WRITE, the reuse file is not read, but is still written 609 | after compression, overwriting the file if it exists. This can be 610 | conveniently used when reuse is not desired, such that it can be 611 | switched on at any time (by changing the reuse mode to STABLE or 612 | IMPROVE) without needing another compression run. 613 | 614 | With OFF, it is as if no reuse file is specified. This is simply 615 | a way to disable the option without removing the file from the 616 | commandline. 617 | 618 | If COMPMODE is set to INSTANT, the reuse mode is also considered 619 | to be OFF. 620 | 621 | /RANGE:[DLL name] 622 | 623 | Import functions from the given DLL (without the .dll suffix) 624 | using ordinal range import. Ordinal range import imports the first 625 | used function by hash and the rest by ordinal relative to the 626 | first one. Ordinal range import is safe to use on DLLs in which 627 | the ordinals are fixed relative to each other, such as opengl32 or 628 | d3dx9_??. This option can be specified multiple times, for 629 | different DLLs. 630 | 631 | /REPLACEDLL:[oldDLL]=[newDLL] 632 | 633 | Whenever a function is imported from oldDLL, import it from newDLL 634 | instead. DLL replacement is useful when the end user might not 635 | have the version of the DLL that you are linking to. A typical use 636 | is to replace one version of d3dx9_?? by another. Only use this 637 | option if you know that the two DLLs are compatible. When 638 | REPLACEDLL and RANGE are used together, RANGE must refer to the 639 | new DLL. 640 | 641 | /FALLBACKDLL:[firstDLL]=[otherDLL] 642 | 643 | If firstDLL fails to load, try loading otherDLL and import the 644 | functions from there instead. For instance, to use d3dcompiler_47 645 | when available but fall back to d3dcompiler_43 otherwise (since 646 | the shader compiler in d3dcompiler_47 is much faster), link 647 | to d3dcompiler_47 and use: 648 | 649 | /FALLBACKDLL:d3dcompiler_47=d3dcompiler_43 650 | 651 | The FALLBACKDLL option can be used together with REPLACEDLL to 652 | specify a primary DLL other than the one your SDK links to. For 653 | instance, if you are using the legacy DirectX SDK (which links to 654 | d3dcompiler_43) and want to have the above prioritization, use: 655 | 656 | /REPLACEDLL:d3dcompiler_43=d3dcompiler_47 657 | /FALLBACKDLL:d3dcompiler_47=d3dcompiler_43 658 | 659 | Arbitrarily long chains of DLL fallback can be used by specifying 660 | the FALLBACKDLL option multiple times, though the chains can of 661 | course not be cyclic. 662 | 663 | /EXPORT:[name] 664 | /EXPORT:[name]=[symbol] 665 | /EXPORT:[name]=[value] 666 | 667 | Include an export table into the executable, containing an export 668 | with the given name. 669 | 670 | The first version exports an existing symbol under its existing 671 | name. The second version exports an existing symbol under a 672 | different name. The third version creates a 32-bit integer with 673 | the given value and exports it under the given name. The value 674 | can be specified in octal (prefixed with 0), decimal or hexadecimal 675 | (prefixed with 0x) format. 676 | 677 | The first version is compatible with the VS linker, but there is 678 | currently no specific field for it in the configuration window. 679 | 680 | The export table will be compressed along with the other data 681 | in the executable and decompressed to the memory address specified 682 | in the export table pointer in the PE header. Thus, the exports 683 | defined this way are only visible to code inspecting the export 684 | table after decompression has taken place. 685 | 686 | For PE header technical reasons, all exports must be placed earlier 687 | in memory than the export table. Thus, only symbols in the code and 688 | data sections can be exported. If an uninitialized (BSS) symbol is 689 | exported, it will be automatically moved to the data section (with 690 | a warning). Beware that this will move the whole section containing 691 | the symbol, so other symbols might be moved along with it. 692 | 693 | The EXPORT option can be used to signal to the graphics driver that 694 | your program desires to run on the high-performance GPU in a multi- 695 | GPU system. This saves the user from having to right-click on the 696 | executable and select "Run with graphics processor...". 697 | 698 | To request high performance on NVIDIA Optimus systems, use: 699 | 700 | /EXPORT:NvOptimusEnablement=1 701 | 702 | To request high performance on AMD PowerXpress/Enduro systems, use: 703 | 704 | /EXPORT:AmdPowerXpressRequestHighPerformance=1 705 | 706 | An arbitrary number of exports can be specified, so the two high 707 | performance declarations can be used together if you have space 708 | enough to spare. 709 | 710 | /UNSAFEIMPORT 711 | 712 | If the executable fails to load some DLL, it will normally pop up 713 | a message box with the DLL name. This option disables this check 714 | to save a few bytes (usually around 20). With unsafe import, the 715 | executable will crash if a needed DLL is not found. 716 | 717 | /TRANSFORM:CALLS 718 | 719 | Change the relative jump offsets in all internal call instructions 720 | (E8 opcode) into absolute offsets from the start of the code. This 721 | usually improves compression, since multiple calls to the same 722 | function become identical. The transformation has an overhead of 723 | about 20 bytes for the detransformation code, but the net savings 724 | on a full 4k can be as large as 50 bytes, depending on the number 725 | of calls in your code. 726 | 727 | /NOINITIALIZERS 728 | 729 | Disable the inclusion of dynamic C++ initializers. The default is 730 | to insert calls to each of the initializers just before the entry 731 | point. 732 | 733 | /TRUNCATEFLOATS:[number of bits] 734 | 735 | Floating point constants can take up a significant amount of space 736 | in an intro, and often much of this space is wasted because the 737 | constants have more precision than needed. Typically, many bytes 738 | can be saved by rounding floating point constants to "nice" values 739 | - that is, values where many bits in the mantissa are zero. 740 | However, such rounding is cumbersome, especially when the 741 | constants are written in decimal notation. 742 | 743 | The purpose of the /TRUNCATEFLOATS option is to automate this 744 | rounding process. When this option is given, Crinkler tries to 745 | identify float and double constants and round them to the number 746 | of bits given (between 1 and 64). If no number is given, 64 is 747 | assumed. 748 | 749 | Typically, object files do not contain any information about what 750 | data is floating point constants and what is not (though the file 751 | format does support such information). This means that in order to 752 | identify floating point constants, Crinkler has to resort to 753 | heuristics based on label names. These heuristics are able to 754 | recognize constants in code and some variables, but far from all. 755 | 756 | You can tell Crinkler explicitly that some variable contains float 757 | data and how much it should be truncated by having the variable 758 | name (or label) start with tf[n]_ where [n] is the number of bits 759 | to truncate the constants to. The number of bits can be omitted, 760 | in which case the number of bits given in the argument to 761 | /TRUNCATEFLOATS is used. Such variables will still only be 762 | truncated if the /TRUNCATEFLOATS option is given. Example: 763 | 764 | const float tf14_positions[] = { 0.1f, 0.35f, 0.25f }; 765 | 766 | This will truncate the constants in the table to 14 bits (5 bits 767 | of mantissa), resulting in the values 0.099609375, 0.3515625 and 768 | 0.25, respectively. Tip: rather than changing the variable name 769 | and all references to it each time you want to change the 770 | truncation precision, use a define: 771 | 772 | #define positions tf14_positions 773 | 774 | Note that /TRUNCATEFLOATS is an unstable and highly experimental 775 | feature. Make sure to test the compressed file to verify that the 776 | result is acceptable. Remember to include the musician in this 777 | verification process. :) 778 | 779 | /OVERRIDEALIGNMENTS:[bits of alignment] 780 | 781 | It is often possible to improve compression by placing 782 | uninitialized variables at addresses divisible by high powers of 783 | two, since this will cause all references to these addresses to 784 | contain more zeros. 785 | 786 | The PE file format only supports up to 13 bits of alignment 787 | (8192), and some tools do not even expose this support fully (for 788 | instance, Nasm only supports alignments up to 64). Usually, much 789 | higher alignments are desirable. 790 | 791 | Crinkler supports explicit alignment of labels at up to one 792 | gigabyte (30 bits). When you specify the /OVERRIDEALIGNMENTS 793 | option, Crinkler will look for labels containing the string 794 | align[n] where [n] is the number of bits of alignment desired 795 | (e.g. 8 for 256-byte alignment). It will then align the section 796 | containing that label such that the label address is divisible by 797 | 2^[n]. The label does not have to be at the beginning of the 798 | section, but there can be at most one explicitly aligned label in 799 | each section. 800 | 801 | The alignment specifier can optionally include an alignment 802 | offset, specified by the string align[n]_[m] where [n] is the 803 | number of bits of alignment and [m] is the offset in bytes. This 804 | will place the label [m] bytes after an aligned address, i.e. such 805 | that the address minus [m] is divisible by 2^[n]. 806 | 807 | If a numerical argument is given to /OVERRIDEALIGNMENTS, all 808 | uninitialized sections which do not contain an explicitly aligned 809 | label will be aligned to the given number of bits (if larger than 810 | their original alignment). If the option is specified without 811 | argument, uninitialized sections which do not contain an 812 | explicitly aligned label will be aligned as specified in the 813 | object file, as normally. 814 | 815 | A convenient way to specify explicit alignments in C++ code is in 816 | a header file included by all files in the project, containing 817 | definitions like this: 818 | 819 | #define MusicBuffer MusicBuffer_align24 820 | 821 | In assembler files, alignments can be specified as local labels: 822 | 823 | MusicBuffer: 824 | .align24 825 | ; buffer space here 826 | 827 | Explicit alignment can be used on code and data sections as well, 828 | except for the section containing the entry point, which will 829 | always be 1-byte aligned. The space between the sections will be 830 | padded with zero bytes. 831 | 832 | /UNALIGNCODE 833 | 834 | Force all code sections to use alignment of 1, eliminating all 835 | padding between them. This usually improves compression, but 836 | can result in slightly lower performance if some functions are 837 | called in performance critical loops. 838 | 839 | The /OVERRIDEALIGNMENTS mechanism has priority over /UNALIGNCODE, 840 | so if you want to excempt a few functions from being unaligned, 841 | you can specify an explicit alignment for these as described for 842 | /OVERRIDEALIGNMENTS. 843 | 844 | 845 | Finally, Crinkler has a number of options for controlling the output 846 | during compression. Just like the other options, these can be 847 | specified under Linker/Command Line/Additional Options: 848 | 849 | /REPORT:[HTML file name] 850 | 851 | Write an HTML file with a detailed, colorful, interactive report 852 | on the compression result. The code section will be shown as hex 853 | dump and disassembly of the code, and the data section will be 854 | shown as hex and ascii dump. All bytes will be colored to show how 855 | much that byte was compressed. This report can be useful in 856 | determining which parts of the executable take up the most space 857 | and which things to change to reduce the size. 858 | 859 | /PRINT:LABELS 860 | 861 | Print a list of all labels in the program along with uncompressed 862 | and compressed sizes for the data between the labels. This is a 863 | stripped down version of the information provided by the /REPORT 864 | option. 865 | 866 | /PRINT:IMPORTS 867 | 868 | List all functions imported from DLLs. The functions are grouped 869 | by DLL, and functions imported by ordinal range import are grouped 870 | into ranges. 871 | 872 | /PRINT:MODELS 873 | 874 | List the model masks and weights selected by the compressor. This 875 | is mostly for internal use. 876 | 877 | /PROGRESSGUI 878 | 879 | Open a window showing a graphical progress indicator. 880 | 881 | 882 | An example commandline for linking and compressing an intro could look 883 | like this (split on multiple lines for readability): 884 | 885 | crinkler.exe /OUT:micropolis.exe /SUBSYSTEM:WINDOWS /RANGE:opengl32 886 | /COMPMODE:SLOW /ORDERTRIES:1000 /PRINT:IMPORTS /PRINT:LABELS 887 | kernel32.lib user32.lib gdi32.lib opengl32.lib glu32.lib winmm.lib 888 | micropolis\startup.obj micropolis\render.obj 889 | micropolis\render-asm.obj micropolis\sound.obj 890 | micropolis\sound-asm.obj 891 | 892 | 893 | 894 | RECOMPRESSION 895 | ------------- 896 | 897 | A new feature in Crinkler 1.2 is the abillity to recompress an already 898 | Crinkler-compressed executable. The main purpose for the feature is to 899 | patch an executable compressed using an earlier version of Crinkler so 900 | that it runs on recent Windows versions. But it can also be used 901 | more generally to change some of the compression parameters of a 902 | compressed program without performing the whole linking and 903 | compression process from scratch and without access to the original 904 | object files. Particularly, if your output executable after a long 905 | time spent compressing is just a few bytes too big due to bytes lost 906 | to hashing, you can recompress the output executable, specifying a 907 | higher value for /HASHSIZE and/or /HASHTRIES, and thus avoid running 908 | through the whole compression process again. 909 | 910 | Recompression mode is activated by the /RECOMPRESS option. When this 911 | option is specified, Crinkler takes a single file argument, which must 912 | be an EXE file produced by Crinkler 0.4 or newer. Most options then 913 | take on slightly different meanings, as described here. 914 | 915 | The /CRINKLER, /PRIORITY, @commandfile and /PROGRESSGUI options work 916 | as normally. The /ENTRY, /LIBPATH, /ORDERTRIES, /RANGE, /FALLBACKDLL, 917 | /UNSAFEIMPORT, /TRANSFORM:CALLS, /NOINITIALIZERS, /TRUNCATEFLOATS, 918 | /OVERRIDEALIGNMENTS, /UNALIGNCODE, /TINYHEADER and /TINYIMPORT options 919 | are ignored, as the parameters specified by these options cannot be 920 | changed via recompression. The /PRINT options are also ignored. The 921 | remaining options work as follows: 922 | 923 | /SUBSYSTEM:CONSOLE 924 | /SUBSYSTEM:WINDOWS 925 | 926 | If this option is given, it specifies the Windows subsystem to use 927 | as normally. If it is omitted, the original subsystem will be 928 | used. 929 | 930 | /LARGEADDRESSAWARE 931 | /LARGEADDRESSAWARE:NO 932 | 933 | If this option is given, it specifies large address awareness of the 934 | executable as normally. If it is omitted, the original large address 935 | awareness will be used. 936 | 937 | /OUT:[file] 938 | 939 | Specify the name of the resulting executable file. The default is 940 | to overwrite the input file. 941 | 942 | /COMPMODE:INSTANT 943 | /COMPMODE:FAST 944 | /COMPMODE:SLOW 945 | 946 | If this option is specified, the compression models will be 947 | reestimated using the specified compression mode. If the option is 948 | omitted, the models used for the original compression will be used 949 | for the recompression, and no model estimation will be performed. 950 | If the executable was originally produced by Crinkler 1.0 or 951 | newer, this will typically yield a compression ratio similar to 952 | the original compression. 953 | 954 | /SATURATE 955 | /SATURATE:NO 956 | 957 | If this option is given, it specifies saturation as normally. If 958 | it is omitted, the original saturation mode will be used. 959 | 960 | /HASHSIZE:[memory size] 961 | 962 | If neither this option nor a compression mode is specified, the 963 | original, optimized hash size will be used. Recompression speed 964 | will be similar to INSTANT compression mode in this case. 965 | 966 | If a compression mode is specified but this option is omitted, 967 | hash size optimization will be performed using the hash size 968 | specified for the original file. 969 | 970 | If this option is given, hash size optimization takes place 971 | normally, using the specified maximum size. 972 | 973 | /HASHTRIES:[number of retries] 974 | 975 | If hash size optimization takes place, this option specifies the 976 | number of tries as normally. Otherwise it is ignored. 977 | 978 | /REPLACEDLL:[oldDLL]=[newDLL] 979 | 980 | Replaces an original DLL by a new one. Only works if the names 981 | of the DLLs are exactly the same length. 982 | 983 | /STRIPEXPORTS 984 | 985 | This is a recompression specific option which instructs Crinkler 986 | to strip away any existing exports from the executable. New exports 987 | can be added using the /EXPORT option whether or not the existing 988 | exports are stripped away. 989 | 990 | /EXPORT:[name] 991 | /EXPORT:[name]=[symbol] 992 | /EXPORT:[name]=[value] 993 | 994 | Adds an export to the executable, as normally. The first two 995 | versions can only refer to an existing export in the executable 996 | that was exported using one of the first two versions in the first 997 | place. They can refer to such an export even if existing exports 998 | are stripped away using the /STRIPEXPORTS option. 999 | 1000 | If an export already exists with the same name, the new export 1001 | replaces the existing one. 1002 | 1003 | /REPORT:[HTML file name] 1004 | 1005 | Writes out an HTML file as normally. Since no symbol information 1006 | is available, this will be a plain disassembly/hex dump without 1007 | labels or cross-linking. 1008 | 1009 | 1010 | 1011 | STANDARD RUNTIME LIBRARIES 1012 | -------------------------- 1013 | 1014 | Under normal circumstances, the Visual Studio compiler generates code 1015 | that requires a C runtime library containing standard C functions and 1016 | various support functions. These functions can either be linked in 1017 | statically (included into the executable) or dynamically via a runtime 1018 | DLL. For size-sensitive applications, you should always link 1019 | dynamically, which is achieved by setting C/C++/Code 1020 | Generation/Runtime Library to Multi-threaded DLL (/MD). 1021 | 1022 | Note however, that the standard runtime libraries for Visual Studio 1023 | 2005 or newer will not work with Crinkler-compressed executables, 1024 | since these runtime libraries require a manifest in the executable, 1025 | and Crinkler does not support manifests. Furthermore, these DLLs are 1026 | not present by default on Windows installations, so you will usually 1027 | not want your program to be dependent on them. 1028 | 1029 | To work around this, link to the Visual Studio 6 runtime library - 1030 | msvcrt.dll - which is distributed with all Windows versions. This is 1031 | done by using the Visual Studio 6 version of msvcrt.lib, which can be 1032 | obtained thus: 1033 | 1034 | - Download Service Pack 6 for Visual Studio 6.0 at 1035 | https://www.microsoft.com/en-us/download/details.aspx?id=9183 1036 | - Place the downloaded self-extractor in an empty directory and run 1037 | it, or open it using an archive tool such as WinRAR. 1038 | - Open the VS6sp61.cab file and go to the vc98\lib directory. There 1039 | you will find the msvcrt.lib file. 1040 | - Rename this file to something else (such as msvcrt_old.lib) and 1041 | place it in your project directory. 1042 | - Add msvcrt_old.lib to the list of libraries to link to at 1043 | Linker/Input/Additional Dependencies. 1044 | 1045 | There are a couple of caveats to using an older runtime library than 1046 | the compiler expects, though. With out-of-the-box compilation 1047 | options, the Visual Studio compiler generates code that requires some 1048 | support functions which are only present in newer runtime DLLs. To 1049 | avoid these dependencies, set the following options under C/C++/Code 1050 | Generation: 1051 | 1052 | - Basic Runtime Checks: Default 1053 | - Buffer Security Check: No (/GS-) 1054 | 1055 | Also, do not use C++ exception handling in your code. And do not use 1056 | STL classes, since they use exceptions all over. 1057 | 1058 | Finally, even when using the DLL-based runtime, not all support code 1059 | is linked dynamically. The runtime library contains an entry function 1060 | which is included into the executable and takes care of things like 1061 | parsing the commandline and executing dynamic initializers. The entry 1062 | function then calls the main function. 1063 | 1064 | The standard entry function is around half a kilobyte in size and is 1065 | usually not needed for intro code to function properly. To avoid this 1066 | overhead, define your own entry function, either by defining a 1067 | function called mainCRTStartup or WinMainCRTStartup (depending on 1068 | which Windows subsystem you use) or by using the /ENTRY option. 1069 | 1070 | The best strategy is of course to avoid linking to a runtime DLL at 1071 | all, assuming you can do without the functions provided by the 1072 | standard runtime library. This will save the space for importing the 1073 | runtime DLL. 1074 | 1075 | To reduce the dependencies on the standard runtime DLL as much as 1076 | possible, set the following options: 1077 | 1078 | - C/C++/Optimization/Enable Intrinsic Functions: Yes (/Oi). This will 1079 | cause several standard functions (mainly math, string and memory 1080 | functions) to generate inline code rather than a function call. 1081 | - C/C++/Code Generation/Floating Point Model: Fast (/fp:fast). 1082 | - C/C++/Command Line: Add the option /QIfist. This will cause 1083 | conversions from floating point to integer to use the FIST 1084 | instruction rather than calling a conversion function. Note that 1085 | this changes the semantics of conversions from truncation to 1086 | round-to-nearest (unless you explicitly change the rounding mode of 1087 | the FPU). On the other hand, it will also give a considerable speed 1088 | boost. 1089 | 1090 | 1091 | 1092 | RECOMMENDATIONS 1093 | --------------- 1094 | 1095 | There are a number of things you can do as intro programmer to boost 1096 | the compression achieved by Crinkler even further. This section 1097 | gives some advice on these. 1098 | 1099 | - Since much of the effectiveness of Crinkler comes from separating 1100 | code and data into different parts of the file and compressing each 1101 | part individually, it is important that this separation is 1102 | possible. Mark your code and data sections as containing code and 1103 | data, respectively, and do not put both code and data into the same 1104 | section. See your assembler manual for information about how to do 1105 | this. For instance, in Nasm, you can write the keyword "text" or 1106 | "data" after the section name and give sections different names to 1107 | prevent them from being merged by the assembler. 1108 | 1109 | - Split both your code and your data into as many sections as 1110 | possible. This gives Crinkler more opportunities to select the 1111 | ordering of the sections to optimize the compression ratio. 1112 | 1113 | - If you are using OpenGL, try using ordinal range import for 1114 | opengl32. If you are using Direct3D, try using ordinal range import 1115 | for d3dx9_??. This may reduce the space needed for function hash 1116 | codes. 1117 | 1118 | - If you are only importing functions from DLLs which are present on 1119 | all Windows systems (d3dx9_?? is not), you can "safely" use the 1120 | /UNSAFEIMPORT option. Run Crinkler with the /PRINT:IMPORTS option 1121 | to check which DLLs you are importing from. 1122 | 1123 | - Avoid large blocks of data, even if they are all zero. Use 1124 | uninitialized (bss) sections instead. Crinkler does not cope well 1125 | with large amounts of data. Be aware that the compressor may use an 1126 | amount of memory up to about 4000 times the uncompressed code/data 1127 | size (whichever is largest). 1128 | 1129 | - When you perform detailed size comparisons, always use the SLOW 1130 | compression mode with plenty of ORDERTRIES and compare the 1131 | "Ideal compressed total size" values. The INSTANT and FAST modes 1132 | are only intended for use during testing and to give a rough estimate 1133 | of the compressed size. Use the /REUSE option when making small 1134 | changes to achieve stable size comparison. Also note that the 1135 | compression is tuned for the 4k size target, so any size comparisons 1136 | you perform on smaller files might turn out to behave differently 1137 | when you get nearer to 4k. 1138 | 1139 | - As a matter of good conduct, do not use TINYIMPORT or UNSAFEIMPORT 1140 | if you can spare the space, and do not set HASHSIZE higher than 1141 | you need. In other words, if your final intro is well below the size 1142 | limit, remove the UNSAFEIMPORT option (if you added it in the 1143 | first place) and then lower HASHSIZE in order not to waste memory 1144 | unnecessarily. Also consider adding the high-performance GPU request 1145 | exports as described under the EXPORT option if your intro could 1146 | benefit from it. 1147 | 1148 | 1149 | 1150 | COMMON PROBLEMS, KNOWN BUGS AND LIMITATIONS 1151 | ------------------------------------------- 1152 | 1153 | Any DLL that is needed by a program that Crinkler compresses must be 1154 | available to Crinkler itself. If you get the error message 'Could not 1155 | open DLL ...', it means that Crinkler needed the DLL but could not 1156 | find it. You must place it either in the same directory as the 1157 | Crinkler executable or somewhere in the DLL path, such as 1158 | C:\WINDOWS\system32. Alternatively, you can use the REPLACEDLL option 1159 | to replace it by one that is available. If you get this message for 1160 | msvcr?? DLLs, you have a dependency on the runtime DLL you need to get 1161 | rid of. See the section on standard libraries. 1162 | 1163 | When running inside Visual Studio, the textual progress bars are not 1164 | updated correctly, since the Visual Studio console does not flush the 1165 | output until a newline is reached, even when explicitly flushed by the 1166 | running program. Use the /PROGRESSGUI option to get a graphical 1167 | progress bar. 1168 | 1169 | The code for parsing object and library files contains only a minimum 1170 | of sanity checks. If you pass a corrupt file to Crinkler, it will most 1171 | likely crash. 1172 | 1173 | The final compressed size must be less than 128k, or Crinkler will fail 1174 | horribly. You shouldn't use it for such big files anyway. 1175 | 1176 | 1177 | 1178 | SUPPORT 1179 | ------- 1180 | 1181 | Try out Crinkler, and let us know what you think about it. If you have 1182 | any problems, comments or suggestions, please write a message at the 1183 | Pouet.net forum: 1184 | 1185 | http://www.pouet.net/prod.php?which=18158 1186 | 1187 | If you want to contact us directly, e.g. for sending us a file, write 1188 | to authors@crinkler.net. 1189 | 1190 | If Crinkler crashes, it will write two dump files named 1191 | dump_mini.dmp and dump_full.dmp, where is an integer making 1192 | the file name unique. These files contain information about the 1193 | execution state of Crinkler at the time of the crash. When reporting a 1194 | crash, please include at least the mini dump, or, if possible, both. 1195 | 1196 | The newest released version of Crinkler can always be found at 1197 | http://www.crinkler.net. 1198 | 1199 | 1200 | 1201 | ACKNOWLEDGEMENTS 1202 | ---------------- 1203 | 1204 | The compression technique used by Crinkler is much inspired by the PAQ 1205 | compressor by Matt Mahoney. 1206 | 1207 | The import code is loosely based on the hashed imports code by Peci. 1208 | 1209 | The disassembly feature of the compression report uses the diStorm 1210 | disassembler library by Gil Dabah. 1211 | 1212 | Many thanks to all the people who have given us comments, bug reports 1213 | and test material, in particular to Rambo, Kusma, Polaris, Gargaj, 1214 | Frenetic, Buzzie, Shash, Auld, Minas, Skarab, Dwing, Freak5, Hunta, 1215 | Snq, Darkblade, Abductee, iq, Las, pirx, Hitchhikr, Gloom, Zephod, 1216 | coda, KK, XMunkki, KammutierSpule, acidbrain, xTrim, jix, SubV242, 1217 | w23, ryg, shinmai, Decipher, xtrium, TomasRiker, smoothstep, XT95, 1218 | NeKoFu, n3Xus, Moerder, merry, RCL, zoom, vampire7, Key-Real, 1219 | quiller, Seven, and all the ones we have forgotten. Also thanks to 1220 | Dwarf, Polygon7 and Gargaj for suggestions for our web design. 1221 | 1222 | Big thanks to Rrrola for his valuable suggestions for optimizing the 1223 | decompression code, and to qkumba for his guidance on the zero-section 1224 | header and for tracking down the NVIDIA driver issue. 1225 | 1226 | Our special thanks to the many people who have demonstrated the 1227 | usefulness of Crinkler by using it for their own productions. 1228 | 1229 | Keep it going! We greatly appreciate your feedback. 1230 | -------------------------------------------------------------------------------- /devour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armak/pbr-devour/9230852fb31e598af6baa835a86d2b5490a64478/devour.png -------------------------------------------------------------------------------- /leviathan.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Editor 10 | Win32 11 | 12 | 13 | Heavy Release 14 | Win32 15 | 16 | 17 | Release 18 | Win32 19 | 20 | 21 | Snapshot 22 | Win32 23 | 24 | 25 | 26 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5} 27 | Leviathan 2.0 28 | 10.0.17134.0 29 | 30 | 31 | 32 | Application 33 | v141 34 | false 35 | 36 | 37 | Application 38 | v141 39 | false 40 | 41 | 42 | Application 43 | v141 44 | false 45 | 46 | 47 | Application 48 | v141 49 | false 50 | 51 | 52 | Application 53 | v141 54 | false 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | <_ProjectFileVersion>12.0.30501.0 81 | 82 | 83 | out\ 84 | bin\debug\ 85 | false 86 | $(SolutionDir);$(ExecutablePath) 87 | leviathan-debug 88 | false 89 | true 90 | 91 | 92 | out\ 93 | bin\debug\ 94 | false 95 | $(SolutionDir);$(ExecutablePath) 96 | leviathan-debug 97 | false 98 | true 99 | 100 | 101 | out\ 102 | bin\release\ 103 | 104 | 105 | 106 | 107 | $(SolutionDir);$(ExecutablePath) 108 | leviathan-release 109 | true 110 | true 111 | 112 | 113 | out\ 114 | bin\release\ 115 | 116 | 117 | 118 | 119 | $(SolutionDir);$(ExecutablePath) 120 | leviathan-release 121 | true 122 | true 123 | 124 | 125 | out\ 126 | bin\release\ 127 | 128 | 129 | 130 | 131 | $(SolutionDir);$(ExecutablePath) 132 | leviathan-snapshot 133 | true 134 | true 135 | 136 | 137 | 138 | _DEBUG;%(PreprocessorDefinitions) 139 | true 140 | true 141 | Win32 142 | .\bin/Debug/Leviathan.tlb 143 | 144 | 145 | 146 | MinSpace 147 | ../;%(AdditionalIncludeDirectories) 148 | _DEBUG;WINDOWS;DEBUG;SIMD;A32BITS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 149 | Default 150 | MultiThreaded 151 | 152 | 153 | bin\debug\ 154 | bin\debug\ 155 | bin\debug\ 156 | false 157 | Level3 158 | false 159 | true 160 | ProgramDatabase 161 | CompileAsCpp 162 | OnlyExplicitInline 163 | true 164 | Size 165 | true 166 | false 167 | false 168 | StdCall 169 | stdcpp14 170 | 171 | 172 | _DEBUG;%(PreprocessorDefinitions) 173 | 0x0409 174 | 175 | 176 | winmm.lib;opengl32.lib;src/4klang/4klang.obj;%(AdditionalDependencies) 177 | out/leviathan-debug.exe 178 | true 179 | true 180 | bin\debug\$(TargetName).pdb 181 | Windows 182 | false 183 | 184 | MachineX86 185 | entrypoint 186 | true 187 | bin\debug\$(TargetName).pgd 188 | 189 | 190 | true 191 | $(OutDir)Leviathan.bsc 192 | 193 | 194 | false 195 | 196 | 197 | shader_minifier.exe -v -o src\shaders\texture.inl src\shaders\texture.frag 198 | shader_minifier.exe -v --no-renaming -o src\shaders\fragment.inl src\shaders\fragment.frag 199 | shader_minifier.exe -v -o src\shaders\post.inl src\shaders\post.frag 200 | Minify user-supplied shader files. 201 | 202 | 203 | 204 | 205 | _DEBUG;%(PreprocessorDefinitions) 206 | true 207 | true 208 | Win32 209 | .\bin/Debug/Leviathan.tlb 210 | 211 | 212 | 213 | 214 | MinSpace 215 | ../;%(AdditionalIncludeDirectories) 216 | EDITOR_CONTROLS;_DEBUG;WINDOWS;DEBUG;SIMD;A32BITS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 217 | Default 218 | MultiThreaded 219 | 220 | 221 | bin\debug\ 222 | bin\debug\ 223 | bin\debug\ 224 | false 225 | Level3 226 | false 227 | true 228 | ProgramDatabase 229 | CompileAsCpp 230 | OnlyExplicitInline 231 | true 232 | Size 233 | true 234 | Sync 235 | false 236 | StdCall 237 | false 238 | 239 | 240 | 241 | 242 | _DEBUG;%(PreprocessorDefinitions) 243 | 0x0409 244 | 245 | 246 | winmm.lib;opengl32.lib;src/4klang/4klang.obj;%(AdditionalDependencies) 247 | out/leviathan-debug.exe 248 | true 249 | true 250 | bin\debug\$(TargetName).pdb 251 | Console 252 | false 253 | 254 | 255 | MachineX86 256 | 257 | 258 | true 259 | bin\debug\$(TargetName).pgd 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | true 268 | $(OutDir)Leviathan.bsc 269 | 270 | 271 | false 272 | 273 | 274 | shader_minifier.exe -v -o src\shaders\texture.inl src\shaders\texture.frag 275 | shader_minifier.exe -v --no-renaming -o src\shaders\fragment.inl src\shaders\fragment.frag 276 | shader_minifier.exe -v -o src\shaders\post.inl src\shaders\post.frag 277 | Minify user-supplied shader files. 278 | 279 | 280 | 281 | 282 | NDEBUG;%(PreprocessorDefinitions) 283 | true 284 | true 285 | Win32 286 | .\bin/Release/Leviathan.tlb 287 | 288 | 289 | 290 | MinSpace 291 | OnlyExplicitInline 292 | true 293 | Size 294 | ../;%(AdditionalIncludeDirectories) 295 | WINDOWS;A32BITS;SIMD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 296 | false 297 | false 298 | MultiThreaded 299 | 1Byte 300 | false 301 | Fast 302 | bin\Release\Leviathan.pch 303 | AssemblyAndSourceCode 304 | bin\release\ 305 | bin\release\ 306 | bin\release\ 307 | Level3 308 | false 309 | true 310 | StdCall 311 | CompileAsCpp 312 | true 313 | false 314 | false 315 | Default 316 | NotSet 317 | true 318 | false 319 | ProgramDatabase 320 | 321 | 322 | false 323 | /GR- /EHa- %(AdditionalOptions) 324 | 325 | 326 | 327 | NDEBUG;%(PreprocessorDefinitions) 328 | 0x0409 329 | 330 | 331 | /CRINKLER /RANGE:opengl32 /HASHTRIES:300 /COMPMODE:FAST /ORDERTRIES:1000 /REPORT:out.html /PROGRESSGUI /HASHSIZE:100 /UNSAFEIMPORT /UNALIGNCODE /SATURATE /NOINITIALIZERS /TRANSFORM:CALLS %(AdditionalOptions) 332 | opengl32.lib;winmm.lib;src/4klang/4klang.obj;%(AdditionalDependencies) 333 | out\leviathan-release.exe 334 | 335 | 336 | true 337 | Windows 338 | entrypoint 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | bin\release\$(TargetName).pdb 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | true 360 | .\bin/Release/Leviathan.bsc 361 | 362 | 363 | false 364 | 365 | 366 | shader_minifier.exe -v -o src\shaders\texture.inl src\shaders\texture.frag 367 | shader_minifier.exe -v --no-renaming -o src\shaders\fragment.inl src\shaders\fragment.frag 368 | shader_minifier.exe -v -o src\shaders\post.inl src\shaders\post.frag 369 | Minify user-supplied shader files. 370 | 371 | 372 | 373 | 374 | NDEBUG;%(PreprocessorDefinitions) 375 | true 376 | true 377 | Win32 378 | .\bin/Release/Leviathan.tlb 379 | 380 | 381 | 382 | 383 | /GR- /EHa- %(AdditionalOptions) 384 | MinSpace 385 | OnlyExplicitInline 386 | true 387 | Size 388 | ../;%(AdditionalIncludeDirectories) 389 | WINDOWS;A32BITS;SIMD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 390 | false 391 | false 392 | MultiThreaded 393 | 1Byte 394 | false 395 | Fast 396 | bin\Release\Leviathan.pch 397 | AssemblyAndSourceCode 398 | bin\release\ 399 | bin\release\ 400 | bin\release\ 401 | Level3 402 | false 403 | true 404 | StdCall 405 | CompileAsCpp 406 | true 407 | false 408 | false 409 | Default 410 | NotSet 411 | true 412 | false 413 | ProgramDatabase 414 | 415 | 416 | false 417 | 418 | 419 | 420 | NDEBUG;%(PreprocessorDefinitions) 421 | 0x0409 422 | 423 | 424 | /CRINKLER /RANGE:opengl32 /HASHTRIES:1000 /COMPMODE:VERYSLOW /ORDERTRIES:30000 /REPORT:out.html /PROGRESSGUI /HASHSIZE:1000 /UNSAFEIMPORT /UNALIGNCODE /SATURATE /NOINITIALIZERS /TRANSFORM:CALLS %(AdditionalOptions) 425 | opengl32.lib;winmm.lib;src/4klang/4klang.obj;%(AdditionalDependencies) 426 | out\leviathan-release.exe 427 | 428 | 429 | true 430 | Windows 431 | entrypoint 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | bin\release\$(TargetName).pdb 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | true 455 | .\bin/Release/Leviathan.bsc 456 | 457 | 458 | false 459 | 460 | 461 | shader_minifier.exe -v -o src\shaders\texture.inl src\shaders\texture.frag 462 | shader_minifier.exe -v --no-renaming -o src\shaders\fragment.inl src\shaders\fragment.frag 463 | shader_minifier.exe -v -o src\shaders\post.inl src\shaders\post.frag 464 | Minify user-supplied shader files. 465 | 466 | 467 | 468 | 469 | NDEBUG;%(PreprocessorDefinitions) 470 | true 471 | true 472 | Win32 473 | .\bin/Release/Leviathan.tlb 474 | 475 | 476 | 477 | 478 | MinSpace 479 | Disabled 480 | true 481 | Size 482 | ../;%(AdditionalIncludeDirectories) 483 | WINDOWS;A32BITS;SIMD;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 484 | false 485 | false 486 | MultiThreaded 487 | 1Byte 488 | false 489 | Fast 490 | bin\Release\Leviathan.pch 491 | AssemblyAndSourceCode 492 | bin\release\ 493 | bin\release\ 494 | bin\release\ 495 | Level3 496 | false 497 | true 498 | StdCall 499 | CompileAsCpp 500 | true 501 | false 502 | false 503 | 504 | 505 | NotSet 506 | true 507 | false 508 | ProgramDatabase 509 | 510 | 511 | 512 | 513 | false 514 | /GR- /EHa- %(AdditionalOptions) 515 | false 516 | 517 | 518 | 519 | NDEBUG;%(PreprocessorDefinitions) 520 | 0x0409 521 | 522 | 523 | /CRINKLER /RANGE:opengl32 /HASHTRIES:100 /COMPMODE:INSTANT /ORDERTRIES:2000 /PROGRESSGUI /HASHSIZE:100 /UNSAFEIMPORT /UNALIGNCODE /SATURATE /NOINITIALIZERS /TRANSFORM:CALLS %(AdditionalOptions) 524 | opengl32.lib;winmm.lib;src/4klang/4klang.obj;%(AdditionalDependencies) 525 | out\leviathan-snapshot.exe 526 | 527 | 528 | true 529 | Windows 530 | entrypoint 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | bin\release\$(TargetName).pdb 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | true 556 | .\bin/Release/Leviathan.bsc 557 | 558 | 559 | false 560 | 561 | 562 | shader_minifier.exe -v -o src\shaders\texture.inl src\shaders\texture.frag 563 | shader_minifier.exe -v --no-renaming -o src\shaders\fragment.inl src\shaders\fragment.frag 564 | shader_minifier.exe -v -o src\shaders\post.inl src\shaders\post.frag 565 | Minify user-supplied shader files. 566 | 567 | 568 | 569 | 570 | true 571 | true 572 | true 573 | 574 | 575 | 576 | false 577 | false 578 | false 579 | 580 | 581 | 582 | 583 | 584 | true 585 | false 586 | true 587 | true 588 | true 589 | 590 | 591 | 592 | 593 | true 594 | true 595 | true 596 | 597 | 598 | 599 | false 600 | true 601 | true 602 | true 603 | true 604 | 605 | 606 | 607 | 608 | true 609 | true 610 | true 611 | true 612 | true 613 | 614 | 615 | 616 | true 617 | true 618 | true 619 | true 620 | true 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | -------------------------------------------------------------------------------- /leviathan.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ext 14 | 15 | 16 | ext 17 | 18 | 19 | 20 | 21 | 22 | 23 | shaders 24 | 25 | 26 | shaders 27 | 28 | 29 | shaders 30 | 31 | 32 | shaders 33 | 34 | 35 | shaders 36 | 37 | 38 | shaders 39 | 40 | 41 | 42 | 43 | {3ade9a72-96b0-4936-93c4-4926808a1ab2} 44 | 45 | 46 | {84f225d6-acf8-4151-aab3-e246410cdeba} 47 | 48 | 49 | -------------------------------------------------------------------------------- /link.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armak/pbr-devour/9230852fb31e598af6baa835a86d2b5490a64478/link.exe -------------------------------------------------------------------------------- /pbr-leviathan-2.0.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Leviathan 2.0", "leviathan.vcxproj", "{59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Editor|Win32 = Editor|Win32 12 | Heavy Release|Win32 = Heavy Release|Win32 13 | Release|Win32 = Release|Win32 14 | Snapshot|Win32 = Snapshot|Win32 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Debug|Win32.ActiveCfg = Debug|Win32 18 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Debug|Win32.Build.0 = Debug|Win32 19 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Editor|Win32.ActiveCfg = Editor|Win32 20 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Editor|Win32.Build.0 = Editor|Win32 21 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Heavy Release|Win32.ActiveCfg = Heavy Release|Win32 22 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Heavy Release|Win32.Build.0 = Heavy Release|Win32 23 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Release|Win32.ActiveCfg = Release|Win32 24 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Release|Win32.Build.0 = Release|Win32 25 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Snapshot|Win32.ActiveCfg = Snapshot|Win32 26 | {59C1D4F3-AE93-4A0A-B4FE-60841CA865E5}.Snapshot|Win32.Build.0 = Snapshot|Win32 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | GlobalSection(ExtensibilityGlobals) = postSolution 32 | SolutionGuid = {BFFCE7AC-AF44-47A1-9B82-AB82AB604DD0} 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /shader_minifier.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armak/pbr-devour/9230852fb31e598af6baa835a86d2b5490a64478/shader_minifier.exe -------------------------------------------------------------------------------- /src/4klang/4klang.h: -------------------------------------------------------------------------------- 1 | // some useful song defines for 4klang 2 | #define SAMPLE_RATE 44100 3 | #define BPM 120.000000 4 | #define MAX_INSTRUMENTS 5 5 | #define MAX_PATTERNS 136 6 | #define PATTERN_SIZE_SHIFT 3 7 | #define PATTERN_SIZE (1 << PATTERN_SIZE_SHIFT) 8 | #define MAX_TICKS (MAX_PATTERNS*PATTERN_SIZE) 9 | #define SAMPLES_PER_TICK 5512 10 | #define MAX_SAMPLES (SAMPLES_PER_TICK*MAX_TICKS) 11 | #define POLYPHONY 2 12 | #define INTEGER_16BIT 13 | #define SAMPLE_TYPE short 14 | 15 | #define WINDOWS_OBJECT 16 | 17 | // declaration of the external synth render function, you'll always need that 18 | extern "C" void __stdcall _4klang_render(void*); 19 | // declaration of the external envelope buffer. access only if you're song was exported with that option -------------------------------------------------------------------------------- /src/4klang/4klang.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/armak/pbr-devour/9230852fb31e598af6baa835a86d2b5490a64478/src/4klang/4klang.obj -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | // This header contains some useful functions for debugging OpenGL. 2 | // Remember to disable them when building your final releases. 3 | 4 | #include 5 | #include 6 | #include "glext.h" 7 | 8 | #define STRINGIFY2(x) #x // Thanks sooda! 9 | #define STRINGIFY(x) STRINGIFY2(x) 10 | #define CHECK_ERRORS() assertGlError(STRINGIFY(__LINE__)) 11 | 12 | static GLchar* getErrorString(GLenum errorCode) 13 | { 14 | if (errorCode == GL_NO_ERROR) { 15 | return (GLchar*) "No error"; 16 | } 17 | else if (errorCode == GL_INVALID_VALUE) { 18 | return (GLchar*) "Invalid value"; 19 | } 20 | else if (errorCode == GL_INVALID_ENUM) { 21 | return (GLchar*) "Invalid enum"; 22 | } 23 | else if (errorCode == GL_INVALID_OPERATION) { 24 | return (GLchar*) "Invalid operation"; 25 | } 26 | else if (errorCode == GL_STACK_OVERFLOW) { 27 | return (GLchar*) "Stack overflow"; 28 | } 29 | else if (errorCode == GL_STACK_UNDERFLOW) { 30 | return (GLchar*) "Stack underflow"; 31 | } 32 | else if (errorCode == GL_OUT_OF_MEMORY) { 33 | return (GLchar*) "Out of memory"; 34 | } 35 | return (GLchar*) "Unknown"; 36 | } 37 | 38 | static void assertGlError(const char* error_message) 39 | { 40 | const GLenum ErrorValue = glGetError(); 41 | if (ErrorValue == GL_NO_ERROR) return; 42 | 43 | const char* APPEND_DETAIL_STRING = ": %s\n"; 44 | const size_t APPEND_LENGTH = strlen(APPEND_DETAIL_STRING) + 1; 45 | const size_t message_length = strlen(error_message); 46 | MessageBox(NULL, error_message, getErrorString(ErrorValue), 0x00000000L); 47 | ExitProcess(0); 48 | } 49 | -------------------------------------------------------------------------------- /src/definitions.h: -------------------------------------------------------------------------------- 1 | // This header contains necessary structures for setting up correct screen modes, 2 | // pixel formats, audio formats, and so on. 3 | 4 | // minify windows.h 5 | #define WIN32_LEAN_AND_MEAN 6 | #define WIN32_EXTRA_LEAN 7 | #define VC_LEANMEAN 8 | #define VC_EXTRALEAN 9 | 10 | #include 11 | #include 12 | 13 | // global resolution 14 | #define XRES 1920 15 | #define YRES 1080 16 | 17 | // declare this symbol if your code uses floating point types 18 | // extern "C" int _fltused; 19 | 20 | #pragma data_seg(".pixelfmt") 21 | static const PIXELFORMATDESCRIPTOR pfd = { 22 | #if BREAK_COMPATIBILITY 23 | #if POST_PASS 24 | 0, 0, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 25 | 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 26 | #else 27 | 0, 0, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 29 | #endif 30 | #else 31 | sizeof(pfd), 1, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 32 | 32, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 33 | #endif 34 | }; 35 | 36 | #pragma data_seg(".screensettings") 37 | static DEVMODE screenSettings = { 38 | {0}, 0, 0, sizeof(screenSettings), 0, DM_PELSWIDTH|DM_PELSHEIGHT, 39 | {0}, 0, 0, 0, 0, 0, {0}, 0, 0, XRES, YRES, 0, 0, 40 | #if(WINVER >= 0x0400) 41 | 0, 0, 0, 0, 0, 0, 42 | #if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) 43 | 0, 0 44 | #endif 45 | #endif 46 | }; 47 | 48 | #if USE_AUDIO 49 | #include 50 | #include 51 | 52 | // this file is auto generated by 4klang 53 | #include "4klang/4klang.h" 54 | 55 | #pragma data_seg(".4klangout") 56 | static SAMPLE_TYPE lpSoundBuffer[MAX_SAMPLES * 2]; 57 | static HWAVEOUT hWaveOut; 58 | 59 | #pragma data_seg(".wavefmt") 60 | static WAVEFORMATEX WaveFMT = 61 | { 62 | #ifdef FLOAT_32BIT 63 | WAVE_FORMAT_IEEE_FLOAT, 64 | #else 65 | WAVE_FORMAT_PCM, 66 | #endif 67 | 2, // channels 68 | SAMPLE_RATE, // samples per sec 69 | SAMPLE_RATE*sizeof(SAMPLE_TYPE) * 2, // bytes per sec 70 | sizeof(SAMPLE_TYPE) * 2, // block alignment; 71 | sizeof(SAMPLE_TYPE) * 8, // bits per sample 72 | 0 // extension not needed 73 | }; 74 | 75 | #pragma data_seg(".wavehdr") 76 | static WAVEHDR WaveHDR = 77 | { 78 | (LPSTR)lpSoundBuffer, MAX_SAMPLES * sizeof(SAMPLE_TYPE) * 2, 0, 0, 0, 0, 0, 0 79 | }; 80 | 81 | static MMTIME MMTime = 82 | { 83 | TIME_SAMPLES, 0 84 | }; 85 | #endif 86 | 87 | // currently unused definitions 88 | #ifdef EDITOR_CONTROLS 89 | #define FAIL_KILL false 90 | #define PID_QUALIFIER 91 | #else 92 | #define FAIL_KILL true 93 | #define PID_QUALIFIER const 94 | #endif -------------------------------------------------------------------------------- /src/editor.cpp: -------------------------------------------------------------------------------- 1 | #include "editor.h" 2 | #include "song.h" 3 | 4 | #include "stdio.h" 5 | #include "windows.h" 6 | #include "GL/gl.h" 7 | #include "glext.h" 8 | 9 | using namespace Leviathan; 10 | 11 | #define USE_MESSAGEBOX 0 12 | 13 | Editor::Editor() : lastFrameStart(0), lastFrameStop(0), trackPosition(0.0), trackEnd(0.0), state(Playing) 14 | { 15 | printf("Editor opened...\n"); 16 | } 17 | 18 | void Editor::beginFrame(const unsigned long time) 19 | { 20 | lastFrameStart = time; 21 | } 22 | 23 | void Editor::endFrame(const unsigned long time) 24 | { 25 | lastFrameStop = time; 26 | } 27 | 28 | void Editor::printFrameStatistics() 29 | { 30 | const int frameTime = lastFrameStop - lastFrameStart; 31 | 32 | // calculate average fps over 'windowSize' of frames 33 | float fps = 0.0f; 34 | for (int i = 0; i < windowSize - 1; ++i) 35 | { 36 | timeHistory[i] = timeHistory[i + 1]; 37 | fps += 1.0f / static_cast(timeHistory[i]); 38 | } 39 | timeHistory[windowSize - 1] = frameTime; 40 | fps += 1.0f / static_cast(frameTime); 41 | fps *= 1000.0f / static_cast(windowSize); 42 | 43 | printf("%s: %0.2i:%0.2i (%i%%), frame duration: %i ms (running fps average: %2.2f) \r", 44 | state == Playing ? "Playing" : " Paused", 45 | // assuming y'all won't be making intros more than an hour long 46 | int(trackPosition/60.0), int(trackPosition) % 60, int(100.0f*trackPosition/trackEnd), 47 | frameTime, fps); 48 | } 49 | 50 | double Editor::handleEvents(Leviathan::Song* track, double position) 51 | { 52 | if (GetAsyncKeyState(VK_MENU)) 53 | { 54 | double seek = 0.0; 55 | if (GetAsyncKeyState(VK_DOWN)) 56 | { 57 | state = Paused; 58 | track->pause(); 59 | } 60 | if (GetAsyncKeyState(VK_UP)) 61 | { 62 | state = Playing; 63 | track->play(); 64 | } 65 | if (GetAsyncKeyState(VK_RIGHT) && !GetAsyncKeyState(VK_SHIFT)) seek += 1.0; 66 | if (GetAsyncKeyState(VK_LEFT) && !GetAsyncKeyState(VK_SHIFT)) seek -= 1.0; 67 | if (GetAsyncKeyState(VK_RIGHT) && GetAsyncKeyState(VK_SHIFT)) seek += 0.1; 68 | if (GetAsyncKeyState(VK_LEFT) && GetAsyncKeyState(VK_SHIFT)) seek -= 0.1; 69 | if (position + seek != position) 70 | { 71 | position += seek; 72 | track->seek(position); 73 | } 74 | } 75 | 76 | if (GetAsyncKeyState(VK_CONTROL) && GetAsyncKeyState('S')) 77 | shaderUpdatePending = true; 78 | 79 | trackPosition = position; 80 | trackEnd = track->getLength(); 81 | 82 | return position; 83 | } 84 | 85 | void Editor::updateShaders(int* mainShaderPID, int* postShaderPID, bool force_update) 86 | { 87 | if (shaderUpdatePending || force_update) 88 | { 89 | // make sure the file has finished writing to disk 90 | if (timeGetTime() - previousUpdateTime > 200) 91 | { 92 | // only way i can think of to clear the line without "status line" residue 93 | printf("Refreshing shaders... \n"); 94 | 95 | Sleep(100); 96 | int newPID = reloadShaderSource("../src/shaders/fragment.frag"); 97 | if (newPID > 0) 98 | *mainShaderPID = newPID; 99 | 100 | newPID = reloadShaderSource("../src/shaders/post.frag"); 101 | if (newPID > 0) 102 | *postShaderPID = newPID; 103 | } 104 | 105 | previousUpdateTime = timeGetTime(); 106 | shaderUpdatePending = false; 107 | } 108 | } 109 | 110 | int Editor::reloadShaderSource(const char* filename) 111 | { 112 | long inputSize = 0; 113 | // we're of course opening a text file, but should be opened in binary ('b') 114 | // longer shaders are known to cause problems by producing garbage input when read 115 | FILE* file = fopen(filename, "rb"); 116 | 117 | if (file) 118 | { 119 | fseek(file, 0, SEEK_END); 120 | inputSize = ftell(file); 121 | rewind(file); 122 | 123 | char* shaderString = static_cast(calloc(inputSize + 1, sizeof(char))); 124 | fread(shaderString, sizeof(char), inputSize, file); 125 | fclose(file); 126 | 127 | // just to be sure... 128 | shaderString[inputSize] = '\0'; 129 | 130 | if (!compileAndDebugShader(shaderString, filename, false)) 131 | { 132 | free(shaderString); 133 | // return an invalid PID value if compilation fails 134 | return -1; 135 | } 136 | 137 | int pid = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &shaderString); 138 | free(shaderString); 139 | 140 | printf("Loaded shader from \"%s\"\n", filename); 141 | return pid; 142 | } 143 | else 144 | { 145 | printf("Input shader file at \"%s\" not found, shader not reloaded\n", filename); 146 | return -1; 147 | } 148 | } 149 | 150 | bool Editor::compileAndDebugShader(const char* shader, const char* filename, bool kill_on_failure) 151 | { 152 | // try and compile the shader 153 | int result = 0; 154 | const int debugid = ((PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"))(GL_FRAGMENT_SHADER); 155 | ((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"))(debugid, 1, &shader, 0); 156 | ((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(debugid); 157 | 158 | // get compile result 159 | ((PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"))(debugid, GL_COMPILE_STATUS, &result); 160 | if (result == GL_FALSE) 161 | { 162 | // display compile log on failure 163 | static char errorBuffer[shaderErrorBufferLength]; 164 | ((PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"))(debugid, shaderErrorBufferLength-1, NULL, static_cast(errorBuffer)); 165 | 166 | #if USE_MESSAGEBOX 167 | MessageBox(NULL, errorBuffer, "", 0x00000000L); 168 | #endif 169 | printf("Compilation errors in %s:\n\n %s\n", filename, errorBuffer); 170 | 171 | if (kill_on_failure) 172 | { 173 | ExitProcess(0); 174 | } 175 | else 176 | { 177 | return false; 178 | } 179 | } 180 | else 181 | { 182 | ((PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader"))(debugid); 183 | return true; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/editor.h: -------------------------------------------------------------------------------- 1 | namespace Leviathan 2 | { 3 | // forward declaration 4 | class Song; 5 | } 6 | 7 | namespace Leviathan 8 | { 9 | // simpler wrapper class for the editor functionality 10 | class Editor 11 | { 12 | public: 13 | Editor(); 14 | 15 | void beginFrame(const unsigned long time); 16 | 17 | void endFrame(const unsigned long time); 18 | 19 | void printFrameStatistics(); 20 | 21 | double handleEvents(Song* track, double position); 22 | 23 | void updateShaders(int* mainShaderPID, int* postShaderPID, bool force_update = false); 24 | 25 | bool compileAndDebugShader(const char* shader, const char* filename, bool kill_on_failure = true); 26 | private: 27 | int reloadShaderSource(const char* filename); 28 | 29 | 30 | enum PlayState {Playing, Paused}; 31 | PlayState state; 32 | 33 | unsigned long lastFrameStart; 34 | unsigned long lastFrameStop; 35 | 36 | static const int shaderErrorBufferLength = 4096; 37 | static const int windowSize = 10; 38 | int timeHistory[windowSize] = {}; 39 | 40 | bool shaderUpdatePending = false; 41 | int previousUpdateTime; 42 | 43 | double trackPosition; 44 | double trackEnd; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /src/fp.h: -------------------------------------------------------------------------------- 1 | // This header is by iq of RGBA and includes some float constants. 2 | // I've never used these myself but I guess they're useful??? 3 | 4 | #ifndef _FPCONSTANTS_H_ 5 | #define _FPCONSTANTS_H_ 6 | #define p0d00 0.0000000000f // 0.00f 0x00000000 7 | #define p0d01 0.0100097656f // 0.01f 0x3c240000 8 | #define p0d02 0.0200195313f // 0.02f 0x3ca40000 9 | #define p0d03 0.0300292969f // 0.03f 0x3cf60000 10 | #define p0d04 0.0400390625f // 0.04f 0x3d240000 11 | #define p0d05 0.0500488281f // 0.05f 0x3d4d0000 12 | #define p0d06 0.0600585938f // 0.06f 0x3d760000 13 | #define p0d07 0.0698242188f // 0.07f 0x3d8f0000 14 | #define p0d08 0.0800781250f // 0.08f 0x3da40000 15 | #define p0d09 0.0898437500f // 0.09f 0x3db80000 16 | #define p0d10 0.1000976563f // 0.10f 0x3dcd0000 17 | #define p0d11 0.1098632813f // 0.11f 0x3de10000 18 | #define p0d12 0.1201171875f // 0.12f 0x3df60000 19 | #define p0d13 0.1298828125f // 0.13f 0x3e050000 20 | #define p0d14 0.1396484375f // 0.14f 0x3e0f0000 21 | #define p0d15 0.1503906250f // 0.15f 0x3e1a0000 22 | #define p0d16 0.1601562500f // 0.16f 0x3e240000 23 | #define p0d17 0.1699218750f // 0.17f 0x3e2e0000 24 | #define p0d18 0.1796875000f // 0.18f 0x3e380000 25 | #define p0d19 0.1904296875f // 0.19f 0x3e430000 26 | #define p0d20 0.2001953125f // 0.20f 0x3e4d0000 27 | #define p0d21 0.2099609375f // 0.21f 0x3e570000 28 | #define p0d22 0.2197265625f // 0.22f 0x3e610000 29 | #define p0d23 0.2304687500f // 0.23f 0x3e6c0000 30 | #define p0d24 0.2402343750f // 0.24f 0x3e760000 31 | #define p0d25 0.2500000000f // 0.25f 0x3e800000 32 | #define p0d26 0.2597656250f // 0.26f 0x3e850000 33 | #define p0d27 0.2695312500f // 0.27f 0x3e8a0000 34 | #define p0d28 0.2792968750f // 0.28f 0x3e8f0000 35 | #define p0d29 0.2890625000f // 0.29f 0x3e940000 36 | #define p0d30 0.3007812500f // 0.30f 0x3e9a0000 37 | #define p0d31 0.3105468750f // 0.31f 0x3e9f0000 38 | #define p0d32 0.3203125000f // 0.32f 0x3ea40000 39 | #define p0d33 0.3300781250f // 0.33f 0x3ea90000 40 | #define p0d34 0.3398437500f // 0.34f 0x3eae0000 41 | #define p0d35 0.3496093750f // 0.35f 0x3eb30000 42 | #define p0d36 0.3593750000f // 0.36f 0x3eb80000 43 | #define p0d37 0.3691406250f // 0.37f 0x3ebd0000 44 | #define p0d38 0.3808593750f // 0.38f 0x3ec30000 45 | #define p0d39 0.3906250000f // 0.39f 0x3ec80000 46 | #define p0d40 0.4003906250f // 0.40f 0x3ecd0000 47 | #define p0d41 0.4101562500f // 0.41f 0x3ed20000 48 | #define p0d42 0.4199218750f // 0.42f 0x3ed70000 49 | #define p0d43 0.4296875000f // 0.43f 0x3edc0000 50 | #define p0d44 0.4394531250f // 0.44f 0x3ee10000 51 | #define p0d45 0.4492187500f // 0.45f 0x3ee60000 52 | #define p0d46 0.4609375000f // 0.46f 0x3eec0000 53 | #define p0d47 0.4707031250f // 0.47f 0x3ef10000 54 | #define p0d48 0.4804687500f // 0.48f 0x3ef60000 55 | #define p0d49 0.4902343750f // 0.49f 0x3efb0000 56 | #define p0d50 0.5000000000f // 0.50f 0x3f000000 57 | #define p0d51 0.5117187500f // 0.51f 0x3f030000 58 | #define p0d52 0.5195312500f // 0.52f 0x3f050000 59 | #define p0d53 0.5312500000f // 0.53f 0x3f080000 60 | #define p0d54 0.5390625000f // 0.54f 0x3f0a0000 61 | #define p0d55 0.5507812500f // 0.55f 0x3f0d0000 62 | #define p0d56 0.5585937500f // 0.56f 0x3f0f0000 63 | #define p0d57 0.5703125000f // 0.57f 0x3f120000 64 | #define p0d58 0.5781250000f // 0.58f 0x3f140000 65 | #define p0d59 0.5898437500f // 0.59f 0x3f170000 66 | #define p0d60 0.6015625000f // 0.60f 0x3f1a0000 67 | #define p0d61 0.6093750000f // 0.61f 0x3f1c0000 68 | #define p0d62 0.6210937500f // 0.62f 0x3f1f0000 69 | #define p0d63 0.6289062500f // 0.63f 0x3f210000 70 | #define p0d64 0.6406250000f // 0.64f 0x3f240000 71 | #define p0d65 0.6484375000f // 0.65f 0x3f260000 72 | #define p0d66 0.6601562500f // 0.66f 0x3f290000 73 | #define p0d67 0.6718750000f // 0.67f 0x3f2c0000 74 | #define p0d68 0.6796875000f // 0.68f 0x3f2e0000 75 | #define p0d69 0.6914062500f // 0.69f 0x3f310000 76 | #define p0d70 0.6992187500f // 0.70f 0x3f330000 77 | #define p0d71 0.7109375000f // 0.71f 0x3f360000 78 | #define p0d72 0.7187500000f // 0.72f 0x3f380000 79 | #define p0d73 0.7304687500f // 0.73f 0x3f3b0000 80 | #define p0d74 0.7382812500f // 0.74f 0x3f3d0000 81 | #define p0d75 0.7500000000f // 0.75f 0x3f400000 82 | #define p0d76 0.7617187500f // 0.76f 0x3f430000 83 | #define p0d77 0.7695312500f // 0.77f 0x3f450000 84 | #define p0d78 0.7812500000f // 0.78f 0x3f480000 85 | #define p0d79 0.7890625000f // 0.79f 0x3f4a0000 86 | #define p0d80 0.8007812500f // 0.80f 0x3f4d0000 87 | #define p0d81 0.8085937500f // 0.81f 0x3f4f0000 88 | #define p0d82 0.8203125000f // 0.82f 0x3f520000 89 | #define p0d83 0.8281250000f // 0.83f 0x3f540000 90 | #define p0d84 0.8398437500f // 0.84f 0x3f570000 91 | #define p0d85 0.8515625000f // 0.85f 0x3f5a0000 92 | #define p0d86 0.8593750000f // 0.86f 0x3f5c0000 93 | #define p0d87 0.8710937500f // 0.87f 0x3f5f0000 94 | #define p0d88 0.8789062500f // 0.88f 0x3f610000 95 | #define p0d89 0.8906250000f // 0.89f 0x3f640000 96 | #define p0d90 0.8984375000f // 0.90f 0x3f660000 97 | #define p0d91 0.9101562500f // 0.91f 0x3f690000 98 | #define p0d92 0.9218750000f // 0.92f 0x3f6c0000 99 | #define p0d93 0.9296875000f // 0.93f 0x3f6e0000 100 | #define p0d94 0.9414062500f // 0.94f 0x3f710000 101 | #define p0d95 0.9492187500f // 0.95f 0x3f730000 102 | #define p0d96 0.9609375000f // 0.96f 0x3f760000 103 | #define p0d97 0.9687500000f // 0.97f 0x3f780000 104 | #define p0d98 0.9804687500f // 0.98f 0x3f7b0000 105 | #define p0d99 0.9882812500f // 0.99f 0x3f7d0000 106 | #define p1d00 1.0000000000f // 1.00f 0x3f800000 107 | #define p1d01 1.0078125000f // 1.01f 0x3f810000 108 | #define p1d02 1.0234375000f // 1.02f 0x3f830000 109 | #define p1d03 1.0312500000f // 1.03f 0x3f840000 110 | #define p1d04 1.0390625000f // 1.04f 0x3f850000 111 | #define p1d05 1.0468750000f // 1.05f 0x3f860000 112 | #define p1d06 1.0625000000f // 1.06f 0x3f880000 113 | #define p1d07 1.0703125000f // 1.07f 0x3f890000 114 | #define p1d08 1.0781250000f // 1.08f 0x3f8a0000 115 | #define p1d09 1.0937500000f // 1.09f 0x3f8c0000 116 | #define p1d10 1.1015625000f // 1.10f 0x3f8d0000 117 | #define p1d11 1.1093750000f // 1.11f 0x3f8e0000 118 | #define p1d12 1.1171875000f // 1.12f 0x3f8f0000 119 | #define p1d13 1.1328125000f // 1.13f 0x3f910000 120 | #define p1d14 1.1406250000f // 1.14f 0x3f920000 121 | #define p1d15 1.1484375000f // 1.15f 0x3f930000 122 | #define p1d16 1.1562500000f // 1.16f 0x3f940000 123 | #define p1d17 1.1718750000f // 1.17f 0x3f960000 124 | #define p1d18 1.1796875000f // 1.18f 0x3f970000 125 | #define p1d19 1.1875000000f // 1.19f 0x3f980000 126 | #define p1d20 1.2031250000f // 1.20f 0x3f9a0000 127 | #define p1d21 1.2109375000f // 1.21f 0x3f9b0000 128 | #define p1d22 1.2187500000f // 1.22f 0x3f9c0000 129 | #define p1d23 1.2265625000f // 1.23f 0x3f9d0000 130 | #define p1d24 1.2421875000f // 1.24f 0x3f9f0000 131 | #define p1d25 1.2500000000f // 1.25f 0x3fa00000 132 | #define p1d26 1.2578125000f // 1.26f 0x3fa10000 133 | #define p1d27 1.2734375000f // 1.27f 0x3fa30000 134 | #define p1d28 1.2812500000f // 1.28f 0x3fa40000 135 | #define p1d29 1.2890625000f // 1.29f 0x3fa50000 136 | #define p1d30 1.2968750000f // 1.30f 0x3fa60000 137 | #define p1d31 1.3125000000f // 1.31f 0x3fa80000 138 | #define p1d32 1.3203125000f // 1.32f 0x3fa90000 139 | #define p1d33 1.3281250000f // 1.33f 0x3faa0000 140 | #define p1d34 1.3437500000f // 1.34f 0x3fac0000 141 | #define p1d35 1.3515625000f // 1.35f 0x3fad0000 142 | #define p1d36 1.3593750000f // 1.36f 0x3fae0000 143 | #define p1d37 1.3671875000f // 1.37f 0x3faf0000 144 | #define p1d38 1.3828125000f // 1.38f 0x3fb10000 145 | #define p1d39 1.3906250000f // 1.39f 0x3fb20000 146 | #define p1d40 1.3984375000f // 1.40f 0x3fb30000 147 | #define p1d41 1.4062500000f // 1.41f 0x3fb40000 148 | #define p1d42 1.4218750000f // 1.42f 0x3fb60000 149 | #define p1d43 1.4296875000f // 1.43f 0x3fb70000 150 | #define p1d44 1.4375000000f // 1.44f 0x3fb80000 151 | #define p1d45 1.4531250000f // 1.45f 0x3fba0000 152 | #define p1d46 1.4609375000f // 1.46f 0x3fbb0000 153 | #define p1d47 1.4687500000f // 1.47f 0x3fbc0000 154 | #define p1d48 1.4765625000f // 1.48f 0x3fbd0000 155 | #define p1d49 1.4921875000f // 1.49f 0x3fbf0000 156 | #define p1d50 1.5000000000f // 1.50f 0x3fc00000 157 | #define p1d51 1.5078125000f // 1.51f 0x3fc10000 158 | #define p1d52 1.5234375000f // 1.52f 0x3fc30000 159 | #define p1d53 1.5312500000f // 1.53f 0x3fc40000 160 | #define p1d54 1.5390625000f // 1.54f 0x3fc50000 161 | #define p1d55 1.5468750000f // 1.55f 0x3fc60000 162 | #define p1d56 1.5625000000f // 1.56f 0x3fc80000 163 | #define p1d57 1.5703125000f // 1.57f 0x3fc90000 164 | #define p1d58 1.5781250000f // 1.58f 0x3fca0000 165 | #define p1d59 1.5937500000f // 1.59f 0x3fcc0000 166 | #define p1d60 1.6015625000f // 1.60f 0x3fcd0000 167 | #define p1d61 1.6093750000f // 1.61f 0x3fce0000 168 | #define p1d62 1.6171875000f // 1.62f 0x3fcf0000 169 | #define p1d63 1.6328125000f // 1.63f 0x3fd10000 170 | #define p1d64 1.6406250000f // 1.64f 0x3fd20000 171 | #define p1d65 1.6484375000f // 1.65f 0x3fd30000 172 | #define p1d66 1.6562500000f // 1.66f 0x3fd40000 173 | #define p1d67 1.6718750000f // 1.67f 0x3fd60000 174 | #define p1d68 1.6796875000f // 1.68f 0x3fd70000 175 | #define p1d69 1.6875000000f // 1.69f 0x3fd80000 176 | #define p1d70 1.7031250000f // 1.70f 0x3fda0000 177 | #define p1d71 1.7109375000f // 1.71f 0x3fdb0000 178 | #define p1d72 1.7187500000f // 1.72f 0x3fdc0000 179 | #define p1d73 1.7265625000f // 1.73f 0x3fdd0000 180 | #define p1d74 1.7421875000f // 1.74f 0x3fdf0000 181 | #define p1d75 1.7500000000f // 1.75f 0x3fe00000 182 | #define p1d76 1.7578125000f // 1.76f 0x3fe10000 183 | #define p1d77 1.7734375000f // 1.77f 0x3fe30000 184 | #define p1d78 1.7812500000f // 1.78f 0x3fe40000 185 | #define p1d79 1.7890625000f // 1.79f 0x3fe50000 186 | #define p1d80 1.7968750000f // 1.80f 0x3fe60000 187 | #define p1d81 1.8125000000f // 1.81f 0x3fe80000 188 | #define p1d82 1.8203125000f // 1.82f 0x3fe90000 189 | #define p1d83 1.8281250000f // 1.83f 0x3fea0000 190 | #define p1d84 1.8437500000f // 1.84f 0x3fec0000 191 | #define p1d85 1.8515625000f // 1.85f 0x3fed0000 192 | #define p1d86 1.8593750000f // 1.86f 0x3fee0000 193 | #define p1d87 1.8671875000f // 1.87f 0x3fef0000 194 | #define p1d88 1.8828125000f // 1.88f 0x3ff10000 195 | #define p1d89 1.8906250000f // 1.89f 0x3ff20000 196 | #define p1d90 1.8984375000f // 1.90f 0x3ff30000 197 | #define p1d91 1.9062500000f // 1.91f 0x3ff40000 198 | #define p1d92 1.9218750000f // 1.92f 0x3ff60000 199 | #define p1d93 1.9296875000f // 1.93f 0x3ff70000 200 | #define p1d94 1.9375000000f // 1.94f 0x3ff80000 201 | #define p1d95 1.9531250000f // 1.95f 0x3ffa0000 202 | #define p1d96 1.9609375000f // 1.96f 0x3ffb0000 203 | #define p1d97 1.9687500000f // 1.97f 0x3ffc0000 204 | #define p1d98 1.9765625000f // 1.98f 0x3ffd0000 205 | #define p1d99 1.9921875000f // 1.99f 0x3fff0000 206 | #define p2d00 2.0000000000f 207 | #define p2d10 2.0937500000f 208 | 209 | #endif -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // custom build and feature flags 2 | #ifdef DEBUG 3 | #define OPENGL_DEBUG 0 4 | #define FULLSCREEN 0 5 | #define DESPERATE 0 6 | #define BREAK_COMPATIBILITY 0 7 | #else 8 | #define OPENGL_DEBUG 0 9 | #define FULLSCREEN 1 10 | #define DESPERATE 0 11 | #define BREAK_COMPATIBILITY 0 12 | #endif 13 | 14 | #define ACCUMULATE 0 15 | 16 | #include "definitions.h" 17 | #include "glext.h" 18 | #include "shaders/texture.inl" 19 | #include "shaders/fragment.inl" 20 | #include "shaders/post.inl" 21 | 22 | #include "timeapi.h" 23 | 24 | #pragma data_seg(".pids") 25 | // static allocation saves a few bytes 26 | static int pidTexture; 27 | static int pidMain; 28 | static int pidPost; 29 | // static HDC hDC; 30 | 31 | #ifndef EDITOR_CONTROLS 32 | #pragma code_seg(".main") 33 | void entrypoint(void) 34 | #else 35 | #include "editor.h" 36 | #include "song.h" 37 | int __cdecl main(int argc, char* argv[]) 38 | #endif 39 | { 40 | // initialize window 41 | #if FULLSCREEN 42 | ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN); 43 | ShowCursor(0); 44 | const HDC hDC = GetDC(CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0)); 45 | #else 46 | #ifdef EDITOR_CONTROLS 47 | HWND window = CreateWindow("static", 0, WS_POPUP | WS_VISIBLE, 0, 0, XRES, YRES, 0, 0, 0, 0); 48 | HDC hDC = GetDC(window); 49 | #else 50 | HDC hDC = GetDC(CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE, 0, 0, XRES, YRES, 0, 0, 0, 0)); 51 | #endif 52 | #endif 53 | 54 | // initalize opengl context 55 | SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd); 56 | wglMakeCurrent(hDC, wglCreateContext(hDC)); 57 | 58 | // create and compile shader programs 59 | pidTexture = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &texture); 60 | pidMain = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &fragment); 61 | pidPost = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &post); 62 | 63 | #ifndef EDITOR_CONTROLS 64 | #else 65 | Leviathan::Editor editor = Leviathan::Editor(); 66 | 67 | editor.compileAndDebugShader(texture, "texture", false); 68 | editor.compileAndDebugShader(fragment, "fragment", false); 69 | editor.compileAndDebugShader(post, "texture", false); 70 | 71 | editor.updateShaders(&pidMain, &pidPost, true); 72 | #endif 73 | 74 | // texture for roughness map 75 | GLuint roughnessTexture = 0; 76 | glGenTextures(1, &roughnessTexture); 77 | glBindTexture(GL_TEXTURE_2D, roughnessTexture); 78 | // parameters 79 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 80 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 81 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 82 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 83 | // render the roughness map to the texture 84 | ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidTexture); 85 | ((PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"))(0, XRES, YRES); 86 | glRects(-1, -1, 1, 1); 87 | glReadBuffer(GL_BACK); 88 | glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, YRES, YRES, 0); 89 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 90 | glClear(GL_COLOR_BUFFER_BIT); 91 | 92 | 93 | // main rendering texture (for accumulating values) 94 | GLuint mainTexture = 0; 95 | glGenTextures(1, &mainTexture); 96 | glBindTexture(GL_TEXTURE_2D, mainTexture); 97 | // parameters 98 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, XRES, YRES, 0, GL_RGBA, GL_FLOAT, 0); 99 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 100 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 101 | // framebuffer 102 | GLuint framebuffer = 0; 103 | ((PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers"))(1, &framebuffer); 104 | ((PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"))(GL_FRAMEBUFFER, framebuffer); 105 | ((PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"))(GL_DRAW_FRAMEBUFFER, framebuffer); 106 | ((PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D"))(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mainTexture, 0); 107 | 108 | const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 }; 109 | ((PFNGLDRAWBUFFERSPROC)wglGetProcAddress("glDrawBuffers"))(1, attachments); 110 | 111 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 112 | glClear(GL_COLOR_BUFFER_BIT); 113 | 114 | // enable blending for accumulating the values over frames 115 | glEnable(GL_BLEND); 116 | glBlendFunc(GL_ONE, GL_ONE); 117 | 118 | // main loop 119 | const unsigned long samples = 750; 120 | unsigned long frame = 1; 121 | glBindTexture(GL_TEXTURE_2D, roughnessTexture); 122 | 123 | const int t = timeGetTime(); 124 | bool done = false; 125 | 126 | do 127 | { 128 | #if !(DESPERATE) 129 | PeekMessage(0, 0, 0, 0, PM_REMOVE); 130 | #endif 131 | 132 | #if ACCUMULATE 133 | // present the result immediately and render iteratively 134 | // not allowed in the compo, just for debugging 135 | glEnable(GL_BLEND); 136 | ((PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"))(GL_DRAW_FRAMEBUFFER, framebuffer); 137 | const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 }; 138 | ((PFNGLDRAWBUFFERSPROC)wglGetProcAddress("glDrawBuffers"))(1, attachments); 139 | glBindTexture(GL_TEXTURE_2D, roughnessTexture); 140 | ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidMain); 141 | ((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, frame); 142 | ((PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"))(2, XRES, YRES); 143 | glRects(-1, -1, 1, 1); 144 | 145 | frame += 1; 146 | 147 | glBindTexture(GL_TEXTURE_2D, mainTexture); 148 | ((PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"))(GL_DRAW_FRAMEBUFFER, 0); 149 | glDrawBuffer(GL_BACK); 150 | ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidPost); 151 | ((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, frame); 152 | ((PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"))(2, XRES, YRES); 153 | glDisable(GL_BLEND); 154 | glRects(-1, -1, 1, 1); 155 | #else 156 | // time based accumulation 157 | //if (timeGetTime()-t < 30*1000) 158 | // render and accumulate N amount of samples 159 | if (frame < samples) 160 | { 161 | ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidMain); 162 | ((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, frame); 163 | ((PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"))(2, XRES, YRES); 164 | glRects(-1, -1, 1, 1); 165 | 166 | frame += 1; 167 | } 168 | // after rendering the desired amount, present the result and render post processing 169 | // includes averaging the samples, tonemapping and more 170 | else 171 | { 172 | /* 173 | if (!done) { 174 | printf("Samples: %d\n", frame); 175 | done = true; 176 | } 177 | */ 178 | 179 | glBindTexture(GL_TEXTURE_2D, mainTexture); 180 | ((PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer"))(GL_DRAW_FRAMEBUFFER, 0); 181 | glDrawBuffer(GL_BACK); 182 | ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidPost); 183 | ((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, frame); 184 | ((PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"))(2, XRES, YRES); 185 | glDisable(GL_BLEND); 186 | glRects(-1, -1, 1, 1); 187 | } 188 | #endif 189 | 190 | SwapBuffers(hDC); 191 | } 192 | while(!GetAsyncKeyState(VK_ESCAPE)); 193 | 194 | ExitProcess(0); 195 | } 196 | -------------------------------------------------------------------------------- /src/shaders/fragment.frag: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | layout(location = 0) uniform int iFrame; 4 | layout(binding = 0) uniform sampler2D roughnessTexture; 5 | layout(location = 2) uniform vec2 resolution; 6 | 7 | const float pi = 3.1415926; 8 | const float pi2 = 2.0*pi; 9 | 10 | float maxcomp(vec3 p) { return max(p.x,max(p.y,p.z));} 11 | vec4 mmin(vec4 a, vec4 b) { return a.x < b.x ? a : b; } 12 | vec4 mmax(vec4 a, vec4 b) { return a.x > b.x ? a : b; } 13 | 14 | float box(vec3 p, vec3 b){ 15 | vec3 di = abs(p) - b; 16 | return maxcomp(di); 17 | } 18 | 19 | float hash13(vec3 p3){ 20 | p3 = fract((p3)*0.1031); 21 | p3 += dot(p3, p3.yzx + 19.19); 22 | return fract((p3.x + p3.y) * p3.z); 23 | } 24 | 25 | float seed; 26 | float hash(){ 27 | vec3 p3 = fract(vec3(seed++) * .1031); 28 | p3 += dot(p3, p3.yzx + 19.19); 29 | return fract((p3.x + p3.y) * p3.z); 30 | } 31 | 32 | vec2 hash(float p){ 33 | vec3 p3 = fract(vec3(p)*vec3(.1031,.1030,.0973)); 34 | p3 += dot(p3,p3.yzx+19.19); 35 | return fract((p3.xx+p3.yz)*p3.zy); 36 | } 37 | 38 | vec2 fc; 39 | float sd; 40 | /* 41 | vec2 hash2(float n){ 42 | vec2 rand = texelFetch(iChannel1, ivec2(mod(fc,1024.)),0).rg; 43 | rand += hash(sd+float(iFrame)+n); 44 | return mod(rand, 1.0); 45 | } 46 | */ 47 | vec2 hash2(float n){return vec2(hash(), hash());} 48 | 49 | const float phi = 1.324717957244746; 50 | const float delta0 = 0.76; 51 | const float i0 = 0.700; 52 | const vec2 alpha = vec2(1.0/phi, 1.0/phi/phi); 53 | 54 | float hash(uvec2 x){ 55 | uvec2 q = 1103515245U * ( (x>>1U) ^ (x.yx ) ); 56 | uint n = 1103515245U * ( (q.x ) ^ (q.y>>3U) ); 57 | return float(n) * (1.0/float(0xffffffffU)); 58 | } 59 | 60 | vec2 hash22(vec2 p){vec3 p3 = fract(vec3(p.xyx)*vec3(.1031,.1030,.0973)); p3 += dot(p3,p3.yzx+19.19); return fract((p3.xx+p3.yz)*p3.zy);} 61 | 62 | vec2 R2_seq(int i, float lambda, float n){ 63 | vec2 u = vec2(hash(uvec2(i, 0)), hash(uvec2(i, 1)))-0.5; 64 | vec2 s = fract(alpha * float(i) + lambda * 1.347065 / (4.0 * sqrt(float(i) - i0)) * u); 65 | s += hash(n); 66 | s += hash22(fc); 67 | return mod(s, 1.0); 68 | } 69 | 70 | #define scale .7 71 | #define maxi 0.75 72 | #define fr_it 8 73 | vec4 map(vec3 p){ 74 | vec3 po = p; 75 | // need to flip this because ??? 76 | p = -p; 77 | float k = 1.; 78 | //float e = 0.0; 79 | for(int i = 0; i < fr_it; ++i){ 80 | vec3 ss = vec3(-.54,0.84,1.22); 81 | p = 2.0*clamp(p,-ss,ss)-p; 82 | float f = max(scale/dot(p,p),maxi); 83 | p *= f; 84 | k *= f*1.05; 85 | //e = f; 86 | } 87 | 88 | //p += 0.025*(-0.5+0.5*texture(roughnessTexture, 0.3*p.xz).b)*step(0.01,po.y)*smoothstep(0.02,0.4,0.4-distance(po*vec3(1,2,1), vec3(0.4,0.7,0.3))); 89 | vec4 res = vec4(max(length(p.xz)-.9,length(p.xz)*abs(p.y)/length(p))/k, 3.0, 0.0, 1.0); 90 | 91 | //vec4 slurm = vec4(length(p.xy)-0.25/k, 2.0, 0, 0); 92 | //res = mmin(res, slurm); 93 | 94 | // flip back because ??? 95 | p = -p; 96 | 97 | // crumbly 98 | res.x += (-1.0+2.0*hash13( floor(p*10.0) ))*0.005 * (1.0-step(0.01, po.y)); 99 | 100 | // glowy bits 101 | const float of = -1.0; 102 | const float l = 0.1; 103 | res.z = max(0.0, 60.0*(smoothstep(of-l,of,p.y)-smoothstep(of,of+l,p.x))); 104 | res.z *= step(-1.0, po.z); 105 | 106 | // res.yz += vec2(-3.0, 5.0)*( max(step(2.0, po.x), step(1.0, po.z)) ); 107 | // blast 108 | const float ang = 0.04; 109 | const mat2 rot = mat2(cos(ang),sin(ang),-sin(ang),cos(ang)); 110 | vec3 tpo = po-vec3(0,0.12,-1.5); 111 | tpo.xy *= rot; 112 | float blast = pow(smoothstep(-1.6, 0.35,po.x)-smoothstep(0.4,0.48,po.x), 3.0); 113 | res = mmin(res, vec4(length( (tpo).yz )-0.02*blast, 2, mix(0.0,25.0, pow(blast,2.0)), 0)); 114 | 115 | // slurm 116 | /* 117 | tpo = po-vec3(0,0.4,0.1); 118 | float an2 = p.y*2.2; 119 | tpo.xy *= mat2(cos(an2), sin(an2), -sin(an2), cos(an2)); 120 | float blas2 = pow(smoothstep(-1.6, 0.35,po.x)-smoothstep(0.4,1.0,po.x), 3.0); 121 | vec4 fume = vec4(length( (tpo).xz )-0.12*blas2, 2, .0, 0); 122 | fume = mmax(fume, vec4( -(length(po-vec3(0.55,0.18,0.0))-0.25), 0,0,0)); 123 | res = mmin(res, vec4(0.03,1,1,1)*fume); 124 | */ 125 | 126 | // ground plane 127 | //res = mmin(res, vec4( po.y+0.015 - 0.01*( max( step(0.92, mod(p.x*8.0, 1.0)), step(0.92, mod(p.z*8.0, 1.0)) ) ) , 0, 0, 0 )); 128 | res = mmin(res, vec4( po.y+0.015, 0, 0, 0 )); 129 | 130 | // bounding box 131 | res = mmax(res, vec4(box(po, vec3(11,1.0,11)), 3, 0, 1)); 132 | 133 | // medium light source (background) 134 | res = mmin(res, vec4(length(po-vec3(-2.4,0,-5))-0.4, vec3(0, 30, 0))); 135 | 136 | // huge light source (off camera) 137 | return mmin(res, vec4(length(po-vec3(-3.8,3.,-2.7))-2.4, vec3(1, 30, 0))); 138 | 139 | } 140 | 141 | const float E = 0.00025; //0.0002 142 | vec4 intersect(vec3 ro, vec3 rd){ 143 | float t = 0.0; 144 | vec4 res = vec4(-1.0); 145 | vec4 h = vec4(1.0); 146 | for(int i = 0; i < 125; i++){ 147 | // dynamic epsilon, aka intersection exit-condition 148 | float eps = E + E*5.0*pow(float(i)/200.0,2.0); 149 | if(h.x < eps || t > 15.0) 150 | break; 151 | h = map(ro + rd*t); 152 | res = vec4(t, h.yzw); 153 | t += h.x; 154 | } 155 | 156 | // signal for no intersection and reached maximum distance 157 | return (t >= 15.0 ? vec4(-999) : res); 158 | } 159 | 160 | vec3 calcNormal(vec3 p){ 161 | // basic central difference with 3 (4) samples 162 | // TODO: optimize the central sample away since it's available from callee always anyway 163 | float c = map(p).x; 164 | const float e = 0.00001; 165 | return normalize(vec3(c-map(p-vec3(e,0,0)).x, c-map(p-vec3(0,e,0)).x, c-map(p-vec3(0,0,e)).x)); 166 | } 167 | 168 | vec3 lambert(in vec3 normal, in vec2 uv){ 169 | float theta = pi2 * uv.x; 170 | uv.y = 2.0 * uv.y - 1.0; 171 | vec3 spherePoint = vec3(sqrt(1.0 - uv.y * uv.y) * vec2(cos(theta), sin(theta)), uv.y); 172 | return normalize(normal + spherePoint); 173 | } 174 | 175 | vec3 ggx(vec3 rd, vec3 n, float rgh, vec2 rand){ 176 | float s = -1 + 2 * step(0., n.z); 177 | float a = -1 / (s+n.z); 178 | float b = n.x * n.y * a; 179 | mat3 cs = mat3(vec3(1.+s*n.x*n.x*a,s*b,-s*n.x),n,vec3(b,s+n.y*n.y*a,-n.y)); 180 | float th = 0.5*pi*atan((rgh*sqrt(rand.x))/sqrt(1.0-rand.x)); 181 | 182 | vec3 reflection = normalize(transpose(cs)*rd); 183 | vec3 normal_distribution = vec3(cos(pi2 * rand.y)*sin(th), cos(th), sin(pi2 * rand.y)*sin(th)); 184 | 185 | //normal_distribution.x *= 0.1; 186 | //normal_distribution = normalize(normal_distribution); 187 | 188 | return normalize(cs*reflect(reflection, normal_distribution)); 189 | } 190 | 191 | vec3 brdf(in vec3 normal, in vec2 uv, in vec3 rd){ 192 | // fizzer's strange but nice brdf 193 | float theta = pi2 * uv.x; 194 | uv.y = 2.0 * uv.y - 1.0; 195 | vec3 p = vec3(sqrt(1.0 - uv.y * uv.y) * vec2(cos(theta), sin(theta)), uv.y); 196 | float z=dot(p,normal); 197 | p-=z*normal; 198 | 199 | p/=sqrt(1.0-z*z); 200 | p*=(1.0-2.0*z+z*z)/4.0; 201 | p+=normal*sqrt(1.0-dot(p,p)); 202 | 203 | vec3 ha=normalize(p); 204 | p=normalize(reflect(normalize(rd),p)); 205 | 206 | return p; 207 | } 208 | 209 | vec3 colors[6]; 210 | 211 | vec3 render(vec3 ro, vec3 rd){ 212 | 213 | vec3 color = vec3(0); 214 | vec3 absorption = vec3(1); 215 | const int bounces = 4; 216 | 217 | for(int b = 0; b < bounces; b++) 218 | { 219 | sd += 1.0; 220 | vec4 tmat = intersect(ro,rd); 221 | if(tmat.x >= 0.0) 222 | { 223 | vec3 pos = ro + tmat.x*rd; 224 | vec3 nor = calcNormal(pos); 225 | 226 | // fake texture coordinates for the metal parts 227 | // looks good enough from the camera position 228 | vec2 metal_coords = cross(nor, normalize(vec3(1,0,2)) ).xy; 229 | // get roughness map values 230 | float rough_map = mix(texture(roughnessTexture, mod(metal_coords*1.21,1.0)).r, 231 | texture(roughnessTexture, mod(metal_coords*0.53,1.0)).r, 0.7); 232 | rough_map = pow(1.0-rough_map,2.0); 233 | 234 | // incoming light value 235 | vec3 incoming = colors[int(tmat.y)]; 236 | // modulate surface color (metal parts) with the roughmess map a bit 237 | incoming *= 1.0+0.1*(1.0-2.0*rough_map)*step(1.0, tmat.w); 238 | 239 | vec2 tval = max(texture(roughnessTexture, pos.zx*0.61).br, texture(roughnessTexture, pos.xz*0.75).rb); 240 | tval = mix(tval, pow(texture(roughnessTexture, pos.zx*-3.43).rb, vec2(2))*0.5, 0.25); 241 | incoming = mix(incoming*vec3(0,0.5,1)*0.2, incoming, mix(0.75+0.5*length(tval), 1.0, step(-0.001, pos.y))); 242 | 243 | // emissive materials 244 | vec3 emission = tmat.z*incoming; 245 | 246 | // recursive color etc 247 | color += absorption * emission; 248 | // physical attenuation factor 249 | absorption *= incoming/pi; 250 | 251 | // calculate the brdf with magic formulas 252 | vec3 rv = reflect(rd, nor); 253 | 254 | float rough1 = 0.3-0.3*pow(min(tval.x, tval.y), 0.1); 255 | //rough1 = 0.55*mix(rough1, 0.22, smoothstep(7.0,9.0,distance(ro,pos))); 256 | 257 | float rough2 = mix(0.04, 0.02+0.04*pow(1.0-rough_map,2.0), step(0.01,pos.y)); 258 | 259 | vec3 metal = mix(brdf(nor, R2_seq(iFrame, 0.5, 31.23+sd), rd), ggx(rd, nor, rough2, R2_seq(iFrame, 0.5, -43.531)), step(0.45,hash())); 260 | vec3 ground = mix(lambert(nor, R2_seq(iFrame, 0.5, 9531.5312+sd)), ggx(rd, nor, rough1, R2_seq(iFrame, 0.5, 86439.3)), step(0.5,hash())); 261 | rd = mix(ground, metal, tmat.w); 262 | // fresnel doesn't look so good here, honestly 263 | //float fresnel = pow(max(0., dot(nor, rd)), 5.); 264 | //rd = mix(rd, rv, 1.0-step(fresnel, hash())); 265 | 266 | // new origin position, offset away from the surface to prevent self-intersection 267 | ro = pos+rd*E*20.0; 268 | } else { 269 | // no intersection, currently actually doesn't doo much of anything... 270 | vec3 pos = ro + tmat.x*rd; 271 | color += absorption * colors[4]*0.9; 272 | break; 273 | } 274 | } 275 | 276 | return color; 277 | } 278 | 279 | void main(){ 280 | // fill global color values 281 | colors[0] = 1.25*vec3(0.75,0.52,0.4).bgr; 282 | colors[1] = 1.25*vec3(0.75,0.38,0.2).bgr; 283 | colors[2] = vec3(0.6,0.32,0.92).bgr; 284 | colors[3] = vec3(0.42,0.66,0.95).bgr; 285 | colors[4] = normalize(vec3(3.0,1.0,0.5)).bgr; 286 | colors[5] = vec3(1.0, 0.1 ,0.04).bgr; 287 | fc = gl_FragCoord.xy; 288 | seed = float(((iFrame*73856093)^int(gl_FragCoord.x)*19349663^int(gl_FragCoord.y)*83492791)%38069); 289 | 290 | const float aspect = resolution.x/resolution.y; 291 | vec2 uv = gl_FragCoord.xy / resolution.xy; 292 | vec3 ro = vec3(.6,.1,.7)*20.0; 293 | 294 | vec3 col = vec3(0.0); 295 | // we only do one sample in this version, the loop is redundant 296 | ///const int samples = 1; 297 | //for(int s = 0; s < samples; s++){ 298 | 299 | // calculate camera/view orientation 300 | vec3 ww = normalize(vec3(0.76,0.38,0.1)-ro); 301 | vec3 uu = normalize(cross(normalize(vec3(0,1,0)),ww)); 302 | vec3 vv = normalize(cross(ww,uu)); 303 | 304 | //vec2 rand = R2_seq(iFrame, 1.0, -10.0); 305 | vec2 rand = hash2(0.0); 306 | 307 | // anti aliasing samples 308 | vec2 p = -1.0 + 2.0 * (uv + 0.667*(-1.0+2.0*hash2(3531.412))/resolution.xy); 309 | p.x *= aspect; 310 | 311 | // lens samples... 312 | vec2 lens_sample = vec2(cos(rand.x*pi2),sin(rand.x*pi2))*pow(rand.y, 0.32); 313 | // anamorphic stretch 314 | lens_sample *= vec2(0.75, 1.333); 315 | // re-calculate view vectors based on the lens samples 316 | float ffac = pow( smoothstep(0.1, 1.99, length(p)), 4.0); 317 | const float depth_of_field = 0.1+0.035*ffac; 318 | const float fov_length = 40.4-0.8*ffac; 319 | const float focus_distance = 0.45; 320 | vec3 lens_pos = ro + (lens_sample.x*uu + lens_sample.y*vv) * depth_of_field; 321 | vec3 vr = normalize(ro+(p.x*uu + p.y*vv + fov_length*ww) * focus_distance-lens_pos); 322 | 323 | // render/sample the scene 324 | // offset the origin with a magic number (a "near plane") 325 | // the result is clamped a bit to make it visually more pleasant 326 | gl_FragColor.rgb = min(render(lens_pos+17.3*vr, vr), 15.0); 327 | //} 328 | } 329 | -------------------------------------------------------------------------------- /src/shaders/fragment.inl: -------------------------------------------------------------------------------- 1 | #pragma data_seg(".shader") 2 | const char* fragment = 3 | "#version 430\n" 4 | "layout(location=0)uniform int iFrame;" 5 | "layout(binding=0)uniform sampler2D roughnessTexture;" 6 | "layout(location=2)uniform vec2 resolution;" 7 | "const float pi=3.14159,pi2=2*pi;" 8 | "float maxcomp(vec3 p)" 9 | "{" 10 | "return max(p.x,max(p.y,p.z));" 11 | "}" 12 | "vec4 mmin(vec4 a,vec4 b)" 13 | "{" 14 | "return a.xb.x?a:b;" 19 | "}" 20 | "float box(vec3 p,vec3 b)" 21 | "{" 22 | "vec3 di=abs(p)-b;" 23 | "return maxcomp(di);" 24 | "}" 25 | "float hash13(vec3 p3)" 26 | "{" 27 | "return p3=fract(p3*.1031),p3+=dot(p3,p3.yzx+19.19),fract((p3.x+p3.y)*p3.z);" 28 | "}" 29 | "float seed;" 30 | "float hash()" 31 | "{" 32 | "vec3 p3=fract(vec3(seed++)*.1031);" 33 | "p3+=dot(p3,p3.yzx+19.19);" 34 | "return fract((p3.x+p3.y)*p3.z);" 35 | "}" 36 | "vec2 hash(float p)" 37 | "{" 38 | "vec3 p3=fract(vec3(p)*vec3(.1031,.103,.0973));" 39 | "p3+=dot(p3,p3.yzx+19.19);" 40 | "return fract((p3.xx+p3.yz)*p3.zy);" 41 | "}" 42 | "vec2 fc;" 43 | "float sd;" 44 | "vec2 hash2(float n)" 45 | "{" 46 | "return vec2(hash(),hash());" 47 | "}" 48 | "const float phi=1.32472,delta0=.76,i0=.7;" 49 | "const vec2 alpha=vec2(1/phi,1/phi/phi);" 50 | "float hash(uvec2 x)" 51 | "{" 52 | "uvec2 q=1103515245U*(x>>1U^x.yx);" 53 | "uint n=1103515245U*(q.x^q.y>>3U);" 54 | "return float(n)*(1/float(-1U));" 55 | "}" 56 | "vec2 hash22(vec2 p)" 57 | "{" 58 | "vec3 p3=fract(vec3(p.xyx)*vec3(.1031,.103,.0973));" 59 | "p3+=dot(p3,p3.yzx+19.19);" 60 | "return fract((p3.xx+p3.yz)*p3.zy);" 61 | "}" 62 | "vec2 R2_seq(int i,float lambda,float n)" 63 | "{" 64 | "vec2 u=vec2(hash(uvec2(i,0)),hash(uvec2(i,1)))-.5,s=fract(alpha*float(i)+lambda*1.34707/(4*sqrt(float(i)-i0))*u);" 65 | "s+=hash(n);" 66 | "s+=hash22(fc);" 67 | "return mod(s,1);" 68 | "}\n" 69 | "#define scale.7\n" 70 | "#define maxi 0.75\n" 71 | "#define fr_it 8\n" 72 | "vec4 map(vec3 p)" 73 | "{" 74 | "vec3 po=p;" 75 | "p=-p;" 76 | "float k=1;" 77 | "for(int i=0;i15)" 111 | "break;" 112 | "h=map(ro+rd*t);" 113 | "res=vec4(t,h.yzw);" 114 | "t+=h.x;" 115 | "}" 116 | "return t>=15?vec4(-999):res;" 117 | "}" 118 | "vec3 calcNormal(vec3 p)" 119 | "{" 120 | "float c=map(p).x;" 121 | "const float e=1e-05;" 122 | "return normalize(vec3(c-map(p-vec3(e,0,0)).x,c-map(p-vec3(0,e,0)).x,c-map(p-vec3(0,0,e)).x));" 123 | "}" 124 | "vec3 lambert(in vec3 normal,in vec2 uv)" 125 | "{" 126 | "float theta=pi2*uv.x;" 127 | "uv.y=2*uv.y-1;" 128 | "vec3 spherePoint=vec3(sqrt(1-uv.y*uv.y)*vec2(cos(theta),sin(theta)),uv.y);" 129 | "return normalize(normal+spherePoint);" 130 | "}" 131 | "vec3 ggx(vec3 rd,vec3 n,float rgh,vec2 rand)" 132 | "{" 133 | "float s=-1+2*step(0,n.z),a=-1/(s+n.z),b=n.x*n.y*a;" 134 | "mat3 cs=mat3(vec3(1+s*n.x*n.x*a,s*b,-s*n.x),n,vec3(b,s+n.y*n.y*a,-n.y));" 135 | "float th=.5*pi*atan(rgh*sqrt(rand.x)/sqrt(1-rand.x));" 136 | "vec3 reflection=normalize(transpose(cs)*rd),normal_distribution=vec3(cos(pi2*rand.y)*sin(th),cos(th),sin(pi2*rand.y)*sin(th));" 137 | "return normalize(cs*reflect(reflection,normal_distribution));" 138 | "}" 139 | "vec3 brdf(in vec3 normal,in vec2 uv,in vec3 rd)" 140 | "{" 141 | "float theta=pi2*uv.x;" 142 | "uv.y=2*uv.y-1;" 143 | "vec3 p=vec3(sqrt(1-uv.y*uv.y)*vec2(cos(theta),sin(theta)),uv.y);" 144 | "float z=dot(p,normal);" 145 | "p-=z*normal;" 146 | "p/=sqrt(1-z*z);" 147 | "p*=(1-2*z+z*z)/4;" 148 | "p+=normal*sqrt(1-dot(p,p));" 149 | "vec3 ha=normalize(p);" 150 | "p=normalize(reflect(normalize(rd),p));" 151 | "return p;" 152 | "}" 153 | "vec3 colors[6];" 154 | "vec3 render(vec3 ro,vec3 rd)" 155 | "{" 156 | "vec3 color=vec3(0),absorption=vec3(1);" 157 | "const int bounces=4;" 158 | "for(int b=0;b=0)" 163 | "{" 164 | "vec3 pos=ro+tmat.x*rd,nor=calcNormal(pos);" 165 | "vec2 metal_coords=cross(nor,normalize(vec3(1,0,2))).xy;" 166 | "float rough_map=mix(texture(roughnessTexture,mod(metal_coords*1.21,1)).x,texture(roughnessTexture,mod(metal_coords*.53,1)).x,.7);" 167 | "rough_map=pow(1-rough_map,2);" 168 | "vec3 incoming=colors[int(tmat.y)];" 169 | "incoming*=1+.1*(1-2*rough_map)*step(1,tmat.w);" 170 | "vec2 tval=max(texture(roughnessTexture,pos.zx*.61).zx,texture(roughnessTexture,pos.xz*.75).xz);" 171 | "tval=mix(tval,pow(texture(roughnessTexture,pos.zx*-3.43).xz,vec2(2))*.5,.25);" 172 | "incoming=mix(incoming*vec3(0,.5,1)*.2,incoming,mix(.75+.5*length(tval),1,step(-.001,pos.y)));" 173 | "vec3 emission=tmat.z*incoming;" 174 | "color+=absorption*emission;" 175 | "absorption*=incoming/pi;" 176 | "vec3 rv=reflect(rd,nor);" 177 | "float rough1=.3-.3*pow(min(tval.x,tval.y),.1),rough2=mix(.04,.02+.04*pow(1-rough_map,2),step(.01,pos.y));" 178 | "vec3 metal=mix(brdf(nor,R2_seq(iFrame,.5,31.23+sd),rd),ggx(rd,nor,rough2,R2_seq(iFrame,.5,-43.531)),step(.45,hash())),ground=mix(lambert(nor,R2_seq(iFrame,.5,9531.53+sd)),ggx(rd,nor,rough1,R2_seq(iFrame,.5,86439.3)),step(.5,hash()));" 179 | "rd=mix(ground,metal,tmat.w);" 180 | "ro=pos+rd*E*20;" 181 | "}" 182 | "else" 183 | "{" 184 | "vec3 pos=ro+tmat.x*rd;" 185 | "color+=absorption*colors[4]*.9;" 186 | "break;" 187 | "}" 188 | "}" 189 | "return color;" 190 | "}" 191 | "void main()" 192 | "{" 193 | "colors[0]=1.25*vec3(.75,.52,.4).zyx;" 194 | "colors[1]=1.25*vec3(.75,.38,.2).zyx;" 195 | "colors[2]=vec3(.6,.32,.92).zyx;" 196 | "colors[3]=vec3(.42,.66,.95).zyx;" 197 | "colors[4]=normalize(vec3(3,1,.5)).zyx;" 198 | "colors[5]=vec3(1,.1,.04).zyx;" 199 | "fc=gl_FragCoord.xy;" 200 | "seed=float((iFrame*73856093^int(gl_FragCoord.x)*19349663^int(gl_FragCoord.y)*83492791)%38069);" 201 | "const float aspect=resolution.x/resolution.y;" 202 | "vec2 uv=gl_FragCoord.xy/resolution.xy;" 203 | "vec3 ro=vec3(.6,.1,.7)*20,col=vec3(0),ww=normalize(vec3(.76,.38,.1)-ro),uu=normalize(cross(normalize(vec3(0,1,0)),ww)),vv=normalize(cross(ww,uu));" 204 | "vec2 rand=hash2(0),p=-1+2*(uv+.667*(-1+2*hash2(3531.41))/resolution.xy);" 205 | "p.x*=aspect;" 206 | "vec2 lens_sample=vec2(cos(rand.x*pi2),sin(rand.x*pi2))*pow(rand.y,.32);" 207 | "lens_sample*=vec2(.75,1.333);" 208 | "float ffac=pow(smoothstep(.1,1.99,length(p)),4);" 209 | "const float depth_of_field=.1+.035*ffac,fov_length=40.4-.8*ffac,focus_distance=.45;" 210 | "vec3 lens_pos=ro+(lens_sample.x*uu+lens_sample.y*vv)*depth_of_field,vr=normalize(ro+(p.x*uu+p.y*vv+fov_length*ww)*focus_distance-lens_pos);" 211 | "gl_FragColor.xyz=min(render(lens_pos+17.3*vr,vr),15);" 212 | "}"; 213 | -------------------------------------------------------------------------------- /src/shaders/post.frag: -------------------------------------------------------------------------------- 1 | #version 430 2 | 3 | layout(location = 0) uniform int iFrame; 4 | layout(binding = 0) uniform sampler2D imageTexture; 5 | layout(location = 2) uniform vec2 resolution; 6 | 7 | float gamma = 2.2; 8 | vec3 tonemap(vec3 color){ 9 | float A = 0.15; 10 | float B = 0.50; 11 | float C = 0.10; 12 | float D = 0.20; 13 | float E = 0.01; 14 | float F = 0.30; 15 | float W = 90.0; 16 | float exposure = 4.5; 17 | color *= exposure; 18 | color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; 19 | float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F; 20 | color /= white; 21 | 22 | return color; 23 | } 24 | 25 | const int N = 8; 26 | vec3 ca(sampler2D t, vec2 UV){ 27 | vec2 uv = 1.0 - 2.0 * UV; 28 | vec3 c = vec3(0); 29 | float rf = 1.0; 30 | float gf = 1.0; 31 | float bf = 1.0; 32 | float f = 1.0/float(N); 33 | for(int i = 0; i < N; ++i){ 34 | c.r += f*texture(t, 0.5-0.5*(uv*rf) ).r; 35 | c.g += f*texture(t, 0.5-0.5*(uv*gf) ).g; 36 | c.b += f*texture(t, 0.5-0.5*(uv*bf) ).b; 37 | bf *= 0.9995; 38 | gf *= 0.9997; 39 | rf *= 0.99988; 40 | } 41 | return c; 42 | } 43 | 44 | vec3 source(vec2 coord){ 45 | //vec4 fc = texelFetch(imageTexture, ivec2(coord), 0)/float(iFrame); 46 | vec3 fc = ca(imageTexture, coord/resolution.xy)/float(iFrame); 47 | 48 | // vignette 49 | vec2 uv = (coord / resolution.xy - 0.5) * (resolution.x/resolution.y) * 2.0; 50 | fc.rgb *= 1.0 / pow(0.09 * dot(uv, uv) + 1.0, 2.0); 51 | 52 | fc.rgb = tonemap(fc.rgb); 53 | fc.rgb = smoothstep(vec3(0), vec3(1), fc.rgb); 54 | return fc.rgb; 55 | } 56 | 57 | void main(){ 58 | vec3 center = source(gl_FragCoord.xy + vec2( 0, 0)); 59 | vec3 c = vec3(0); 60 | //c += source(fragCoord + vec2(-1,-1)); 61 | //c += source(fragCoord + vec2( 1,-1)); 62 | //c += source(fragCoord + vec2(-1, 1)); 63 | //c += source(fragCoord + vec2( 1, 1)); 64 | c += -1.0*source(gl_FragCoord.xy + vec2( 0,-1)); 65 | c += -1.0*source(gl_FragCoord.xy + vec2(-1, 0)); 66 | c += 5.0*center; 67 | c += -1.0*source(gl_FragCoord.xy + vec2( 1, 0)); 68 | c += -1.0*source(gl_FragCoord.xy + vec2( 0, 1)); 69 | 70 | c = mix(center, c, 0.12); 71 | gl_FragColor.rgb = pow( pow(c, vec3(1.005, 1.0, 0.99)), vec3(1.0 / gamma)); 72 | } 73 | -------------------------------------------------------------------------------- /src/shaders/post.inl: -------------------------------------------------------------------------------- 1 | #pragma data_seg(".shader") 2 | const char* post = 3 | "#version 430\n" 4 | "layout(location=0)uniform int g;" 5 | "layout(binding=0)uniform sampler2D v;" 6 | "layout(location=2)uniform vec2 d;" 7 | "float z=2.2;" 8 | "vec3 t(vec3 g)" 9 | "{" 10 | "float v=.15,z=.5,n=.1,i=.2,e=.01,d=.3,y=90,t=4.5;" 11 | "g*=t;" 12 | "g=(g*(v*g+n*z)+i*e)/(g*(v*g+z)+i*d)-e/d;" 13 | "float m=(y*(v*y+n*z)+i*e)/(y*(v*y+z)+i*d)-e/d;" 14 | "g/=m;" 15 | "return g;" 16 | "}" 17 | "const int i=8;" 18 | "vec3 t(sampler2D v,vec2 z)" 19 | "{" 20 | "vec2 g=1-2*z;" 21 | "vec3 d=vec3(0);" 22 | "float e=1,y=1,n=1,t=1/float(i);" 23 | "for(int f=0;f 2 | #include "song.h" 3 | #pragma comment(lib, "strmiids.lib") 4 | #pragma comment(lib, "ComCtl32.lib") 5 | #pragma comment(lib, "winmm.lib") 6 | 7 | #pragma comment(lib, "strmbase.lib") 8 | 9 | using namespace Leviathan; 10 | 11 | Song::Song() 12 | { 13 | } 14 | 15 | Song::Song(LPCWSTR path) : playing(false) 16 | { 17 | __int64 trackLength; 18 | IGraphBuilder * graph; 19 | 20 | CoInitialize(0); 21 | CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC, IID_IGraphBuilder, (void**)&graph); 22 | 23 | graph->QueryInterface(IID_IMediaControl, (void**)&mediaControl); 24 | graph->QueryInterface(IID_IMediaSeeking, (void**)&mediaSeeking); 25 | graph->QueryInterface(IID_IBasicAudio, (void**)&audioControl); 26 | audioControl->put_Volume(long(-90000)); 27 | 28 | HRESULT hr = graph->RenderFile(path, 0); 29 | 30 | if (hr == S_OK) 31 | printf("Audio file opened successfully\n"); 32 | else 33 | printf("Failed to open audio file\n"); 34 | 35 | graph->Release(); 36 | 37 | mediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME); 38 | mediaSeeking->GetDuration(&trackLength); 39 | length = (long double)(trackLength) / (long double)10000000.0; 40 | 41 | __int64 position = 0; 42 | mediaSeeking->SetPositions(&position, AM_SEEKING_AbsolutePositioning, &position, AM_SEEKING_NoPositioning); 43 | pause(); 44 | } 45 | 46 | Song::~Song() 47 | { 48 | audioControl->Release(); 49 | mediaControl->Release(); 50 | mediaSeeking->Release(); 51 | } 52 | 53 | int Song::play() 54 | { 55 | mediaControl->Run(); 56 | playing = true; 57 | return 0; 58 | } 59 | 60 | int Song::pause() 61 | { 62 | mediaControl->Stop(); 63 | playing = false; 64 | return 0; 65 | } 66 | 67 | int Song::toggle() 68 | { 69 | playing = !playing; 70 | if (playing) 71 | play(); 72 | else 73 | pause(); 74 | return 0; 75 | } 76 | 77 | bool Song::is_playing() 78 | { 79 | OAFilterState state; 80 | mediaControl->GetState(INFINITE, &state); 81 | __int64 trackLength, position; 82 | mediaSeeking->GetDuration(&trackLength); 83 | mediaSeeking->GetCurrentPosition(&position); 84 | return positionlength) 90 | position = length; 91 | if (position<.0) 92 | position = .0; 93 | 94 | __int64 seekPosition = __int64(10000000.0*position); 95 | 96 | if (playing) 97 | mediaControl->Stop(); 98 | mediaSeeking->SetPositions(&seekPosition, AM_SEEKING_AbsolutePositioning, &seekPosition, AM_SEEKING_NoPositioning); 99 | if (playing) 100 | mediaControl->Run(); 101 | 102 | return 0; 103 | } 104 | 105 | long double Song::getTime() 106 | { 107 | __int64 position; 108 | mediaSeeking->GetCurrentPosition(&position); 109 | return (long double)(position) / (long double)10000000.0; 110 | } 111 | 112 | long double Song::getLength() 113 | { 114 | return length; 115 | } -------------------------------------------------------------------------------- /src/song.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma warning(disable:995) 4 | #include 5 | #pragma warning(default:995) 6 | 7 | namespace Leviathan 8 | { 9 | class Song 10 | { 11 | public: 12 | Song(); 13 | 14 | Song(LPCWSTR path); 15 | 16 | ~Song(); 17 | 18 | int play(); 19 | 20 | int pause(); 21 | 22 | int toggle(); 23 | 24 | bool is_playing(); 25 | 26 | int seek(long double position); 27 | 28 | long double getTime(); 29 | 30 | long double getLength(); 31 | 32 | private: 33 | long double length; 34 | bool playing; 35 | IMediaControl * mediaControl; 36 | IMediaSeeking * mediaSeeking; 37 | IBasicAudio * audioControl; 38 | }; 39 | } 40 | --------------------------------------------------------------------------------