diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000000000000000000000000000000000..daa3738fe5bd7a0202e5329a2b7f69f217ed8ede --- /dev/null +++ b/.clang-format @@ -0,0 +1,70 @@ +BasedOnStyle: WebKit +IndentWidth: 4 +ColumnLimit: 100 + +Language: Cpp +Standard: c++20 +DerivePointerAlignment: false + +PointerAlignment: Left +ReferenceAlignment: Right + +SortIncludes: CaseSensitive + +AccessModifierOffset: -4 + +AlignAfterOpenBracket: true +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: None +AlignEscapedNewlines: Left +AlignOperands: AlignAfterOperator +AlignTrailingComments: true + +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Attach +BreakBeforeConceptDeclarations: true +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: true + +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false + +AlwaysBreakTemplateDeclarations: Yes +EmptyLineBeforeAccessModifier: Always + +BinPackParameters: true +ReflowComments: true + +ShortNamespaceLines: 0 +NamespaceIndentation: All +FixNamespaceComments: true + +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Leave +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false + +TabWidth: 4 +UseTab: ForContinuationAndIndentation + +UseCRLF: false diff --git a/.gitignore b/.gitignore index 7ee4ff1903e902c4715c6e2b0c3e784ed5755aaf..8ab30e60ef913a4d49762b7326c878fc130696cc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ .editorconfig # build directories +bin/ build/ cmake-build-debug/ cmake-build-release/ @@ -18,3 +19,11 @@ cmake-build-release/ # GUI configuration files imgui.ini + +# Generated source and header files for shaders +*.hxx +*.cxx + +# Cache for different tasks like source code indexing +.cache/ + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 33b70018e368ecc3ad019ea33e57485814eb233a..0000000000000000000000000000000000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,89 +0,0 @@ -variables: - RUN: - value: "all" - description: "The tests that should run. Possible values: ubuntu, win, all." - GIT_DEPTH: 1 - -stages: - - build - - deploy - -build_ubuntu_gcc: - only: - variables: - - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i - stage: build - tags: - - ubuntu-gcc-cached - variables: - GIT_SUBMODULE_STRATEGY: recursive - timeout: 10m - retry: 1 - script: - - mkdir debug - - cd debug - - cmake -DCMAKE_BUILD_TYPE=Debug .. - - cmake --build . - artifacts: - name: "Documentation - $CI_PIPELINE_ID" - paths: - - doc/html - - doc/latex - expire_in: never - -build_win10_msvc: - only: - variables: - - $RUN =~ /\bwin.*/i || $RUN =~ /\ball.*/i - stage: build - tags: - - win10-msvc-cached - variables: - GIT_SUBMODULE_STRATEGY: recursive - timeout: 10m - retry: 0 - script: - - cd 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\' - - .\Launch-VsDevShell.ps1 - - cd $CI_PROJECT_DIR - - mkdir debug - - cd debug - - cmake -DCMAKE_BUILD_TYPE=Debug .. - - cmake --build . - -deploy_doc_develop: - only: - variables: - - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i - refs: - - develop - stage: deploy - needs: ["build_ubuntu_gcc"] - dependencies: - - build_ubuntu_gcc - tags: - - webserver - variables: - GIT_STRATEGY: none - script: - - rsync -avh doc/html/ /var/www/html/develop --delete - - echo "Check it out at https://vkcv.de/develop" - -deploy_doc_branch: - only: - variables: - - $RUN =~ /\bubuntu.*/i || $RUN =~ /\ball.*/i - except: - refs: - - develop - stage: deploy - needs: ["build_ubuntu_gcc"] - dependencies: - - build_ubuntu_gcc - tags: - - webserver - variables: - GIT_STRATEGY: none - script: - - rsync -avh doc/html/ /var/www/html/branch/$CI_COMMIT_BRANCH --delete - - echo "Check it out at https://vkcv.de/branch/$CI_COMMIT_BRANCH" \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index e0aaf2d17c340f98ae875f7e0f1238bfe04f7e5d..d3b0a1404e8b1bb1a7b29154759d006adc8615e4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,7 +14,7 @@ path = modules/asset_loader/lib/stb url = https://github.com/nothings/stb.git [submodule "modules/camera/lib/glm"] - path = modules/camera/lib/glm + path = modules/geometry/lib/glm url = https://github.com/g-truc/glm.git [submodule "modules/shader_compiler/lib/glslang"] path = modules/shader_compiler/lib/glslang @@ -22,3 +22,27 @@ [submodule "modules/gui/lib/imgui"] path = modules/gui/lib/imgui url = https://github.com/ocornut/imgui.git +[submodule "modules/upscaling/lib/FidelityFX-FSR"] + path = modules/upscaling/lib/FidelityFX-FSR + url = https://github.com/GPUOpen-Effects/FidelityFX-FSR.git +[submodule "lib/Vulkan-Headers"] + path = lib/Vulkan-Headers + url = https://github.com/KhronosGroup/Vulkan-Headers.git +[submodule "lib/Vulkan-Hpp"] + path = lib/Vulkan-Hpp + url = https://github.com/KhronosGroup/Vulkan-Hpp +[submodule "modules/upscaling/lib/NVIDIAImageScaling"] + path = modules/upscaling/lib/NVIDIAImageScaling + url = https://github.com/NVIDIAGameWorks/NVIDIAImageScaling.git +[submodule "lib/VulkanMemoryAllocator-Hpp"] + path = lib/VulkanMemoryAllocator-Hpp + url = https://github.com/YaaZ/VulkanMemoryAllocator-Hpp.git +[submodule "modules/algorithm/lib/FidelityFX-SPD"] + path = modules/algorithm/lib/FidelityFX-SPD + url = https://github.com/GPUOpen-Effects/FidelityFX-SPD.git +[submodule "modules/upscaling/lib/FidelityFX-FSR2"] + path = modules/upscaling/lib/FidelityFX-FSR2 + url = https://github.com/TheJackiMonster/FidelityFX-FSR2.git +[submodule "lib/VulkanMemoryAllocator"] + path = lib/VulkanMemoryAllocator + url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000000000000000000000000000000000..540b00747eee74a94be3c987d1ac4c8595738a09 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,20 @@ +Alexander Gauggel +Artur Wasmut +Josch Morgenstern +Katharina Krämer +Lars Hoerttrich +Leonie Franken +Mara Vogt +Mark O. Mints +Sebastian Gaida +Simeon Hermann +Susanne Dötsch +Tobias Frisch +Trevor Hollmann +Vanessa Karolek + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..3e2cfbfed372f4d7aa182b6129c34f2bc6abccb6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,43 @@ +# Changelog + +## [0.1.0](https://gitlab.uni-koblenz.de/vulkan2021/vkcv-framework/tree/0.1.0) (2021-12-07) + +** Platform support:** + + - Linux support (GCC and CLang) + - MacOS support (Apple CLang) + - Windows support (MSVC and MinGW-GCC experimentally) + +** New modules:** + + - [Asset-Loader](modules/asset_loader/README.md): A VkCV module to load basic assets like models, materials and images + - [Camera](modules/asset_loader/README.md): A VkCV module to manage cameras and their handle view and projection matrices + - [GUI](modules/gui/README.md): A VkCV module to integrate GUI rendering to your application as additional pass + - [Material](modules/material/README.md): A VkCV module to abstract typical kinds of materials for rendering + - [Meshlet](modules/meshlet/README.md): A VkCV module to divide vertex data of a mesh into meshlets + - [Scene](modules/scene/README.md): A VkCV module to load and manage a scene, simplify its rendering and potentially optimize it + - [Shader-Compiler](modules/shader_compiler/README.md): A VkCV module to compile shaders at runtime + - [Upscaling](modules/upscaling/README.md): A VkCV module to upscale images in realtime + +** New features:** + + - Resizable windows + - Multiple windows and multiple swapchains (window management) + - Dynamically requesting Vulkan features and extensions + - Shader reflection and runtime shader compilation (various shader stages) + - Realtime ray tracing + - Mesh shaders + - Indirect dispatch + - Compute pipelines and compute shaders + - Multiple queues and graphic pipelines + - Bindless textures + - ImGUI support + - Mipmapping + - Logging + - Command buffer synchronization + - Doxygen source code documentation + - Buffer, sampler and image management + - Camera management with gamepad support + - Input event synchronization + - Resource management with handles + diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ae078a428a8e5e640ed8dc7bcc2f4e58e159c6b..15f953b3b3b46e7e6f86526d90a53a5d4d40d3bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,41 @@ cmake_minimum_required(VERSION 3.16) project(vkcv) +# cmake options +option(BUILD_MODULES "Enables building VkCV as shared libraries" ON) +option(BUILD_PROJECTS "Enables building the VkCV projects" ON) +option(BUILD_CLANG_FORMAT "Enables formatting the source code" OFF) +option(BUILD_DOXYGEN_DOCS "Enables building the VkCV doxygen documentation" OFF) +option(BUILD_SHARED "Enables building VkCV as shared libraries" OFF) +option(BUILD_VMA_VULKAN_VERSION "Enforce a specific Vulkan version for VMA" OFF) +option(BUILD_VALIDATION_FORCED "Enforce validation layers being built-in" OFF) + +# uncomment the following line if cmake will refuse to build projects +#set(BUILD_PROJECTS ON) + +if ((WIN32) AND (NOT BUILD_VMA_VULKAN_VERSION)) + set(BUILD_VMA_VULKAN_VERSION "1.3.0") +endif() + +if (BUILD_PROJECTS) + set(BUILD_MODULES ${BUILD_PROJECTS}) +endif() + +message(STATUS "Options:") +message(" - BUILD_MODULES: ${BUILD_MODULES}") +message(" - BUILD_PROJECTS: ${BUILD_PROJECTS}") +message(" - BUILD_CLANG_FORMAT: ${BUILD_CLANG_FORMAT}") +message(" - BUILD_DOXYGEN_DOCS: ${BUILD_DOXYGEN_DOCS}") +message(" - BUILD_SHARED: ${BUILD_SHARED}") + +if (BUILD_SHARED) + set(vkcv_build_attribute SHARED) +else() + set(vkcv_build_attribute STATIC) +endif() + # settings c++ standard for the framework -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # checking build type and setting up a variable @@ -11,11 +44,18 @@ if (CMAKE_BUILD_TYPE) set(vkcv_build_${_vkcv_build_type} 1) endif() -message("-- Language: [ C++ " ${CMAKE_CXX_STANDARD} " ]") -message("-- Compiler: [ " ${CMAKE_CXX_COMPILER_ID} " " ${CMAKE_CXX_COMPILER_VERSION} " ]") +if (EXISTS "/usr/bin/mold") + set(CMAKE_LINKER "/usr/bin/mold") +endif() + +message(STATUS "Language: [ C++ " ${CMAKE_CXX_STANDARD} " ]") +message(STATUS "Compiler: [ " ${CMAKE_CXX_COMPILER_ID} " " ${CMAKE_CXX_COMPILER_VERSION} " ]") +message(STATUS "Linker: [ " ${CMAKE_LINKER} " ]") if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0")) message(FATAL_ERROR "Upgrade your compiler! GCC 9.0+ is required!") +elseif(MINGW) + message(WARNING "MinGW is impressively unstable! So beware it may not compile or crash during runtime!") endif() # setting up different paths @@ -33,38 +73,61 @@ set(vkcv_flags ${CMAKE_CXX_FLAGS}) # enabling warnings in the debug build if (vkcv_build_debug) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(vkcv_flags ${vkcv_flags} " -Weverything") + #set(vkcv_flags ${vkcv_flags} " -Weverything") + set(vkcv_flags ${vkcv_flags} " -Wextra -Wall -Wno-unused-parameter") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(vkcv_flags ${vkcv_flags} " -Wextra -Wall -pedantic") + set(vkcv_flags ${vkcv_flags} " -Wextra -Wall -pedantic -Wno-unused-parameter") else() set(vkcv_flags ${vkcv_flags} " -W4") endif() + + list(APPEND vkcv_definitions VULKAN_VALIDATION_LAYERS) +elseif (BUILD_VALIDATION_FORCED) + list(APPEND vkcv_definitions VULKAN_VALIDATION_LAYERS) +endif() + +if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "11.0.0")) + list(APPEND vkcv_definitions __NO_SEMAPHORES__) endif() # configure everything to use the required dependencies include(${vkcv_config}/Libraries.cmake) +# set macro to enable vulkan debug labels +list(APPEND vkcv_definitions VULKAN_DEBUG_LABELS) + # set the compile definitions aka preprocessor variables add_compile_definitions(${vkcv_definitions}) -# add modules as targets -add_subdirectory(modules) +# check if the framework is used from a parent scope +get_directory_property(vkcv_parent_scope PARENT_DIRECTORY) + +if (BUILD_MODULES) + message(STATUS "Modules: ON") + + # add modules as targets + add_subdirectory(modules) +else() + message(STATUS "Modules: OFF") +endif() # add source files for compilation include(${vkcv_config}/Sources.cmake) -message("-- Libraries: [ ${vkcv_libraries} ]") -message("-- Flags: [ ${vkcv_flags} ]") +message(STATUS "Framework:") +message(" - Includes: [ ${vkcv_includes} ]") +message(" - Libraries: [ ${vkcv_libraries} ]") +message(" - Flags: [ ${vkcv_flags} ]") # set the compiler flags for the framework set(CMAKE_CXX_FLAGS ${vkcv_flags}) -# create VkCV framework as static library using all source files -add_library(vkcv STATIC ${vkcv_sources}) +# create VkCV framework as library using all source files +add_library(vkcv ${vkcv_build_attribute} ${vkcv_sources}) if(MSVC) #enable multicore compilation on visual studio - target_compile_options(vkcv PRIVATE "/MP" "/openmp") + target_compile_options(vkcv PRIVATE "/MP" "/openmp" "/Zc:offsetof-") #set source groups to create proper filters in visual studio source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${vkcv_sources}) @@ -72,20 +135,40 @@ endif() # add include directories from dependencies as system includes target_include_directories(vkcv SYSTEM BEFORE PRIVATE ${vkcv_includes}) -message(STATUS ${vkcv_includes}) # add the own include directory for public headers target_include_directories(vkcv BEFORE PUBLIC ${vkcv_include}) -message(STATUS ${vkcv_include}) # link the framework using all required libraries target_link_libraries(vkcv ${vkcv_libraries}) -message(STATUS ${vkcv_libraries}) -# add sub-projects/examples as targets -add_subdirectory(projects) +if (BUILD_PROJECTS) + message(STATUS "Projects: ON") + + # add sub-projects/examples as targets + add_subdirectory(projects) +else() + message(STATUS "Projects: OFF") +endif() -if (NOT WIN32) +if (BUILD_DOXYGEN_DOCS) + message(STATUS "Doxygen: ON") + # add doxygen as target if installed include(${vkcv_config}/ext/Doxygen.cmake) +else() + message(STATUS "Doxygen: OFF") +endif() + +if (vkcv_parent_scope) + list(APPEND vkcv_includes ${vkcv_include}) + list(APPEND vkcv_libraries vkcv) + + if (BUILD_MODULES) + list(APPEND vkcv_includes ${vkcv_modules_includes}) + list(APPEND vkcv_libraries ${vkcv_modules_libraries}) + endif() + + set(vkcv_includes ${vkcv_includes} PARENT_SCOPE) + set(vkcv_libraries ${vkcv_libraries} PARENT_SCOPE) endif() \ No newline at end of file diff --git a/Doxyfile b/Doxyfile index a657ede81c43b3cae1fc5c17f071aa7dc65add04..a7c5c76bb52fe4a0cd4b2f982abff642af011f8f 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.1 +# Doxyfile 1.9.3 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -38,7 +38,7 @@ PROJECT_NAME = "VkCV Framework" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.0.1 +PROJECT_NUMBER = 0.1.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -93,14 +93,6 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -258,16 +250,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -312,8 +304,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -365,13 +357,13 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. -CPP_CLI_SUPPORT = NO +CPP_CLI_SUPPORT = YES # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen @@ -404,7 +396,7 @@ DISTRIBUTE_GROUP_DOC = NO # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. -GROUP_NESTED_COMPOUNDS = NO +GROUP_NESTED_COMPOUNDS = YES # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that @@ -434,7 +426,7 @@ INLINE_GROUPED_CLASSES = NO # Man pages) or section (for LaTeX and RTF). # The default value is: NO. -INLINE_SIMPLE_STRUCTS = NO +INLINE_SIMPLE_STRUCTS = YES # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So @@ -466,12 +458,12 @@ LOOKUP_CACHE_SIZE = 0 # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. -NUM_PROC_THREADS = 1 +NUM_PROC_THREADS = 0 #--------------------------------------------------------------------------- # Build related configuration options @@ -491,13 +483,13 @@ EXTRACT_ALL = NO # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = YES +EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. -EXTRACT_PRIV_VIRTUAL = YES +EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. @@ -517,7 +509,7 @@ EXTRACT_STATIC = YES # for Java sources. # The default value is: YES. -EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are @@ -564,7 +556,7 @@ HIDE_UNDOC_CLASSES = NO # documentation. # The default value is: NO. -HIDE_FRIEND_COMPOUNDS = NO +HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these @@ -610,6 +602,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -667,7 +665,7 @@ SORT_MEMBERS_CTORS_1ST = NO # appear in their defined order. # The default value is: NO. -SORT_GROUP_NAMES = NO +SORT_GROUP_NAMES = YES # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will @@ -677,7 +675,7 @@ SORT_GROUP_NAMES = NO # list. # The default value is: NO. -SORT_BY_SCOPE_NAME = NO +SORT_BY_SCOPE_NAME = YES # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between @@ -743,7 +741,7 @@ SHOW_USED_FILES = YES # (if specified). # The default value is: YES. -SHOW_FILES = YES +SHOW_FILES = NO # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the @@ -767,7 +765,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -813,21 +812,29 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS @@ -850,7 +857,10 @@ WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -864,8 +874,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = src \ - include \ +INPUT = include \ modules # This tag can be used to specify the character encoding of the source files @@ -890,10 +899,10 @@ INPUT_ENCODING = UTF-8 # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, -# *.ucf, *.qsf and *.ice. +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -976,7 +985,7 @@ EXCLUDE_PATTERNS = */lib/* # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test +# ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* @@ -1076,7 +1085,7 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = YES +SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -1095,13 +1104,13 @@ STRIP_CODE_COMMENTS = YES # entity all documented functions referencing it will be listed. # The default value is: NO. -REFERENCED_BY_RELATION = YES +REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. -REFERENCES_RELATION = YES +REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and @@ -1261,17 +1270,17 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_HUE = 0 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1287,7 +1296,7 @@ HTML_COLORSTYLE_SAT = 100 # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_GAMMA = 80 +HTML_COLORSTYLE_GAMMA = 220 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this @@ -1353,6 +1362,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1378,8 +1394,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: -# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1538,16 +1558,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1572,6 +1604,13 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1620,11 +1659,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1637,15 +1694,21 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = @@ -1825,29 +1888,31 @@ PAPER_TYPE = a4 EXTRA_PACKAGES = -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the -# generated LaTeX document. The header should contain everything until the first -# chapter. If it is left blank doxygen will generate a standard header. See -# section "Doxygen usage" for information on how to let doxygen write the -# default header to a separate file. +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. # -# Note: Only use a user-defined header if you know what you are doing! The -# following commands have a special meaning inside the header: $title, -# $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empty -# string, for the replacement values of the other commands the user is referred -# to HTML_HEADER. +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the -# generated LaTeX document. The footer should contain everything after the last -# chapter. If it is left blank doxygen will generate a standard footer. See +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See # LATEX_HEADER for more information on how to generate a default footer and what -# special commands can be used inside the footer. -# -# Note: Only use a user-defined footer if you know what you are doing! +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_FOOTER = @@ -1892,8 +1957,7 @@ USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode # command to the generated LaTeX files. This will instruct LaTeX to keep running -# if errors occur, instead of asking the user for help. This option is also used -# when generating formulas in HTML. +# if errors occur, instead of asking the user for help. # The default value is: NO. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1906,16 +1970,6 @@ LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source -# code with syntax highlighting in the LaTeX output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_LATEX is set to YES. - -LATEX_SOURCE_CODE = NO - # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See # https://en.wikipedia.org/wiki/BibTeX and \cite for more info. @@ -1996,16 +2050,6 @@ RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code -# with syntax highlighting in the RTF output. -# -# Note that which sources are shown also depends on other settings such as -# SOURCE_BROWSER. -# The default value is: NO. -# This tag requires that the tag GENERATE_RTF is set to YES. - -RTF_SOURCE_CODE = NO - #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -2102,15 +2146,6 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the -# program listings (including syntax highlighting and cross-referencing -# information) to the DOCBOOK output. Note that enabling this will significantly -# increase the size of the DOCBOOK output. -# The default value is: NO. -# This tag requires that the tag GENERATE_DOCBOOK is set to YES. - -DOCBOOK_PROGRAMLISTING = NO - #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -2293,15 +2328,6 @@ EXTERNAL_PAGES = YES # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram -# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to -# NO turns the diagrams off. Note that this option also works with HAVE_DOT -# disabled, but it is recommended to install and use dot, since it yields more -# powerful graphs. -# The default value is: YES. - -CLASS_DIAGRAMS = NO - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2322,7 +2348,7 @@ HIDE_UNDOC_RELATIONS = YES # set to NO # The default value is: NO. -HAVE_DOT = YES +HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2358,11 +2384,14 @@ DOT_FONTSIZE = 10 DOT_FONTPATH = -# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for -# each documented class showing the direct and indirect inheritance relations. -# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a +# graph for each documented class showing the direct and indirect inheritance +# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, +# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set +# to TEXT the direct and indirect inheritance relations will be shown as texts / +# links. +# Possible values are: NO, YES, TEXT and GRAPH. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. CLASS_GRAPH = YES @@ -2461,7 +2490,7 @@ INCLUDED_BY_GRAPH = YES # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALL_GRAPH = YES +CALL_GRAPH = NO # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # dependency graph for every global function or class method. @@ -2473,7 +2502,7 @@ CALL_GRAPH = YES # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALLER_GRAPH = YES +CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # hierarchy of all classes instead of a textual one. @@ -2491,6 +2520,13 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. For an explanation of the image formats see the section # output formats in the documentation of the dot tool (Graphviz (see: @@ -2544,10 +2580,10 @@ MSCFILE_DIRS = DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the -# path where java can find the plantuml.jar file. If left blank, it is assumed -# PlantUML is not used or called during a preprocessing step. Doxygen will -# generate a warning when it encounters a \startuml command in this case and -# will not generate output for the diagram. +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. PLANTUML_JAR_PATH = @@ -2609,6 +2645,8 @@ DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page # explaining the meaning of the various boxes and arrows in the dot generated # graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2617,8 +2655,8 @@ GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. # -# Note: This setting is not only used for dot files but also for msc and -# plantuml temporary files. +# Note: This setting is not only used for dot files but also for msc temporary +# files. # The default value is: YES. DOT_CLEANUP = YES diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c1f3c5685b29d78b26dcc8c9e454b74c33f73ba5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Universität Koblenz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 29c45f2660cb29d8ded6cdac14d1bf2db6b2ffc3..db60c73d3bfb25aa47d8c9808e0f2a07989724c2 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,84 @@ ## Repository -Git LFS is used for bigger resource files like meshes and textures. So you need to install Git LFS and use `git lfs install` after cloning. +Git LFS is used for bigger resource files like meshes and textures. So you need to install Git LFS +and use `git lfs install` after cloning. More information about Git LFS [here](https://git-lfs.github.com/). ## Build - [](https://gitlab.uni-koblenz.de/vulkan2021/vkcv-framework/-/commits/develop) +Git submodules are used for libraries. To download the submodules either clone using +`git clone --recurse-submodules` or after `git clone` use `git submodule init` and +`git submodule update`. -Git submodules are used for libraries. -To download the submodules either clone using `git clone --recurse-submodules` or after `git clone` use `git submodule init` and `git submodule update`. +Detailed build process: + - [How to build on Windows](doc/BUILD_WINDOWS.md) + - [How to build on macOS](doc/BUILD_MACOS.md) + - [How to build on Linux](doc/BUILD_LINUX.md) -## Documentation +### Dependencies (required): + +Most dependencies are used via submodules but for example Vulkan needs to be installed correctly +depending on your platform. So please setup your environment properly. + +| Name of dependency | Used as submodule | +|-----------------------------------------------------------------------------------|---| +| [Vulkan](https://www.vulkan.org/) | ⌠| +| [Vulkan-Headers](https://github.com/KhronosGroup/Vulkan-Headers) | ✅ | +| [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp) | ✅ | +| [GLFW](https://www.glfw.org/) | ✅ | +| [SPIRV-CROSS](https://github.com/KhronosGroup/SPIRV-Cross) | ✅ | +| [VulkanMemoryAllocator-Hpp](https://github.com/malte-v/VulkanMemoryAllocator-Hpp) | ✅ | + +### Modules (optional): + +The following modules are provided in this repository and they build automatically together with +the framework if used. You can configure/adjust the build using CMake if necessary. + + - [Algorithm](modules/algorithm/README.md) + - [Asset-Loader](modules/asset_loader/README.md) + - [Camera](modules/asset_loader/README.md) + - [GUI](modules/gui/README.md) + - [Effects](modules/effects/README.md) + - [Geometry](modules/geometry/README.md) + - [Material](modules/material/README.md) + - [Meshlet](modules/meshlet/README.md) + - [Scene](modules/scene/README.md) + - [Shader-Compiler](modules/shader_compiler/README.md) + - [Upscaling](modules/upscaling/README.md) -The documentation for the develop-branch can be found here: -https://vkcv.de/develop/ +### Projects (optional): + +The following projects are provided in this repository and can be build with their own CMake +targets: + + - [bindless_textures](projects/bindless_textures/README.md) + - [fire_works](projects/fire_works/README.md) + - [first_mesh](projects/first_mesh/README.md) + - [first_scene](projects/first_scene/README.md) + - [first_triangle](projects/first_triangle/README.md) + - [head_demo](projects/head_demo/README.md) + - [indirect_dispatch](projects/indirect_dispatch/README.md) + - [indirect_draw](projects/indirect_draw/README.md) + - [mesh_shader](projects/mesh_shader/README.md) + - [mpm](projects/mpm/README.md) + - [particle_simulation](projects/particle_simulation/README.md) + - [path_tracer](projects/path_tracer/README.md) + - [ray_tracer](projects/ray_tracer/README.md) + - [rtx_ambient_occlusion](projects/rtx_ambient_occlusion/README.md) + - [sph](projects/sph/README.md) + - [voxelization](projects/voxelization/README.md) + +## Development + +See this guide to setup your IDE for most forward development. + - [How to setup your IDE](doc/SETUP_IDE.md) + +## Documentation -The documentation concerning the respective merge request is listed here: -https://vkcv.de/branch/ +A pre-built documentation can be found here: +https://userpages.uni-koblenz.de/~vkcv/doc/ -It is automatically generated and uploaded using the CI pipeline. +But it is recommended to build the documentation with Doxygen locally to get the most recent +changes. There is also an optional CMake target to build the documentation via Doxygen. diff --git a/config/Libraries.cmake b/config/Libraries.cmake index ec014f84c820abf4988b070d5b733be08c377319..f78ca1f5df4ef40683b7948473dbc6a506f67cd1 100644 --- a/config/Libraries.cmake +++ b/config/Libraries.cmake @@ -3,13 +3,24 @@ set(vkcv_config_lib ${vkcv_config}/lib) set(vkcv_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_lib}) if(NOT WIN32) - set(vkcv_libraries stdc++fs) + if (((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.1.0")) OR + ((CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0"))) + set(vkcv_libraries stdc++fs) + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + list(APPEND vkcv_flags -Xpreprocessor) + else() + # optimization for loading times + list(APPEND vkcv_flags -fopenmp) + endif() - # optimization for loading times list(APPEND vkcv_flags -pthread) - list(APPEND vkcv_flags -fopenmp) endif() +# add custom functions to check for git submodules +include(${vkcv_config_ext}/Git.cmake) + list(APPEND vkcv_definitions _USE_MATH_DEFINES) # some formatted printing @@ -19,6 +30,7 @@ set(vkcv_config_msg " - Library: ") include(${vkcv_config_lib}/GLFW.cmake) # glfw-x11 / glfw-wayland # libglfw3-dev include(${vkcv_config_lib}/Vulkan.cmake) # vulkan-intel / vulkan-radeon / nvidia # libvulkan-dev include(${vkcv_config_lib}/SPIRV_Cross.cmake) # SPIRV-Cross # libspirv_cross_c_shared +include(${vkcv_config_lib}/VulkanMemoryAllocator.cmake) # VulkanMemoryAllocator # cleanup of compiler flags if (vkcv_flags) @@ -33,6 +45,9 @@ endif () # fix dependencies for different Linux distros (looking at you Ubuntu) include(${vkcv_config_ext}/CheckLibraries.cmake) +# add custom function to include a file like a shader as string +include(${vkcv_config_ext}/IncludeShader.cmake) + # cleanup of compiler definitions aka preprocessor variables if (vkcv_definitions) list(REMOVE_DUPLICATES vkcv_definitions) diff --git a/config/Sources.cmake b/config/Sources.cmake index 4397e4978eb022d267571d185a1f122d053a5ea1..6cf9c6f4663c4307ee6c9350941cbe6e2f2b5a2c 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -1,11 +1,23 @@ # adding all source files and header files of the framework: set(vkcv_sources + ${vkcv_include}/vkcv/Features.hpp + ${vkcv_source}/vkcv/Features.cpp + + ${vkcv_include}/vkcv/FeatureManager.hpp + ${vkcv_source}/vkcv/FeatureManager.cpp + ${vkcv_include}/vkcv/Context.hpp ${vkcv_source}/vkcv/Context.cpp ${vkcv_include}/vkcv/Core.hpp ${vkcv_source}/vkcv/Core.cpp + + ${vkcv_include}/vkcv/File.hpp + ${vkcv_source}/vkcv/File.cpp + + ${vkcv_include}/vkcv/Pass.hpp + ${vkcv_source}/vkcv/Pass.cpp ${vkcv_include}/vkcv/PassConfig.hpp ${vkcv_source}/vkcv/PassConfig.cpp @@ -15,14 +27,23 @@ set(vkcv_sources ${vkcv_include}/vkcv/Handles.hpp ${vkcv_source}/vkcv/Handles.cpp + + ${vkcv_source}/vkcv/HandleManager.hpp ${vkcv_include}/vkcv/Window.hpp ${vkcv_source}/vkcv/Window.cpp - + + ${vkcv_include}/vkcv/BufferTypes.hpp ${vkcv_include}/vkcv/Buffer.hpp - ${vkcv_include}/vkcv/BufferManager.hpp + ${vkcv_include}/vkcv/PushConstants.hpp + ${vkcv_source}/vkcv/PushConstants.cpp + + ${vkcv_source}/vkcv/BufferManager.hpp ${vkcv_source}/vkcv/BufferManager.cpp + + ${vkcv_include}/vkcv/ImageConfig.hpp + ${vkcv_source}/vkcv/ImageConfig.cpp ${vkcv_include}/vkcv/Image.hpp ${vkcv_source}/vkcv/Image.cpp @@ -30,54 +51,100 @@ set(vkcv_sources ${vkcv_source}/vkcv/ImageManager.hpp ${vkcv_source}/vkcv/ImageManager.cpp + ${vkcv_include}/vkcv/PipelineConfig.hpp + ${vkcv_source}/vkcv/PipelineConfig.cpp + ${vkcv_include}/vkcv/Logger.hpp - - ${vkcv_include}/vkcv/Swapchain.hpp - ${vkcv_source}/vkcv/Swapchain.cpp ${vkcv_include}/vkcv/ShaderStage.hpp ${vkcv_include}/vkcv/ShaderProgram.hpp ${vkcv_source}/vkcv/ShaderProgram.cpp - ${vkcv_include}/vkcv/PipelineConfig.hpp + ${vkcv_include}/vkcv/GraphicsPipelineConfig.hpp + ${vkcv_source}/vkcv/GraphicsPipelineConfig.cpp + + ${vkcv_include}/vkcv/ComputePipelineConfig.hpp - ${vkcv_source}/vkcv/PipelineManager.hpp - ${vkcv_source}/vkcv/PipelineManager.cpp - - ${vkcv_include}/vkcv/CommandResources.hpp - ${vkcv_source}/vkcv/CommandResources.cpp - - ${vkcv_include}/vkcv/SyncResources.hpp - ${vkcv_source}/vkcv/SyncResources.cpp + ${vkcv_source}/vkcv/ComputePipelineManager.hpp + ${vkcv_source}/vkcv/ComputePipelineManager.cpp + + ${vkcv_source}/vkcv/GraphicsPipelineManager.hpp + ${vkcv_source}/vkcv/GraphicsPipelineManager.cpp ${vkcv_include}/vkcv/QueueManager.hpp ${vkcv_source}/vkcv/QueueManager.cpp - ${vkcv_source}/vkcv/ImageLayoutTransitions.hpp - ${vkcv_source}/vkcv/ImageLayoutTransitions.cpp - ${vkcv_include}/vkcv/VertexLayout.hpp ${vkcv_source}/vkcv/VertexLayout.cpp + + ${vkcv_include}/vkcv/DispatchSize.hpp + ${vkcv_source}/vkcv/DispatchSize.cpp ${vkcv_include}/vkcv/Event.hpp + + ${vkcv_include}/vkcv/TypeGuard.hpp + ${vkcv_source}/vkcv/TypeGuard.cpp + + ${vkcv_include}/vkcv/DescriptorTypes.hpp - ${vkcv_source}/vkcv/DescriptorManager.hpp - ${vkcv_source}/vkcv/DescriptorManager.cpp - - ${vkcv_include}/vkcv/DescriptorConfig.hpp - ${vkcv_source}/vkcv/DescriptorConfig.cpp + ${vkcv_include}/vkcv/DescriptorBinding.hpp + ${vkcv_source}/vkcv/DescriptorBinding.cpp + + ${vkcv_include}/vkcv/DescriptorWrites.hpp + ${vkcv_source}/vkcv/DescriptorWrites.cpp + + ${vkcv_source}/vkcv/DescriptorSetLayoutManager.hpp + ${vkcv_source}/vkcv/DescriptorSetLayoutManager.cpp + + ${vkcv_source}/vkcv/DescriptorSetManager.hpp + ${vkcv_source}/vkcv/DescriptorSetManager.cpp ${vkcv_source}/vkcv/SamplerManager.hpp ${vkcv_source}/vkcv/SamplerManager.cpp + + ${vkcv_source}/vkcv/WindowManager.hpp + ${vkcv_source}/vkcv/WindowManager.cpp + + ${vkcv_source}/vkcv/SwapchainManager.hpp + ${vkcv_source}/vkcv/SwapchainManager.cpp - ${vkcv_include}/vkcv/DescriptorWrites.hpp - - ${vkcv_include}/vkcv/DrawcallRecording.hpp - ${vkcv_source}/vkcv/DrawcallRecording.cpp - - ${vkcv_include}/vkcv/CommandStreamManager.hpp + ${vkcv_source}/vkcv/CommandStreamManager.hpp ${vkcv_source}/vkcv/CommandStreamManager.cpp - ${vkcv_include}/vkcv/CommandRecordingFunctionTypes.hpp + ${vkcv_include}/vkcv/EventFunctionTypes.hpp + + ${vkcv_include}/vkcv/Multisampling.hpp + ${vkcv_source}/vkcv/Multisampling.cpp + + ${vkcv_include}/vkcv/Downsampler.hpp + ${vkcv_source}/vkcv/Downsampler.cpp + + ${vkcv_include}/vkcv/BlitDownsampler.hpp + ${vkcv_source}/vkcv/BlitDownsampler.cpp + + ${vkcv_include}/vkcv/SamplerTypes.hpp + + ${vkcv_include}/vkcv/Sampler.hpp + ${vkcv_source}/vkcv/Sampler.cpp + + ${vkcv_include}/vkcv/DescriptorSetUsage.hpp + ${vkcv_source}/vkcv/DescriptorSetUsage.cpp + + ${vkcv_include}/vkcv/Drawcall.hpp + ${vkcv_source}/vkcv/Drawcall.cpp + + ${vkcv_include}/vkcv/VertexData.hpp + ${vkcv_source}/vkcv/VertexData.cpp + + ${vkcv_include}/vkcv/Result.hpp ) + +if (BUILD_CLANG_FORMAT) + message(STATUS "Clang-Format: ON") + + # add clang-format as target if installed + include(${vkcv_config}/ext/ClangFormat.cmake) +else() + message(STATUS "Clang-Format: OFF") +endif() diff --git a/config/ext/ClangFormat.cmake b/config/ext/ClangFormat.cmake new file mode 100644 index 0000000000000000000000000000000000000000..723fd7a9f52d19d2d1c3b0279eee243fa11afe91 --- /dev/null +++ b/config/ext/ClangFormat.cmake @@ -0,0 +1,11 @@ + +if (EXISTS "/usr/bin/clang-format") + # note the option ALL which allows to format the source together with the application + add_custom_target( clang_format ALL + COMMAND /usr/bin/clang-format -style=file --sort-includes -i ${vkcv_sources} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMENT "Formatting code with Clang-Format" + VERBATIM ) +else () + message(WARNING "Doxygen need to be installed to generate the doxygen documentation") +endif () diff --git a/config/ext/Doxygen.cmake b/config/ext/Doxygen.cmake index b711ac4b24777642fc3202be8909ac405b84d92b..c5f6d7f88cbdb9ef7c119c4b5dd6ee7de1199bc9 100644 --- a/config/ext/Doxygen.cmake +++ b/config/ext/Doxygen.cmake @@ -1,6 +1,6 @@ # check if Doxygen is installed -find_package(Doxygen) +find_package(Doxygen QUIET) if (DOXYGEN_FOUND) # note the option ALL which allows to build the docs together with the application diff --git a/config/ext/Git.cmake b/config/ext/Git.cmake new file mode 100644 index 0000000000000000000000000000000000000000..a943ce02ab3a170a67fae7394fa6f616239ab5b4 --- /dev/null +++ b/config/ext/Git.cmake @@ -0,0 +1,15 @@ + +function(use_git_submodule submodule_path submodule_status) + file(GLOB path_glob "${submodule_path}/*") + list(LENGTH path_glob glob_len) + + if(glob_len GREATER 0) + set(${submodule_status} TRUE PARENT_SCOPE) + return() + endif() + + get_filename_component(submodule_name ${submodule_path} NAME) + + message(WARNING "${submodule_name} is required..! Update the submodules!") + set(${submodule_status} FALSE PARENT_SCOPE) +endfunction() diff --git a/config/ext/IncludeShader.cmake b/config/ext/IncludeShader.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e67a8716fb32a953c93a3c6624f0d459a025e950 --- /dev/null +++ b/config/ext/IncludeShader.cmake @@ -0,0 +1,75 @@ + +function(include_shader shader include_dir source_dir) + if (NOT EXISTS ${shader}) + message(WARNING "Shader file does not exist: ${shader}") + else() + get_filename_component(filename ${shader} NAME) + file(SIZE ${shader} filesize) + + set(include_target_file ${include_dir}/${filename}.hxx) + set(source_target_file ${source_dir}/${filename}.cxx) + + if ((EXISTS ${source_target_file}) AND (EXISTS ${include_target_file})) + file(TIMESTAMP ${shader} shader_timestamp "%Y-%m-%dT%H:%M:%S") + file(TIMESTAMP ${source_target_file} source_timestamp "%Y-%m-%dT%H:%M:%S") + + string(COMPARE GREATER ${shader_timestamp} ${source_timestamp} shader_update) + else() + set(shader_update true) + endif() + + if (shader_update) + string(TOUPPER ${filename} varname) + string(REPLACE "." "_" varname ${varname}) + + set(shader_header "#pragma once\n") + string(APPEND shader_header "// This file is auto-generated via cmake, so don't touch it!\n") + string(APPEND shader_header "extern unsigned char ${varname} [${filesize}]\;\n") + string(APPEND shader_header "extern unsigned int ${varname}_LEN\;\n") + string(APPEND shader_header "const std::string ${varname}_SHADER (reinterpret_cast<const char*>(${varname}), ${varname}_LEN)\;") + + file(WRITE ${include_target_file} ${shader_header}) + + find_program(xxd_program "xxd") + + if (EXISTS ${xxd_program}) + get_filename_component(shader_directory ${shader} DIRECTORY) + + add_custom_command( + OUTPUT ${source_target_file} + WORKING_DIRECTORY "${shader_directory}" + COMMAND xxd -i -C "${filename}" "${source_target_file}" + COMMENT "Processing shader into source files: ${shader}" + ) + else() + set(shader_source "// This file is auto-generated via cmake, so don't touch it!\n") + string(APPEND shader_source "unsigned char ${varname}[] = {") + + math(EXPR max_fileoffset "${filesize} - 1" OUTPUT_FORMAT DECIMAL) + + message(STATUS "Processing shader into source files: ${shader}") + + foreach(fileoffset RANGE ${max_fileoffset}) + file(READ ${shader} shader_source_byte OFFSET ${fileoffset} LIMIT 1 HEX) + + math(EXPR offset_modulo "${fileoffset} % 12" OUTPUT_FORMAT DECIMAL) + + if (${offset_modulo} EQUAL 0) + string(APPEND shader_source "\n ") + endif() + + if (${fileoffset} LESS ${max_fileoffset}) + string(APPEND shader_source "0x${shader_source_byte}, ") + else() + string(APPEND shader_source "0x${shader_source_byte}\n") + endif() + endforeach() + + string(APPEND shader_source "}\;\n") + string(APPEND shader_source "unsigned int ${varname}_LEN = ${filesize}\;") + + file(WRITE ${source_target_file} ${shader_source}) + endif() + endif() + endif() +endfunction() diff --git a/config/ext/Project.cmake b/config/ext/Project.cmake new file mode 100644 index 0000000000000000000000000000000000000000..6b27862bc5eab401c3d98b061fdfca7f9d1bce3b --- /dev/null +++ b/config/ext/Project.cmake @@ -0,0 +1,19 @@ + +# the first argument should be the project's target +macro(add_project) + # this should fix the execution path to load local files from the project + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + + # this will create an executable for the project + add_executable(${ARGN}) + + # this should fix the execution path to load local files from the project (for MSVC) + if(MSVC) + set_target_properties(${ARGV0} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + set_target_properties(${ARGV0} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + + # in addition to setting the output directory, the working directory has to be set + # by default visual studio sets the working directory to the build directory, when using the debugger + set_target_properties(${ARGV0} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + endif() +endmacro() diff --git a/config/lib/GLFW.cmake b/config/lib/GLFW.cmake index 1b68d8aa97ba59158a7bd805ab2470f554f705aa..977eabc8c3916da694420eb8b069e058b94c15f2 100644 --- a/config/lib/GLFW.cmake +++ b/config/lib/GLFW.cmake @@ -6,13 +6,14 @@ if (glfw3_FOUND) message(${vkcv_config_msg} " GLFW - " ${glfw3_VERSION}) else() - if (EXISTS "${vkcv_lib_path}/glfw") + use_git_submodule("${vkcv_lib_path}/glfw" glfw_status) + + if (${glfw_status}) add_subdirectory(${vkcv_lib}/glfw) list(APPEND vkcv_libraries glfw) + list(APPEND vkcv_includes ${vkcv_lib_path}/glfw/include) message(${vkcv_config_msg} " GLFW - " ${glfw3_VERSION}) - else() - message(WARNING "GLFW is required..! Update the submodules!") endif () endif () diff --git a/config/lib/SPIRV_Cross.cmake b/config/lib/SPIRV_Cross.cmake index 2e705d7d5a006e3851d14d22a57fd667c61c79f5..c6304606a14a30f8de7dba1fe2e479ed63743e1e 100644 --- a/config/lib/SPIRV_Cross.cmake +++ b/config/lib/SPIRV_Cross.cmake @@ -1,3 +1,4 @@ + find_package(spirv_cross_c_shared QUIET) if (spirv-cross_FOUND) @@ -5,7 +6,9 @@ if (spirv-cross_FOUND) message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) else() - if (EXISTS "${vkcv_lib_path}/SPIRV-Cross") + use_git_submodule("${vkcv_lib_path}/SPIRV-Cross" spirv_cross_status) + + if (${spirv_cross_status}) set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF CACHE INTERNAL "") set(SPIRV_CROSS_SHARED OFF CACHE INTERNAL "") set(SPIRV_CROSS_STATIC ON CACHE INTERNAL "") @@ -25,9 +28,8 @@ else() add_subdirectory(${vkcv_lib}/SPIRV-Cross) list(APPEND vkcv_libraries spirv-cross-cpp) + list(APPEND vkcv_includes ${vkcv_lib_path}/SPIV-Cross/include) message(${vkcv_config_msg} " SPIRV Cross - " ${SPIRV_CROSS_VERSION}) - else() - message(WARNING "SPIRV-Cross is required..! Update the submodules!") endif () -endif () \ No newline at end of file +endif () diff --git a/config/lib/Vulkan.cmake b/config/lib/Vulkan.cmake index e8fe4aee3a949fa7bf9906558f8a0c558eb47cf2..11e254a67349488b48718bd48ac69ead641fb167 100644 --- a/config/lib/Vulkan.cmake +++ b/config/lib/Vulkan.cmake @@ -2,8 +2,19 @@ find_package(Vulkan REQUIRED) if (Vulkan_FOUND) - list(APPEND vkcv_includes ${Vulkan_INCLUDE_DIR}) + use_git_submodule("${vkcv_lib_path}/Vulkan-Headers" vulkan_headers_status) + + if (${vulkan_headers_status}) + list(APPEND vkcv_includes ${vkcv_lib_path}/Vulkan-Headers/include) + endif() + + use_git_submodule("${vkcv_lib_path}/Vulkan-Hpp" vulkan_hpp_status) + + if (${vulkan_hpp_status}) + list(APPEND vkcv_includes ${vkcv_lib_path}/Vulkan-Hpp) + endif() + list(APPEND vkcv_libraries ${Vulkan_LIBRARIES}) - message(${vkcv_config_msg} " Vulkan - ") + message(${vkcv_config_msg} " Vulkan - " ${Vulkan_VERSION}) endif () diff --git a/config/lib/VulkanMemoryAllocator.cmake b/config/lib/VulkanMemoryAllocator.cmake new file mode 100644 index 0000000000000000000000000000000000000000..fa69b0fd7825c6511592123162f4b5570890e1da --- /dev/null +++ b/config/lib/VulkanMemoryAllocator.cmake @@ -0,0 +1,70 @@ + +find_package(Vulkan QUIET) + +if (Vulkan_VERSION) + set(BUILD_VMA_VULKAN_VERSION ${Vulkan_VERSION}) +endif() + +set(VMA_VULKAN_VERSION OFF CACHE INTERNAL "") + +if (BUILD_VMA_VULKAN_VERSION) + string(REGEX REPLACE "(\\.[0-9]+)+$" "" VMA_VULKAN_MAJOR_VERSION ${BUILD_VMA_VULKAN_VERSION}) + string(REGEX REPLACE "^[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" VMA_VULKAN_MINOR_VERSION ${BUILD_VMA_VULKAN_VERSION}) + string(REGEX REPLACE "^([0-9]+\\.)+" "" VMA_VULKAN_PATCH_VERSION ${BUILD_VMA_VULKAN_VERSION}) + + math(EXPR VMA_VULKAN_VERSION "1000000 * ${VMA_VULKAN_MAJOR_VERSION} + 1000 * ${VMA_VULKAN_MINOR_VERSION} + ${VMA_VULKAN_PATCH_VERSION}" OUTPUT_FORMAT DECIMAL) +endif() + +use_git_submodule("${vkcv_lib_path}/VulkanMemoryAllocator" vma_status) + +if (${vma_status}) + if (EXISTS "${vkcv_lib_path}/VulkanMemoryAllocator/include") + set(VMA_H_PATH "${vkcv_lib_path}/VulkanMemoryAllocator/include" CACHE INTERNAL "") + else() + set(VMA_H_PATH "${vkcv_lib_path}/VulkanMemoryAllocator" CACHE INTERNAL "") + endif() + + list(APPEND vkcv_includes ${VMA_H_PATH}) +endif() + +use_git_submodule("${vkcv_lib_path}/VulkanMemoryAllocator-Hpp" vma_hpp_status) + +if (${vma_hpp_status}) + if (EXISTS "${vkcv_lib_path}/VulkanMemoryAllocator-Hpp/include") + set(VMA_HPP_PATH "${vkcv_lib_path}/VulkanMemoryAllocator-Hpp/include" CACHE INTERNAL "") + else() + set(VMA_HPP_PATH "${vkcv_lib_path}/VulkanMemoryAllocator-Hpp" CACHE INTERNAL "") + endif() + + set(VMA_VULKAN_H_PATH "${vkcv_lib_path}/Vulkan-Headers/include" CACHE INTERNAL "") + set(VMA_VULKAN_HPP_PATH "${vkcv_lib_path}/Vulkan-Hpp" CACHE INTERNAL "") + + set(VMA_RECORDING_ENABLED OFF CACHE INTERNAL "") + set(VMA_USE_STL_CONTAINERS OFF CACHE INTERNAL "") + + string(COMPARE EQUAL "${vkcv_build_attribute}" "SHARED" VMA_USE_DYNAMIC_LINKING) + + if (${VMA_USE_DYNAMIC_LINKING}) + set(VMA_STATIC_VULKAN_FUNCTIONS OFF CACHE INTERNAL "") + set(VMA_DYNAMIC_VULKAN_FUNCTIONS ON CACHE INTERNAL "") + else() + set(VMA_STATIC_VULKAN_FUNCTIONS ON CACHE INTERNAL "") + set(VMA_DYNAMIC_VULKAN_FUNCTIONS OFF CACHE INTERNAL "") + endif() + + set(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY OFF CACHE INTERNAL "") + set(VMA_DEBUG_INITIALIZE_ALLOCATIONS OFF CACHE INTERNAL "") + set(VMA_DEBUG_GLOBAL_MUTEX OFF CACHE INTERNAL "") + set(VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT OFF CACHE INTERNAL "") + + add_subdirectory(${vkcv_config_lib}/vma) + + list(APPEND vkcv_libraries VulkanMemoryAllocator) + list(APPEND vkcv_includes ${VMA_HPP_PATH}) + + if (VMA_VULKAN_VERSION) + list(APPEND vkcv_definitions "VMA_VULKAN_VERSION=${VMA_VULKAN_VERSION}") + endif() + + message(${vkcv_config_msg} " VMA - " ${BUILD_VMA_VULKAN_VERSION}) +endif () diff --git a/config/lib/vma/CMakeLists.txt b/config/lib/vma/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..42126320aae984dd9040f302ba3d81cb1a90ac4c --- /dev/null +++ b/config/lib/vma/CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 3.9) + +project(VulkanMemoryAllocator) + +# settings c++ standard for the vma +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Vulkan REQUIRED) + +option(VMA_VULKAN_VERSION "Enforced Vulkan version" OFF) +option(VMA_H_PATH "Location of C headers" "") +option(VMA_HPP_PATH "Location of C++ headers" "") + +option(VMA_VULKAN_H_PATH "Location of Vulkan C headers" "") +option(VMA_VULKAN_HPP_PATH "Location of Vulkan C++ headers" "") + +message(STATUS "VMA_BUILD_SAMPLE = ${VMA_BUILD_SAMPLE}") +message(STATUS "VMA_BUILD_SAMPLE_SHADERS = ${VMA_BUILD_SAMPLE_SHADERS}") +message(STATUS "VMA_BUILD_REPLAY = ${VMA_BUILD_REPLAY}") + +option(VMA_RECORDING_ENABLED "Enable VMA memory recording for debugging" OFF) +option(VMA_USE_STL_CONTAINERS "Use C++ STL containers instead of VMA's containers" OFF) +option(VMA_STATIC_VULKAN_FUNCTIONS "Link statically with Vulkan API" OFF) +option(VMA_DYNAMIC_VULKAN_FUNCTIONS "Fetch pointers to Vulkan functions internally (no static linking)" ON) +option(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY "Every allocation will have its own memory block" OFF) +option(VMA_DEBUG_INITIALIZE_ALLOCATIONS "Automatically fill new allocations and destroyed allocations with some bit pattern" OFF) +option(VMA_DEBUG_GLOBAL_MUTEX "Enable single mutex protecting all entry calls to the library" OFF) +option(VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT "Never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount and return error" OFF) + +message(STATUS "VMA_RECORDING_ENABLED = ${VMA_RECORDING_ENABLED}") +message(STATUS "VMA_USE_STL_CONTAINERS = ${VMA_USE_STL_CONTAINERS}") +message(STATUS "VMA_DYNAMIC_VULKAN_FUNCTIONS = ${VMA_DYNAMIC_VULKAN_FUNCTIONS}") +message(STATUS "VMA_DEBUG_ALWAYS_DEDICATED_MEMORY = ${VMA_DEBUG_ALWAYS_DEDICATED_MEMORY}") +message(STATUS "VMA_DEBUG_INITIALIZE_ALLOCATIONS = ${VMA_DEBUG_INITIALIZE_ALLOCATIONS}") +message(STATUS "VMA_DEBUG_GLOBAL_MUTEX = ${VMA_DEBUG_GLOBAL_MUTEX}") +message(STATUS "VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT = ${VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT}") + +if (${VMA_STATIC_VULKAN_FUNCTIONS}) + add_library(VulkanMemoryAllocator STATIC vma.cpp) +else() + add_library(VulkanMemoryAllocator SHARED vma.cpp) +endif() + +set_target_properties( + VulkanMemoryAllocator PROPERTIES + + CXX_EXTENSIONS OFF + CXX_STANDARD ${CMAKE_CXX_STANDARD} + CXX_STANDARD_REQUIRED ON +) + +target_include_directories(VulkanMemoryAllocator SYSTEM BEFORE PRIVATE ${VMA_VULKAN_H_PATH} ${VMA_VULKAN_HPP_PATH} ${VMA_H_PATH}) +target_include_directories(VulkanMemoryAllocator PUBLIC ${VMA_HPP_PATH}) + +# Only link to Vulkan if static linking is used +if (NOT ${VMA_DYNAMIC_VULKAN_FUNCTIONS}) + target_link_libraries(VulkanMemoryAllocator PUBLIC Vulkan::Vulkan) +endif() + +target_compile_definitions( + VulkanMemoryAllocator + + PUBLIC + VMA_USE_STL_CONTAINERS=$<BOOL:${VMA_USE_STL_CONTAINERS}> + VMA_DYNAMIC_VULKAN_FUNCTIONS=$<BOOL:${VMA_DYNAMIC_VULKAN_FUNCTIONS}> + VMA_DEBUG_ALWAYS_DEDICATED_MEMORY=$<BOOL:${VMA_DEBUG_ALWAYS_DEDICATED_MEMORY}> + VMA_DEBUG_INITIALIZE_ALLOCATIONS=$<BOOL:${VMA_DEBUG_INITIALIZE_ALLOCATIONS}> + VMA_DEBUG_GLOBAL_MUTEX=$<BOOL:${VMA_DEBUG_GLOBAL_MUTEX}> + VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT=$<BOOL:${VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT}> + VMA_RECORDING_ENABLED=$<BOOL:${VMA_RECORDING_ENABLED}> + VMA_VULKAN_VERSION=${VMA_VULKAN_VERSION} +) diff --git a/config/lib/vma/vma.cpp b/config/lib/vma/vma.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1fc40eb10123b1c89f6c348f4c2e898df21b43b0 --- /dev/null +++ b/config/lib/vma/vma.cpp @@ -0,0 +1,60 @@ + +#ifndef NDEBUG +#ifndef _DEBUG +#define _DEBUG +#endif +#endif + +#ifndef _MSVC_LANG +#ifdef __MINGW32__ +#include <stdint.h> +#include <stdlib.h> + +class VmaMutex { +public: + VmaMutex() : m_locked(false) {} + + void Lock() { + while (m_locked); + m_locked = true; + } + + void Unlock() { + m_locked = false; + } +private: + bool m_locked; +}; + +#define VMA_MUTEX VmaMutex + +template <typename T> +T* custom_overestimate_malloc(size_t size) { + return new T[size + (sizeof(T) - 1) / sizeof(T)]; +} + +void* custom_aligned_malloc(size_t alignment, size_t size) { + if (alignment > 4) { + return custom_overestimate_malloc<uint64_t>(size); + } else + if (alignment > 2) { + return custom_overestimate_malloc<uint32_t>(size); + } else + if (alignment > 1) { + return custom_overestimate_malloc<uint16_t>(size); + } else { + return custom_overestimate_malloc<uint8_t>(size); + } +} + +void custom_free(void *ptr) { + delete[] reinterpret_cast<uint8_t*>(ptr); +} + +#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (custom_aligned_malloc(alignment, size)) +#define VMA_SYSTEM_FREE(ptr) (custom_free(ptr)) +#endif +#endif + +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.hpp" diff --git a/doc/BUILD_LINUX.md b/doc/BUILD_LINUX.md new file mode 100644 index 0000000000000000000000000000000000000000..149679e24a05fbeb8ce65346b7df9de0392c41c2 --- /dev/null +++ b/doc/BUILD_LINUX.md @@ -0,0 +1,51 @@ +# How to build on Linux + +## How to build on Ubuntu + +1. Install the required and useful packages to develop: +``` +sudo apt install cmake make gcc g++ libvulkan-dev vulkan-validationlayers-dev vulkan-tools +``` +2. Install the Vulkan drivers for your system: +``` +# For most GPUs (except Nvidia): +sudo apt install mesa-vulkan-drivers +``` +3. Clone the repository and run the following commands: +``` +mkdir debug +cd debug +cmake -DCMAKE_C_COMPILER="/usr/bin/gcc" -DCMAKE_CXX_COMPILER="/usr/bin/g++" -DCMAKE_BUILD_TYPE=Debug .. +cmake --build . +``` + +## How to build on Archlinux by the way + +1. Install required and useful tools to develop: +``` +sudo pacman -S cmake make gcc vulkan-icd-loader vulkan-headers vulkan-validation-layers vulkan-tools +``` +2. Install the matching Vulkan drivers for your system: +``` +# For discrete Nvidia GPUs: +sudo pacman -S nvidia + +# For integrated or discrete (AMD) Radeon GPUs: +sudo pacman -S vulkan-radeon + +# For integrated or discrete Intel GPUs: +sudo pacman -S vulkan-intel +``` +3. Clone the repository and run the following commands: +``` +mkdir debug +cd debug +cmake -DCMAKE_C_COMPILER="/usr/bin/gcc" -DCMAKE_CXX_COMPILER="/usr/bin/g++" -DCMAKE_BUILD_TYPE=Debug .. +cmake --build . +``` + +## How to build with Clang on Linux + +Just follow the steps of "How to build on YOUR DISTRO" but replace `gcc` with `clang` and `g++` with `clang++`. +This should work actually but maybe the names of the packages are a little different. So look at your official +repository for the packages and their names if it doesn't work. diff --git a/doc/BUILD_MACOS.md b/doc/BUILD_MACOS.md new file mode 100644 index 0000000000000000000000000000000000000000..c0b20bd1c59030492cea66d6b562b6204e2025db --- /dev/null +++ b/doc/BUILD_MACOS.md @@ -0,0 +1,19 @@ +# How to build on macOS + +## How to build with Clang on macOS + +1. Install Homebrew +2. Install required tools to compile: +``` +brew install cmake llvm libomp +``` +3. Download and install latest [vulkansdk-macos-*.dmg](https://vulkan.lunarg.com/doc/sdk/1.2.189.0/mac/getting_started.html) to ~/VulkanSDK +4. Clone the repository and run the following commands: +``` +mkdir debug +cd debug +export LDFLAGS="-L/usr/local/opt/llvm/lib" +export CPPFLAGS="-I/usr/local/opt/llvm/include" +cmake -DCMAKE_C_COMPILER="/usr/local/opt/llvm/bin/clang" -DCMAKE_CXX_COMPILER="/usr/local/opt/llvm/bin/clang++" -DCMAKE_BUILD_TYPE=Debug .. +cmake --build . +``` \ No newline at end of file diff --git a/doc/BUILD_WINDOWS.md b/doc/BUILD_WINDOWS.md new file mode 100644 index 0000000000000000000000000000000000000000..729c202abac9273a821c7d21ac2ad1536a887045 --- /dev/null +++ b/doc/BUILD_WINDOWS.md @@ -0,0 +1,35 @@ +# How to build on Windows + +## How to build with MSVC on Windows + +1. Download and install [VulkanSDK](https://vulkan.lunarg.com/sdk/home#windows) +2. Download and install [CMake](https://cmake.org/install/) +3. Download and install [VisualStudio](https://visualstudio.microsoft.com/vs/features/cplusplus/) for MSVC +4. Create and configure a project with the framework and build it + +## How to build with GCC on Windows + +1. Install [MSYS2](https://www.msys2.org/) +2. Run "MSYS2 MSYS" from Start menu +3. Enter the following commands into MSYS2 console: (this will update and install required packages) +``` +pacman -Syu +pacman -Su +pacman -S --needed base-devel mingw-w64-x86_64-toolchain +pacman -S mingw-w64-x86_64-vulkan-devel +pacman -S cmake +``` +4. Add to Path: +``` +C:\msys64\usr\bin +C:\msys64\usr\local\bin +C:\msys64\mingw64\bin +C:\msys64\mingw32\bin +``` +5. Clone the repository and run the following commands: +``` +mkdir debug +cd debug +cmake --no-warn-unused-cli -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_C_COMPILER:FILEPATH=C:\msys64\mingw64\bin\x86_64-w64-mingw32-gcc.exe -DCMAKE_CXX_COMPILER:FILEPATH=C:\msys64\mingw64\bin\x86_64-w64-mingw32-g++.exe .. -G "Unix Makefiles" +cmake --build . +``` \ No newline at end of file diff --git a/doc/SETUP_IDE.md b/doc/SETUP_IDE.md new file mode 100644 index 0000000000000000000000000000000000000000..93f0878c11ae0194b1522bd3eb087d557dc0ad05 --- /dev/null +++ b/doc/SETUP_IDE.md @@ -0,0 +1,35 @@ +# How to setup your IDE + +## VSCodium + +1. Install [VSCodium](https://github.com/VSCodium/vscodium) +2. Run `scripts/setup-codium.sh` in your local repository +3. Run VSCodium and open your local repository as project + +### VSCode + +If you should prefer to use the official binaries from Microsoft including their own telemetry, you can follow these steps: + +1. Download and install [VSCode](https://code.visualstudio.com/Download) +2. Run `scripts/setup-codium.sh` in your local repository or in case of a Windows host run following line from PowerShell: +``` +Get-Content resources\extensions.txt | ForEach-Object {code --install-extension $_} +``` +3. Run VSCode and open your local repository as project + +## Eclipse CDT + +1. Download and install [Eclipse CDT](https://www.eclipse.org/cdt/downloads.php) +2. Run Eclipse CDT and open your local repository as project + +## CLion + +1. Download and install [CLion](https://www.jetbrains.com/clion/download/) +2. Run CLion and open your local repository as project + +## Visual Studio + +1. Download and install [Visual Studio](https://visualstudio.microsoft.com/downloads/) +2. Make sure you have installed the requirements for [CMake projects](https://docs.microsoft.com/en-us/cpp/build/cmake-projects-in-visual-studio?view=msvc-170). +3. Run Visual Studio and open your local repository as project + diff --git a/include/vkcv/BlitDownsampler.hpp b/include/vkcv/BlitDownsampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d5b7d1008bafa7e7d636e4d498fcaba997ed0da4 --- /dev/null +++ b/include/vkcv/BlitDownsampler.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "Downsampler.hpp" + +namespace vkcv { + + class ImageManager; + + /** + * @brief A class to handle downsampling via blit from a graphics queue. + */ + class BlitDownsampler : public Downsampler { + friend class Core; + + private: + /** + * Reference to the image manager. + */ + ImageManager &m_imageManager; + + /** + * @brief Constructor to create a blit downsampler instance. + * + * @param[in,out] core Reference to a Core instance + * @param[in,out] imageManager Reference to an image manager + */ + BlitDownsampler(Core &core, ImageManager &imageManager); + + public: + /** + * @brief Record the commands of the downsampling instance to + * generate all mip levels of an input image via a + * command stream. + * + * @param[in] cmdStream Command stream handle + * @param[in] image Image handle + */ + void recordDownsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &image) override; + }; + +} // namespace vkcv diff --git a/include/vkcv/Buffer.hpp b/include/vkcv/Buffer.hpp index ae935ba9501d4d7776cad7e3ba190a2dd02e5e38..37bab6b8b1156e036f4eb17681843812e49165f5 100644 --- a/include/vkcv/Buffer.hpp +++ b/include/vkcv/Buffer.hpp @@ -1,84 +1,172 @@ #pragma once /** - * @authors Lars Hoerttrich, Tobias Frisch + * @authors Tobias Frisch, Lars Hoerttrich, Alexander Gauggel * @file vkcv/Buffer.hpp - * @brief template buffer class, template for type security, implemented here because template classes can't be written in .cpp + * @brief Template buffer class for type security with buffers. */ -#include "Handles.hpp" -#include "BufferManager.hpp" #include <vector> +#include "BufferTypes.hpp" +#include "Core.hpp" +#include "Handles.hpp" + namespace vkcv { - template<typename T> + /** + * @brief Template class for buffer handling and filling data. + * + * @tparam T Buffer content type + */ + template <typename T> class Buffer { - friend class Core; public: - // explicit destruction of default constructor - Buffer<T>() = delete; - - [[nodiscard]] - const BufferHandle& getHandle() const { + Buffer() : m_core(nullptr), m_handle() {} + + Buffer(Core* core, const BufferHandle &handle) : m_core(core), m_handle(handle) {} + + Buffer(const Buffer &other) = default; + Buffer(Buffer &&other) noexcept = default; + + ~Buffer() = default; + + Buffer &operator=(const Buffer &other) = default; + Buffer &operator=(Buffer &&other) noexcept = default; + + /** + * @brief Returns the buffers handle. + * + * @return The #BufferHandle to be used with the #Core + */ + [[nodiscard]] const BufferHandle &getHandle() const { return m_handle; } - - [[nodiscard]] - BufferType getType() const { - return m_type; + + /** + * @brief Returns the type of the buffer. + * + * @return The #BufferType of the #Buffer + */ + [[nodiscard]] BufferType getType() const { + return m_core->getBufferType(m_handle); }; - - [[nodiscard]] - size_t getCount() const { - return m_count; + + [[nodiscard]] BufferMemoryType getMemoryType() const { + return m_core->getBufferMemoryType(m_handle); } - - [[nodiscard]] - size_t getSize() const { - return m_count * sizeof(T); + + /** + * @brief Returns the count of elements in the buffer. + * + * @return The number of objects of type T the #Buffer holds + */ + [[nodiscard]] size_t getCount() const { + return m_core->getBufferSize(m_handle) / sizeof(T); } - [[nodiscard]] - const vk::Buffer getVulkanHandle() const { - return m_manager->getBuffer(m_handle); - } - + /** + * @brief Returns the size of the buffer in bytes. + * + * @return The size of the #Buffer in bytes + */ + [[nodiscard]] size_t getSize() const { + return m_core->getBufferSize(m_handle); + } + + /** + * @brief Returns the vulkan buffer handle of the buffer. + * + * @return The vulkan handle of the #Buffer to be used for manual vulkan commands + */ + [[nodiscard]] vk::Buffer getVulkanHandle() const { + return m_core->getBuffer(m_handle); + } + + /** + * @brief Fills the #Buffer with data of type T. + * + * @param[in] data Pointer to the array of object type T + * @param[in] count The number of objects to copy from the data array + * @param[in] offset The offset into the #Buffer where the data is copied into + */ void fill(const T* data, size_t count = 0, size_t offset = 0) { - m_manager->fillBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T)); + m_core->fillBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T)); } - - void fill(const std::vector<T>& vector, size_t offset = 0) { - fill( static_cast<const T*>(vector.data()), static_cast<size_t>(vector.size()), offset); + + /** + * @brief Fills the #Buffer with data from a vector of type T. + * + * @param vector Vector of type T to be copied into the #Buffer + * @param offset The offset into the #Buffer where the data is copied into + */ + void fill(const std::vector<T> &vector, size_t offset = 0) { + fill(static_cast<const T*>(vector.data()), static_cast<size_t>(vector.size()), offset); } - [[nodiscard]] - T* map(size_t offset = 0, size_t count = 0) { - return reinterpret_cast<T*>(m_manager->mapBuffer(m_handle, offset * sizeof(T), count * sizeof(T))); + /** + * @brief Fills the #Buffer with data from an array of type T + * and size N. + * + * @tparam N Size of the array to be copied into the #Buffer + * @param array Array of type T to be copied into the #Buffer + * @param offset The offset into the #Buffer where the data is copied into + */ + template<size_t N> + void fill(const std::array<T, N> &array, size_t offset = 0) { + fill(static_cast<const T*>(array.data()), N, offset); + } + + /** + * @brief Reads the #Buffer directly into a data pointer of type T. + * + * @param[in] data Pointer to the array of object type T + * @param[in] count The number of objects to copy from the buffer + * @param[in] offset The offset into the #Buffer where the data is copied from + */ + void read(T* data, size_t count = 0, size_t offset = 0) { + m_core->readBuffer(m_handle, data, count * sizeof(T), offset * sizeof(T)); + } + + /** + * @brief Reads the #Buffer directly to a vector of type T. + * + * @param vector Vector of type T to be copied into from the #Buffer + * @param offset The offset into the #Buffer where the data is copied from + */ + void read(std::vector<T> &vector, size_t offset = 0) { + read(static_cast<T*>(vector.data()), static_cast<size_t>(vector.size()), offset); } + /** + * @brief Maps memory to the #Buffer and returns it. + * + * @param[in] offset Offset of mapping in objects of type T + * @param[in] count Count of objects of type T that are mapped + * @return Pointer to mapped memory as type T + */ + [[nodiscard]] T* map(size_t offset = 0, size_t count = 0) { + return reinterpret_cast<T*>( + m_core->mapBuffer(m_handle, offset * sizeof(T), count * sizeof(T))); + } + + /** + * @brief Unmaps the #Buffer, invalidates the pointer obtained by map(). + */ void unmap() { - m_manager->unmapBuffer(m_handle); + m_core->unmapBuffer(m_handle); } private: - BufferManager* const m_manager; - const BufferHandle m_handle; - const BufferType m_type; - const size_t m_count; - const BufferMemoryType m_memoryType; - - Buffer<T>(BufferManager* manager, BufferHandle handle, BufferType type, size_t count, BufferMemoryType memoryType) : - m_manager(manager), - m_handle(handle), - m_type(type), - m_count(count), - m_memoryType(memoryType) - {} - - [[nodiscard]] - static Buffer<T> create(BufferManager* manager, BufferType type, size_t count, BufferMemoryType memoryType) { - return Buffer<T>(manager, manager->createBuffer(type, count * sizeof(T), memoryType), type, count, memoryType); - } - + Core* m_core; + BufferHandle m_handle; }; -} + + template <typename T> + Buffer<T> buffer(Core &core, BufferType type, size_t count, + BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL, + bool readable = false) { + return Buffer<T>(&core, + core.createBuffer(type, typeGuard<T>(), count, memoryType, readable)); + } + +} // namespace vkcv diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp deleted file mode 100644 index 9eb80d70862a79a01593e6fe4c3aabe98d253ac8..0000000000000000000000000000000000000000 --- a/include/vkcv/BufferManager.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include <vector> -#include <vulkan/vulkan.hpp> - -#include "Handles.hpp" - -namespace vkcv -{ - enum class BufferType { - INDEX, - VERTEX, - UNIFORM, - STORAGE, - STAGING - }; - - enum class BufferMemoryType { - DEVICE_LOCAL, - HOST_VISIBLE - }; - - class Core; - - class BufferManager - { - friend class Core; - private: - - struct Buffer - { - vk::Buffer m_handle; - vk::DeviceMemory m_memory; - size_t m_size = 0; - void* m_mapped = nullptr; - bool m_mappable = false; - }; - - Core* m_core; - std::vector<Buffer> m_buffers; - BufferHandle m_stagingBuffer; - - BufferManager() noexcept; - - void init(); - - /** - * Destroys and deallocates buffer represented by a given - * buffer handle id. - * - * @param id Buffer handle id - */ - void destroyBufferById(uint64_t id); - - public: - ~BufferManager() noexcept; - - BufferManager(BufferManager&& other) = delete; - BufferManager(const BufferManager& other) = delete; - - BufferManager& operator=(BufferManager&& other) = delete; - BufferManager& operator=(const BufferManager& other) = delete; - - /** - * Creates and allocates a new buffer and returns its - * unique buffer handle. - * - * @param type Type of buffer - * @param size Size of buffer in bytes - * @param memoryType Type of buffers memory - * @return New buffer handle - */ - BufferHandle createBuffer(BufferType type, size_t size, BufferMemoryType memoryType); - - /** - * Returns the Vulkan buffer handle of a buffer - * represented by a given buffer handle. - * - * @param handle Buffer handle - * @return Vulkan buffer handle - */ - [[nodiscard]] - vk::Buffer getBuffer(const BufferHandle& handle) const; - - /** - * Returns the size of a buffer represented - * by a given buffer handle. - * - * @param handle Buffer handle - * @return Size of the buffer - */ - [[nodiscard]] - size_t getBufferSize(const BufferHandle& handle) const; - - /** - * Returns the Vulkan device memory handle of a buffer - * represented by a given buffer handle id. - * - * @param handle Buffer handle - * @return Vulkan device memory handle - */ - [[nodiscard]] - vk::DeviceMemory getDeviceMemory(const BufferHandle& handle) const; - - /** - * Fills a buffer represented by a given buffer - * handle with custom data. - * - * @param handle Buffer handle - * @param data Pointer to data - * @param size Size of data in bytes - * @param offset Offset to fill in data in bytes - */ - void fillBuffer(const BufferHandle& handle, const void* data, size_t size, size_t offset); - - /** - * Maps memory to a buffer represented by a given - * buffer handle and returns it. - * - * @param handle Buffer handle - * @param offset Offset of mapping in bytes - * @param size Size of mapping in bytes - * @return Pointer to mapped memory - */ - void* mapBuffer(const BufferHandle& handle, size_t offset, size_t size); - - /** - * Unmaps memory from a buffer represented by a given - * buffer handle. - * - * @param handle Buffer handle - */ - void unmapBuffer(const BufferHandle& handle); - - void recordBufferMemoryBarrier( - const BufferHandle& handle, - vk::CommandBuffer cmdBuffer); - }; - -} diff --git a/include/vkcv/BufferTypes.hpp b/include/vkcv/BufferTypes.hpp new file mode 100644 index 0000000000000000000000000000000000000000..85bc0f2a1b7af5d1351bb6aae396e052743572dd --- /dev/null +++ b/include/vkcv/BufferTypes.hpp @@ -0,0 +1,34 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/BufferTypes.hpp + * @brief Enum classes to specify attributes of buffers. + */ + +namespace vkcv { + + /** + * @brief Enum class to specify types of buffers. + */ + enum class BufferType { + INDEX, + VERTEX, + UNIFORM, + STORAGE, + STAGING, + INDIRECT, + + UNKNOWN + }; + + /** + * @brief Enum class to specify types of buffer memory. + */ + enum class BufferMemoryType { + DEVICE_LOCAL, + HOST_VISIBLE, + + UNKNOWN + }; + +} // namespace vkcv diff --git a/include/vkcv/CommandRecordingFunctionTypes.hpp b/include/vkcv/CommandRecordingFunctionTypes.hpp deleted file mode 100644 index c236fb2c717afd2a3bafc4b3a22708cdac942ffe..0000000000000000000000000000000000000000 --- a/include/vkcv/CommandRecordingFunctionTypes.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "vkcv/Event.hpp" -#include <vulkan/vulkan.hpp> - -namespace vkcv { - typedef typename event_function<const vk::CommandBuffer&>::type RecordCommandFunction; - typedef typename event_function<>::type FinishCommandFunction; -} \ No newline at end of file diff --git a/include/vkcv/CommandResources.hpp b/include/vkcv/CommandResources.hpp deleted file mode 100644 index ffdd6d0315549c7522623f535856bbaffc8e5c6e..0000000000000000000000000000000000000000 --- a/include/vkcv/CommandResources.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> -#include <unordered_set> -#include "QueueManager.hpp" - -namespace vkcv { - struct CommandResources { - std::vector<vk::CommandPool> cmdPoolPerQueueFamily; - }; - - std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager& queueManager); - CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int> &familyIndexSet); - void destroyCommandResources(const vk::Device& device, const CommandResources& resources); - vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool); - vk::CommandPool chooseCmdPool(const Queue &queue, const CommandResources &cmdResources); - Queue getQueueForSubmit(const QueueType type, const QueueManager &queueManager); - void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags); - - void submitCommandBufferToQueue( - const vk::Queue queue, - const vk::CommandBuffer cmdBuffer, - const vk::Fence fence, - const std::vector<vk::Semaphore>& waitSemaphores, - const std::vector<vk::Semaphore>& signalSemaphores); -} \ No newline at end of file diff --git a/include/vkcv/CommandStreamManager.hpp b/include/vkcv/CommandStreamManager.hpp deleted file mode 100644 index 4af2127ccf6271f1076e3dde05304b8f9c556139..0000000000000000000000000000000000000000 --- a/include/vkcv/CommandStreamManager.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> -#include <vector> -#include "vkcv/Event.hpp" -#include "vkcv/Handles.hpp" -#include "vkcv/CommandRecordingFunctionTypes.hpp" - -namespace vkcv { - - class Core; - - class CommandStreamManager - { - friend class Core; - private: - struct CommandStream { - inline CommandStream(vk::CommandBuffer cmdBuffer, vk::Queue queue, vk::CommandPool cmdPool) - : cmdBuffer(cmdBuffer), cmdPool(cmdPool), queue(queue) {}; - vk::CommandBuffer cmdBuffer; - vk::CommandPool cmdPool; - vk::Queue queue; - std::vector<FinishCommandFunction> callbacks; - }; - - Core* m_core; - std::vector<CommandStream> m_commandStreams; - - CommandStreamManager() noexcept; - - void init(Core* core); - - public: - ~CommandStreamManager() noexcept; - - CommandStreamManager(CommandStreamManager&& other) = delete; - CommandStreamManager(const CommandStreamManager& other) = delete; - - CommandStreamManager& operator=(CommandStreamManager&& other) = delete; - CommandStreamManager& operator=(const CommandStreamManager& other) = delete; - - CommandStreamHandle createCommandStream( - const vk::Queue queue, - vk::CommandPool cmdPool); - - void recordCommandsToStream(const CommandStreamHandle handle, const RecordCommandFunction record); - void addFinishCallbackToStream(const CommandStreamHandle handle, const FinishCommandFunction finish); - void submitCommandStreamSynchronous( - const CommandStreamHandle handle, - std::vector<vk::Semaphore> &waitSemaphores, - std::vector<vk::Semaphore> &signalSemaphores); - - vk::CommandBuffer getStreamCommandBuffer(const CommandStreamHandle handle); - }; - -} \ No newline at end of file diff --git a/include/vkcv/ComputePipelineConfig.hpp b/include/vkcv/ComputePipelineConfig.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8a030ba9c1a66c1b7ffcb74e283ce95e06d93243 --- /dev/null +++ b/include/vkcv/ComputePipelineConfig.hpp @@ -0,0 +1,19 @@ +#pragma once +/** + * @authors Mark Mints, Tobias Frisch + * @file vkcv/ComputePipelineConfig.hpp + * @brief Compute pipeline config struct to hand over required information to pipeline creation. + */ + +#include "PipelineConfig.hpp" + +namespace vkcv { + + /** + * @brief Class to configure a compute pipeline before its creation. + */ + class ComputePipelineConfig : public PipelineConfig { + using PipelineConfig::PipelineConfig; + }; + +} // namespace vkcv \ No newline at end of file diff --git a/include/vkcv/Context.hpp b/include/vkcv/Context.hpp index 1c01a6134ba1642b3a130a7a4d3d299cc3f7b875..9146c59102b9e7e2cc51132f92e59274168c806e 100644 --- a/include/vkcv/Context.hpp +++ b/include/vkcv/Context.hpp @@ -1,57 +1,121 @@ #pragma once +/** + * @authors Tobias Frisch, Artur Wasmut, Sebastian Gaida, Alexander Gauggel + * @file vkcv/Context.hpp + * @brief Class to handle the instance, device, allocator and features of the current context. + */ +#include <string> +#include <vk_mem_alloc.hpp> #include <vulkan/vulkan.hpp> +#include "Features.hpp" +#include "Handles.hpp" #include "QueueManager.hpp" -namespace vkcv -{ - class Context - { - friend class Core; - public: - // explicit destruction of default constructor - Context() = delete; - // is never called directly - ~Context() noexcept; - - Context(const Context &other) = delete; // copy-ctor - Context(Context &&other) noexcept; // move-ctor - - Context & operator=(const Context &other) = delete; // copy assignment - Context & operator=(Context &&other) noexcept; // move assignment - - [[nodiscard]] - const vk::Instance &getInstance() const; - - [[nodiscard]] - const vk::PhysicalDevice &getPhysicalDevice() const; - - [[nodiscard]] - const vk::Device &getDevice() const; - - [[nodiscard]] - const QueueManager& getQueueManager() const; - - static Context create(const char *applicationName, - uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions); - - private: - /** - * Constructor of #Context requires an @p instance, a @p physicalDevice and a @p device. - * - * @param instance Vulkan-Instance - * @param physicalDevice Vulkan-PhysicalDevice - * @param device Vulkan-Device - */ - Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, QueueManager&& queueManager) noexcept; - - vk::Instance m_Instance; - vk::PhysicalDevice m_PhysicalDevice; - vk::Device m_Device; - QueueManager m_QueueManager; - }; -} +namespace vkcv { + + /** + * @brief Class to manage core resources for vulkan callbacks. + * + * The class to manage the vulkan resources as an instance, + * a device, a physical device and a memory allocator. Additionally + * instances of this class will hold the feature manager and the + * queue manager. + */ + class Context { + friend class Core; + + public: + // explicit destruction of default constructor + Context() = delete; + // is never called directly + ~Context() noexcept; + + Context(const Context &other) = delete; // copy-ctor + Context(Context &&other) noexcept; // move-ctor + + Context &operator=(const Context &other) = delete; // copy assignment + Context &operator=(Context &&other) noexcept; // move assignment + + /** + * @brief Returns the vulkan instance of the context. + * + * @return Vulkan instance + */ + [[nodiscard]] const vk::Instance &getInstance() const; + + /** + * @brief Returns the vulkan physical device of the context. + * + * @return Vulkan physical device + */ + [[nodiscard]] const vk::PhysicalDevice &getPhysicalDevice() const; + + /** + * @brief Returns the vulkan device of the context. + * + * @return Vulkan device + */ + [[nodiscard]] const vk::Device &getDevice() const; + + /** + * @brief Returns the feature manager of the context. + * + * @return Feature manager + */ + [[nodiscard]] const FeatureManager &getFeatureManager() const; + + /** + * @brief Returns the queue manager of the context. + * + * @return Queue manager + */ + [[nodiscard]] const QueueManager &getQueueManager() const; + + /** + * @brief Returns the VMA allocator of the context. + * + * @return VMA allocator + */ + [[nodiscard]] const vma::Allocator &getAllocator() const; + + /** + * @brief Creates a context for a given application with + * a specific name, version, queue requirements, features and + * required instance extensions. + * + * @param applicationName Application name + * @param applicationVersion Application version + * @param queueFlags Queue flags + * @param features Features + * @param instanceExtensions Instance extensions + * @return New context + */ + static Context create(const std::string &applicationName, uint32_t applicationVersion, + const std::vector<vk::QueueFlagBits> &queueFlags, + const Features &features, + const std::vector<const char*> &instanceExtensions = {}); + + private: + /** + * @brief Constructor of #Context requires an @p instance, + * a @p physicalDevice and a @p device. + * + * @param instance Vulkan-Instance + * @param physicalDevice Vulkan-PhysicalDevice + * @param device Vulkan-Device + */ + Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, + FeatureManager &&featureManager, QueueManager &&queueManager, + vma::Allocator &&allocator) noexcept; + + vk::Instance m_Instance; + vk::PhysicalDevice m_PhysicalDevice; + vk::Device m_Device; + FeatureManager m_FeatureManager; + QueueManager m_QueueManager; + vma::Allocator m_Allocator; + }; + +} // namespace vkcv diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp index c7512346c9137b77c365e807b679b3950925f535..1cec2a7149ab17de76f390944e27941c317cd6e6 100644 --- a/include/vkcv/Core.hpp +++ b/include/vkcv/Core.hpp @@ -1,292 +1,944 @@ #pragma once /** - * @file src/vkcv/Core.hpp - * @brief Handling of global states regarding dependencies + * @authors Alexander Gauggel, Tobias Frisch, Sebastian Gaida, Artur Wasmut, Lars Hoerttrich, + * Mara Vogt, Mark Mints, Simeon Hermann, Alex Laptop, Katharina Krämer, Vanessa Karolek + * @file vkcv/Core.hpp + * @brief Handling of global states regarding dependencies. */ #include <memory> +#include <string> #include <vulkan/vulkan.hpp> -#include "vkcv/Context.hpp" -#include "vkcv/Swapchain.hpp" -#include "vkcv/Window.hpp" -#include "vkcv/PassConfig.hpp" -#include "vkcv/Handles.hpp" -#include "vkcv/Buffer.hpp" -#include "vkcv/Image.hpp" -#include "vkcv/PipelineConfig.hpp" -#include "CommandResources.hpp" -#include "SyncResources.hpp" -#include "Result.hpp" -#include "vkcv/DescriptorConfig.hpp" -#include "Sampler.hpp" +#include "BlitDownsampler.hpp" +#include "BufferTypes.hpp" +#include "ComputePipelineConfig.hpp" +#include "Context.hpp" #include "DescriptorWrites.hpp" +#include "DispatchSize.hpp" +#include "Drawcall.hpp" #include "Event.hpp" -#include "DrawcallRecording.hpp" -#include "CommandRecordingFunctionTypes.hpp" - -namespace vkcv -{ - - // forward declarations - class PassManager; - class PipelineManager; - class DescriptorManager; - class BufferManager; - class SamplerManager; - class ImageManager; +#include "EventFunctionTypes.hpp" +#include "GraphicsPipelineConfig.hpp" +#include "Handles.hpp" +#include "ImageConfig.hpp" +#include "PassConfig.hpp" +#include "PushConstants.hpp" +#include "Result.hpp" +#include "SamplerTypes.hpp" +#include "Window.hpp" + +#define VKCV_FRAMEWORK_NAME "VkCV" +#define VKCV_FRAMEWORK_VERSION (VK_MAKE_VERSION(0, 1, 0)) + +namespace vkcv { + + // forward declarations + class PassManager; + class GraphicsPipelineManager; + class ComputePipelineManager; + class DescriptorSetLayoutManager; + class DescriptorSetManager; + class BufferManager; + class SamplerManager; + class ImageManager; class CommandStreamManager; + class WindowManager; + class SwapchainManager; + + /** + * @brief Class to handle the core functionality of the framework. + * + * The class handles the core functionality of the framework with most + * calls addressing resource management via more simplified abstraction. + */ + class Core final { + private: + /** + * Constructor of #Core requires an @p context. + * + * @param context encapsulates various Vulkan objects + */ + explicit Core(Context &&context) noexcept; - struct SubmitInfo { - QueueType queueType; - std::vector<vk::Semaphore> waitSemaphores; - std::vector<vk::Semaphore> signalSemaphores; - }; + // explicit destruction of default constructor + Core() = delete; + + Result acquireSwapchainImage(const SwapchainHandle &swapchainHandle); + + Context m_Context; + + std::unique_ptr<PassManager> m_PassManager; + std::unique_ptr<GraphicsPipelineManager> m_GraphicsPipelineManager; + std::unique_ptr<ComputePipelineManager> m_ComputePipelineManager; + std::unique_ptr<DescriptorSetLayoutManager> m_DescriptorSetLayoutManager; + std::unique_ptr<DescriptorSetManager> m_DescriptorSetManager; + std::unique_ptr<BufferManager> m_BufferManager; + std::unique_ptr<SamplerManager> m_SamplerManager; + std::unique_ptr<ImageManager> m_ImageManager; + std::unique_ptr<CommandStreamManager> m_CommandStreamManager; + std::unique_ptr<WindowManager> m_WindowManager; + std::unique_ptr<SwapchainManager> m_SwapchainManager; + + std::vector<vk::CommandPool> m_CommandPools; + vk::Semaphore m_RenderFinished; + vk::Semaphore m_SwapchainImageAcquired; + uint32_t m_currentSwapchainImageIndex; + + std::unique_ptr<Downsampler> m_downsampler; + + /** + * Sets up swapchain images + * @param handle Handle of swapchain + */ + void setSwapchainImages(SwapchainHandle handle); + + public: + /** + * Destructor of #Core destroys the Vulkan objects contained in the core's context. + */ + ~Core() noexcept; + + /** + * Copy-constructor of #Core is deleted! + * + * @param other Other instance of #Context + */ + Core(const Core &other) = delete; + + /** + * Move-constructor of #Core uses default behavior! + * + * @param other Other instance of #Context + */ + Core(Core &&other) = delete; // move-ctor + + /** + * Copy assignment operator of #Core is deleted! + * + * @param other Other instance of Context + * @return Reference to itself + */ + Core &operator=(const Core &other) = delete; + + /** + * Move assignment operator of #Core uses default behavior! + * + * @param other Other instance of Context + * @return Reference to itself + */ + Core &operator=(Core &&other) = delete; + + /** + * Returns the context of a Core instance. + * + * @return Current Context + */ + [[nodiscard]] const Context &getContext() const; + + /** + * Creates a #Core with given @p applicationName and @p applicationVersion for your + * application. + * + * It is also possible to require a specific amount of queues, ask for specific queue-flags + * or extensions. This function will take care of the required arguments as best as + * possible. + * + * To pass a valid version for your application, you should use #VK_MAKE_VERSION(). + * + * @param[in] applicationName Name of the application + * @param[in] applicationVersion Version of the application + * @param[in] queueFlags (optional) Requested flags of queues + * @param[in] instanceExtensions (optional) Requested instance extensions + * @param[in] deviceExtensions (optional) Requested device extensions + * @return New instance of #Context + */ + static Core create(const std::string &applicationName, uint32_t applicationVersion, + const std::vector<vk::QueueFlagBits> &queueFlags = {}, + const Features &features = {}, + const std::vector<const char*> &instanceExtensions = {}); + + /** + * Creates a basic vulkan graphics pipeline using @p config from the pipeline config class + * and returns it using the @p handle. Fixed Functions for pipeline are set with standard + * values. + * + * @param config a pipeline config object from the pipeline config class + * @param handle a handle to return the created vulkan handle + * @return True if pipeline creation was successful, False if not + */ + [[nodiscard]] GraphicsPipelineHandle + createGraphicsPipeline(const GraphicsPipelineConfig &config); + + /** + * Creates a basic vulkan compute pipeline using @p shader program and returns it using the + * @p handle. Fixed Functions for pipeline are set with standard values. + * + * @param config Contains the compiles compute shader and the corresponding descriptor set + * layout + * @return True if pipeline creation was successful, False if not + */ + [[nodiscard]] ComputePipelineHandle + createComputePipeline(const ComputePipelineConfig &config); + + /** + * Creates a basic vulkan render pass using @p config from the render pass config class and + * returns it. Fixed Functions for pipeline are set with standard values. + * + * @param[in] config a render pass config object from the render pass config class + * @return A handle to represent the created pass + */ + [[nodiscard]] PassHandle createPass(const PassConfig &config); + + /** + * Returns the used configuration for a created render pass which is + * represented by the given handle. + * + * @param[in] pass Pass handle + * @return Pass configuration + */ + [[nodiscard]] const PassConfig &getPassConfiguration(const PassHandle &pass); + + /** + * @brief Creates a buffer with given parameters and returns its handle. + * + * @param[in] type Type of buffer created + * @param[in] typeGuard Type guard for the buffer + * @param[in] count Count of elements of its guarded type + * @param[in] memoryType Type of buffers memory + * @param[in] readable Flag whether the buffer supports reading from it + * @return A handle to represent the created buffer + */ + BufferHandle createBuffer(BufferType type, const TypeGuard &typeGuard, size_t count, + BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL, + bool readable = false); + + /** + * @brief Creates a buffer with given parameters and returns its handle. + * + * @param[in] type Type of buffer created + * @param[in] size Size of the buffer + * @param[in] memoryType Type of buffers memory + * @param[in] readable Flag whether the buffer supports reading from it + * @return A handle to represent the created buffer + */ + BufferHandle createBuffer(BufferType type, size_t size, + BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL, + bool readable = false); + + /** + * @brief Returns the vulkan buffer of a given buffer handle. + * + * @param[in] buffer Buffer handle + * @return Vulkan buffer + */ + vk::Buffer getBuffer(const BufferHandle &buffer) const; + + /** + * @brief Returns the buffer type of a buffer represented + * by a given buffer handle. + * + * @param[in] buffer Buffer handle + * @return Buffer type + */ + [[nodiscard]] BufferType getBufferType(const BufferHandle &buffer) const; + + /** + * @brief Returns the buffer memory type of a buffer + * represented by a given buffer handle. + * + * @param[in] buffer Buffer handle + * @return Buffer memory type + */ + [[nodiscard]] BufferMemoryType getBufferMemoryType(const BufferHandle &buffer) const; + + /** + * @brief Returns the size of a buffer represented + * by a given buffer handle. + * + * @param[in] buffer Buffer handle + * @return Size of the buffer + */ + [[nodiscard]] size_t getBufferSize(const BufferHandle &buffer) const; + + /** + * @brief Fills a buffer represented by a given buffer + * handle with custom data. + * + * @param[in] buffer Buffer handle + * @param[in] data Pointer to data + * @param[in] size Size of data in bytes + * @param[in] offset Offset to fill in data in bytes + */ + void fillBuffer(const BufferHandle &buffer, const void* data, size_t size, size_t offset); + + /** + * @brief Reads from a buffer represented by a given + * buffer handle to some data pointer. + * + * @param[in] buffer Buffer handle + * @param[in] data Pointer to data + * @param[in] size Size of data to read in bytes + * @param[in] offset Offset to read from buffer in bytes + */ + void readBuffer(const BufferHandle &buffer, void* data, size_t size, size_t offset); + + /** + * @brief Maps memory to a buffer represented by a given + * buffer handle and returns it. + * + * @param[in] buffer Buffer handle + * @param[in] offset Offset of mapping in bytes + * @param[in] size Size of mapping in bytes + * @return Pointer to mapped memory + */ + void* mapBuffer(const BufferHandle &buffer, size_t offset, size_t size); + + /** + * @brief Unmaps memory from a buffer represented by a given + * buffer handle. + * + * @param[in] buffer Buffer handle + */ + void unmapBuffer(const BufferHandle &buffer); + + /** + * Creates a Sampler with given attributes. + * + * @param[in] magFilter Magnifying filter + * @param[in] minFilter Minimizing filter + * @param[in] mipmapMode Mipmapping filter + * @param[in] addressMode Address mode + * @param[in] mipLodBias Mip level of detail bias + * @return Sampler handle + */ + [[nodiscard]] SamplerHandle + createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode, + float mipLodBias = 0.0f, + SamplerBorderColor borderColor = SamplerBorderColor::INT_ZERO_OPAQUE); + + /** + * Creates an #Image with a given format, configuration + * and whether a mipchain should be created. + * + * @param[in] format Image format + * @param[in] config Image configuration + * @param[in] createMipChain Flag to create a mip chain + * @return Image handle + */ + [[nodiscard]] ImageHandle createImage(vk::Format format, + const ImageConfig& config, + bool createMipChain = false); + + /** + * @brief Fills the image with given data of a specified size + * in bytes. + * + * @param[in] image Image handle + * @param[in] data Image data pointer + * @param[in] size Size of data + * @param[in] firstLayer First image layer + * @param[in] layerCount Image layer count + */ + void fillImage(const ImageHandle &image, + const void* data, + size_t size, + uint32_t firstLayer, + uint32_t layerCount); + + /** + * @brief Switches the images layout synchronously if possible. + * + * @param[in] image Image handle + * @param[in] layout New image layout + */ + void switchImageLayout(const ImageHandle &image, vk::ImageLayout layout); + + /** + * @brief Returns the default blit-downsampler. + * + * @return Blit-downsampler + */ + [[nodiscard]] Downsampler &getDownsampler(); + + /** + * Creates a new window and returns it's handle + * @param[in] applicationName Window title + * @param[in] windowWidth Window width + * @param[in] windowHeight Window height + * @param[in] resizeable resizeability bool + * @return windowHandle + */ + [[nodiscard]] WindowHandle createWindow(const std::string &applicationName, + uint32_t windowWidth, uint32_t windowHeight, + bool resizeable); + + /** + * Getter for window reference + * @param[in] handle of the window + * @return the window + */ + [[nodiscard]] Window &getWindow(const WindowHandle &handle); + + /** + * @brief Returns the image format for the current surface + * of the swapchain. + * + * @param[in] handle Swapchain handle + * @return Swapchain image format + */ + [[nodiscard]] vk::Format getSwapchainFormat(const SwapchainHandle &swapchain) const; + + /** + * @brief Returns the amount of images for the swapchain. + * + * @param[in] handle Swapchain handle + * @return Number of images + */ + [[nodiscard]] uint32_t getSwapchainImageCount(const SwapchainHandle &swapchain) const; + + /** + * @brief Returns the extent from the current surface of + * the swapchain. + * + * @param[in] handle Swapchain handle + * @return Extent of the swapchains surface + */ + [[nodiscard]] vk::Extent2D getSwapchainExtent(const SwapchainHandle &swapchain) const; + + /** + * @brief Returns the image width. + * + * @param[in] image Image handle + * @return imageWidth + */ + [[nodiscard]] uint32_t getImageWidth(const ImageHandle &image); + + /** + * @brief Returns the image height. + * + * @param[in] image Image handle + * @return imageHeight + */ + [[nodiscard]] uint32_t getImageHeight(const ImageHandle &image); + + /** + * @brief Returns the image depth. + * + * @param[in] image Image handle + * @return imageDepth + */ + [[nodiscard]] uint32_t getImageDepth(const ImageHandle &image); + + /** + * @brief Returns the image format of the image. + * + * @param[in] image Image handle + * @return imageFormat + */ + [[nodiscard]] vk::Format getImageFormat(const ImageHandle &image); + + /** + * @brief Returns whether the image supports storage or not. + * + * @param[in] image Image handle + * @return True, if the image supports storage, otherwise false. + */ + [[nodiscard]] bool isImageSupportingStorage(const ImageHandle &image); + + /** + * @brief Returns the images amount of mip levels. + * + * @param[in] image Image handle + * @return Amount of mip levels + */ + [[nodiscard]] uint32_t getImageMipLevels(const ImageHandle &image); + + /** + * @brief Returns the images amount of array layers. + * + * @param[in] image Image handle + * @return Amount of array layers + */ + [[nodiscard]] uint32_t getImageArrayLayers(const ImageHandle &image); + + /** + * @brief Creates a descriptor set layout handle by a set of descriptor bindings. + * + * @param[in] bindings Descriptor bindings + * @return Descriptor set layout handle + */ + [[nodiscard]] DescriptorSetLayoutHandle + createDescriptorSetLayout(const DescriptorBindings &bindings); + + /** + * @brief Creates a new descriptor set + * + * @param[in] layout Handle to the layout that the descriptor set will use + * @return Handle that represents the descriptor set + */ + [[nodiscard]] DescriptorSetHandle + createDescriptorSet(const DescriptorSetLayoutHandle &layout); - class Core final - { - private: - - /** - * Constructor of #Core requires an @p context. - * - * @param context encapsulates various Vulkan objects - */ - Core(Context &&context, Window &window, const Swapchain& swapChain, std::vector<vk::ImageView> imageViews, - const CommandResources& commandResources, const SyncResources& syncResources) noexcept; - // explicit destruction of default constructor - Core() = delete; - - Result acquireSwapchainImage(); - - Context m_Context; - - Swapchain m_swapchain; - Window& m_window; - - std::unique_ptr<PassManager> m_PassManager; - std::unique_ptr<PipelineManager> m_PipelineManager; - std::unique_ptr<DescriptorManager> m_DescriptorManager; - std::unique_ptr<BufferManager> m_BufferManager; - std::unique_ptr<SamplerManager> m_SamplerManager; - std::unique_ptr<ImageManager> m_ImageManager; - std::unique_ptr<CommandStreamManager> m_CommandStreamManager; - - CommandResources m_CommandResources; - SyncResources m_SyncResources; - uint32_t m_currentSwapchainImageIndex; - - event_handle<int,int> e_resizeHandle; - - static std::vector<vk::ImageView> createSwapchainImageViews( Context &context, Swapchain& swapChain); - - public: - /** - * Destructor of #Core destroys the Vulkan objects contained in the core's context. - */ - ~Core() noexcept; - - /** - * Copy-constructor of #Core is deleted! - * - * @param other Other instance of #Context - */ - Core(const Core& other) = delete; - - /** - * Move-constructor of #Core uses default behavior! - * - * @param other Other instance of #Context - */ - Core(Core &&other) = delete; // move-ctor - - /** - * Copy assignment operator of #Core is deleted! - * - * @param other Other instance of #Context - * @return Reference to itself - */ - Core & operator=(const Core &other) = delete; - - /** - * Move assignment operator of #Core uses default behavior! - * - * @param other Other instance of #Context - * @return Reference to itself - */ - Core & operator=(Core &&other) = delete; - - [[nodiscard]] - const Context &getContext() const; - - [[nodiscard]] - const Swapchain& getSwapchain() const; - - /** - * Creates a #Core with given @p applicationName and @p applicationVersion for your application. - * - * It is also possible to require a specific amount of queues, ask for specific queue-flags or - * extensions. This function will take care of the required arguments as best as possible. - * - * To pass a valid version for your application, you should use #VK_MAKE_VERSION(). - * - * @param[in] applicationName Name of the application - * @param[in] applicationVersion Version of the application - * @param[in] queueFlags (optional) Requested flags of queues - * @param[in] instanceExtensions (optional) Requested instance extensions - * @param[in] deviceExtensions (optional) Requested device extensions - * @return New instance of #Context - */ - static Core create(Window &window, - const char *applicationName, - uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags = {}, - std::vector<const char*> instanceExtensions = {}, - std::vector<const char*> deviceExtensions = {}); - - /** - * Creates a basic vulkan graphics pipeline using @p config from the pipeline config class and returns it using the @p handle. - * Fixed Functions for pipeline are set with standard values. - * - * @param config a pipeline config object from the pipeline config class - * @param handle a handle to return the created vulkan handle - * @return True if pipeline creation was successful, False if not - */ - [[nodiscard]] - PipelineHandle createGraphicsPipeline(const PipelineConfig &config); - - /** - * Creates a basic vulkan compute pipeline using @p shader program and returns it using the @p handle. - * Fixed Functions for pipeline are set with standard values. - * - * @param shader program that hold the compiles compute shader - * @param handle a handle to return the created vulkan handle - * @return True if pipeline creation was successful, False if not - */ - [[nodiscard]] - PipelineHandle createComputePipeline( - const ShaderProgram &config, - const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts); - - /** - * Creates a basic vulkan render pass using @p config from the render pass config class and returns it using the @p handle. - * Fixed Functions for pipeline are set with standard values. - * - * @param config a render pass config object from the render pass config class - * @param handle a handle to return the created vulkan handle - * @return True if render pass creation was successful, False if not - */ - [[nodiscard]] - PassHandle createPass(const PassConfig &config); - - /** - * Creates a #Buffer with data-type T and @p bufferType - * @param type Type of Buffer created - * @param count Count of elements of type T - * @param memoryType Type of Buffers memory - * return Buffer-Object - */ - template<typename T> - Buffer<T> createBuffer(vkcv::BufferType type, size_t count, BufferMemoryType memoryType = BufferMemoryType::DEVICE_LOCAL) { - return Buffer<T>::create(m_BufferManager.get(), type, count, memoryType); - } - - /** - * Creates a Sampler with given attributes. - * - * @param magFilter Magnifying filter - * @param minFilter Minimizing filter - * @param mipmapMode Mipmapping filter - * @param addressMode Address mode - * @return Sampler handle - */ - [[nodiscard]] - SamplerHandle createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, - SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode); - - /** - * Creates an #Image with a given format, width, height and depth. - * - * @param format Image format - * @param width Image width - * @param height Image height - * @param depth Image depth - * @return Image-Object - */ - [[nodiscard]] - Image createImage( - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth = 1, - bool createMipChain = false, - bool supportStorage = false, - bool supportColorAttachment = false); - - /** TODO: - * @param setDescriptions - * @return - */ - [[nodiscard]] - DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &bindings); - void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites& writes); - - DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; - - /** - * @brief start recording command buffers and increment frame index - */ - bool beginFrame(uint32_t& width, uint32_t& height); - - void recordDrawcallsToCmdStream( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, - const PipelineHandle pipelineHandle, - const PushConstantData &pushConstantData, - const std::vector<DrawcallInfo> &drawcalls, - const std::vector<ImageHandle> &renderTargets); - - void recordComputeDispatchToCmdStream( - CommandStreamHandle cmdStream, - PipelineHandle computePipeline, - const uint32_t dispatchCount[3], + /** + * @brief Writes resources bindings to a descriptor set + * + * @param handle Handle of the descriptor set + * @param writes Struct containing the resource bindings to be written + * must be compatible with the descriptor set's layout + */ + void writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes); + + /** + * @brief Start recording command buffers and increment frame index + */ + bool beginFrame(uint32_t &width, uint32_t &height, const WindowHandle &windowHandle); + + /** + * @brief Records drawcalls to a command stream + * + * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into + * @param pipelineHandle Handle of the pipeline that is used for the drawcalls + * @param pushConstants Push constants that are used for the drawcalls, ignored if constant + * size is set to 0 + * @param drawcalls Information about each drawcall, consisting of mesh handle, descriptor + * set bindings and instance count + * @param renderTargets Image handles that are used as render targets + * @param windowHandle Window handle that is used to retrieve the corresponding swapchain + */ + void recordDrawcallsToCmdStream(const CommandStreamHandle &cmdStreamHandle, + const GraphicsPipelineHandle &pipelineHandle, + const PushConstants &pushConstants, + const std::vector<InstanceDrawcall> &drawcalls, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle); + + /** + * @brief Records indirect drawcalls to a command stream + * + * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into + * @param pipelineHandle Handle of the pipeline that is used for the drawcalls + * @param pushConstantData Push constants that are used for the drawcalls, ignored if + * constant size is set to 0 + * @param drawcalls Information about each drawcall, consisting of mesh handle, descriptor + * set bindings and draw count + * @param renderTargets Image handles that are used as render targets + * @param windowHandle Window handle that is used to retrieve the corresponding swapchain + */ + void recordIndirectDrawcallsToCmdStream(const CommandStreamHandle cmdStreamHandle, + const GraphicsPipelineHandle &pipelineHandle, + const PushConstants &pushConstantData, + const std::vector<IndirectDrawcall> &drawcalls, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle); + + /** + * @brief Records mesh shader drawcalls to a command stream + * + * @param cmdStreamHandle Handle of the command stream that the drawcalls are recorded into + * @param pipelineHandle Handle of the pipeline that is used for the drawcalls + * @param pushConstantData Push constants that are used for the drawcalls, ignored if + * constant size is set to 0 + * @param drawcalls Information about each drawcall, consisting of descriptor set bindings + * and task shader task count + * @param renderTargets Image handles that are used as render targets + * @param windowHandle Window handle that is used to retrieve the corresponding swapchain + */ + void recordMeshShaderDrawcalls(const CommandStreamHandle &cmdStreamHandle, + const GraphicsPipelineHandle &pipelineHandle, + const PushConstants &pushConstantData, + const std::vector<TaskDrawcall> &drawcalls, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle); + + /** + * Records the rtx ray generation to the @p cmdStreamHandle. + * Currently only supports @p closestHit, @p rayGen and @c miss shaderstages @c. + * + * @param cmdStreamHandle The command stream handle which receives relevant commands for + * drawing. + * @param rtxPipeline The raytracing pipeline from the RTXModule. + * @param rtxPipelineLayout The raytracing pipeline layout from the RTXModule. + * @param rgenRegion The shader binding table region for ray generation shaders. + * @param rmissRegion The shader binding table region for ray miss shaders. + * @param rchitRegion The shader binding table region for ray closest hit shaders. + * @param rcallRegion The shader binding table region for callable shaders. + * @param descriptorSetUsages The descriptor set usages. + * @param pushConstants The push constants. + * @param windowHandle The window handle defining in which window to render. + */ + void recordRayGenerationToCmdStream( + CommandStreamHandle cmdStreamHandle, vk::Pipeline rtxPipeline, + vk::PipelineLayout rtxPipelineLayout, vk::StridedDeviceAddressRegionKHR rgenRegion, + vk::StridedDeviceAddressRegionKHR rmissRegion, + vk::StridedDeviceAddressRegionKHR rchitRegion, + vk::StridedDeviceAddressRegionKHR rcallRegion, const std::vector<DescriptorSetUsage> &descriptorSetUsages, - const PushConstantData& pushConstantData); + const PushConstants &pushConstants, const WindowHandle &windowHandle); /** - * @brief end recording and present image - */ - void endFrame(); + * @brief Record a compute shader dispatch into a command stream + * + * @param cmdStream Handle of the command stream that the dispatch is recorded into + * @param computePipeline Handle of the pipeline that is used for the dispatch + * @param dispatchSize How many work groups are dispatched + * @param descriptorSetUsages Descriptor set usages of the dispatch + * @param pushConstants Push constant data for the dispatch + */ + void + recordComputeDispatchToCmdStream(const CommandStreamHandle &cmdStream, + const ComputePipelineHandle &computePipeline, + const DispatchSize &dispatchSize, + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstants &pushConstants); /** - * Submit a command buffer to any queue of selected type. The recording can be customized by a - * custom record-command-function. If the command submission has finished, an optional finish-function - * will be called. + * @brief Record the start of a debug label into a command stream. + * Debug labels are displayed in GPU debuggers, such as RenderDoc * - * @param submitInfo Submit information - * @param record Record-command-function - * @param finish Finish-command-function or nullptr + * @param cmdStream Handle of the command stream that the label start is recorded into + * @param label Label name, which is displayed in a debugger + * @param color Display color for the label in a debugger + */ + void recordBeginDebugLabel(const CommandStreamHandle &cmdStream, const std::string &label, + const std::array<float, 4> &color); + + /** + * @brief Record the end of a debug label into a command stream + * @param cmdStream Handle of the command stream that the label end is recorded into */ - void recordAndSubmitCommandsImmediate( - const SubmitInfo &submitInfo, - const RecordCommandFunction &record, - const FinishCommandFunction &finish); + void recordEndDebugLabel(const CommandStreamHandle &cmdStream); + /** + * @brief Record an indirect compute shader dispatch into a command stream + * + * @param cmdStream Handle of the command stream that the indirect dispatch is recorded into + * @param computePipeline Handle of the pipeline that is used for the indirect dispatch + * @param buffer GPU Buffer from which the dispatch counts are read + * @param bufferArgOffset Offset into the GPU Buffer from where the dispatch counts are read + * @param descriptorSetUsages Descriptor set bindings of the indirect dispatch + * @param pushConstants Push constant data for the indirect dispatch + */ + void recordComputeIndirectDispatchToCmdStream( + const CommandStreamHandle cmdStream, const ComputePipelineHandle computePipeline, + const vkcv::BufferHandle buffer, const size_t bufferArgOffset, + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstants &pushConstants); + + /** + * @brief End recording and present image + */ + void endFrame(const WindowHandle &windowHandle); + + /** + * @brief Create a new command stream + * + * @param queueType The type of queue to which the command stream will be submitted to + * @return Handle which represents the command stream + */ CommandStreamHandle createCommandStream(QueueType queueType); - void recordCommandsToStream( - const CommandStreamHandle cmdStreamHandle, - const RecordCommandFunction &record, - const FinishCommandFunction &finish); - - void submitCommandStream(const CommandStreamHandle handle); - void prepareSwapchainImageForPresent(const CommandStreamHandle handle); - void prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image); - void prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image); - void recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image); - void recordBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer); - - vk::ImageView getSwapchainImageView() const; - - }; -} + /** + * @brief Record commands to a command stream by providing a function + * + * @param cmdStreamHandle Handle of the command stream to record to + * @param record Recording function + * @param finish Finish function, called after execution of commands is finished + */ + void recordCommandsToStream(const CommandStreamHandle &stream, + const RecordCommandFunction &record, + const FinishCommandFunction &finish); + + /** + * @brief Submit command stream to GPU for actual execution + * + * @param[in] handle Command stream to submit + * @param[in] signalRendering Flag to specify if the command stream finishes rendering + */ + void submitCommandStream(const CommandStreamHandle &stream, bool signalRendering = true); + + /** + * @brief Prepare swapchain image for presentation to screen. + * Handles internal state such as image format, also acts as a memory barrier + * + * @param handle Handle of the command stream to record the preparation commands to + */ + void prepareSwapchainImageForPresent(const CommandStreamHandle &handle); + + /** + * @brief Prepare image for use as a sampled image. + * Handles internal state such as image format, also acts as a memory barrier + * + * @param[in] cmdStream Handle of the command stream to record the preparation commands to + * @param[in] image Handle of the image to prepare + * @param[in] mipLevelCount Count of mip levels to prepare + * @param[in] mipLevelOffset Offset to start preparing mip levels + */ + void prepareImageForSampling(const CommandStreamHandle &cmdStream, const ImageHandle &image, + uint32_t mipLevelCount = 0, uint32_t mipLevelOffset = 0); + + /** + * @brief Prepare image for use as a storage image. + * Handles internal state such as image format, also acts as a memory barrier + * + * @param[in] cmdStream Handle of the command stream to record the preparation commands to + * @param[in] image Handle of the image to prepare + * @param[in] mipLevelCount Count of mip levels to prepare + * @param[in] mipLevelOffset Offset to start preparing mip levels + */ + void prepareImageForStorage(const CommandStreamHandle &cmdStream, const ImageHandle &image, + uint32_t mipLevelCount = 0, uint32_t mipLevelOffset = 0); + + /** + * @brief Manual trigger to record commands to prepare an image for use as an attachment + * + * normally layout transitions for attachments are handled by the core + * however for manual vulkan use, e.g. ImGui integration, this function is exposed + * this is also why the command buffer is passed directly, instead of the command stream + * handle + * + * @param cmdBuffer The vulkan command buffer to record to + * @param image Handle of the image to prepare + */ + void prepareImageForAttachmentManually(const vk::CommandBuffer &cmdBuffer, + const ImageHandle &image); + + /** + * @brief Indicate an external change of an image's layout + * + * if manual vulkan work, e.g. ImGui integration, changes an image layout this function must + * be used to update the internal image state + * + * @param image Handle of the image whose layout was changed + * @param layout The current layout of the image + */ + void updateImageLayoutManual(const vkcv::ImageHandle &image, const vk::ImageLayout layout); + + /** + * @brief Records a memory barrier to synchronize subsequent accesses to the image's data + * + * @param cmdStream Handle of the command stream to record the barrier to + * @param image Handle of the image the barrier belongs to + */ + void recordImageMemoryBarrier(const CommandStreamHandle &cmdStream, + const ImageHandle &image); + + /** + * @brief Records a buffer barrier to synchronize subsequent accesses to the buffer's data + * + * @param cmdStream Handle of the command stream to record the barrier to + * @param buffer Handle of the buffer the barrier belongs to + */ + void recordBufferMemoryBarrier(const CommandStreamHandle &cmdStream, + const BufferHandle &buffer); + + /** + * @brief Resolve a source MSAA image into a destination image for further use + * + * @param cmdStream Handle of the command stream to record the resolve to + * @param src The MSAA image that is resolved + * @param dst The target non-MSAA image that is resolved into + */ + void resolveMSAAImage(const CommandStreamHandle &cmdStream, const ImageHandle &src, + const ImageHandle &dst); + + /** + * @return Vulkan image view of the current swapchain image + */ + [[nodiscard]] vk::ImageView getSwapchainImageView() const; + + /** + * @brief Records a generic memory barrier to a command stream + * + * @param cmdStream Handle of the command stream the barrier is recorded to + */ + void recordMemoryBarrier(const CommandStreamHandle &cmdStream); + + /** + * @brief Record a blit (bit block image transfer) of a source image into a destination + * image, mip 0 is used for both + * + * @param cmdStream Handle of the command stream the blit operation is recorded into + * @param src The source image that is read from + * @param dst The destination image that is written into + * @param filterType The type of interpolation that is used + */ + void recordBlitImage(const CommandStreamHandle &cmdStream, const ImageHandle &src, + const ImageHandle &dst, SamplerFilterType filterType); + + /** + * @brief Sets a debug label to a buffer handle. + * + * @param[in,out] handle Buffer handle + * @param[in] label Debug label + */ + void setDebugLabel(const BufferHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to a pass handle. + * + * @param[in,out] handle Pass handle + * @param[in] label Debug label + */ + void setDebugLabel(const PassHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to a graphics pipeline handle. + * + * @param[in,out] handle Graphics pipeline handle + * @param[in] label Debug label + */ + void setDebugLabel(const GraphicsPipelineHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to a compute pipeline handle. + * + * @param[in,out] handle Compute pipeline handle + * @param[in] label Debug label + */ + void setDebugLabel(const ComputePipelineHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to a descriptor set handle. + * + * @param[in,out] handle Descriptor set handle + * @param[in] label Debug label + */ + void setDebugLabel(const DescriptorSetHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to a sampler handle. + * + * @param[in,out] handle Sampler handle + * @param[in] label Debug label + */ + void setDebugLabel(const SamplerHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to an image handle. + * + * @param[in,out] handle Image handle + * @param[in] label Debug label + */ + void setDebugLabel(const ImageHandle &handle, const std::string &label); + + /** + * @brief Sets a debug label to a command stream handle. + * + * @param[in,out] handle Command stream handle + * @param[in] label Debug label + */ + void setDebugLabel(const CommandStreamHandle &handle, const std::string &label); + + /** + * @brief Runs the application in the current until all windows get closed. + * + * The frame callback will be called for each window every single frame. + * + * @param[in] frame Frame callback + */ + void run(const WindowFrameFunction &frame); + + /** + * @brief Return the underlying vulkan handle for a render pass + * by its given pass handle. + * + * @param[in] handle Pass handle + * @return Vulkan render pass + */ + [[nodiscard]] vk::RenderPass getVulkanRenderPass(const PassHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a pipeline + * by its given graphics pipeline handle. + * + * @param[in] handle Graphics pipeline handle + * @return Vulkan pipeline + */ + [[nodiscard]] vk::Pipeline getVulkanPipeline(const GraphicsPipelineHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a pipeline + * by its given compute pipeline handle. + * + * @param[in] handle Compute pipeline handle + * @return Vulkan pipeline + */ + [[nodiscard]] vk::Pipeline getVulkanPipeline(const ComputePipelineHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a descriptor set layout + * by its given descriptor set layout handle. + * + * @param[in] handle Descriptor set layout handle + * @return Vulkan descriptor set layout + */ + [[nodiscard]] vk::DescriptorSetLayout + getVulkanDescriptorSetLayout(const DescriptorSetLayoutHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a descriptor set + * by its given descriptor set handle. + * + * @param[in] handle Descriptor set handle + * @return Vulkan descriptor set + */ + [[nodiscard]] vk::DescriptorSet + getVulkanDescriptorSet(const DescriptorSetHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a buffer + * by its given buffer handle. + * + * @param[in] handle Buffer handle + * @return Vulkan buffer + */ + [[nodiscard]] vk::Buffer getVulkanBuffer(const BufferHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a sampler + * by its given sampler handle. + * + * @param[in] handle Sampler handle + * @return Vulkan sampler + */ + [[nodiscard]] vk::Sampler getVulkanSampler(const SamplerHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a image + * by its given image handle. + * + * @param[in] handle Image handle + * @return Vulkan image + */ + [[nodiscard]] vk::Image getVulkanImage(const ImageHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a image view + * by its given image handle. + * + * @param[in] handle Image handle + * @return Vulkan image view + */ + [[nodiscard]] vk::ImageView getVulkanImageView(const ImageHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a device memory + * by its given buffer handle. + * + * @param[in] handle Buffer handle + * @return Vulkan device memory + */ + [[nodiscard]] vk::DeviceMemory getVulkanDeviceMemory(const BufferHandle &handle) const; + + /** + * @brief Return the underlying vulkan handle for a device memory + * by its given image handle. + * + * @param[in] handle Image handle + * @return Vulkan device memory + */ + [[nodiscard]] vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle &handle) const; + }; +} // namespace vkcv diff --git a/include/vkcv/DescriptorBinding.hpp b/include/vkcv/DescriptorBinding.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c97d1dfebc6d4415c04f51a761ddcdbc34ba6d68 --- /dev/null +++ b/include/vkcv/DescriptorBinding.hpp @@ -0,0 +1,31 @@ +#pragma once +/** + * @authors Artur Wasmut, Tobias Frisch, Simeon Hermann, Alexander Gauggel, Vanessa Karolek + * @file vkcv/DescriptorConfig.hpp + * @brief Structures to handle descriptor bindings. + */ + +#include <unordered_map> + +#include "DescriptorTypes.hpp" +#include "ShaderStage.hpp" + +namespace vkcv { + + /** + * @brief Structure to store details from a descriptor binding. + */ + struct DescriptorBinding { + uint32_t bindingID; + DescriptorType descriptorType; + uint32_t descriptorCount; + ShaderStages shaderStages; + bool variableCount; + bool partialBinding; + + bool operator==(const DescriptorBinding &other) const; + }; + + typedef std::unordered_map<uint32_t, DescriptorBinding> DescriptorBindings; + +} // namespace vkcv diff --git a/include/vkcv/DescriptorConfig.hpp b/include/vkcv/DescriptorConfig.hpp deleted file mode 100644 index c6d0dfd1bc60988afb8b6a9326a8d50d8a4ea32e..0000000000000000000000000000000000000000 --- a/include/vkcv/DescriptorConfig.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include <vulkan/vulkan.hpp> - -#include "vkcv/Handles.hpp" -#include "vkcv/ShaderStage.hpp" - -namespace vkcv -{ - struct DescriptorSet - { - vk::DescriptorSet vulkanHandle; - vk::DescriptorSetLayout layout; - }; - - /* - * All the types of descriptors (resources) that can be retrieved by the shaders - */ - enum class DescriptorType - { - UNIFORM_BUFFER, - STORAGE_BUFFER, - SAMPLER, - IMAGE_SAMPLED, - IMAGE_STORAGE - }; - - /* - * One binding for a descriptor set - * @param[in] a unique binding ID - * @param[in] a descriptor type - * @param[in] the number of descriptors of this type (arrays of the same type possible) - * @param[in] the shader stage where the descriptor is supposed to be retrieved - */ - struct DescriptorBinding - { - DescriptorBinding( - uint32_t bindingID, - DescriptorType descriptorType, - uint32_t descriptorCount, - ShaderStage shaderStage - ) noexcept; - - uint32_t bindingID; - DescriptorType descriptorType; - uint32_t descriptorCount; - ShaderStage shaderStage; - }; -} diff --git a/include/vkcv/DescriptorSetUsage.hpp b/include/vkcv/DescriptorSetUsage.hpp new file mode 100644 index 0000000000000000000000000000000000000000..098d1846c35b3724fb2540ebb41a1b6cd61d62fc --- /dev/null +++ b/include/vkcv/DescriptorSetUsage.hpp @@ -0,0 +1,26 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/DescriptorUsage.hpp + * @brief Structures to handle descriptor usages. + */ + +#include <vector> + +#include "Handles.hpp" + +namespace vkcv { + + /** + * @brief Structure to configure a descriptor set usage. + */ + struct DescriptorSetUsage { + uint32_t location; + DescriptorSetHandle descriptorSet; + std::vector<uint32_t> dynamicOffsets; + }; + + DescriptorSetUsage useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet, + const std::vector<uint32_t> &dynamicOffsets = {}); + +} // namespace vkcv diff --git a/include/vkcv/DescriptorTypes.hpp b/include/vkcv/DescriptorTypes.hpp new file mode 100644 index 0000000000000000000000000000000000000000..75035b2071554e8b2fe7c8309b5b8269c0c0ca72 --- /dev/null +++ b/include/vkcv/DescriptorTypes.hpp @@ -0,0 +1,56 @@ +#pragma once +/** + * @authors Artur Wasmut, Tobias Frisch, Simeon Hermann, Alexander Gauggel, Vanessa Karolek + * @file vkcv/DescriptorConfig.hpp + * @brief Enum classes to handle descriptor types. + */ + +#include <vulkan/vulkan.hpp> + +namespace vkcv { + + /** + * @brief Enum class to specify the type of a descriptor set binding. + */ + enum class DescriptorType { + UNIFORM_BUFFER, + STORAGE_BUFFER, + SAMPLER, + IMAGE_SAMPLED, + IMAGE_STORAGE, + UNIFORM_BUFFER_DYNAMIC, + STORAGE_BUFFER_DYNAMIC, + ACCELERATION_STRUCTURE_KHR + }; + + /** + * @brief Converts the descriptor type from the frameworks enumeration + * to the Vulkan type specifier. + * + * @param[in] type Descriptor type + * @return Vulkan descriptor type + */ + constexpr vk::DescriptorType getVkDescriptorType(DescriptorType type) noexcept { + switch (type) { + case DescriptorType::UNIFORM_BUFFER: + return vk::DescriptorType::eUniformBuffer; + case DescriptorType::UNIFORM_BUFFER_DYNAMIC: + return vk::DescriptorType::eUniformBufferDynamic; + case DescriptorType::STORAGE_BUFFER: + return vk::DescriptorType::eStorageBuffer; + case DescriptorType::STORAGE_BUFFER_DYNAMIC: + return vk::DescriptorType::eStorageBufferDynamic; + case DescriptorType::SAMPLER: + return vk::DescriptorType::eSampler; + case DescriptorType::IMAGE_SAMPLED: + return vk::DescriptorType::eSampledImage; + case DescriptorType::IMAGE_STORAGE: + return vk::DescriptorType::eStorageImage; + case DescriptorType::ACCELERATION_STRUCTURE_KHR: + return vk::DescriptorType::eAccelerationStructureKHR; + default: + return vk::DescriptorType::eMutableVALVE; + } + } + +} // namespace vkcv diff --git a/include/vkcv/DescriptorWrites.hpp b/include/vkcv/DescriptorWrites.hpp index f28a6c91e189b13413ffefec0f05e5a0a358ee26..caff4cef865c2cfc909e0aa41974a18e5d18a9b3 100644 --- a/include/vkcv/DescriptorWrites.hpp +++ b/include/vkcv/DescriptorWrites.hpp @@ -1,48 +1,207 @@ #pragma once -#include "Handles.hpp" +/** + * @authors Artur Wasmut, Tobias Frisch, Alexander Gauggel, Vanessa Karolek + * @file vkcv/DescriptorWrites.hpp + * @brief Structures to handle descriptor writes. + */ + #include <vector> +#include <vulkan/vulkan.hpp> + +#include "Handles.hpp" namespace vkcv { + + /** + * @brief Structure to store details writing a sampled image to a descriptor set. + */ struct SampledImageDescriptorWrite { - inline SampledImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0, bool useGeneralLayout = false) - : binding(binding), image(image), mipLevel(mipLevel), useGeneralLayout(useGeneralLayout) {}; - uint32_t binding; - ImageHandle image; - uint32_t mipLevel; - bool useGeneralLayout; + uint32_t binding; + ImageHandle image; + uint32_t mipLevel; + bool useGeneralLayout; + uint32_t arrayIndex; + uint32_t mipCount; + bool arrayView; }; + /** + * @brief Structure to store details writing a storage image to a descriptor set. + */ struct StorageImageDescriptorWrite { - inline StorageImageDescriptorWrite(uint32_t binding, ImageHandle image, uint32_t mipLevel = 0) - : binding(binding), image(image), mipLevel(mipLevel) {}; - uint32_t binding; - ImageHandle image; - uint32_t mipLevel; + uint32_t binding; + ImageHandle image; + uint32_t mipLevel; + uint32_t mipCount; + bool arrayView; }; - struct UniformBufferDescriptorWrite { - inline UniformBufferDescriptorWrite(uint32_t binding, BufferHandle buffer) : binding(binding), buffer(buffer) {}; - uint32_t binding; - BufferHandle buffer; + /** + * @brief Structure to store details writing a buffer to a descriptor set. + */ + struct BufferDescriptorWrite { + uint32_t binding; + BufferHandle buffer; + bool dynamic; + uint32_t offset; + uint32_t size; }; - struct StorageBufferDescriptorWrite { - inline StorageBufferDescriptorWrite(uint32_t binding, BufferHandle buffer) : binding(binding), buffer(buffer) {}; - uint32_t binding; - BufferHandle buffer; + /** + * @brief Structure to store details writing a sampler to a descriptor set. + */ + struct SamplerDescriptorWrite { + uint32_t binding; + SamplerHandle sampler; }; - struct SamplerDescriptorWrite { - inline SamplerDescriptorWrite(uint32_t binding, SamplerHandle sampler) : binding(binding), sampler(sampler) {}; - uint32_t binding; - SamplerHandle sampler; + /** + * @brief Structure to store details writing an acceleration structure to + * a descriptor set. + */ + struct AccelerationDescriptorWrite { + uint32_t binding; + std::vector<vk::AccelerationStructureKHR> structures; }; - struct DescriptorWrites { - std::vector<SampledImageDescriptorWrite> sampledImageWrites; - std::vector<StorageImageDescriptorWrite> storageImageWrites; - std::vector<UniformBufferDescriptorWrite> uniformBufferWrites; - std::vector<StorageBufferDescriptorWrite> storageBufferWrites; - std::vector<SamplerDescriptorWrite> samplerWrites; + /** + * @brief Class to store details about writing to + * a descriptor set and its bindings. + */ + class DescriptorWrites { + private: + std::vector<SampledImageDescriptorWrite> m_sampledImageWrites; + std::vector<StorageImageDescriptorWrite> m_storageImageWrites; + std::vector<BufferDescriptorWrite> m_uniformBufferWrites; + std::vector<BufferDescriptorWrite> m_storageBufferWrites; + std::vector<SamplerDescriptorWrite> m_samplerWrites; + std::vector<AccelerationDescriptorWrite> m_accelerationWrites; + + public: + /** + * @brief Adds an entry to write an image to a given binding + * of a descriptor set to sample from it using specific details. + * + * @param[in] binding Binding index + * @param[in] image Image handle + * @param[in] mipLevel Mip level index + * @param[in] useGeneralLayout Flag to use a general layout + * @param[in] arrayIndex Image array index + * @param[in] mipCount Mip level count + * @return Instance of descriptor writes + */ + DescriptorWrites &writeSampledImage(uint32_t binding, const ImageHandle& image, + uint32_t mipLevel = 0, bool useGeneralLayout = false, + uint32_t arrayIndex = 0, uint32_t mipCount = 1, + bool arrayView = false); + + /** + * @brief Adds an entry to write an image to a given binding + * of a descriptor set to store into it using specific details. + * + * @param[in] binding Binding index + * @param[in,out] image Image handle + * @param[in] mipLevel Mip level index + * @param[in] mipCount Mip level count + * @return Instance of descriptor writes + */ + DescriptorWrites &writeStorageImage(uint32_t binding, const ImageHandle& image, + uint32_t mipLevel = 0, uint32_t mipCount = 1, + bool arrayView = false); + + /** + * @brief Adds an entry to write a buffer to a given binding + * of a descriptor set as uniform buffer using specific details. + * + * @param[in] binding Binding index + * @param[in] buffer Buffer handle + * @param[in] dynamic Flag to use dynamic access + * @param[in] offset Offset for buffer access range + * @param[in] size Size of the buffer access range + * @return Instance of descriptor writes + */ + DescriptorWrites &writeUniformBuffer(uint32_t binding, const BufferHandle& buffer, + bool dynamic = false, uint32_t offset = 0, + uint32_t size = 0); + + /** + * @brief Adds an entry to write a buffer to a given binding + * of a descriptor set as storage buffer using specific details. + * + * @param[in] binding Binding index + * @param[in] buffer Buffer handle + * @param[in,out] dynamic Flag to use dynamic access + * @param[in] offset Offset for buffer access range + * @param[in] size Size of the buffer access range + * @return Instance of descriptor writes + */ + DescriptorWrites &writeStorageBuffer(uint32_t binding, const BufferHandle& buffer, + bool dynamic = false, uint32_t offset = 0, + uint32_t size = 0); + + /** + * @brief Adds an entry to write a sampler to a given binding + * of a descriptor set. + * + * @param[in] binding Binding index + * @param[in] sampler Sampler handle + * @return Instance of descriptor writes + */ + DescriptorWrites &writeSampler(uint32_t binding, const SamplerHandle& sampler); + + /** + * @brief Adds an entry for acceleration to a given binding + * of a descriptor set. + * + * @param[in] binding Binding index + * @param[in] structures Acceleration structures + * @return Instance of descriptor writes + */ + DescriptorWrites & + writeAcceleration(uint32_t binding, + const std::vector<vk::AccelerationStructureKHR> &structures); + + /** + * @brief Returns the list of stored write entries for sampled images. + * + * @return Sampled image write details + */ + [[nodiscard]] const std::vector<SampledImageDescriptorWrite> &getSampledImageWrites() const; + + /** + * @brief Returns the list of stored write entries for storage images. + * + * @return Storage image write details + */ + [[nodiscard]] const std::vector<StorageImageDescriptorWrite> &getStorageImageWrites() const; + + /** + * @brief Returns the list of stored write entries for uniform buffers. + * + * @return Uniform buffers write details + */ + [[nodiscard]] const std::vector<BufferDescriptorWrite> &getUniformBufferWrites() const; + + /** + * @brief Returns the list of stored write entries for storage buffers. + * + * @return Storage buffers write details + */ + [[nodiscard]] const std::vector<BufferDescriptorWrite> &getStorageBufferWrites() const; + + /** + * @brief Returns the list of stored write entries for samplers. + * + * @return Samplers write details + */ + [[nodiscard]] const std::vector<SamplerDescriptorWrite> &getSamplerWrites() const; + + /** + * @brief Returns the list of stored write entries for accelerations. + * + * @return Accelerations write details + */ + [[nodiscard]] const std::vector<AccelerationDescriptorWrite> &getAccelerationWrites() const; }; -} \ No newline at end of file + +} // namespace vkcv \ No newline at end of file diff --git a/include/vkcv/DispatchSize.hpp b/include/vkcv/DispatchSize.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7f8a27feef9262968cb62bc451c8a0169d0ca9e2 --- /dev/null +++ b/include/vkcv/DispatchSize.hpp @@ -0,0 +1,109 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/DispatchSize.hpp + * @brief Class to handle dispatch sizes. + */ + +#include <array> +#include <vulkan/vulkan.hpp> + +namespace vkcv { + + /** + * @brief Class representing a dispatch size to invoke a compute pipeline with. + */ + class DispatchSize final { + private: + std::array<uint32_t, 3> m_Dispatch; + + public: + /** + * Implicit constructor to convert an unsigned integer to a + * one dimensional dispatch size. + * + * @param[in] count Count of invocations + */ + DispatchSize(uint32_t count); + + /** + * Explicit constructor to create a dispatch size with two or + * three dimensions setting each value specifically. + * + * @param[in] dimensionX Size of X dimension + * @param[in] dimentionY Size of Y dimension + * @param[in] dimensionZ Size of Z dimension (optional) + */ + DispatchSize(uint32_t dimensionX, uint32_t dimentionY, uint32_t dimensionZ = 1); + + DispatchSize(const DispatchSize &other) = default; + DispatchSize(DispatchSize &&other) = default; + + ~DispatchSize() = default; + + DispatchSize &operator=(const DispatchSize &other) = default; + DispatchSize &operator=(DispatchSize &&other) = default; + + /** + * Returns the raw data of the dispatch size as readonly unsigned + * integer pointer. + * + * @return Pointer to data + */ + [[nodiscard]] const uint32_t* data() const; + + /** + * Returns the size value of the dispatch size by a given index. + * + * @param[in] index Size index + * @return Size value by index + */ + [[nodiscard]] uint32_t operator[](size_t index) const; + + /** + * Returns the value for the X dimension of the dispatch size. + * + * @return Size of X dimension + */ + [[nodiscard]] uint32_t x() const; + + /** + * Returns the value for the Y dimension of the dispatch size. + * + * @return Size of Y dimension + */ + [[nodiscard]] uint32_t y() const; + + /** + * Returns the value for the Z dimension of the dispatch size. + * + * @return Size of Z dimension + */ + [[nodiscard]] uint32_t z() const; + + /** + * Checks whether the dispatch size is valid for compute shader + * invocations and returns the result as boolean value. + * + * @return True if the dispatch size is valid, otherwise false. + */ + bool check() const; + }; + + /** + * Returns the proper dispatch size by dividing a global amount of invocations + * as three dimensional dispatch size into invocations of a fixed group size + * for the used work groups of the compute shader. + * + * This function will generate over fitted results to make sure all global + * invocations get computed. So make sure the used compute shader handles those + * additional invocations out of bounds from the original global invocations. + * + * @param[in] globalInvocations Size of planned global invocations + * @param[in] groupSize Size of work group in compute stage + * @return Dispatch size respecting the actual work group size + */ + [[nodiscard]] DispatchSize dispatchInvocations(DispatchSize globalInvocations, + DispatchSize groupSize); + +} // namespace vkcv diff --git a/include/vkcv/Downsampler.hpp b/include/vkcv/Downsampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..82416fd2c957f58de8e7ba9092dfc3d9d677e56a --- /dev/null +++ b/include/vkcv/Downsampler.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "Handles.hpp" + +namespace vkcv { + + class Core; + + /** + * @brief An abstract class to handle downsampling of images for mip generation. + */ + class Downsampler { + protected: + /** + * Reference to the current Core instance. + */ + Core &m_core; + + public: + /** + * @brief Constructor to create a downsampler instance. + * + * @param[in,out] core Reference to a Core instance + */ + explicit Downsampler(Core &core); + + virtual ~Downsampler() = default; + + /** + * @brief Record the commands of the given downsampler instance to + * scale the image down on its own mip levels. + * + * @param[in] cmdStream Command stream handle + * @param[in] image Image handle + */ + virtual void recordDownsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &image) = 0; + }; + +} // namespace vkcv \ No newline at end of file diff --git a/include/vkcv/Drawcall.hpp b/include/vkcv/Drawcall.hpp new file mode 100644 index 0000000000000000000000000000000000000000..08e436ae52d44c3514b504ae2d573d7c447fab80 --- /dev/null +++ b/include/vkcv/Drawcall.hpp @@ -0,0 +1,89 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Drawcall.hpp + * @brief Classes to define different drawcalls. + */ + +#include <vector> + +#include "DescriptorSetUsage.hpp" +#include "Handles.hpp" +#include "VertexData.hpp" + +namespace vkcv { + + /** + * @brief Base class to store details for a general drawcall. + */ + class Drawcall { + private: + std::vector<DescriptorSetUsage> m_usages; + + public: + Drawcall() = default; + + Drawcall(const Drawcall &other) = default; + Drawcall(Drawcall &&other) noexcept = default; + + ~Drawcall() = default; + + Drawcall &operator=(const Drawcall &other) = default; + Drawcall &operator=(Drawcall &&other) noexcept = default; + + [[nodiscard]] const std::vector<DescriptorSetUsage> &getDescriptorSetUsages() const; + + void useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet, + const std::vector<uint32_t> &dynamicOffsets = {}); + }; + + /** + * @brief Class to store details for an instance drawcall. + */ + class InstanceDrawcall : public Drawcall { + private: + VertexData m_vertexData; + uint32_t m_instanceCount; + + public: + explicit InstanceDrawcall(const VertexData &vertexData, uint32_t instanceCount = 1); + + [[nodiscard]] const VertexData &getVertexData() const; + + [[nodiscard]] uint32_t getInstanceCount() const; + }; + + /** + * @brief Class to store details for an indirect drawcall. + */ + class IndirectDrawcall : public Drawcall { + private: + BufferHandle m_indirectDrawBuffer; + VertexData m_vertexData; + uint32_t m_drawCount; + + public: + explicit IndirectDrawcall(const BufferHandle &indirectDrawBuffer, + const VertexData &vertexData, uint32_t drawCount = 1); + + [[nodiscard]] BufferHandle getIndirectDrawBuffer() const; + + [[nodiscard]] const VertexData &getVertexData() const; + + [[nodiscard]] uint32_t getDrawCount() const; + }; + + /** + * @brief Class to store details for a task drawcall. + */ + class TaskDrawcall : public Drawcall { + private: + uint32_t m_taskCount; + + public: + explicit TaskDrawcall(uint32_t taskCount = 1); + + [[nodiscard]] uint32_t getTaskCount() const; + }; + +} // namespace vkcv diff --git a/include/vkcv/DrawcallRecording.hpp b/include/vkcv/DrawcallRecording.hpp deleted file mode 100644 index 0929ad038fb95ec1573e7c76e5ce13adb84ab760..0000000000000000000000000000000000000000 --- a/include/vkcv/DrawcallRecording.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> -#include <vkcv/Handles.hpp> -#include <vkcv/DescriptorConfig.hpp> - -namespace vkcv { - struct VertexBufferBinding { - inline VertexBufferBinding(vk::DeviceSize offset, vk::Buffer buffer) noexcept - : offset(offset), buffer(buffer) {} - - vk::DeviceSize offset; - vk::Buffer buffer; - }; - - struct DescriptorSetUsage { - inline DescriptorSetUsage(uint32_t setLocation, vk::DescriptorSet vulkanHandle) noexcept - : setLocation(setLocation), vulkanHandle(vulkanHandle) {} - - const uint32_t setLocation; - const vk::DescriptorSet vulkanHandle; - }; - - struct Mesh { - inline Mesh(std::vector<VertexBufferBinding> vertexBufferBindings, vk::Buffer indexBuffer, size_t indexCount) noexcept - : vertexBufferBindings(vertexBufferBindings), indexBuffer(indexBuffer), indexCount(indexCount){} - - std::vector<VertexBufferBinding> vertexBufferBindings; - vk::Buffer indexBuffer; - size_t indexCount; - }; - - struct PushConstantData { - inline PushConstantData(void* data, size_t sizePerDrawcall) : data(data), sizePerDrawcall(sizePerDrawcall) {} - - void* data; - size_t sizePerDrawcall; - }; - - struct DrawcallInfo { - inline DrawcallInfo(const Mesh& mesh, const std::vector<DescriptorSetUsage>& descriptorSets) - : mesh(mesh), descriptorSets(descriptorSets) {} - - Mesh mesh; - std::vector<DescriptorSetUsage> descriptorSets; - }; - - void recordDrawcall( - const DrawcallInfo &drawcall, - vk::CommandBuffer cmdBuffer, - vk::PipelineLayout pipelineLayout, - const PushConstantData &pushConstantData, - const size_t drawcallIndex); - -} \ No newline at end of file diff --git a/include/vkcv/Event.hpp b/include/vkcv/Event.hpp index da5cbc72fbb3eee3a71a35c1da6fe32dff06b057..3bba94b20afb56dd185751e8cafc787076822c2a 100644 --- a/include/vkcv/Event.hpp +++ b/include/vkcv/Event.hpp @@ -1,104 +1,163 @@ #pragma once +/** + * @authors Tobias Frisch, Sebastian Gaida, Josch Morgenstern, Katharina Krämer + * @file vkcv/Event.hpp + * @brief Template event struct to synchronize callbacks. + */ #include <functional> + +#ifndef __MINGW32__ +#ifdef __NO_SEMAPHORES__ #include <mutex> +#else +#include <semaphore> +#endif +#endif + +#include <vector> namespace vkcv { - - template<typename... T> + + /** + * @brief Template for a function handle to an event + * + * @tparam T Event parameter type list + */ + template <typename... T> struct event_handle { uint32_t id; }; - template<typename... T> - struct event_function { - typedef std::function<void(T...)> type; - + /** + * @brief Template for an event function + * + * @tparam T Event parameter type list + */ + template <typename... T> + struct event_function { + typedef std::function<void(T...)> type; + event_handle<T...> handle; - type callback; - }; - - /** - * template for event handling - * @tparam T parameter list - */ - template<typename... T> - struct event { - private: - std::vector< event_function<T...> > m_functions; - uint32_t m_id_counter; - std::mutex m_mutex; + type callback; + }; + + /** + * @brief Template for event handling + * + * @tparam T Event parameter type list + */ + template <typename... T> + struct event { + private: + std::vector< event_function<T...> > m_functions; + uint32_t m_id_counter; - public: +#ifndef __MINGW32__ +#ifdef __NO_SEMAPHORES__ + std::mutex m_mutex; +#else + std::binary_semaphore m_semaphore; +#endif +#endif - /** - * calls all function handles with the given arguments - * @param arguments of the given function - */ - void operator()(T... arguments) { + public: + /** + * @brief Calls all function handles with the given arguments. + * + * @param[in,out] arguments Arguments of the given event + */ + void operator()(T... arguments) { lock(); - for (auto &function : this->m_functions) { + for (auto &function : this->m_functions) { function.callback(arguments...); } - - unlock(); - } - - /** - * adds a function handle to the event to be called - * @param callback of the function - * @return handle of the function - */ + + unlock(); + } + + /** + * @brief Adds a function handle to the event to be called. + * + * @param[in] callback Event callback + * @return Handle of the function + */ event_handle<T...> add(typename event_function<T...>::type callback) { event_function<T...> function; function.handle = { m_id_counter++ }; function.callback = callback; - this->m_functions.push_back(function); - return function.handle; - } - - /** - * removes a function handle of the event - * @param handle of the function - */ - void remove(event_handle<T...> handle) { - this->m_functions.erase( - std::remove_if(this->m_functions.begin(), this->m_functions.end(), [&handle](auto function){ - return (handle.id == function.handle.id); - }), - this->m_functions.end() - ); - } - - /** - * locks the event so its function handles won't be called - */ - void lock() { + this->m_functions.push_back(function); + return function.handle; + } + + /** + * @brief Removes a function handle of the event. + * + * @param handle Handle of the function + */ + void remove(event_handle<T...> handle) { + this->m_functions.erase(std::remove_if(this->m_functions.begin(), + this->m_functions.end(), + [&handle](auto function) { + return (handle.id == function.handle.id); + }), + this->m_functions.end()); + } + + /** + * @brief Locks the event so its function handles won't + * be called until unlocked. + */ + void lock() { +#ifndef __MINGW32__ +#ifdef __NO_SEMAPHORES__ m_mutex.lock(); - } - +#else + m_semaphore.acquire(); +#endif +#endif + } + /** - * unlocks the event so its function handles can be called after locking - */ - void unlock() { + * @brief Unlocks the event so its function handles can + * be called after locking. + */ + void unlock() { +#ifndef __MINGW32__ +#ifdef __NO_SEMAPHORES__ m_mutex.unlock(); - } +#else + m_semaphore.release(); +#endif +#endif + } - explicit event(bool locked = false) { - if (locked) { - lock(); - } - } + explicit event(bool locked = false) +#ifndef __MINGW32__ +#ifndef __NO_SEMAPHORES__ + : + m_semaphore(locked ? 1 : 0) +#endif +#endif + { +#ifndef __MINGW32__ +#ifdef __NO_SEMAPHORES__ + if (locked) + m_mutex.lock(); +#endif +#endif + } - event(const event &other) = delete; + event(const event &other) = delete; - event(event &&other) = delete; + event(event &&other) = delete; - ~event() = default; + ~event() = default; - event &operator=(const event &other) = delete; + event &operator=(const event &other) = delete; + + event &operator=(event &&other) = delete; + }; - event &operator=(event &&other) = delete; - }; -} +} // namespace vkcv diff --git a/include/vkcv/EventFunctionTypes.hpp b/include/vkcv/EventFunctionTypes.hpp new file mode 100644 index 0000000000000000000000000000000000000000..12871cbce3b6d06c1849cdf3a90956a91dad724d --- /dev/null +++ b/include/vkcv/EventFunctionTypes.hpp @@ -0,0 +1,31 @@ +#pragma once +/** + * @authors Alexander Gauggel, Tobias Frisch + * @file vkcv/CommandRecordingFunctionTypes.hpp + * @brief Abstract function types to handle command recording for example. + */ + +#include <vulkan/vulkan.hpp> + +#include "Event.hpp" +#include "Handles.hpp" + +namespace vkcv { + + /** + * @brief Function to be called for recording a command buffer. + */ + typedef typename event_function<const vk::CommandBuffer &>::type RecordCommandFunction; + + /** + * @brief Function to be called after finishing a given process. + */ + typedef typename event_function<>::type FinishCommandFunction; + + /** + * @brief Function to be called each frame for every open window. + */ + typedef typename event_function<const WindowHandle &, double, double, uint32_t, uint32_t>::type + WindowFrameFunction; + +} // namespace vkcv \ No newline at end of file diff --git a/include/vkcv/FeatureManager.hpp b/include/vkcv/FeatureManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fa9f77befd4e36f6beef1a10cc95392463a0266e --- /dev/null +++ b/include/vkcv/FeatureManager.hpp @@ -0,0 +1,531 @@ +#pragma once +/** + * @authors Tobias Frisch, Artur Wasmut, Vanessa Karolek, Sebastian Gaida + * @file vkcv/FeatureManager.hpp + * @brief Class to manage feature support and extension usage. + */ + +#include <functional> +#include <unordered_set> +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "Logger.hpp" + +namespace vkcv { + + /** + * @brief Class to manage extension and feature requirements, support and usage. + */ + class FeatureManager { + private: + /** + * Physical device to check feature support against. + */ + vk::PhysicalDevice &m_physicalDevice; + + /** + * List of supported extensions. + */ + std::vector<const char*> m_supportedExtensions; + + /** + * List of activated extensions for usage. + */ + std::vector<const char*> m_activeExtensions; + + /** + * Feature structure chain to request activated features. + */ + vk::PhysicalDeviceFeatures2 m_featuresBase; + + /** + * List of base structures allocated to request extension specific features. + */ + std::vector<vk::BaseOutStructure*> m_featuresExtensions; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDevice16BitStorageFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDevice16BitStorageFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDevice8BitStorageFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDevice8BitStorageFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceBufferDeviceAddressFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceBufferDeviceAddressFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceDescriptorIndexingFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceDescriptorIndexingFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceHostQueryResetFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceHostQueryResetFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceImagelessFramebufferFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceImagelessFramebufferFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceMultiviewFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceMultiviewFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceProtectedMemoryFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceProtectedMemoryFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceSamplerYcbcrConversionFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceSamplerYcbcrConversionFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceScalarBlockLayoutFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceScalarBlockLayoutFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceShaderAtomicInt64Features. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceShaderAtomicInt64Features &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceShaderFloat16Int8Features. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceShaderFloat16Int8Features &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceTimelineSemaphoreFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceTimelineSemaphoreFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceUniformBufferStandardLayoutFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceUniformBufferStandardLayoutFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceVariablePointersFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceVariablePointersFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceVulkanMemoryModelFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceVulkanMemoryModelFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceMeshShaderFeaturesNV. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceMeshShaderFeaturesNV &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceVulkan12Features. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceVulkan12Features &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceVulkan11Features. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceVulkan11Features &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceAccelerationStructureFeaturesKHR. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceAccelerationStructureFeaturesKHR &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceRayTracingPipelineFeaturesKHR. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool + checkSupport(const vk::PhysicalDeviceRayTracingPipelineFeaturesKHR &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceVulkan13Features. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceVulkan13Features &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceCoherentMemoryFeaturesAMD. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceCoherentMemoryFeaturesAMD &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceSubgroupSizeControlFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceSubgroupSizeControlFeatures &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceIndexTypeUint8FeaturesEXT. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceIndexTypeUint8FeaturesEXT &features, + bool required) const; + + /** + * @brief Checks support of the @p vk::PhysicalDeviceShaderTerminateInvocationFeatures. + * + * @param[in] features The features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the @p features are supported, else @p false + */ + [[nodiscard]] bool checkSupport(const vk::PhysicalDeviceShaderTerminateInvocationFeatures &features, + bool required) const; + + /** + * @brief Searches for a base structure of a given structure type. + * + * @param[in] type Structure type + * @return Pointer to first matching base structure or nullptr + */ + [[nodiscard]] vk::BaseOutStructure* findFeatureStructure(vk::StructureType type) const; + + public: + /** + * @brief Constructor of a feature manager with a given physical device. + * + * @param[in,out] physicalDevice Physical device + */ + explicit FeatureManager(vk::PhysicalDevice &physicalDevice); + + FeatureManager(const FeatureManager &other) = delete; + + /** + * @brief Move-constructor of a feature manager. + * + * @param[in,out] other Other feature manager instance + */ + FeatureManager(FeatureManager &&other) noexcept; + + /** + * @brief Destructor of a feature manager. + */ + ~FeatureManager(); + + FeatureManager &operator=(const FeatureManager &other) = delete; + + /** + * @brief Move-operator of a feature manager. + * + * @param[in,out] other Other feature manager instance + * @return Reference to the feature manager itself + */ + FeatureManager &operator=(FeatureManager &&other) noexcept; + + /** + * @brief Check if a specific extension is supported by the managers physical device. + * + * @param[in] extension Extension identifier string + * @return @p True, if the @p extension is supported, else @p false + */ + [[nodiscard]] bool isExtensionSupported(const std::string &extension) const; + + /** + * @brief Activate a specific extension if supported by the managers physical device. + * + * @param[in] extension Extension identifier string + * @param[in] required True, if the @p extension is required, else false + * @return @p True, if the @p extension could be activated, else @p false + */ + bool useExtension(const std::string &extension, bool required = true); + + /** + * @brief Check if a specific extension is activated by the manager. + * + * @param[in] extension Extension identifier string + * @return @p True, if the @p extension is activated, else @p false + */ + [[nodiscard]] bool isExtensionActive(const std::string &extension) const; + + /** + * @brief Return list of activated extensions for usage. + * + * @return List of activated extensions + */ + [[nodiscard]] const std::vector<const char*> &getActiveExtensions() const; + + /** + * @brief Request specific features for optional or required usage ( only core Vulkan 1.0 ). + * + * @param[in] featureFunction Function or lambda to request specific features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the requested features could be activated, else @p false + */ + bool useFeatures(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction, + bool required = true); + + /** + * @brief Request specific features for optional or required usage. + * + * @tparam T Template parameter to use specific base structure types + * @param[in] featureFunction Function or lambda to request specific features + * @param[in] required True, if the @p features are required, else false + * @return @p True, if the requested features could be activated, else @p false + * @see checkSupport() + */ + template <typename T> + bool useFeatures(const std::function<void(T &)> &featureFunction, bool required = true) { + T features; + T* features_ptr = reinterpret_cast<T*>(findFeatureStructure(features.sType)); + + if (features_ptr) { + features = *features_ptr; + } + + featureFunction(features); + + if (!checkSupport(features, required)) { + if (required) { + vkcv_log_throw_error("Required feature is not supported!"); + } + + return false; + } + + if (features_ptr) { + *features_ptr = features; + return true; + } + + features_ptr = new T(features); + + if (m_featuresExtensions.empty()) { + m_featuresBase.setPNext(features_ptr); + } else { + m_featuresExtensions.back()->setPNext( + reinterpret_cast<vk::BaseOutStructure*>(features_ptr)); + } + + m_featuresExtensions.push_back(reinterpret_cast<vk::BaseOutStructure*>(features_ptr)); + + return true; + } + + /** + * @brief Return feature structure chain to request all activated features. + * + * @return Head of feature structure chain + */ + [[nodiscard]] const vk::PhysicalDeviceFeatures2 &getFeatures() const; + + /** + * @brief Checks all activated features for a specific feature and returns its state. + * + * @tparam T Template parameter to use specific base structure types + * @param[in] type Vulkan structure type identifier + * @param[in] featureTestFunction Function to test feature structure with for requested + * attributes + * @return True, if the requested attributes are available, else false + */ + template <typename T> + bool checkFeatures(vk::StructureType type, + const std::function<bool(const T &)> &featureTestFunction) const { + const auto* base = reinterpret_cast<const vk::BaseInStructure*>(&getFeatures()); + + while (base) { + if ((base->sType == type) + && (featureTestFunction(*reinterpret_cast<const T*>(base)))) { + return true; + } + + base = base->pNext; + } + + return false; + } + }; + +} // namespace vkcv diff --git a/include/vkcv/Features.hpp b/include/vkcv/Features.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8b39de4f8f1183664f163d8add7f135102dab631 --- /dev/null +++ b/include/vkcv/Features.hpp @@ -0,0 +1,203 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Features.hpp + * @brief Class to manage feature requests. + */ + +#include <functional> +#include <initializer_list> +#include <vector> + +#include "FeatureManager.hpp" + +namespace vkcv { + + /** + * Abstract function type to request a feature via the feature manager. + */ + typedef std::function<bool(FeatureManager &)> Feature; + + /** + * @brief Class to manage a list of feature requests at once. + */ + class Features { + private: + /** + * List of feature requests. + */ + std::vector<Feature> m_features; + + public: + /** + * @brief Constructor of a features instance. + */ + Features() = default; + + /** + * @brief Constructor of a features instance with a given list of extension identifier + * strings. + * + * @param[in] list List of extension identifier strings + */ + Features(const std::initializer_list<std::string> &list); + + /** + * @brief Copy-constructor of a features instance. + * + * @param[in] other Other features instance + */ + Features(const Features &other) = default; + + /** + * @brief Move-constructor of a features instance. + * + * @param[in,out] other Other features instance + */ + Features(Features &&other) = default; + + /** + * @brief Destructor of a features instance. + */ + ~Features() = default; + + /** + * @brief Copy-operator of a features instance. + * + * @param[in] other Other features instance + * @return Reference to the features instance itself + */ + Features &operator=(const Features &other) = default; + + /** + * @brief Move-operator of a features instance. + * + * @param[in,out] other Other features instance + * @return Reference to the features instance itself + */ + Features &operator=(Features &&other) = default; + + /** + * @brief Request a specific extension as required. + * + * @param[in] extension Extension identifier string + */ + void requireExtension(const std::string &extension); + + /** + * @brief Request a specific extension and some of its features as required ( only core + * Vulkan 1.0 ). + * + * @param[in] extension Extension identifier string + * @param[in] featureFunction + */ + void requireExtensionFeature( + const std::string &extension, + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction); + + /** + * @brief Request a specific extension and some of its features as required. + * + * @tparam T Template parameter to use specific base structure types + * @param[in] extension Extension identifier string + * @param[in] featureFunction Function or lambda to request specific features + */ + template <typename T> + void requireExtensionFeature(const std::string &extension, + const std::function<void(T &)> &featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager &featureManager) { + if (featureManager.useExtension(extension, true)) { + return featureManager.template useFeatures<T>(featureFunction, true); + } else { + return false; + } + }); + } + + /** + * @brief Request a specific set of features as required ( only core Vulkan 1.0 ). + * + * @param[in] featureFunction Function or lambda to request specific features + */ + void + requireFeature(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction); + + /** + * @brief Request a specific set of features as required. + * + * @tparam T Template parameter to use specific base structure types + * @param[in] featureFunction Function or lambda to request specific features + */ + template <typename T> + void requireFeature(const std::function<void(T &)> &featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager &featureManager) { + return featureManager.template useFeatures<T>(featureFunction, true); + }); + } + + /** + * @brief Request a specific extension as optional. + * + * @param[in] extension Extension identifier string + */ + void tryExtension(const std::string &extension); + + /** + * @brief Request a specific extension and some of its features as optional ( only core + * Vulkan 1.0 ). + * + * @param[in] extension Extension identifier string + * @param[in] featureFunction Function or lambda to request specific features + */ + void tryExtensionFeature( + const std::string &extension, + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction); + + /** + * @brief Request a specific extension and some of its features as optional. + * + * @tparam T Template parameter to use specific base structure types + * @param[in] extension Extension identifier string + * @param[in] featureFunction Function or lambda to request specific features + */ + template <typename T> + void tryExtensionFeature(const std::string &extension, + const std::function<void(T &)> &featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager &featureManager) { + if (featureManager.useExtension(extension, false)) { + return featureManager.template useFeatures<T>(featureFunction, false); + } else { + return false; + } + }); + } + + /** + * @brief Request a specific set of features as optional ( only core Vulkan 1.0 ). + * + * @param[in] featureFunction Function or lambda to request specific features + */ + void tryFeature(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction); + + /** + * @brief Request a specific set of features as optional. + * + * @tparam T Template parameter to use specific base structure types + * @param[in] featureFunction Function or lambda to request specific features + */ + template <typename T> + void tryFeature(const std::function<void(T &)> &featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager &featureManager) { + return featureManager.template useFeatures<T>(featureFunction, false); + }); + } + + /** + * @brief Return list of feature requests. + * + * @return List of feature requests + */ + [[nodiscard]] const std::vector<Feature> &getList() const; + }; + +} // namespace vkcv diff --git a/include/vkcv/File.hpp b/include/vkcv/File.hpp new file mode 100644 index 0000000000000000000000000000000000000000..51491b1b04a2a658f410e4872f7661237ca913b2 --- /dev/null +++ b/include/vkcv/File.hpp @@ -0,0 +1,26 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/File.hpp + * @brief Functions to handle generating temporary file paths. + */ + +#include <filesystem> + +namespace vkcv { + + /** + * @brief Generate a new temporary file path and return it. + * + * @return A unique path for a temporary file + */ + std::filesystem::path generateTemporaryFilePath(); + + /** + * @brief Generate a new temporary directory path and return it. + * + * @return A unique path for a temporary directory + */ + std::filesystem::path generateTemporaryDirectoryPath(); + +} // namespace vkcv diff --git a/include/vkcv/GraphicsPipelineConfig.hpp b/include/vkcv/GraphicsPipelineConfig.hpp new file mode 100644 index 0000000000000000000000000000000000000000..80084cb1408a71ebd2abe12ec12d6a395e9b6d49 --- /dev/null +++ b/include/vkcv/GraphicsPipelineConfig.hpp @@ -0,0 +1,144 @@ +#pragma once +/** + * @authors Mara Vogt, Mark Mints, Tobias Frisch + * @file vkcv/GraphicsPipelineConfig.hpp + * @brief Graphics pipeline config struct to hand over required information to pipeline creation. + */ + +#include <cstdint> +#include <vector> + +#include "Multisampling.hpp" +#include "PipelineConfig.hpp" +#include "VertexLayout.hpp" + +namespace vkcv { + + /** + * @brief Enum class to specify types of primitive topology. + */ + enum class PrimitiveTopology { + PointList, + LineList, + TriangleList, + PatchList + }; + + /** + * @brief Enum class to specify modes of culling. + */ + enum class CullMode { + None, + Front, + Back, + Both + }; + + /** + * @brief Enum class to specify depth-test modes. + */ + enum class DepthTest { + None, + Less, + LessEqual, + Greater, + GreatherEqual, + Equal + }; + + // add more as needed + // alternatively we could expose the blend factors directly + /** + * @brief Enum class to specify blending modes. + */ + enum class BlendMode { + None, + Additive + }; + + /** + * @brief Class to configure a graphics pipeline before its creation. + */ + class GraphicsPipelineConfig : public PipelineConfig { + private: + PassHandle m_PassHandle; + VertexLayout m_VertexLayout; + + uint32_t m_Width; + uint32_t m_Height; + + bool m_UseConservativeRasterization = false; + PrimitiveTopology m_PrimitiveTopology = PrimitiveTopology::TriangleList; + BlendMode m_blendMode = BlendMode::None; + bool m_EnableDepthClamping = false; + CullMode m_Culling = CullMode::None; + DepthTest m_DepthTest = DepthTest::LessEqual; + bool m_DepthWrite = true; + bool m_AlphaToCoverage = false; + uint32_t m_TessellationControlPoints = 0; + + public: + GraphicsPipelineConfig(); + + GraphicsPipelineConfig(const ShaderProgram &program, const PassHandle &pass, + const VertexLayout &vertexLayout, + const std::vector<DescriptorSetLayoutHandle> &layouts); + + GraphicsPipelineConfig(const GraphicsPipelineConfig &other) = default; + GraphicsPipelineConfig(GraphicsPipelineConfig &&other) = default; + + ~GraphicsPipelineConfig() = default; + + GraphicsPipelineConfig &operator=(const GraphicsPipelineConfig &other) = default; + GraphicsPipelineConfig &operator=(GraphicsPipelineConfig &&other) = default; + + [[nodiscard]] const PassHandle &getPass() const; + + [[nodiscard]] const VertexLayout &getVertexLayout() const; + + [[nodiscard]] uint32_t getWidth() const; + + [[nodiscard]] uint32_t getHeight() const; + + void setResolution(uint32_t width, uint32_t height); + + [[nodiscard]] bool isViewportDynamic() const; + + [[nodiscard]] bool isUsingConservativeRasterization() const; + + void setUsingConservativeRasterization(bool conservativeRasterization); + + [[nodiscard]] PrimitiveTopology getPrimitiveTopology() const; + + void setPrimitiveTopology(PrimitiveTopology primitiveTopology); + + [[nodiscard]] BlendMode getBlendMode() const; + + void setBlendMode(BlendMode blendMode); + + [[nodiscard]] bool isDepthClampingEnabled() const; + + void setDepthClampingEnabled(bool depthClamping); + + [[nodiscard]] CullMode getCulling() const; + + void setCulling(CullMode cullMode); + + [[nodiscard]] DepthTest getDepthTest() const; + + void setDepthTest(DepthTest depthTest); + + [[nodiscard]] bool isWritingDepth() const; + + void setWritingDepth(bool writingDepth); + + [[nodiscard]] bool isWritingAlphaToCoverage() const; + + void setWritingAlphaToCoverage(bool alphaToCoverage); + + [[nodiscard]] uint32_t getTesselationControlPoints() const; + + void setTesselationControlPoints(uint32_t tessellationControlPoints); + }; + +} // namespace vkcv \ No newline at end of file diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp index ea05bd212dd9314957e4771070bedb3963b1b2dc..3e92ee1557bac4be5b03f8b74cab1319c501d707 100644 --- a/include/vkcv/Handles.hpp +++ b/include/vkcv/Handles.hpp @@ -1,7 +1,7 @@ #pragma once /** - * @authors Artur Wasmut - * @file src/vkcv/Handles.cpp + * @authors Tobias Frisch, Alexander Gauggel, Artur Wasmut, Sebastian Gaida, Mark Mints + * @file vkcv/Handles.hpp * @brief Central header file for all possible handles that the framework will hand out. */ @@ -9,103 +9,214 @@ #include "Event.hpp" -namespace vkcv -{ - +namespace vkcv { + + /** + * @brief Function to be called when a handles resources can be destroyed. + */ typedef typename event_function<uint64_t>::type HandleDestroyFunction; - + + /** + * @brief Class for general memory management via handles. + */ class Handle { - friend std::ostream& operator << (std::ostream& out, const Handle& handle); - + friend std::ostream &operator<<(std::ostream &out, const Handle &handle); + private: uint64_t m_id; uint64_t* m_rc; - + HandleDestroyFunction m_destroy; - + protected: + /** + * @brief Constructor of an invalid handle + */ Handle(); - - explicit Handle(uint64_t id, const HandleDestroyFunction& destroy = nullptr); - + + /** + * @brief Constructor of a valid handle with an + * unique id and an optional destroy callback. + * + * @param[in] id Unique handle id + * @param[in] destroy Destroy callback (optional) + */ + explicit Handle(uint64_t id, const HandleDestroyFunction &destroy = nullptr); + /** - * Returns the actual handle id of a handle. + * @brief Returns the actual handle id of a handle. * * @return Handle id */ - [[nodiscard]] - uint64_t getId() const; - + [[nodiscard]] uint64_t getId() const; + /** - * Returns the reference counter of a handle + * @brief Returns the reference counter of a handle * * @return Reference counter */ - [[nodiscard]] - uint64_t getRC() const; - + [[nodiscard]] uint64_t getRC() const; + public: virtual ~Handle(); - - Handle(const Handle& other); - Handle(Handle&& other) noexcept; - - Handle& operator=(const Handle& other); - Handle& operator=(Handle&& other) noexcept; - + + Handle(const Handle &other); + Handle(Handle &&other) noexcept; + + Handle &operator=(const Handle &other); + Handle &operator=(Handle &&other) noexcept; + + /** + * @brief Returns whether a handle is valid to use. + * + * @return True, if the handle is valid, else false. + */ explicit operator bool() const; + + /** + * @brief Returns whether a handle is invalid to use. + * + * @return True, if the handle is invalid, else false. + */ bool operator!() const; - }; - - std::ostream& operator << (std::ostream& out, const Handle& handle); - - // Handle returned for any buffer created with the core/context objects - class BufferHandle : public Handle { - friend class BufferManager; + + /** + * @brief Stream operator to print a handle into an output + * stream. + * + * @param[out] out Output stream + * @param[in] handle + * @return Output stream after printing + */ + std::ostream &operator<<(std::ostream &out, const Handle &handle); + + /** + * @brief Handle class for buffers. + */ + class BufferHandle : public Handle { + friend class BufferManager; + private: using Handle::Handle; - }; - + }; + + /** + * @brief Handle class for render passes. + */ class PassHandle : public Handle { friend class PassManager; + private: using Handle::Handle; }; - - class PipelineHandle : public Handle { - friend class PipelineManager; + + /** + * @brief Handle class for graphics pipelines. + */ + class GraphicsPipelineHandle : public Handle { + friend class GraphicsPipelineManager; + private: using Handle::Handle; }; - + + /** + * @brief Handle class for compute pipelines. + */ + class ComputePipelineHandle : public Handle { + friend class ComputePipelineManager; + + private: + using Handle::Handle; + }; + + /** + * @brief Handle class for descriptor set layouts. + */ + class DescriptorSetLayoutHandle : public Handle { + friend class DescriptorSetLayoutManager; + + private: + using Handle::Handle; + }; + + /** + * @brief Handle class for descriptor sets. + */ class DescriptorSetHandle : public Handle { - friend class DescriptorManager; + friend class DescriptorSetManager; + private: using Handle::Handle; }; - + + /** + * @brief Handle class for samplers. + */ class SamplerHandle : public Handle { friend class SamplerManager; + private: using Handle::Handle; }; + /** + * @brief Handle class for images. + */ class ImageHandle : public Handle { friend class ImageManager; + + private: using Handle::Handle; + public: - [[nodiscard]] - bool isSwapchainImage() const; - - static ImageHandle createSwapchainImageHandle(const HandleDestroyFunction& destroy = nullptr); - + /** + * @brief Returns whether the handle represents an swapchain image. + * + * @return True, if the handle represents a swapchain image, else false. + */ + [[nodiscard]] bool isSwapchainImage() const; + + /** + * @brief Creates a valid image handle to represent a swapchain image + * using an optional destroy callback. + * + * @param[in] destroy Destroy callback (optional) + * @return New swapchain image handle + */ + static ImageHandle + createSwapchainImageHandle(const HandleDestroyFunction &destroy = nullptr); + }; + + /** + * @brief Handle class for windows. + */ + class WindowHandle : public Handle { + friend class WindowManager; + + private: + using Handle::Handle; + }; + + /** + * @brief Handle class for swapchains. + */ + class SwapchainHandle : public Handle { + friend class SwapchainManager; + + private: + using Handle::Handle; + }; + + /** + * @brief Handle class for command streams. + */ + class CommandStreamHandle : public Handle { + friend class CommandStreamManager; + + private: + using Handle::Handle; }; - class CommandStreamHandle : public Handle { - friend class CommandStreamManager; - private: - using Handle::Handle; - }; - -} +} // namespace vkcv diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp index eac96975886a92e0043bbb97cbe64f76943efa7e..c9c91db25fb4398439d609247d71ba6be3524464 100644 --- a/include/vkcv/Image.hpp +++ b/include/vkcv/Image.hpp @@ -1,64 +1,152 @@ #pragma once /** - * @authors Lars Hoerttrich - * @file vkcv/Buffer.hpp - * @brief class for image handles + * @authors Alexander Gauggel, Tobias Frisch, Lars Hoerttrich, Artur Wasmut + * @file vkcv/Image.hpp + * @brief Class for image handling. */ -#include "vulkan/vulkan.hpp" +#include <vulkan/vulkan.hpp> + +#include "BufferTypes.hpp" +#include "Core.hpp" #include "Handles.hpp" +#include "ImageConfig.hpp" +#include "Multisampling.hpp" namespace vkcv { - // forward declares - class ImageManager; + class Downsampler; + /** + * @brief Returns whether an image format is usable as depth buffer. + * + * @param format Vulkan image format + * @return True, if the format is valid to use as depth buffer, + * otherwise false. + */ bool isDepthFormat(const vk::Format format); + /** + * @brief Returns whether an image format is usable as stencil buffer. + * + * @param format Vulkan image format + * @return True, if the format is valid to use as stencil buffer, + * otherwise false. + */ + bool isStencilFormat(const vk::Format format); + + /** + * @brief Class for image handling and filling data. + */ class Image { friend class Core; + public: - [[nodiscard]] - vk::Format getFormat() const; - - [[nodiscard]] - uint32_t getWidth() const; - - [[nodiscard]] - uint32_t getHeight() const; - - [[nodiscard]] - uint32_t getDepth() const; + Image() : m_core(nullptr), m_handle() {}; + + Image(Core* core, const ImageHandle &handle) : m_core(core), m_handle(handle) {} + + Image(const Image &other) = default; + Image(Image &&other) = default; + + ~Image() = default; + + Image &operator=(const Image &other) = default; + Image &operator=(Image &&other) = default; + + /** + * @brief Returns the format of the image. + * + * @return Vulkan image format + */ + [[nodiscard]] vk::Format getFormat() const; - [[nodiscard]] - vkcv::ImageHandle getHandle() const; + /** + * @brief Returns the width of the image. + * + * @return Width of the image + */ + [[nodiscard]] uint32_t getWidth() const; - [[nodiscard]] - uint32_t getMipCount() const; + /** + * @brief Returns the height of the image. + * + * @return Height of the image + */ + [[nodiscard]] uint32_t getHeight() const; + /** + * @brief Returns the depth of the image. + * + * @return Depth of the image + */ + [[nodiscard]] uint32_t getDepth() const; + + /** + * @brief Returns the image handle of the image. + * + * @return Handle of the image + */ + [[nodiscard]] const vkcv::ImageHandle &getHandle() const; + + /** + * @brief Returns the amount of mip levels of the image. + * + * @return Number of mip levels + */ + [[nodiscard]] uint32_t getMipLevels() const; + + /** + * @brief Switches the image layout, + * returns after operation is finished. + * + * @param[in] newLayout Layout that image is switched to + */ void switchLayout(vk::ImageLayout newLayout); - - void fill(void* data, size_t size = SIZE_MAX); - void generateMipChainImmediate(); - void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream); - private: - // TODO: const qualifier removed, very hacky!!! - // Else you cannot recreate an image. Pls fix. - ImageManager* m_manager; - ImageHandle m_handle; - Image(ImageManager* manager, const ImageHandle& handle); - - static Image create( - ImageManager* manager, - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment); + /** + * @brief Fills the image with data of a given size in bytes. + * + * @param[in] data Pointer to the source data + * @param[in] size Lower limit of the data size to copy in bytes, + * the actual number of copied bytes is min(size, imageDataSize) + */ + void fill(const void* data, size_t size = SIZE_MAX); + /** + * @brief Fills a specific image layer with data of a given + * size in bytes. + * + * @param[in] layer Image layer destination + * @param[in] data Pointer to the source data + * @param[in] size Lower limit of the data size to copy in bytes, + * the actual number of copied bytes is min(size, imageDataSize) + */ + void fillLayer(uint32_t layer, const void* data, size_t size = SIZE_MAX); + + /** + * @brief Records mip chain generation to command stream, + * mip level zero is used as source + * + * @param[out] cmdStream Command stream that the commands are recorded into + * @param[in,out] downsampler Downsampler to generate mip levels with + */ + void recordMipChainGeneration(const vkcv::CommandStreamHandle &cmdStream, + Downsampler &downsampler); + + private: + Core* m_core; + ImageHandle m_handle; }; + + Image image(Core &core, vk::Format format, uint32_t width, uint32_t height, uint32_t depth = 1, + bool createMipChain = false, bool supportStorage = false, + bool supportColorAttachment = false, + Multisampling multisampling = Multisampling::None); -} + Image image(Core &core, + vk::Format format, + const ImageConfig &config, + bool createMipChain = false); + +} // namespace vkcv diff --git a/include/vkcv/ImageConfig.hpp b/include/vkcv/ImageConfig.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5f495f39871335b602a4b8a087968c4085dc2518 --- /dev/null +++ b/include/vkcv/ImageConfig.hpp @@ -0,0 +1,163 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/ImageConfig.hpp + * @brief Structure for image configuration. + */ + +#include "Multisampling.hpp" + +namespace vkcv { + + /** + * @brief Structure to configure image before its creation + */ + struct ImageConfig { + private: + uint32_t m_width; + uint32_t m_height; + uint32_t m_depth; + + bool m_supportStorage; + bool m_supportColorAttachment; + bool m_cubeMapImage; + + Multisampling m_msaa; + + public: + /** + * Constructor of the image configuration by + * a given resolution. + * + * @param[in] width Image width + * @param[in] height Image height + * @param[in] depth Image depth + */ + ImageConfig(uint32_t width, + uint32_t height, + uint32_t depth = 1); + + ImageConfig(const ImageConfig &other) = default; + ImageConfig(ImageConfig&& other) = default; + + ~ImageConfig() = default; + + ImageConfig& operator=(const ImageConfig &other) = default; + ImageConfig& operator=(ImageConfig&& other) = default; + + /** + * Return the configured width of the image. + * + * @return Image width + */ + [[nodiscard]] + uint32_t getWidth() const; + + /** + * Set configured width of the image. + * + * @param[in] width Image width + */ + void setWidth(uint32_t width); + + /** + * Return the configured height of the image. + * + * @return Image height + */ + [[nodiscard]] + uint32_t getHeight() const; + + /** + * Set configured height of the image. + * + * @param[in] height Image height + */ + void setHeight(uint32_t height); + + /** + * Return the configured depth of the image. + * + * @return Image depth + */ + [[nodiscard]] + uint32_t getDepth() const; + + /** + * Set configured depth of the image. + * + * @param[in] depth Image depth + */ + void setDepth(uint32_t depth); + + /** + * Return whether the image is configured to + * support storage operations. + * + * @return True, if it supports storage, otherwise false + */ + [[nodiscard]] + bool isSupportingStorage() const; + + /** + * Set whether the image is configured to + * support storage operations. + * + * @param[in] supportStorage Support storage + */ + void setSupportingStorage(bool supportStorage); + + /** + * Return whether the image is configured to + * support being used as color attachment. + * + * @return True, if it supports color attachment, otherwise false + */ + [[nodiscard]] + bool isSupportingColorAttachment() const; + + /** + * Set whether the image is configured to + * support being used as color attachment. + * + * @param[in] supportColorAttachment Support color attachment + */ + void setSupportingColorAttachment(bool supportColorAttachment); + + /** + * Return whether the image is configured to + * be a cube map. + * + * @return True, if the image is a cube map, otherwise false + */ + [[nodiscard]] + bool isCubeMapImage() const; + + /** + * Set whether the image is configured to + * be a cube map. + * + * @param[in] cubeMapImage Is cube map image + */ + void setCubeMapImage(bool cubeMapImage); + + /** + * Return type of multisampling the image + * is configured to use. + * + * @return Multisampling + */ + [[nodiscard]] + Multisampling getMultisampling() const; + + /** + * Set the multisampling of the image + * configuration. + * + * @param[in] msaa Multisampling + */ + void setMultisampling(Multisampling msaa); + + }; + +} diff --git a/include/vkcv/Logger.hpp b/include/vkcv/Logger.hpp index d484711f642506926b1281a830fb2c9caf8240a2..8a366b265bfb029ccc0f379b8e193834c54343df 100644 --- a/include/vkcv/Logger.hpp +++ b/include/vkcv/Logger.hpp @@ -1,37 +1,63 @@ #pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Logger.hpp + * @brief Logging macro function to print line of code specific information. + */ -#include <stdio.h> +#include <cstdio> +#include <exception> namespace vkcv { - + + /** + * @brief Enum class to specify the level of logging. + */ enum class LogLevel { + RAW_INFO, INFO, WARNING, ERROR }; - + + /** + * @brief Return the fitting output stream to print messages + * of a given level of logging. + * + * @param[in] level Level of logging + * @return Output stream (stdout or stderr) + */ constexpr auto getLogOutput(LogLevel level) { switch (level) { - case LogLevel::INFO: - return stdout; - default: - return stderr; + case LogLevel::RAW_INFO: + case LogLevel::INFO: + return stdout; + default: + return stderr; } } - + + /** + * @brief Returns the fitting identifier for messages of + * a given level of logging. + * + * @param[in] level Level of logging + * @return Identifier of the given level of logging + */ constexpr const char* getLogName(LogLevel level) { switch (level) { - case LogLevel::INFO: - return "INFO"; - case LogLevel::WARNING: - return "WARNING"; - case LogLevel::ERROR: - return "ERROR"; - default: - return "UNKNOWN"; + case LogLevel::RAW_INFO: + case LogLevel::INFO: + return "INFO"; + case LogLevel::WARNING: + return "WARNING"; + case LogLevel::ERROR: + return "ERROR"; + default: + return "UNKNOWN"; } } - + #ifndef NDEBUG #ifndef VKCV_DEBUG_MESSAGE_LEN #define VKCV_DEBUG_MESSAGE_LEN 1024 @@ -41,28 +67,69 @@ namespace vkcv { #define __PRETTY_FUNCTION__ __FUNCSIG__ #endif -#define vkcv_log(level, ...) { \ - char output_message [ \ - VKCV_DEBUG_MESSAGE_LEN \ - ]; \ - snprintf( \ - output_message, \ - VKCV_DEBUG_MESSAGE_LEN, \ - __VA_ARGS__ \ - ); \ - fprintf( \ - getLogOutput(level), \ - "[%s]: %s [%s, line %d: %s]\n", \ - vkcv::getLogName(level), \ - output_message, \ - __FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__ \ - ); \ -} +/** + * @brief Macro-function to log formatting messages with + * a specific level of logging. + * + * @param[in] level Level of logging + */ +#define vkcv_log(level, ...) \ + { \ + char output_message [VKCV_DEBUG_MESSAGE_LEN]; \ + snprintf(output_message, VKCV_DEBUG_MESSAGE_LEN, __VA_ARGS__); \ + auto output = getLogOutput(level); \ + if (level != vkcv::LogLevel::RAW_INFO) { \ + fprintf(output, "[%s]: %s [%s, line %d: %s]\n", vkcv::getLogName(level), \ + output_message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ + } else { \ + fprintf(output, "[%s]: %s\n", vkcv::getLogName(level), output_message); \ + } \ + fflush(output); \ + } #else -#define vkcv_log(level, ...) {} +/** + * @brief Macro-function to log formatting messages with + * a specific level of logging. + * + * @param[in] level Level of logging + */ +#define vkcv_log(level, ...) \ + {} #endif -} +/** + * @brief Macro-function to log the message of any error + * or an exception. + * + * @param[in] error Error or exception + */ +#define vkcv_log_error(error) \ + { vkcv_log(LogLevel::ERROR, "%s", (error).what()); } + +/** + * @brief Macro-function to throw and log any error or + * an exception. + * + * @param[in] error Error or exception + */ +#define vkcv_log_throw(error) \ + { \ + try { \ + throw error; \ + } catch (const std::exception &e) { \ + vkcv_log_error(e); \ + throw; \ + } \ + } + +/** + * @brief Macro-function to throw and log an error + * with its custom message. + * + * @param[in] message Error message + */ +#define vkcv_log_throw_error(message) \ + { vkcv_log_throw(std::runtime_error(message)); } + +} // namespace vkcv diff --git a/include/vkcv/Multisampling.hpp b/include/vkcv/Multisampling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5b285d191875d4e530a6d694241961bf6b19f781 --- /dev/null +++ b/include/vkcv/Multisampling.hpp @@ -0,0 +1,37 @@ +#pragma once +/** + * @authors Alexander Gauggel, Tobias Frisch + * @file vkcv/ImageConfig.hpp + * @brief File to provide functions supporting the use of multisampling. + */ + +#include <vulkan/vulkan.hpp> + +namespace vkcv { + + enum class Multisampling { + None, + MSAA2X, + MSAA4X, + MSAA8X + }; + + /** + * @brief Returns the sample count flag bits of a given + * multi-sample anti-aliasing mode. + * + * @param[in] msaa MSAA mode + * @return Sample count flag bits + */ + vk::SampleCountFlagBits msaaToSampleCountFlagBits(Multisampling msaa); + + /** + * @brief Returns the amount of samples of a given + * multi-sample anti-aliasing mode. + * + * @param msaa MSAA mode + * @return Number of samples + */ + uint32_t msaaToSampleCount(Multisampling msaa); + +} // namespace vkcv diff --git a/include/vkcv/Pass.hpp b/include/vkcv/Pass.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c5299d391c0c3760441a15740a68719d6e480bd4 --- /dev/null +++ b/include/vkcv/Pass.hpp @@ -0,0 +1,24 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Pass.hpp + * @brief Support functions for basic pass creation. + */ + +#include "Core.hpp" +#include "Handles.hpp" +#include "PassConfig.hpp" + +namespace vkcv { + + PassHandle passFormats(Core &core, const std::vector<vk::Format> &formats, bool clear = true, + Multisampling multisampling = Multisampling::None); + + PassHandle passFormat(Core &core, vk::Format format, bool clear = true, + Multisampling multisampling = Multisampling::None); + + PassHandle passSwapchain(Core &core, const SwapchainHandle &swapchain, + const std::vector<vk::Format> &formats, bool clear = true, + Multisampling multisampling = Multisampling::None); + +} // namespace vkcv diff --git a/include/vkcv/PassConfig.hpp b/include/vkcv/PassConfig.hpp index 8f3b516d4b4451c513366fbd8469908bccde6a5f..efe741d07fd731a2c71fd6bf706cda7e39d43a50 100644 --- a/include/vkcv/PassConfig.hpp +++ b/include/vkcv/PassConfig.hpp @@ -1,51 +1,94 @@ #pragma once +/** + * @authors Alexander Gauggel, Artur Wasmut, Tobias Frisch + * @file vkcv/PassConfig.hpp + * @brief Enums and structures to handle render pass configuration. + */ #include <vector> #include <vulkan/vulkan.hpp> -namespace vkcv -{ - enum class AttachmentLayout - { - UNDEFINED, - GENERAL, - - COLOR_ATTACHMENT, - SHADER_READ_ONLY, - - DEPTH_STENCIL_ATTACHMENT, - DEPTH_STENCIL_READ_ONLY, - - TRANSFER_SRC, - TRANSFER_DST, - - PRESENTATION - }; - - enum class AttachmentOperation - { - LOAD, - CLEAR, - STORE, - DONT_CARE - }; - - struct AttachmentDescription - { - AttachmentDescription( - AttachmentOperation store_op, - AttachmentOperation load_op, - vk::Format format) noexcept; - - AttachmentOperation store_operation; - AttachmentOperation load_operation; - - vk::Format format; - }; - - struct PassConfig - { - explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept; - std::vector<AttachmentDescription> attachments{}; - }; -} \ No newline at end of file +#include "Multisampling.hpp" + +namespace vkcv { + + /** + * @brief Enum class to specify types of attachment operations. + */ + enum class AttachmentOperation { + LOAD, + CLEAR, + STORE, + DONT_CARE + }; + + /** + * @brief Class to store details about an attachment of a pass. + */ + class AttachmentDescription { + private: + vk::Format m_format; + + AttachmentOperation m_load_op; + AttachmentOperation m_store_op; + + vk::ClearValue m_clear_value; + + public: + AttachmentDescription(vk::Format format, AttachmentOperation load, + AttachmentOperation store); + + AttachmentDescription(vk::Format format, AttachmentOperation load, + AttachmentOperation store, const vk::ClearValue &clear); + + AttachmentDescription(const AttachmentDescription &other) = default; + AttachmentDescription(AttachmentDescription &&other) = default; + + ~AttachmentDescription() = default; + + AttachmentDescription &operator=(const AttachmentDescription &other) = default; + AttachmentDescription &operator=(AttachmentDescription &&other) = default; + + [[nodiscard]] vk::Format getFormat() const; + + [[nodiscard]] AttachmentOperation getLoadOperation() const; + + [[nodiscard]] AttachmentOperation getStoreOperation() const; + + void setClearValue(const vk::ClearValue &clear); + + [[nodiscard]] const vk::ClearValue &getClearValue() const; + }; + + using AttachmentDescriptions = std::vector<AttachmentDescription>; + + /** + * @brief Class to configure a pass for usage. + */ + class PassConfig { + private: + AttachmentDescriptions m_attachments; + Multisampling m_multisampling; + + public: + PassConfig(); + + explicit PassConfig(const AttachmentDescriptions &attachments, + Multisampling multisampling = Multisampling::None); + + PassConfig(const PassConfig &other) = default; + PassConfig(PassConfig &&other) = default; + + ~PassConfig() = default; + + PassConfig &operator=(const PassConfig &other) = default; + PassConfig &operator=(PassConfig &&other) = default; + + [[nodiscard]] const AttachmentDescriptions &getAttachments() const; + + void setMultisampling(Multisampling multisampling); + + [[nodiscard]] Multisampling getMultisampling() const; + }; + +} // namespace vkcv \ No newline at end of file diff --git a/include/vkcv/PipelineConfig.hpp b/include/vkcv/PipelineConfig.hpp index 1e00c5209118469a7dc6ff1eb1e3c98a3c6903a7..575f17057ed1f0a8588fcf141a11a0e539aca178 100644 --- a/include/vkcv/PipelineConfig.hpp +++ b/include/vkcv/PipelineConfig.hpp @@ -1,30 +1,48 @@ #pragma once /** - * @authors Mara Vogt, Mark Mints - * @file src/vkcv/Pipeline.hpp - * @brief Pipeline class to handle shader stages + * @authors Tobias Frisch + * @file vkcv/PipelineConfig.hpp + * @brief Pipeline config class to hand over required information to pipeline creation */ #include <vector> -#include <cstdint> + #include "Handles.hpp" #include "ShaderProgram.hpp" -#include "VertexLayout.hpp" namespace vkcv { - enum class PrimitiveTopology{PointList, LineList, TriangleList }; - - struct PipelineConfig { - ShaderProgram m_ShaderProgram; - uint32_t m_Width; - uint32_t m_Height; - PassHandle m_PassHandle; - VertexLayout m_VertexLayout; - std::vector<vk::DescriptorSetLayout> m_DescriptorLayouts; - bool m_UseDynamicViewport; - bool m_UseConservativeRasterization = false; - PrimitiveTopology m_PrimitiveTopology = PrimitiveTopology::TriangleList; - }; - -} \ No newline at end of file + /** + * @brief Class to configure a general pipeline before its creation. + */ + class PipelineConfig { + private: + ShaderProgram m_ShaderProgram; + std::vector<DescriptorSetLayoutHandle> m_DescriptorSetLayouts; + + public: + PipelineConfig(); + + PipelineConfig(const ShaderProgram &program, + const std::vector<DescriptorSetLayoutHandle> &layouts); + + PipelineConfig(const PipelineConfig &other) = default; + PipelineConfig(PipelineConfig &&other) = default; + + ~PipelineConfig() = default; + + PipelineConfig &operator=(const PipelineConfig &other) = default; + PipelineConfig &operator=(PipelineConfig &&other) = default; + + void setShaderProgram(const ShaderProgram &program); + + [[nodiscard]] const ShaderProgram &getShaderProgram() const; + + void addDescriptorSetLayout(const DescriptorSetLayoutHandle &layout); + + void addDescriptorSetLayouts(const std::vector<DescriptorSetLayoutHandle> &layouts); + + [[nodiscard]] const std::vector<DescriptorSetLayoutHandle> &getDescriptorSetLayouts() const; + }; + +} // namespace vkcv diff --git a/include/vkcv/PushConstants.hpp b/include/vkcv/PushConstants.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4c577acbe09cd6b19d82213526bc4b7d498a6ede --- /dev/null +++ b/include/vkcv/PushConstants.hpp @@ -0,0 +1,157 @@ +#pragma once +/** + * @authors Tobias Frisch, Alexander Gauggel + * @file vkcv/PushConstants.hpp + * @brief Class to manage push constants for pipeline recording. + */ + +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "Logger.hpp" +#include "TypeGuard.hpp" + +namespace vkcv { + + /** + * @brief Class to handle push constants data per drawcall. + */ + class PushConstants final { + private: + TypeGuard m_typeGuard; + std::vector<uint8_t> m_data; + + public: + explicit PushConstants(size_t sizePerDrawcall); + explicit PushConstants(const TypeGuard &guard); + + PushConstants(const PushConstants &other) = default; + PushConstants(PushConstants &&other) = default; + + ~PushConstants() = default; + + PushConstants &operator=(const PushConstants &other) = default; + PushConstants &operator=(PushConstants &&other) = default; + + /** + * @brief Returns the size of the data that is bound + * per drawcall in bytes. + * + * @return Size of data per drawcall + */ + [[nodiscard]] size_t getSizePerDrawcall() const; + + /** + * @brief Returns the size of total data stored for + * push constants in bytes + * + * @return Total size of data + */ + [[nodiscard]] size_t getFullSize() const; + + /** + * @brief Returns the number of drawcalls that data + * is stored for. + * + * @return Number of drawcalls + */ + [[nodiscard]] size_t getDrawcallCount() const; + + /** + * @brief Clears the data for all drawcalls currently. + * stored. + */ + void clear(); + + /** + * @brief Appends data for a single drawcall to the + * storage with a given type. + * + * @tparam T Type of data (must match the size per drawcall) + * @param[in] value Data to append + * @return True, if operation was successfull, otherwise false + */ + template <typename T = uint8_t> + bool appendDrawcall(const T &value) { + if (!m_typeGuard.template check<T>()) { + return false; + } + + const size_t offset = m_data.size(); + m_data.resize(offset + sizeof(value)); + std::memcpy(m_data.data() + offset, &value, sizeof(value)); + return true; + } + + /** + * @brief Returns the data of the drawcall by a given index + * as reference. + * + * @tparam T Type of data + * @param[in] index Index of the drawcall + * @return Drawcall data + */ + template <typename T = uint8_t> + T &getDrawcall(size_t index) { + const size_t offset = (index * getSizePerDrawcall()); + return *reinterpret_cast<T*>(m_data.data() + offset); + } + + /** + * @brief Returns the data of the drawcall by a given index + * as const reference. + * + * @tparam T Type of data + * @param[in] index Index of the drawcall + * @return Drawcall data + */ + template <typename T = uint8_t> + const T &getDrawcall(size_t index) const { + const size_t offset = (index * getSizePerDrawcall()); + return *reinterpret_cast<const T*>(m_data.data() + offset); + } + + /** + * @brief Returns the data of the drawcall by a given index + * as a pointer. + * + * @param[in] index Index of the drawcall + * @return Drawcall data + */ + [[nodiscard]] const void* getDrawcallData(size_t index) const; + + /** + * @brief Returns the pointer to the entire drawcall data which + * might be nullptr if the data is empty. + * + * @return Pointer to the data + */ + [[nodiscard]] const void* getData() const; + }; + + template <typename T> + PushConstants pushConstants() { + return PushConstants(typeGuard<T>()); + } + + template <typename T> + PushConstants pushConstants(const T &value) { + auto pc = pushConstants<T>(); + pc.template appendDrawcall<T>(value); + return pc; + } + + template <typename T> + PushConstants pushConstants(const std::vector<T> &values) { + auto pc = pushConstants<T>(); + + for (const T &value : values) { + if (!(pc.template appendDrawcall<T>(value))) { + break; + } + } + + return pc; + } + +} // namespace vkcv diff --git a/include/vkcv/QueueManager.hpp b/include/vkcv/QueueManager.hpp index ac043b2d351014ea79fcae0d0fc439bb64a87b72..5bdf64dd2ac10f9cbfb4a03d58a3ed27d827a755 100644 --- a/include/vkcv/QueueManager.hpp +++ b/include/vkcv/QueueManager.hpp @@ -1,51 +1,102 @@ #pragma once +/** + * @authors Sebastian Gaida, Tobias Frisch, Alexander Gauggel + * @file vkcv/QueueManager.hpp + * @brief Types to manage queues of a device. + */ + #include <vulkan/vulkan.hpp> namespace vkcv { - enum class QueueType { Compute, Transfer, Graphics, Present }; + /** + * @brief Enum class to represent types of queues. + */ + enum class QueueType { + Compute, + Transfer, + Graphics, + Present + }; + /** + * @brief Structure to represent a queue and its details. + */ struct Queue { int familyIndex; int queueIndex; - + vk::Queue handle; }; - + + /** + * @brief Class to manage queues of a device. + */ class QueueManager { public: + /** + * @brief Creates a queue manager with the given pairs of queues. + * + * @param[in,out] device Vulkan device that holds the queues + * @param[in] queuePairsGraphics Graphic queue pairs of queueFamily and queueIndex + * @param[in] queuePairsCompute Compute queue pairs of queueFamily and queueIndex + * @param[in] queuePairsTransfer Transfer queue pairs of queueFamily and queueIndex + * @return New queue manager with the specified queue pairs + */ static QueueManager create(vk::Device device, - std::vector<std::pair<int, int>> &queuePairsGraphics, - std::vector<std::pair<int, int>> &queuePairsCompute, - std::vector<std::pair<int, int>> &queuePairsTransfer); - - [[nodiscard]] - const Queue &getPresentQueue() const; - - [[nodiscard]] - const std::vector<Queue> &getGraphicsQueues() const; - - [[nodiscard]] - const std::vector<Queue> &getComputeQueues() const; - - [[nodiscard]] - const std::vector<Queue> &getTransferQueues() const; - - static void queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice, - std::vector<float> &queuePriorities, - std::vector<vk::QueueFlagBits> &queueFlags, - std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos, - std::vector<std::pair<int, int>> &queuePairsGraphics, - std::vector<std::pair<int, int>> &queuePairsCompute, - std::vector<std::pair<int, int>> &queuePairsTransfer); - - private: - std::vector<Queue> m_graphicsQueues; - std::vector<Queue> m_computeQueues; - std::vector<Queue> m_transferQueues; - + const std::vector<std::pair<int, int>> &queuePairsGraphics, + const std::vector<std::pair<int, int>> &queuePairsCompute, + const std::vector<std::pair<int, int>> &queuePairsTransfer); + + /** + * @brief Returns the default queue with present support. + * Recommended to use the present queue in the swapchain. + * + * @return Default present queue + */ + [[nodiscard]] const Queue &getPresentQueue() const; + + /** + * @brief Returns all queues with the graphics flag. + * + * @return Vector of graphics queues + */ + [[nodiscard]] const std::vector<Queue> &getGraphicsQueues() const; + + /** + * @brief Returns all queues with the compute flag. + * + * @return Vector of compute queues + */ + [[nodiscard]] const std::vector<Queue> &getComputeQueues() const; + + /** + * @brief Returns all queues with the transfer flag. + * + * @return Vector of transfer queues + */ + [[nodiscard]] const std::vector<Queue> &getTransferQueues() const; + + /** + * @brief Checks for presenting support of a given surface + * in the queues and returns the queue family index of the + * supporting queue. + * + * @param[in] physicalDevice Vulkan physical device + * @param[in] surface Surface + * @return Queue family index of the supporting present queue + */ + static uint32_t checkSurfaceSupport(const vk::PhysicalDevice &physicalDevice, + const vk::SurfaceKHR &surface); + + private: + std::vector<Queue> m_graphicsQueues; + std::vector<Queue> m_computeQueues; + std::vector<Queue> m_transferQueues; + size_t m_presentIndex; - QueueManager(std::vector<Queue>&& graphicsQueues, std::vector<Queue>&& computeQueues, std::vector<Queue>&& transferQueues, size_t presentIndex); + QueueManager(std::vector<Queue> &&graphicsQueues, std::vector<Queue> &&computeQueues, + std::vector<Queue> &&transferQueues, size_t presentIndex); }; -} +} // namespace vkcv diff --git a/include/vkcv/Result.hpp b/include/vkcv/Result.hpp index b78e444de9040f2982122d9242f584c7f9c340cf..7a11031913cd4c0604f4023f5e8c5d9be6abba2f 100644 --- a/include/vkcv/Result.hpp +++ b/include/vkcv/Result.hpp @@ -1,12 +1,18 @@ #pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Result.hpp + * @brief Enum to represent result values of function which can fail. + */ namespace vkcv { - + + /** + * @brief Enum class to specify the result of a function call. + */ enum class Result { - SUCCESS = 0, ERROR = 1 - }; - -} + +} // namespace vkcv diff --git a/include/vkcv/Sampler.hpp b/include/vkcv/Sampler.hpp index 007ed5ae4737275ddacbc5881a2c4c202b8806a4..c4dc00e6e6178edeb8e056ac21838a268d60a66c 100644 --- a/include/vkcv/Sampler.hpp +++ b/include/vkcv/Sampler.hpp @@ -1,22 +1,18 @@ #pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Sampler.hpp + * @brief Support functions for basic sampler creation. + */ + +#include "Core.hpp" +#include "Handles.hpp" +#include "SamplerTypes.hpp" namespace vkcv { - enum class SamplerFilterType { - NEAREST = 1, - LINEAR = 2 - }; - - enum class SamplerMipmapMode { - NEAREST = 1, - LINEAR = 2 - }; - - enum class SamplerAddressMode { - REPEAT = 1, - MIRRORED_REPEAT = 2, - CLAMP_TO_EDGE = 3, - MIRROR_CLAMP_TO_EDGE = 4 - }; + [[nodiscard]] SamplerHandle samplerLinear(Core &core, bool clampToEdge = false); + + [[nodiscard]] SamplerHandle samplerNearest(Core &core, bool clampToEdge = false); -} +} // namespace vkcv diff --git a/include/vkcv/SamplerTypes.hpp b/include/vkcv/SamplerTypes.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2c64da27acb6c5a2b689ef56e242303917f2f03a --- /dev/null +++ b/include/vkcv/SamplerTypes.hpp @@ -0,0 +1,51 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/SamplerTypes.hpp + * @brief Enums for different sampler attributes. + */ + +namespace vkcv { + + /** + * @brief Enum class to specify a samplers type to filter during access. + */ + enum class SamplerFilterType { + NEAREST = 1, + LINEAR = 2 + }; + + /** + * @brief Enum class to specify a samplers mode to access mipmaps. + */ + enum class SamplerMipmapMode { + NEAREST = 1, + LINEAR = 2 + }; + + /** + * @brief Enum class to specify a samplers mode to access via address space. + */ + enum class SamplerAddressMode { + REPEAT = 1, + MIRRORED_REPEAT = 2, + CLAMP_TO_EDGE = 3, + MIRROR_CLAMP_TO_EDGE = 4, + CLAMP_TO_BORDER = 5 + }; + + /** + * @brief Enum class to specify a samplers color beyond a textures border. + */ + enum class SamplerBorderColor { + INT_ZERO_OPAQUE = 1, + INT_ZERO_TRANSPARENT = 2, + + FLOAT_ZERO_OPAQUE = 3, + FLOAT_ZERO_TRANSPARENT = 4, + + INT_ONE_OPAQUE = 5, + FLOAT_ONE_OPAQUE = 6 + }; + +} // namespace vkcv diff --git a/include/vkcv/ShaderProgram.hpp b/include/vkcv/ShaderProgram.hpp index 78b1f02169fe630427b9f66150e32078d42b7b3f..11156fdb5111da53df53ba7cbe80fa130d45b060 100644 --- a/include/vkcv/ShaderProgram.hpp +++ b/include/vkcv/ShaderProgram.hpp @@ -1,71 +1,105 @@ #pragma once /** - * @authors Simeon Hermann, Leonie Franken - * @file src/vkcv/ShaderProgram.hpp - * @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline + * @authors Artur Wasmut, Leonie Franken, Tobias Frisch, Simeon Hermann, Alexander Gauggel, Mark + * Mints + * @file vkcv/ShaderProgram.hpp + * @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline. */ -#include <unordered_map> -#include <fstream> -#include <iostream> #include <algorithm> #include <filesystem> -#include <vulkan/vulkan.hpp> +#include <fstream> +#include <iostream> #include <spirv_cross.hpp> -#include "VertexLayout.hpp" +#include <unordered_map> +#include <vulkan/vulkan.hpp> + +#include "DescriptorBinding.hpp" #include "ShaderStage.hpp" -#include "DescriptorConfig.hpp" +#include "VertexLayout.hpp" namespace vkcv { - struct Shader - { - std::vector<char> shaderCode; - ShaderStage shaderStage; - }; + /** + * @brief Class to manage and reflect shaders as a program. + */ + class ShaderProgram { + public: + ShaderProgram() noexcept; // ctor + ~ShaderProgram() = default; // dtor - class ShaderProgram - { - public: - ShaderProgram() noexcept; // ctor - ~ShaderProgram() = default; // dtor + /** + * @brief Adds a shader into the shader program. + * The shader is only added if the shader program does not contain + * the particular shader stage already. + * Contains: + * (1) reading the SPIR-V file, + * (2) creating a shader module, + * (3) creating a shader stage, + * (4) adding to the shader stage list, + * (5) destroying of the shader module + * + * @param[in] stage The stage of the shader + * @param[in] path Path to the SPIR-V shader file + */ + bool addShader(ShaderStage stage, const std::filesystem::path &path); - /** - * Adds a shader into the shader program. - * The shader is only added if the shader program does not contain the particular shader stage already. - * Contains: (1) reading of the code, (2) creation of a shader module, (3) creation of a shader stage, (4) adding to the shader stage list, (5) destroying of the shader module - * @param[in] flag that signals the respective shaderStage (e.g. VK_SHADER_STAGE_VERTEX_BIT) - * @param[in] relative path to the shader code (e.g. "../../../../../shaders/vert.spv") - */ - bool addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath); + /** + * @brief Returns the shader binary of a specified stage from the program. + * Needed for the transfer to the pipeline. + * + * @param[in] stage The stage of the shader + * @return Shader code binary of the given stage + */ + const std::vector<uint32_t> &getShaderBinary(ShaderStage stage) const; - /** - * Returns the shader program's shader of the specified shader. - * Needed for the transfer to the pipeline. - * @return Shader object consisting of buffer with shader code and shader stage enum - */ - const Shader &getShader(ShaderStage shaderStage) const; + /** + * @brief Returns whether a shader exists in the program for a + * specified shader stage. + * + * @param[in] stage The stage of the shader + * @return True, if a shader exists for the stage, else false + */ + bool existsShader(ShaderStage stage) const; - bool existsShader(ShaderStage shaderStage) const; + /** + * @brief Returns the vertex attachments for the program and its + * shader stages. + * + * @return Vertex attachments + */ + const std::vector<VertexAttachment> &getVertexAttachments() const; - const std::vector<VertexAttachment> &getVertexAttachments() const; - size_t getPushConstantSize() const; + /** + * @brief Returns the size of the programs push constants. + * + * @return Size of push constants + */ + size_t getPushConstantsSize() const; - const std::vector<std::vector<DescriptorBinding>>& getReflectedDescriptors() const; + /** + * @brief Returns the reflected descriptor set bindings mapped via + * their descriptor set id. + * + * @return Reflected descriptor set bindings + */ + const std::unordered_map<uint32_t, DescriptorBindings> &getReflectedDescriptors() const; private: - /** - * Called after successfully adding a shader to the program. - * Fills vertex input attachments and descriptor sets (if present). - * @param shaderStage the stage to reflect data from - */ - void reflectShader(ShaderStage shaderStage); + /** + * @brief Called after successfully adding a shader to the program. + * Fills vertex input attachments and descriptor sets (if present). + * + * @param[in] shaderStage the stage to reflect data from + */ + void reflectShader(ShaderStage shaderStage); - std::unordered_map<ShaderStage, Shader> m_Shaders; + std::unordered_map<ShaderStage, std::vector<uint32_t> > m_Shaders; - // contains all vertex input attachments used in the vertex buffer - std::vector<VertexAttachment> m_VertexAttachments; - std::vector<std::vector<DescriptorBinding>> m_DescriptorSets; - size_t m_pushConstantSize = 0; + // contains all vertex input attachments used in the vertex buffer + VertexAttachments m_VertexAttachments; + std::unordered_map<uint32_t, DescriptorBindings> m_DescriptorSets; + size_t m_pushConstantsSize = 0; }; -} + +} // namespace vkcv diff --git a/include/vkcv/ShaderStage.hpp b/include/vkcv/ShaderStage.hpp index dca395bdba82a2f1cb38bb0a25196cfd3dab8019..2b6ab23ca623bb837d3e4fd3c5b22ca3b187fd43 100644 --- a/include/vkcv/ShaderStage.hpp +++ b/include/vkcv/ShaderStage.hpp @@ -1,15 +1,88 @@ #pragma once +/** + * @authors Artur Wasmut, Simeon Hermann, Tobias Frisch, Vanessa Karolek, Alexander Gauggel, Lars + * Hoerttrich + * @file vkcv/ShaderStage.hpp + * @brief Enum and struct to operate with multiple shader stages. + */ + +#include <vulkan/vulkan.hpp> namespace vkcv { - - enum class ShaderStage - { - VERTEX, - TESS_CONTROL, - TESS_EVAL, - GEOMETRY, - FRAGMENT, - COMPUTE + + /** + * @brief Enum class to specify the stage of a shader. + */ + enum class ShaderStage : VkShaderStageFlags { + VERTEX = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eVertex), + TESS_CONTROL = + static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eTessellationControl), + TESS_EVAL = + static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eTessellationEvaluation), + GEOMETRY = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eGeometry), + FRAGMENT = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eFragment), + COMPUTE = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eCompute), + TASK = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eTaskNV), + MESH = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eMeshNV), + RAY_GEN = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eRaygenKHR), + RAY_ANY_HIT = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eAnyHitKHR), + RAY_CLOSEST_HIT = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eClosestHitKHR), + RAY_MISS = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eMissKHR), + RAY_INTERSECTION = + static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eIntersectionKHR), + RAY_CALLABLE = static_cast<VkShaderStageFlags>(vk::ShaderStageFlagBits::eCallableKHR) + }; + + using ShaderStages = vk::Flags<ShaderStage>; + +} // namespace vkcv + +/** + * @cond VULKAN_TYPES + */ +namespace vk { + + template <> + struct [[maybe_unused]] FlagTraits<vkcv::ShaderStage> { + enum : VkFlags { + allFlags [[maybe_unused]] = + (VkFlags(vkcv::ShaderStage::VERTEX) | VkFlags(vkcv::ShaderStage::TESS_CONTROL) + | VkFlags(vkcv::ShaderStage::TESS_EVAL) | VkFlags(vkcv::ShaderStage::GEOMETRY) + | VkFlags(vkcv::ShaderStage::FRAGMENT) | VkFlags(vkcv::ShaderStage::COMPUTE) + | VkFlags(vkcv::ShaderStage::TASK) | VkFlags(vkcv::ShaderStage::MESH) + | VkFlags(vkcv::ShaderStage::RAY_GEN) | VkFlags(vkcv::ShaderStage::RAY_ANY_HIT) + | VkFlags(vkcv::ShaderStage::RAY_CLOSEST_HIT) + | VkFlags(vkcv::ShaderStage::RAY_MISS) + | VkFlags(vkcv::ShaderStage::RAY_INTERSECTION) + | VkFlags(vkcv::ShaderStage::RAY_CALLABLE)) + }; }; -} +} // namespace vk +/** + * @endcond + */ + +namespace vkcv { + + constexpr vk::ShaderStageFlags getShaderStageFlags(ShaderStages shaderStages) noexcept { + return vk::ShaderStageFlags(static_cast<VkShaderStageFlags>(shaderStages)); + } + + constexpr ShaderStages operator|(ShaderStage stage0, ShaderStage stage1) noexcept { + return ShaderStages(stage0) | stage1; + } + + constexpr ShaderStages operator&(ShaderStage stage0, ShaderStage stage1) noexcept { + return ShaderStages(stage0) & stage1; + } + + constexpr ShaderStages operator^(ShaderStage stage0, ShaderStage stage1) noexcept { + return ShaderStages(stage0) ^ stage1; + } + + constexpr ShaderStages operator~(ShaderStage stage) noexcept { + return ~(ShaderStages(stage)); + } + +} // namespace vkcv diff --git a/include/vkcv/Swapchain.hpp b/include/vkcv/Swapchain.hpp deleted file mode 100644 index b75fc5a87156ea56061e41b4b0974928c83ffa28..0000000000000000000000000000000000000000 --- a/include/vkcv/Swapchain.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#pragma once -#include "vulkan/vulkan.hpp" -#include "Context.hpp" -#include "vkcv/Window.hpp" - -#include <atomic> - -namespace vkcv -{ - class Swapchain final { - private: - friend class Core; - - struct Surface - { - vk::SurfaceKHR handle; - std::vector<vk::SurfaceFormatKHR> formats; - vk::SurfaceCapabilitiesKHR capabilities; - std::vector<vk::PresentModeKHR> presentModes; - }; - - Surface m_Surface; - - vk::SwapchainKHR m_Swapchain; - vk::Format m_Format; - vk::ColorSpaceKHR m_ColorSpace; - vk::PresentModeKHR m_PresentMode; - uint32_t m_ImageCount; - - vk::Extent2D m_Extent; - - std::atomic<bool> m_RecreationRequired; - - /** - * Constructor of a SwapChain object - * glfw is not initialized in this class because ist must be sure that there exists a context first - * glfw is already initialized by the window class - * @param surface used by the swapchain - * @param swapchain to show images in the window - * @param format - */ - // TODO: - Swapchain(const Surface &surface, - vk::SwapchainKHR swapchain, - vk::Format format, - vk::ColorSpaceKHR colorSpace, - vk::PresentModeKHR presentMode, - uint32_t imageCount, - vk::Extent2D extent) noexcept; - - /** - * TODO - * - * @return - */ - bool shouldUpdateSwapchain() const; - - /** - * TODO - * - * context - * window - */ - void updateSwapchain(const Context &context, const Window &window); - - /** - * - */ - void signalSwapchainRecreation(); - - public: - Swapchain(const Swapchain& other); - - /** - * @return The swapchain linked with the #SwapChain class - * @note The reference to our Swapchain variable is needed for the recreation step - */ - [[nodiscard]] - const vk::SwapchainKHR& getSwapchain() const; - - /** - * gets the current surface object - * @return current surface - */ - [[nodiscard]] - vk::SurfaceKHR getSurface() const; - - /** - * gets the chosen swapchain format - * @return gets the chosen swapchain format - */ - [[nodiscard]] - vk::Format getFormat() const; - - /** - * creates a swap chain object out of the given window and the given context - * @param window a wrapper that represents a glfw window - * @param context of the application - * @return returns an object of swapChain - */ - static Swapchain create(const Window &window, const Context &context); - - /** - * Destructor of SwapChain - */ - virtual ~Swapchain(); - - /** - * @return number of images in swapchain - */ - uint32_t getImageCount() const; - - /** - * TODO - * - * @return - */ - [[nodiscard]] - const vk::Extent2D& getExtent() const; - - }; -} diff --git a/include/vkcv/SyncResources.hpp b/include/vkcv/SyncResources.hpp deleted file mode 100644 index c41019cc46ee1375a83323a6ecc877ecc1c1727a..0000000000000000000000000000000000000000 --- a/include/vkcv/SyncResources.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> - -namespace vkcv { - struct SyncResources { - vk::Semaphore renderFinished; - vk::Semaphore swapchainImageAcquired; - vk::Fence presentFinished; - }; - - SyncResources createSyncResources(const vk::Device &device); - void destroySyncResources(const vk::Device &device, const SyncResources &resources); - vk::Fence createFence(const vk::Device &device); - void waitForFence(const vk::Device& device, const vk::Fence fence); -} \ No newline at end of file diff --git a/include/vkcv/TypeGuard.hpp b/include/vkcv/TypeGuard.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a5312b161d4ded7b061d351608e269b04f20807a --- /dev/null +++ b/include/vkcv/TypeGuard.hpp @@ -0,0 +1,149 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/TypeGuard.hpp + * @brief Support type safety for classes in debug compilation. + */ + +#include <stdlib.h> +#include <typeinfo> + +namespace vkcv { + + /** + * @brief Class bringing type safety during runtime to other classes. + */ + class TypeGuard { + private: +#ifndef NDEBUG + const char* m_typeName; + size_t m_typeHash; + + [[nodiscard]] bool checkType(const char* name, size_t hash, size_t size) const; +#endif + size_t m_typeSize; + + [[nodiscard]] bool checkTypeSize(size_t size) const; + + public: + /** + * Explicit constructor to create a type guard by a specific + * size only. The guard will not verify an individual type but + * whether its size matches the requirement. + * + * @param[in] size Size of type + */ + explicit TypeGuard(size_t size = 0); + + /** + * Explicit constructor to create a type guard by a types + * ID information and its size. The guard will verify a type + * by all available information in debug mode. + * + * @param[in] info ID information of type + * @param[in] size Size of type + */ + TypeGuard(const std::type_info &info, size_t size); + + TypeGuard(const TypeGuard &other) = default; + TypeGuard(TypeGuard &&other) noexcept = default; + + ~TypeGuard() = default; + + TypeGuard &operator=(const TypeGuard &other) = default; + TypeGuard &operator=(TypeGuard &&other) noexcept = default; + + /** + * Operator to compare two type guards and returns + * whether their stored type information and size + * match as boolean value. + * + * @param[in] other Other type guard + * @return True if the details match, otherwise false. + */ + bool operator==(const TypeGuard &other) const; + + /** + * Operator to compare two type guards and returns + * whether their stored type information and size + * do not match as boolean value. + * + * @param[in] other Other type guard + * @return True if the details differ, otherwise false. + */ + bool operator!=(const TypeGuard &other) const; + + /** + * Checks whether a type from a template parameter + * matches with the used type by the given guard. + * + * @tparam T Type to check against + * @return True if both types match, otherwise false. + */ + template <typename T> + [[nodiscard]] bool check() const { +#ifndef NDEBUG + return checkType(typeid(T).name(), typeid(T).hash_code(), sizeof(T)); +#else + return checkTypeSize(sizeof(T)); +#endif + } + + /** + * Returns the size of this guards type in bytes. + * + * @return Size of type + */ + [[nodiscard]] size_t typeSize() const; + }; + + /** + * Creates a new type guard with a given type specified + * as template parameter. + * + * @tparam T Type + * @return New type guard + */ + template <typename T> + TypeGuard typeGuard() { + static TypeGuard guard(typeid(T), sizeof(T)); + return guard; + } + + /** + * Creates a new type guard with a given type specified + * as template parameter by the passed parameter. + * + * @tparam T Type + * @return New type guard + */ + template <typename T> + TypeGuard typeGuard(T) { + return typeGuard<T>(); + } + + /** + * Creates a new type guard with a given type specified + * as template parameter by the passed parameter. + * + * @tparam T Type + * @return New type guard + */ + template <typename T> + TypeGuard typeGuard(const T &) { + return typeGuard<T>(); + } + + /** + * Creates a new type guard with a given type specified + * as template parameter by the passed parameter. + * + * @tparam T Type + * @return New type guard + */ + template <typename T> + TypeGuard typeGuard(T &&) { + return typeGuard<T>(); + } + +} // namespace vkcv diff --git a/include/vkcv/VertexData.hpp b/include/vkcv/VertexData.hpp new file mode 100644 index 0000000000000000000000000000000000000000..845b9fba9a646d702377c297112029944a254f17 --- /dev/null +++ b/include/vkcv/VertexData.hpp @@ -0,0 +1,109 @@ +#pragma once +/** + * @authors Sebastian Gaida, Alexander Gauggel, Artur Wasmut, Tobias Frisch + * @file vkcv/VertexData.hpp + * @brief Types to configure vertex data for drawcalls. + */ + +#include <vector> + +#include "Handles.hpp" + +namespace vkcv { + + /** + * @brief Structure to store details about a vertex buffer binding. + */ + struct VertexBufferBinding { + BufferHandle buffer; + size_t offset; + }; + + VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer, size_t offset = 0); + + typedef std::vector<VertexBufferBinding> VertexBufferBindings; + + /** + * @brief Enum class to specify the size of indexes. + */ + enum class IndexBitCount { + Bit8, + Bit16, + Bit32 + }; + + /** + * @brief Class to store the details of vertex data for rendering + */ + class VertexData { + private: + VertexBufferBindings m_bindings; + BufferHandle m_indices; + IndexBitCount m_indexBitCount; + size_t m_count; + + public: + /** + * @brief Constructor of vertex data by providing an optional vector + * of vertex buffer bindings. + * + * @param[in] bindings Vertex buffer bindings (optional) + */ + explicit VertexData(const VertexBufferBindings &bindings = {}); + + VertexData(const VertexData &other) = default; + VertexData(VertexData &&other) noexcept = default; + + ~VertexData() = default; + + VertexData &operator=(const VertexData &other) = default; + VertexData &operator=(VertexData &&other) noexcept = default; + + /** + * @brief Return the used vertex buffer bindings of the vertex data. + * + * @return Vertex buffer bindings + */ + [[nodiscard]] const VertexBufferBindings &getVertexBufferBindings() const; + + /** + * @brief Set the optional index buffer and its used index bit count. + * + * @param[in] indices Index buffer handle + * @param[in] indexBitCount Index bit count + */ + void setIndexBuffer(const BufferHandle &indices, + IndexBitCount indexBitCount = IndexBitCount::Bit16); + + /** + * @brief Return the handle from the used index buffer of the vertex + * data. + * + * @return Index buffer handle + */ + [[nodiscard]] const BufferHandle &getIndexBuffer() const; + + /** + * @brief Return the index bit count of the indices used in the + * vertex data. + * + * @return Index bit count + */ + [[nodiscard]] IndexBitCount getIndexBitCount() const; + + /** + * @brief Set the count of elements to use by the vertex data. + * + * @param count Count of vertex elements + */ + void setCount(size_t count); + + /** + * @brief Return the count of elements in use by the vertex data. + * + * @return Count of vertex elements + */ + [[nodiscard]] size_t getCount() const; + }; + +} // namespace vkcv diff --git a/include/vkcv/VertexLayout.hpp b/include/vkcv/VertexLayout.hpp index 0600b99a24a327605e89b2e8ec304c20dbf7ad2e..2c6cfb486348dea7f255c4fbdc26a27b3bfdda56 100644 --- a/include/vkcv/VertexLayout.hpp +++ b/include/vkcv/VertexLayout.hpp @@ -1,66 +1,108 @@ #pragma once +/** + * @authors Alexander Gauggel, Artur Wasmut, Mara Vogt, Susanne Dötsch, + * Trevor Hollmann, Leonie Franken, Simeon Hermann, Tobias Frisch + * @file vkcv/VertexLayout.hpp + * @brief Structures to handle vertex layout, bindings and attachments. + */ -#include <vector> #include <iostream> #include <string> +#include <vector> + +namespace vkcv { -namespace vkcv{ - enum class VertexAttachmentFormat{ - FLOAT, - FLOAT2, - FLOAT3, - FLOAT4, - INT, - INT2, - INT3, - INT4 - }; + /** + * @brief Enum class to specify the format of vertex attributes. + */ + enum class VertexAttachmentFormat { + FLOAT, + FLOAT2, + FLOAT3, + FLOAT4, + INT, + INT2, + INT3, + INT4 + }; + /** + * @brief Returns the size in bytes of a vertex with a + * given vertex format. + * + * @param[in] format Vertex format + * @return Size in bytes + */ uint32_t getFormatSize(VertexAttachmentFormat format); - struct VertexAttachment{ - friend struct VertexBinding; - /** - * Describes an individual vertex input attribute/attachment. - * @param inputLocation its location in the vertex shader. - * @param name the name referred to in the shader. - * @param format the format (and therefore, the size) this attachment is in. - * The offset is calculated when a collection of attachments forms a binding, hence the friend declaration. - */ - VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format) noexcept; - VertexAttachment() = delete; + /** + * @brief Structure to store the details of a vertex input attachment. + * + * Describes an individual vertex input attribute/attachment. The offset + * is calculated when a collection of attachments forms a binding. + */ + struct VertexAttachment { + uint32_t inputLocation; + std::string name; + VertexAttachmentFormat format; + uint32_t offset; + }; + + typedef std::vector<VertexAttachment> VertexAttachments; + + /** + * @brief Structure to store the details of a vertex buffer binding. + * + * Describes all vertex input attachments _one_ buffer contains to create + * a vertex buffer binding. NOTE: multiple vertex layouts may contain + * various (mutually exclusive) vertex input attachments to form one + * complete vertex buffer binding! + */ + struct VertexBinding { + uint32_t bindingLocation; + uint32_t stride; + VertexAttachments vertexAttachments; + }; - uint32_t inputLocation; - std::string name; - VertexAttachmentFormat format; - uint32_t offset; - }; + /** + * Creates a vertex binding with given parameters and calculates its strides + * depending on its attachments. + * + * @param[in] bindingLocation Its entry in the buffers that make up the whole vertex buffer. + * @param[in] attachments The vertex input attachments this specific buffer layout contains. + * @return Vertex binding with calculated stride + */ + VertexBinding createVertexBinding(uint32_t bindingLocation, + const VertexAttachments &attachments); - struct VertexBinding{ - /** - * Describes all vertex input attachments _one_ buffer contains to create a vertex buffer binding. - * NOTE: multiple vertex layouts may contain various (mutually exclusive) vertex input attachments - * to form one complete vertex buffer binding! - * @param bindingLocation its entry in the buffers that make up the whole vertex buffer. - * @param attachments the vertex input attachments this specific buffer layout contains. - */ - VertexBinding(uint32_t bindingLocation, const std::vector<VertexAttachment> &attachments) noexcept; - VertexBinding() = delete; + typedef std::vector<VertexBinding> VertexBindings; - uint32_t bindingLocation; - uint32_t stride; - std::vector<VertexAttachment> vertexAttachments; - }; + /** + * Creates vertex bindings in a very simplified way with one vertex binding for + * each attachment. + * + * @param[in] attachments The vertex input attachments. + * @return Vertex bindings with calculated stride + */ + VertexBindings createVertexBindings(const VertexAttachments &attachments); - struct VertexLayout{ - /** - * Describes the complete layout of one vertex, e.g. all of the vertex input attachments used, - * and all of the buffer bindings that refer to the attachments (for when multiple buffers are used). - * @param bindings bindings the complete vertex buffer is comprised of. - */ - VertexLayout() noexcept; - VertexLayout(const std::vector<VertexBinding> &bindings) noexcept; + /** + * @brief Structure to store the details of a vertex layout. + * + * Describes the complete layout of one vertex, e.g. all of the vertex input + * attachments used, and all of the buffer bindings that refer to the attachments + * (for when multiple buffers are used). + */ + struct VertexLayout { + VertexBindings vertexBindings; + }; + + /** + * Creates vertex layout from a list of vertex bindings in a simplified way. + * + * @param[in] bindings The vertex bindings + * @return Vertex layout + */ + VertexLayout createVertexLayout(const VertexBindings &bindings); - std::vector<VertexBinding> vertexBindings; - }; -} +} // namespace vkcv diff --git a/include/vkcv/Window.hpp b/include/vkcv/Window.hpp index 7dc6c1b7dc8fef4d5de7de5b0a9976bf714e6ac2..3d157b98220f17752d6b109401c3d21899299f18 100644 --- a/include/vkcv/Window.hpp +++ b/include/vkcv/Window.hpp @@ -1,162 +1,170 @@ #pragma once /** - * @authors Sebastian Gaida - * @file src/vkcv/Window.hpp - * @brief Window class to handle a basic rendering surface and input + * @authors Tobias Frisch, Sebastian Gaida, Vanessa Karolek, Artur Wasmut + * @file vkcv/Window.hpp + * @brief Class to represent and manage a window with its input. */ -#define NOMINMAX +#ifndef NOMINMAX +#define NOMINMAX 1 +#endif + #include <algorithm> +#include <string> #include "Event.hpp" +#include "Handles.hpp" struct GLFWwindow; namespace vkcv { - class Window { - protected: - GLFWwindow *m_window; - - /** - * - * @param GLFWwindow of the class - */ - explicit Window(GLFWwindow *window); - - private: - /** - * mouse callback for moving the mouse on the screen - * @param[in] window The window that received the event. - * @param[in] xpos The new cursor x-coordinate, relative to the left edge of the content area. - * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the content area. - */ - static void onMouseMoveEvent(GLFWwindow *window, double x, double y); - - /** - * mouseButton callback for mouse buttons - * @param[in] button The [mouse button](@ref buttons) that was pressed or released. - * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases may add more actions. - * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down. - */ - static void onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods); - - /** - * @brief A callback function for handling mouse scrolling events. - * @param[in] callbackWindow The window that received the event. - * @param[in] xoffset The extent of horizontal scrolling. - * @param[in] yoffset The extent of vertical scrolling. - */ - static void onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset); - - /** - * resize callback for the resize option of the window - * @param[in] window The window that was resized. - * @param[in] width The new width, in screen coordinates, of the window. - * @param[in] height The new height, in screen coordinates, of the window. - */ - static void onResize(GLFWwindow *callbackWindow, int width, int height); - - /** - * key callback for the pressed key - * @param[in] window The window that received the event. - * @param[in] key The [keyboard key](@ref keys) that was pressed or released. - * @param[in] scancode The system-specific scancode of the key. - * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. - * @param[in] mods Bit field describing which [modifier keys](@ref mods) were held down. - */ - static void onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods); - - /** - * char callback for any typed character - * @param[in] window The window that received the event - * @param[in] c The character that got typed - */ - static void onCharEvent(GLFWwindow *callbackWindow, unsigned int c); - - /** - * @brief A callback function for gamepad input events. - * @param gamepadIndex The gamepad index. - */ - static void onGamepadEvent(int gamepadIndex); - - public: - /** - * creates a GLFWwindow with the parameters in the function - * @param[in] windowTitle of the window - * @param[in] width of the window (optional) - * @param[in] height of the window (optional) - * @param[in] resizable resize ability of the window (optional) - * @return Window class - */ - static Window create( const char *windowTitle, int width = -1, int height = -1, bool resizable = false); - /** - * checks if the window is still open, or the close event was called - * This function should be changed/removed later on - * @return bool if the window is still open - */ - [[nodiscard]] - bool isWindowOpen() const; - - /** - * polls all events on the GLFWwindow - */ - static void pollEvents(); - - /** - * basic events of the window - */ - event< int, int, int> e_mouseButton; - event< double, double > e_mouseMove; - event< double, double > e_mouseScroll; - event< int, int > e_resize; - event< int, int, int, int > e_key; - event< unsigned int > e_char; - event< int > e_gamepad; - - /** - * returns the current window - * @return window handle - */ - [[nodiscard]] - GLFWwindow *getWindow() const; - - /** - * Copy-operator of #Window is deleted! - * - * @param other Other instance of #Window - * @return Reference to itself - */ - Window &operator=(const Window &other) = delete; - - /** - * Move-operator of #Window uses default behavior! - * - * @param other Other instance of #Window - * @return Reference to itself - */ - Window &operator=(Window &&other) = default; - - /** - * gets the window width - * @param window glfwWindow - * @return int with window width - */ - [[nodiscard]] - int getWidth() const; - - /** - * gets the window height - * @param window glfwWindow - * @return int with window height - */ - [[nodiscard]] - int getHeight() const; - - /** - * Destructor of #Window, terminates GLFW - */ - virtual ~Window(); - }; - -} + /** + * @brief Class to handle a window. + */ + class Window { + friend class WindowManager; + friend class SwapchainManager; + + private: + std::string m_title; + bool m_resizable; + bool m_shouldClose; + GLFWwindow* m_window; + SwapchainHandle m_swapchainHandle; + event_handle<int, int> m_resizeHandle; + + public: + /** + * @brief Constructor of an uninitialized #Window + */ + Window(); + + /** + * @brief Constructor of a #Window with an optional width, + * height and resizable attribute. + * + * @param[in] title title of the window + * @param[in] width width of the window (optional) + * @param[in] height height of the window (optional) + * @param[in] resizable resize ability of the window (optional) + */ + explicit Window(const std::string &title, int width = -1, int height = -1, + bool resizable = false); + + /** + * @brief Copy-constructor of a #Window + * + * @param[in] other Other instance of #Window + */ + Window(const Window &other) = delete; + + /** + * @brief Copy-operator of a #Window + * + * @param[in] other Other instance of #Window + * @return Reference to itself + */ + Window &operator=(const Window &other) = delete; + + /** + * @brief Checks if the window is still open, or the close event was called. + * TODO: This function should be changed/removed later on + * + * @return True, if the window is still open, else false + */ + [[nodiscard]] bool isOpen() const; + + /** + * @brief Returns the currently focused window. + * TODO: only accessible to WindowManager + * + * @return Current window in focus + */ + static Window &getFocusedWindow(); + + /** + * @brief Checks if any windows are active and open. + * + * @return True, if any window is open, else false + */ + static bool hasOpenWindow(); + + /** + * @brief Polls all events on the active windows. + */ + static void pollEvents(); + + /** + * @brief Returns the required extensions to use GLFW windows with Vulkan. + * + * @return Required surface extensions + */ + static const std::vector<std::string> &getExtensions(); + + event< int, int, int> e_mouseButton; + event< double, double > e_mouseMove; + event< double, double > e_mouseScroll; + event< int, int > e_resize; + event< int, int, int, int > e_key; + event< unsigned int > e_char; + event< int > e_gamepad; + + /** + * @brief Returns the GLFW window handle. + * + * @return GLFW window handle + */ + [[nodiscard]] GLFWwindow* getWindow() const; + + /** + * @brief Returns the title of the window. + * + * @return Window title + */ + [[nodiscard]] const std::string &getTitle() const; + + /** + * @brief Returns the width of the window. + * + * @return Window width + */ + [[nodiscard]] int getWidth() const; + + /** + * @brief Returns the height of the window. + * + * @return Window height + */ + [[nodiscard]] int getHeight() const; + + /** + * @brief Returns whether the window is resizable or not. + * + * @return True, if the window is resizable, else false + */ + [[nodiscard]] bool isResizable() const; + + /** + * @brief Destructor of the window which terminates GLFW in case + * of the last window got destroyed. + */ + virtual ~Window(); + + /** + * @brief Requests the windows framebuffer size. + * + * @param[out] width + * @param[out] height + */ + void getFramebufferSize(int &width, int &height) const; + + /** + * @brief Retruns the handle of the swapchain in use by the window. + * + * @return Swapchain handle + */ + SwapchainHandle getSwapchain() const; + }; + +} // namespace vkcv diff --git a/lib/SPIRV-Cross b/lib/SPIRV-Cross index ff61890722a91e97c44940494be5b6eed0d5ff5b..f09ba2777714871bddb70d049878af34b94fa54d 160000 --- a/lib/SPIRV-Cross +++ b/lib/SPIRV-Cross @@ -1 +1 @@ -Subproject commit ff61890722a91e97c44940494be5b6eed0d5ff5b +Subproject commit f09ba2777714871bddb70d049878af34b94fa54d diff --git a/lib/Vulkan-Headers b/lib/Vulkan-Headers new file mode 160000 index 0000000000000000000000000000000000000000..8ba8294c86d0e99fcb457bedbd573dd678ccc9b3 --- /dev/null +++ b/lib/Vulkan-Headers @@ -0,0 +1 @@ +Subproject commit 8ba8294c86d0e99fcb457bedbd573dd678ccc9b3 diff --git a/lib/Vulkan-Hpp b/lib/Vulkan-Hpp new file mode 160000 index 0000000000000000000000000000000000000000..ef609a2f77dd1756e672712f264e76b64acdba61 --- /dev/null +++ b/lib/Vulkan-Hpp @@ -0,0 +1 @@ +Subproject commit ef609a2f77dd1756e672712f264e76b64acdba61 diff --git a/lib/VulkanMemoryAllocator b/lib/VulkanMemoryAllocator new file mode 160000 index 0000000000000000000000000000000000000000..c351692490513cdb0e5a2c925aaf7ea4a9b672f4 --- /dev/null +++ b/lib/VulkanMemoryAllocator @@ -0,0 +1 @@ +Subproject commit c351692490513cdb0e5a2c925aaf7ea4a9b672f4 diff --git a/lib/VulkanMemoryAllocator-Hpp b/lib/VulkanMemoryAllocator-Hpp new file mode 160000 index 0000000000000000000000000000000000000000..e00a0b1ab8bba230e8c701423540ff3828aea2d5 --- /dev/null +++ b/lib/VulkanMemoryAllocator-Hpp @@ -0,0 +1 @@ +Subproject commit e00a0b1ab8bba230e8c701423540ff3828aea2d5 diff --git a/lib/glfw b/lib/glfw index 0e9ec7788b4985a0df698080258e4091d18dcc3b..dd8a678a66f1967372e5a5e3deac41ebf65ee127 160000 --- a/lib/glfw +++ b/lib/glfw @@ -1 +1 @@ -Subproject commit 0e9ec7788b4985a0df698080258e4091d18dcc3b +Subproject commit dd8a678a66f1967372e5a5e3deac41ebf65ee127 diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 5edb802b3adf16878c2dec4050d8444278739026..e5bc34dfdf183f8dc26639771f44201597beaa73 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,7 +1,26 @@ +set(vkcv_modules_includes) +set(vkcv_modules_libraries) + # Add new modules here: +add_subdirectory(algorithm) add_subdirectory(asset_loader) add_subdirectory(camera) +add_subdirectory(effects) +add_subdirectory(geometry) add_subdirectory(gui) +add_subdirectory(material) +add_subdirectory(meshlet) +add_subdirectory(scene) add_subdirectory(shader_compiler) add_subdirectory(testing) +add_subdirectory(upscaling) + +message(STATUS "Modules:") +message(" - Includes: [ ${vkcv_modules_includes} ]") +message(" - Libraries: [ ${vkcv_modules_libraries} ]") + +if (vkcv_parent_scope) + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/algorithm/CMakeLists.txt b/modules/algorithm/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d4eeaa261d10f50bd6c4bc8a43455cb252be50cf --- /dev/null +++ b/modules/algorithm/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_algorithm) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_algorithm_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_algorithm_include ${PROJECT_SOURCE_DIR}/include) + +set(vkcv_algorithm_sources + ${vkcv_algorithm_include}/vkcv/algorithm/SinglePassDownsampler.hpp + ${vkcv_algorithm_source}/vkcv/algorithm/SinglePassDownsampler.cpp +) + +# Setup some path variables to load libraries +set(vkcv_algorithm_lib lib) +set(vkcv_algorithm_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_algorithm_lib}) + +# Check and load FidelityFX_SPD +include(config/FidelityFX_SPD.cmake) + +# adding source files to the project +add_library(vkcv_algorithm ${vkcv_build_attribute} ${vkcv_algorithm_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_algorithm ${vkcv_algorithm_libraries} vkcv vkcv_shader_compiler vkcv_camera vkcv_asset_loader) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_algorithm SYSTEM BEFORE PRIVATE ${vkcv_algorithm_includes} ${vkcv_include} ${vkcv_includes} ${vkcv_shader_compiler_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_algorithm BEFORE PUBLIC ${vkcv_algorithm_include}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_algorithm_include}) + list(APPEND vkcv_modules_libraries vkcv_algorithm) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/algorithm/README.md b/modules/algorithm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..551fe0941f84c4417717b7b7dfbdebfa37c245f2 --- /dev/null +++ b/modules/algorithm/README.md @@ -0,0 +1,15 @@ +# Algorithm + +A VkCV module to use different optimized algorithms + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|----------------------------------------------------------------------|---| +| [FidelityFX-SPD](https://github.com/GPUOpen-Effects/FidelityFX-SPD/) | ✅ | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__algorithm.html) to this module. diff --git a/modules/algorithm/config/FidelityFX_SPD.cmake b/modules/algorithm/config/FidelityFX_SPD.cmake new file mode 100644 index 0000000000000000000000000000000000000000..eec11bfde46e7079417cabfbdc47a9e387877bff --- /dev/null +++ b/modules/algorithm/config/FidelityFX_SPD.cmake @@ -0,0 +1,21 @@ + +use_git_submodule("${vkcv_algorithm_lib_path}/FidelityFX-SPD" ffx_spd_status) + +if (${ffx_spd_status}) + include_shader(${vkcv_algorithm_lib_path}/FidelityFX-SPD/ffx-spd/ffx_a.h ${vkcv_algorithm_include} ${vkcv_algorithm_source}) + include_shader(${vkcv_algorithm_lib_path}/FidelityFX-SPD/ffx-spd/ffx_spd.h ${vkcv_algorithm_include} ${vkcv_algorithm_source}) + include_shader(${vkcv_algorithm_lib_path}/FidelityFX-SPD/sample/src/VK/SPDIntegration.glsl ${vkcv_algorithm_include} ${vkcv_algorithm_source}) + include_shader(${vkcv_algorithm_lib_path}/FidelityFX-SPD/sample/src/VK/SPDIntegrationLinearSampler.glsl ${vkcv_algorithm_include} ${vkcv_algorithm_source}) + + list(APPEND vkcv_algorithm_includes ${vkcv_algorithm_lib}/FidelityFX-SPD/ffx-spd) + + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_source}/ffx_a.h.cxx) + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_source}/ffx_spd.h.cxx) + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_source}/SPDIntegration.glsl.cxx) + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_source}/SPDIntegrationLinearSampler.glsl.cxx) + + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_include}/ffx_a.h.hxx) + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_include}/ffx_spd.h.hxx) + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_include}/SPDIntegration.glsl.hxx) + list(APPEND vkcv_algorithm_sources ${vkcv_algorithm_include}/SPDIntegrationLinearSampler.glsl.hxx) +endif () diff --git a/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp b/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a46ab4fc4e7ff237af52ba629455a06758e54e0f --- /dev/null +++ b/modules/algorithm/include/vkcv/algorithm/SinglePassDownsampler.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include <vector> + +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Downsampler.hpp> +#include <vkcv/ShaderProgram.hpp> + +namespace vkcv::algorithm { + + /** + * @defgroup vkcv_algorithm Algorithm Module + * A module to use different optimized algorithms. + * @{ + */ + + /** + * @brief A class to handle downsampling via FidelityFX Single Pass Downsampler. + * https://github.com/GPUOpen-Effects/FidelityFX-SPD + */ + class SinglePassDownsampler : public vkcv::Downsampler { + private: + /** + * The SPD compute pipeline of the downsampler. + */ + ComputePipelineHandle m_pipeline; + + /** + * The descriptor set layout of the SPD pipeline. + */ + DescriptorSetLayoutHandle m_descriptorSetLayout; + + /** + * The vector of descriptor sets currently in use for downsampling. + */ + std::vector<DescriptorSetHandle> m_descriptorSets; + + /** + * The buffer template to handle global atomic counters for SPD. + */ + Buffer<uint32_t> m_globalCounter; + + /** + * The optional sampler handle to use for the downsampling. + */ + SamplerHandle m_sampler; + + public: + /** + * @brief Constructor to create instance for single pass downsampling. + * + * @param[in,out] core Reference to a Core instance + * @param[in] sampler Sampler handle + */ + explicit SinglePassDownsampler(Core& core, + const SamplerHandle &sampler = SamplerHandle()); + + /** + * @brief Record the commands of the downsampling instance to + * generate all mip levels of an input image via a + * command stream. + * + * @param[in] cmdStream Command stream handle + * @param[in] image Image handle + */ + void recordDownsampling(const CommandStreamHandle& cmdStream, + const ImageHandle& image) override; + + }; + + /** @} */ + +} diff --git a/modules/algorithm/lib/FidelityFX-SPD b/modules/algorithm/lib/FidelityFX-SPD new file mode 160000 index 0000000000000000000000000000000000000000..7c796c6d9fa6a9439e3610478148cfd742d97daf --- /dev/null +++ b/modules/algorithm/lib/FidelityFX-SPD @@ -0,0 +1 @@ +Subproject commit 7c796c6d9fa6a9439e3610478148cfd742d97daf diff --git a/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..051ae80d8d98e2ca5ce54f20d1e047b2151e87ca --- /dev/null +++ b/modules/algorithm/src/vkcv/algorithm/SinglePassDownsampler.cpp @@ -0,0 +1,354 @@ + +#include "vkcv/algorithm/SinglePassDownsampler.hpp" + +#include <cstdint> +#include <cmath> +#include <vector> + +#define A_CPU 1 +#include <ffx_a.h> +#include <ffx_spd.h> + +#include "ffx_a.h.hxx" +#include "ffx_spd.h.hxx" +#include "SPDIntegration.glsl.hxx" +#include "SPDIntegrationLinearSampler.glsl.hxx" + +#include <vkcv/ComputePipelineConfig.hpp> +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +namespace vkcv::algorithm { + + #define SPD_MAX_MIP_LEVELS 12 + + struct SPDConstants { + int mips; + int numWorkGroupsPerSlice; + int workGroupOffset[2]; + }; + + struct SPDConstantsSampler { + int mips; + int numWorkGroupsPerSlice; + int workGroupOffset[2]; + float invInputSize[2]; + }; + + static DescriptorBindings getDescriptorBindings(const SamplerHandle &sampler) { + DescriptorBindings descriptorBindings = {}; + + auto binding_0 = DescriptorBinding { + 0, + DescriptorType::IMAGE_STORAGE, + SPD_MAX_MIP_LEVELS + 1, + ShaderStage::COMPUTE, + false, + true + }; + + auto binding_1 = DescriptorBinding { + 1, + DescriptorType::IMAGE_STORAGE, + 1, + ShaderStage::COMPUTE, + false, + false + }; + + auto binding_2 = DescriptorBinding{ + 2, + DescriptorType::STORAGE_BUFFER, + 1, + ShaderStage::COMPUTE, + false, + false + }; + + auto binding_3 = DescriptorBinding{ + 3, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false, + false + }; + + auto binding_4 = DescriptorBinding{ + 4, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE, + false, + false + }; + + descriptorBindings.insert(std::make_pair(0, binding_0)); + descriptorBindings.insert(std::make_pair(1, binding_1)); + descriptorBindings.insert(std::make_pair(2, binding_2)); + + if (sampler) { + descriptorBindings.insert(std::make_pair(3, binding_3)); + descriptorBindings.insert(std::make_pair(4, binding_4)); + } + + return descriptorBindings; + } + + static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) { + std::ofstream file (shaderPath.string(), std::ios::out); + + if (!file.is_open()) { + vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str()); + return false; + } + + file.seekp(0); + file.write(code.c_str(), static_cast<std::streamsize>(code.length())); + file.close(); + + return true; + } + + static bool compileSPDShader(vkcv::shader::GLSLCompiler& compiler, + const std::string &source, + const shader::ShaderCompiledFunction& compiled) { + std::filesystem::path directory = generateTemporaryDirectoryPath(); + + if (!std::filesystem::create_directory(directory)) { + vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str()); + return false; + } + + if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) { + return false; + } + + if (!writeShaderCode(directory / "ffx_spd.h", FFX_SPD_H_SHADER)) { + return false; + } + + return compiler.compileSource( + vkcv::ShaderStage::COMPUTE, + source.c_str(), + [&directory, &compiled] (vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + if (compiled) { + compiled(shaderStage, path); + } + + std::filesystem::remove_all(directory); + }, directory + ); + } + + SinglePassDownsampler::SinglePassDownsampler(Core &core, + const SamplerHandle &sampler) : + vkcv::Downsampler(core), + m_pipeline(), + + m_descriptorSetLayout(), + m_descriptorSets(), + + m_globalCounter(buffer<uint32_t>( + m_core, + vkcv::BufferType::STORAGE, + 6 + )), + + m_sampler(sampler) { + const auto& featureManager = m_core.getContext().getFeatureManager(); + + const bool partialBound = featureManager.checkFeatures<vk::PhysicalDeviceDescriptorIndexingFeatures>( + vk::StructureType::ePhysicalDeviceDescriptorIndexingFeatures, + [](const vk::PhysicalDeviceDescriptorIndexingFeatures& features) { + return features.descriptorBindingPartiallyBound; + } + ); + + if (!partialBound) { + return; + } + + m_descriptorSetLayout = m_core.createDescriptorSetLayout(getDescriptorBindings(sampler)); + + vkcv::shader::GLSLCompiler compiler (vkcv::shader::GLSLCompileTarget::SUBGROUP_OP); + + vk::PhysicalDeviceSubgroupProperties subgroupProperties; + + vk::PhysicalDeviceProperties2 properties2; + properties2.pNext = &subgroupProperties; + m_core.getContext().getPhysicalDevice().getProperties2(&properties2); + + const bool no_subgroup_quad = !(subgroupProperties.supportedOperations & vk::SubgroupFeatureFlagBits::eQuad); + + if (no_subgroup_quad) { + compiler.setDefine("SPD_NO_WAVE_OPERATIONS", "1"); + } + + const bool float16Support = ( + featureManager.checkFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>( + vk::StructureType::ePhysicalDeviceShaderFloat16Int8FeaturesKHR, + [](const vk::PhysicalDeviceFloat16Int8FeaturesKHR& features) { + return features.shaderFloat16; + } + ) && + featureManager.checkFeatures<vk::PhysicalDevice16BitStorageFeaturesKHR>( + vk::StructureType::ePhysicalDevice16BitStorageFeaturesKHR, + [](const vk::PhysicalDevice16BitStorageFeaturesKHR& features) { + return features.storageBuffer16BitAccess; + } + ) && + ((no_subgroup_quad) || + (featureManager.checkFeatures<vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures>( + vk::StructureType::ePhysicalDeviceShaderSubgroupExtendedTypesFeatures, + [](const vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures& features) { + return features.shaderSubgroupExtendedTypes; + } + ))) + ); + + if (float16Support) { + compiler.setDefine("A_HALF", "1"); + compiler.setDefine("SPD_PACKED_ONLY", "1"); + } + + ShaderProgram program; + if (m_sampler) { + compileSPDShader( + compiler, + SPDINTEGRATIONLINEARSAMPLER_GLSL_SHADER, + [&program](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + program.addShader(shaderStage, path); + } + ); + } else { + compileSPDShader( + compiler, + SPDINTEGRATION_GLSL_SHADER, + [&program](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + program.addShader(shaderStage, path); + } + ); + } + + m_pipeline = m_core.createComputePipeline(ComputePipelineConfig( + program, + { m_descriptorSetLayout } + )); + + std::vector<uint32_t> zeroes; + zeroes.resize(m_globalCounter.getCount()); + memset(zeroes.data(), 0, m_globalCounter.getSize()); + m_globalCounter.fill(zeroes); + } + + void SinglePassDownsampler::recordDownsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &image) { + Downsampler& fallback = m_core.getDownsampler(); + + if (m_pipeline) { + fallback.recordDownsampling(cmdStream, image); + return; + } + + const uint32_t mipLevels = m_core.getImageMipLevels(image); + const uint32_t depth = m_core.getImageDepth(image); + + m_core.prepareImageForSampling(cmdStream, image); + + if ((mipLevels < 4) || (depth > 1) || (!m_core.isImageSupportingStorage(image))) { + fallback.recordDownsampling(cmdStream, image); + return; + } + + auto descriptorSet = m_core.createDescriptorSet(m_descriptorSetLayout); + + vkcv::DescriptorWrites writes; + writes.writeStorageImage(1, image, 6, 1, true); + writes.writeStorageBuffer(2, m_globalCounter.getHandle()); + + if (m_sampler) { + writes.writeStorageImage(0, image, 1, mipLevels - 1, true); + + writes.writeSampledImage(3, image, 0, false, 0, 1, true); + writes.writeSampler(4, m_sampler); + } else { + writes.writeStorageImage(0, image, 0, mipLevels, true); + } + + m_core.writeDescriptorSet(descriptorSet, writes); + m_descriptorSets.push_back(descriptorSet); + + m_core.recordCommandsToStream(cmdStream, nullptr, [this]() { + m_descriptorSets.erase(m_descriptorSets.begin()); + }); + + varAU2(dispatchThreadGroupCountXY); + varAU2(workGroupOffset); + varAU2(numWorkGroupsAndMips); + varAU4(rectInfo) = initAU4( + 0, + 0, + m_core.getImageWidth(image), + m_core.getImageHeight(image) + ); + + SpdSetup( + dispatchThreadGroupCountXY, + workGroupOffset, + numWorkGroupsAndMips, + rectInfo + ); + + if (m_sampler) { + m_core.prepareImageForSampling(cmdStream, image, 1); + m_core.prepareImageForStorage(cmdStream, image, m_core.getImageMipLevels(image) - 1, 1); + } else { + m_core.prepareImageForStorage(cmdStream, image); + } + + vkcv::DispatchSize dispatch ( + dispatchThreadGroupCountXY[0], + dispatchThreadGroupCountXY[1], + m_core.getImageArrayLayers(image) + ); + + vkcv::PushConstants pushConstants = (m_sampler? + vkcv::pushConstants<SPDConstantsSampler>() : + vkcv::pushConstants<SPDConstants>() + ); + + if (m_sampler) { + SPDConstantsSampler data; + data.numWorkGroupsPerSlice = numWorkGroupsAndMips[0]; + data.mips = numWorkGroupsAndMips[1]; + data.workGroupOffset[0] = workGroupOffset[0]; + data.workGroupOffset[1] = workGroupOffset[1]; + data.invInputSize[0] = 1.0f / m_core.getImageWidth(image); + data.invInputSize[1] = 1.0f / m_core.getImageHeight(image); + + pushConstants.appendDrawcall<SPDConstantsSampler>(data); + } else { + SPDConstants data; + data.numWorkGroupsPerSlice = numWorkGroupsAndMips[0]; + data.mips = numWorkGroupsAndMips[1]; + data.workGroupOffset[0] = workGroupOffset[0]; + data.workGroupOffset[1] = workGroupOffset[1]; + + pushConstants.appendDrawcall<SPDConstants>(data); + } + + m_core.recordComputeDispatchToCmdStream(cmdStream, m_pipeline, dispatch, { + useDescriptorSet(0, descriptorSet) + }, pushConstants); + + if (m_sampler) { + m_core.prepareImageForSampling(cmdStream, image, mipLevels - 1, 1); + } else { + m_core.prepareImageForSampling(cmdStream, image); + } + } + +} diff --git a/modules/asset_loader/CMakeLists.txt b/modules/asset_loader/CMakeLists.txt index c5a1fd0eb9620d3a95af1c52a9e7456337047c7b..03e1888786d5382d854edaf77ee89d4e063e0fc6 100644 --- a/modules/asset_loader/CMakeLists.txt +++ b/modules/asset_loader/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(vkcv_asset_loader) # setting c++ standard for the module -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(vkcv_asset_loader_source ${PROJECT_SOURCE_DIR}/src) @@ -15,7 +15,7 @@ set(vkcv_asset_loader_sources ) # adding source files to the module -add_library(vkcv_asset_loader STATIC ${vkcv_asset_loader_sources}) +add_library(vkcv_asset_loader ${vkcv_build_attribute} ${vkcv_asset_loader_sources}) # Setup some path variables to load libraries set(vkcv_asset_loader_lib lib) @@ -31,12 +31,20 @@ include(config/FX_GLTF.cmake) include(config/STB.cmake) # link the required libraries to the module -target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv) +target_link_libraries(vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv ${vkcv_libraries}) # including headers of dependencies and the VkCV framework -target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes}) +target_include_directories(vkcv_asset_loader SYSTEM BEFORE PRIVATE ${vkcv_asset_loader_includes} ${vkcv_include} ${vkcv_includes}) # add the own include directory for public headers target_include_directories(vkcv_asset_loader BEFORE PUBLIC ${vkcv_asset_loader_include}) target_compile_definitions(vkcv_asset_loader PUBLIC ${vkcv_asset_loader_definitions}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_asset_loader_include}) + list(APPEND vkcv_modules_libraries vkcv_asset_loader) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/asset_loader/README.md b/modules/asset_loader/README.md new file mode 100644 index 0000000000000000000000000000000000000000..aad7cdb53c7ae95d39e68a5cd814cb939b121d44 --- /dev/null +++ b/modules/asset_loader/README.md @@ -0,0 +1,17 @@ +# Asset-Loader + +A VkCV module to load basic assets like models, materials and images + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|----------------------------------------------------|---| +| [fx-gltf](https://github.com/jessey-git/fx-gltf) | ✅ | +| [nlohmann::json](https://github.com/nlohmann/json) | ✅ | +| [stb](https://github.com/nothings/stb) | ✅ | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__asset.html) to this module. diff --git a/modules/asset_loader/config/FX_GLTF.cmake b/modules/asset_loader/config/FX_GLTF.cmake index 37cd162422d8277022067498f5d5ba3e26e2ae1b..4164c2786d9ada53c979b98bf23f973113edfda5 100644 --- a/modules/asset_loader/config/FX_GLTF.cmake +++ b/modules/asset_loader/config/FX_GLTF.cmake @@ -1,5 +1,7 @@ -if (EXISTS "${vkcv_asset_loader_lib_path}/fx-gltf") +use_git_submodule("${vkcv_asset_loader_lib_path}/fx-gltf" fx_gltf_status) + +if (${fx_gltf_status}) set(FX_GLTF_INSTALL OFF CACHE INTERNAL "") set(FX_GLTF_USE_INSTALLED_DEPS OFF CACHE INTERNAL "") set(BUILD_TESTING OFF CACHE INTERNAL "") @@ -7,6 +9,4 @@ if (EXISTS "${vkcv_asset_loader_lib_path}/fx-gltf") add_subdirectory(${vkcv_asset_loader_lib}/fx-gltf) list(APPEND vkcv_asset_loader_libraries fx-gltf) -else() - message(WARNING "FX-GLTF is required..! Update the submodules!") endif () diff --git a/modules/asset_loader/config/NLOHMANN_JSON.cmake b/modules/asset_loader/config/NLOHMANN_JSON.cmake index 018f6a19809fd3e53e6e790a6fe6447348e43c09..3a082ae59c5778aa5d172fab9eb71b108b3f652b 100644 --- a/modules/asset_loader/config/NLOHMANN_JSON.cmake +++ b/modules/asset_loader/config/NLOHMANN_JSON.cmake @@ -1,10 +1,16 @@ -if (EXISTS "${vkcv_asset_loader_lib_path}/json") - set(JSON_BuildTests OFF CACHE INTERNAL "") - - add_subdirectory(${vkcv_asset_loader_lib}/json) - - list(APPEND vkcv_asset_loader_libraries nlohmann_json::nlohmann_json) +find_package(nlohmann_json QUIET) + +if (nlohmann_json_FOUND) + list(APPEND vkcv_libraries nlohmann_json::nlohmann_json) else() - message(WARNING "NLOHMANN_JSON is required..! Update the submodules!") + use_git_submodule("${vkcv_asset_loader_lib_path}/json" json_status) + + if (${json_status}) + set(JSON_BuildTests OFF CACHE INTERNAL "") + + add_subdirectory(${vkcv_asset_loader_lib}/json) + + list(APPEND vkcv_asset_loader_libraries nlohmann_json::nlohmann_json) + endif () endif () diff --git a/modules/asset_loader/config/STB.cmake b/modules/asset_loader/config/STB.cmake index 1287d0a9ddda559e061ddd680bc815e24b3e2075..61b7cc929b2b5ae96fbbbe479f6e9d48d9db5c69 100644 --- a/modules/asset_loader/config/STB.cmake +++ b/modules/asset_loader/config/STB.cmake @@ -1,10 +1,10 @@ -if (EXISTS "${vkcv_asset_loader_lib_path}/stb") +use_git_submodule("${vkcv_asset_loader_lib_path}/stb" stb_status) + +if (${stb_status}) list(APPEND vkcv_asset_loader_includes ${vkcv_asset_loader_lib}/stb) list(APPEND vkcv_asset_loader_definitions STB_IMAGE_IMPLEMENTATION) list(APPEND vkcv_asset_loader_definitions STBI_ONLY_JPEG) list(APPEND vkcv_asset_loader_definitions STBI_ONLY_PNG) -else() - message(WARNING "STB is required..! Update the submodules!") endif () diff --git a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp index 4107d57ee97a6efe0475c6d9dbd80d2603e0afe8..d6b5cf5d5382e0e03bd7950c1e87bf3c6a8ee445 100644 --- a/modules/asset_loader/include/vkcv/asset/asset_loader.hpp +++ b/modules/asset_loader/include/vkcv/asset/asset_loader.hpp @@ -9,18 +9,11 @@ #include <vector> #include <array> #include <cstdint> +#include <filesystem> -/** These macros define limits of the following structs. Implementations can - * test against these limits when performing sanity checks. The main constraint - * expressed is that of the data type: Material indices are identified by a - * uint8_t in the VertexGroup struct, so there can't be more than UINT8_MAX - * materials in the mesh. Should these limits be too narrow, the data type has - * to be changed, but the current ones should be generous enough for most use - * cases. */ -#define MAX_MATERIALS_PER_MESH UINT8_MAX -#define MAX_VERTICES_PER_VERTEX_GROUP UINT32_MAX - -/** LOADING MESHES +#include "vkcv/VertexData.hpp" + +/* LOADING MESHES * The description of meshes is a hierarchy of structures with the Mesh at the * top. * @@ -45,53 +38,95 @@ namespace vkcv::asset { -/** This enum matches modes in fx-gltf, the library returns a standard mode - * (TRIANGLES) if no mode is given in the file. */ +/** + * @defgroup vkcv_asset Asset Loader Module + * A module to load assets like scenes, meshes and textures. + * @{ + */ + +/** + * These return codes are limited to the asset loader module. If unified return + * codes are defined for the vkcv framework, these will be used instead. + */ +#define ASSET_ERROR 0 +#define ASSET_SUCCESS 1 + +/** + * This enum matches modes in fx-gltf, the library returns a standard mode + * (TRIANGLES) if no mode is given in the file. + */ enum class PrimitiveMode : uint8_t { - POINTS=0, LINES, LINELOOP, LINESTRIP, TRIANGLES, TRIANGLESTRIP, - TRIANGLEFAN + POINTS = 0, + LINES = 1, + LINELOOP = 2, + LINESTRIP = 3, + TRIANGLES = 4, + TRIANGLESTRIP = 5, + TRIANGLEFAN = 6 }; -/** The indices in the index buffer can be of different bit width. */ -enum class IndexType : uint8_t { UNDEFINED=0, UINT8=1, UINT16=2, UINT32=3 }; - -typedef struct { - // TODO define struct for samplers (low priority) - // NOTE: glTF defines samplers based on OpenGL, which can not be - // directly translated to Vulkan. Specifically, OpenGL (and glTF) - // define a different set of Min/Mag-filters than Vulkan. -} Sampler; - -/** struct for defining the loaded texture */ -typedef struct { - int sampler; // index into the sampler array of the Scene - uint8_t channels; // number of channels - uint16_t w, h; // width and height of the texture - std::vector<uint8_t> data; // binary data of the decoded texture -} Texture; +/** + * The indices in the index buffer can be of different bit width. + */ +enum class IndexType : uint8_t { + UNDEFINED=0, + UINT8=1, + UINT16=2, + UINT32=3 +}; -/** The asset loader module only supports the PBR-MetallicRoughness model for - * materials.*/ -typedef struct { - uint16_t textureMask; // bit mask with active texture targets - // Indices into the Array.textures array - int baseColor, metalRough, normal, occlusion, emissive; - // Scaling factors for each texture target - struct { float r, g, b, a; } baseColorFactor; - float metallicFactor, roughnessFactor; - float normalScale; - float occlusionStrength; - struct { float r, g, b; } emissiveFactor; -} Material; +/** + * This struct defines a sampler for a texture object. All values here can + * directly be passed to VkSamplerCreateInfo. + * NOTE that glTF defines samplers based on OpenGL, which can not be directly + * translated to Vulkan. The vkcv::asset::Sampler struct defined here adheres + * to the Vulkan spec, having alerady translated the flags from glTF to Vulkan. + * Since glTF does not specify border sampling for more than two dimensions, + * the addressModeW is hardcoded to a default: VK_SAMPLER_ADDRESS_MODE_REPEAT. + */ +struct Sampler { + int minFilter, magFilter; + int mipmapMode; + float minLOD, maxLOD; + int addressModeU, addressModeV, addressModeW; +}; + +/** + * This struct describes a (partially) loaded texture. + * The data member is not populated after calling probeScene() but only when + * calling loadMesh(), loadScene() or loadTexture(). Note that textures are + * currently always loaded with 4 channels as RGBA, even if the image has just + * RGB or is grayscale. In the case where the glTF-file does not provide a URI + * but references a buffer view for the raw data, the path member will be empty + * even though the rest is initialized properly. + * NOTE: Loading textures without URI is untested. + */ +struct Texture { + std::filesystem::path path; // URI to the encoded texture data + int sampler; // index into the sampler array of the Scene + + union { int width; int w; }; + union { int height; int h; }; + int channels; + + std::vector<uint8_t> data; // binary data of the decoded texture +}; -/** Flags for the bit-mask in the Material struct. To check if a material has a +/** + * Flags for the bit-mask in the Material struct. To check if a material has a * certain texture target, you can use the hasTexture() function below, passing - * the material struct and the enum. */ + * the material struct and the enum. + */ enum class PBRTextureTarget { - baseColor=1, metalRough=2, normal=4, occlusion=8, emissive=16 + baseColor=1, + metalRough=2, + normal=4, + occlusion=8, + emissive=16 }; -/** This macro translates the index of an enum in the defined order to an +/** + * This macro translates the index of an enum in the defined order to an * integer with a single bit set in the corresponding place. It is used for * working with the bitmask of texture targets ("textureMask") in the Material * struct: @@ -102,92 +137,212 @@ enum class PBRTextureTarget { * contact with bit-level operations. */ #define bitflag(ENUM) (0x1u << ((unsigned)(ENUM))) -/** To signal that a certain texture target is active in a Material struct, its - * bit is set in the textureMask. You can use this function to check that: - * Material mat = ...; - * if (materialHasTexture(&mat, baseColor)) {...} */ -bool materialHasTexture(const Material *const m, const PBRTextureTarget t); +/** + * The asset loader module only supports the PBR-MetallicRoughness model for + * materials. + */ +struct Material { + uint16_t textureMask; // bit mask with active texture targets + + // Indices into the Scene.textures vector + int baseColor, metalRough, normal, occlusion, emissive; + + // Scaling factors for each texture target + struct { float r, g, b, a; } baseColorFactor; + float metallicFactor, roughnessFactor; + float normalScale; + float occlusionStrength; + struct { float r, g, b; } emissiveFactor; + + /** + * To signal that a certain texture target is active in this Material + * struct, its bit is set in the textureMask. You can use this function + * to check that: + * if (myMaterial.hasTexture(baseColor)) {...} + * + * @param t The target to query for + * @return Boolean to signal whether the texture target is active in + * the material. + */ + bool hasTexture(PBRTextureTarget target) const; +}; -/** With these enums, 0 is reserved to signal uninitialized or invalid data. */ +/* With these enums, 0 is reserved to signal uninitialized or invalid data. */ enum class PrimitiveType : uint32_t { UNDEFINED = 0, POSITION = 1, NORMAL = 2, TEXCOORD_0 = 3, - TEXCOORD_1 = 4 + TEXCOORD_1 = 4, + TANGENT = 5, + COLOR_0 = 6, + COLOR_1 = 7, + JOINTS_0 = 8, + WEIGHTS_0 = 9 }; -/** These integer values are used the same way in OpenGL, Vulkan and glTF. This +/** + * These integer values are used the same way in OpenGL, Vulkan and glTF. This * enum is not needed for translation, it's only for the programmers * convenience (easier to read in if/switch statements etc). While this enum * exists in (almost) the same definition in the fx-gltf library, we want to - * avoid exposing that dependency, thus it is re-defined here. */ + * avoid exposing that dependency, thus it is re-defined here. + */ enum class ComponentType : uint16_t { - NONE = 0, INT8 = 5120, UINT8 = 5121, INT16 = 5122, UINT16 = 5123, - UINT32 = 5125, FLOAT32 = 5126 + NONE = 0, + INT8 = 5120, + UINT8 = 5121, + INT16 = 5122, + UINT16 = 5123, + UINT32 = 5125, + FLOAT32 = 5126 }; -/** This struct describes one vertex attribute of a vertex buffer. */ -typedef struct { +/** + * This struct describes one vertex attribute of a vertex buffer. + */ +struct VertexAttribute { PrimitiveType type; // POSITION, NORMAL, ... + uint32_t offset; // offset in bytes uint32_t length; // length of ... in bytes uint32_t stride; // stride in bytes - ComponentType componentType; // eg. 5126 for float - uint8_t componentCount; // eg. 3 for vec3 -} VertexAttribute; + + ComponentType componentType; // eg. 5126 for float + uint8_t componentCount; // eg. 3 for vec3 +}; -/** This struct represents one (possibly the only) part of a mesh. There is +/** + * This struct represents one (possibly the only) part of a mesh. There is * always one vertexBuffer and zero or one indexBuffer (indexed rendering is * common but not always used). If there is no index buffer, this is indicated * by indexBuffer.data being empty. Each vertex buffer can have one or more - * vertex attributes. */ -typedef struct { + * vertex attributes. + */ +struct VertexGroup { enum PrimitiveMode mode; // draw as points, lines or triangle? - size_t numIndices, numVertices; + size_t numIndices; + size_t numVertices; + struct { enum IndexType type; // data type of the indices std::vector<uint8_t> data; // binary data of the index buffer } indexBuffer; + struct { std::vector<uint8_t> data; // binary data of the vertex buffer std::vector<VertexAttribute> attributes; // description of one } vertexBuffer; + struct { float x, y, z; } min; // bounding box lower left struct { float x, y, z; } max; // bounding box upper right + int materialIndex; // index to one of the materials -} VertexGroup; +}; -/** This struct represents a single mesh as it was loaded from a glTF file. It +/** + * This struct represents a single mesh as it was loaded from a glTF file. It * consists of at least one VertexGroup, which then references other resources - * such as Materials. */ -typedef struct { + * such as Materials. + */ +struct Mesh { std::string name; std::array<float, 16> modelMatrix; std::vector<int> vertexGroups; -} Mesh; +}; -/** The scene struct is simply a collection of objects in the scene as well as +/** + * The scene struct is simply a collection of objects in the scene as well as * the resources used by those objects. - * For now the only type of object are the meshes and they are represented in a - * flat array. - * Note that parent-child relations are not yet possible. */ -typedef struct { + * Note that parent-child relations are not yet possible. + */ +struct Scene { std::vector<Mesh> meshes; std::vector<VertexGroup> vertexGroups; std::vector<Material> materials; std::vector<Texture> textures; std::vector<Sampler> samplers; -} Scene; + std::vector<std::string> uris; +}; + +/** + * Parse the given glTF file and create a shallow description of the content. + * Only the meta-data of the objects in the scene is loaded, not the binary + * content. The rationale is to provide a means of probing the content of a + * glTF file without the costly process of loading and decoding large amounts + * of data. The returned Scene struct can be used to search for specific meshes + * in the scene, that can then be loaded on their own using the loadMesh() + * function. Note that the Scene struct received as output argument will be + * overwritten by this function. + * After this function completes, the returned Scene struct is completely + * initialized and all information is final, except for the missing binary + * data. This means that indices to vectors will remain valid even when the + * shallow scene struct is filled with data by loadMesh(). + * Note that for URIs only (local) filesystem paths are supported, no + * URLs using network protocols etc. + * + * @param[in] path must be the path to a glTF- or glb-file. + * @param[out] scene is a reference to a Scene struct that will be filled with the + * meta-data of all objects described in the glTF file. + * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS + */ +int probeScene(const std::filesystem::path &path, Scene &scene); + +/** + * This function loads a single mesh from the given file and adds it to the + * given scene. The scene must already be initialized (via probeScene()). + * The mesh_index refers to the Scenes meshes array and identifies the mesh to + * load. To find the mesh you want, iterate over the probed scene and check the + * meshes details (eg. name). + * Besides the mesh, this function will also add any associated data to the + * Scene struct such as Materials and Textures required by the Mesh. + * + * @param[in,out] scene is the scene struct to which the results will be written. + * @param[in] mesh_index Index of the mesh to load + * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS + */ +int loadMesh(Scene &scene, int mesh_index); /** - * Load every mesh from the glTF file, as well as materials and textures. + * Load every mesh from the glTF file, as well as materials, textures and other + * associated objects. * - * @param path must be the path to a glTF or glb file. - * @param scene is a reference to a Scene struct that will be filled with the + * @param[in] path must be the path to a glTF- or glb-file. + * @param[out] scene is a reference to a Scene struct that will be filled with the * content of the glTF file being loaded. - * */ -int loadScene(const std::string &path, Scene &scene); + * @return ASSET_ERROR on failure, otherwise ASSET_SUCCESS + */ +int loadScene(const std::filesystem::path &path, Scene &scene); + +/** + * Simply loads a single image at the given path and returns a Texture + * struct describing it. This is for special use cases only (eg. + * loading a font atlas) and not meant to be used for regular assets. + * The sampler is set to -1, signalling that this Texture was loaded + * outside the context of a glTF-file. + * If there was an error loading or decoding the image, the returned struct + * will be cleared to all 0 with path and data being empty; make sure to always + * check that !data.empty() before using the struct. + * + * @param[in] path must be the path to an image file. + * @return Texture struct describing the loaded image. + */ +Texture loadTexture(const std::filesystem::path& path); + +/** + * Loads up the vertex attributes and creates usable vertex buffer bindings + * to match the desired order of primitive types as used in the vertex + * shader. + * + * @param[in] attributes Vertex attributes + * @param[in] buffer Buffer handle + * @param[in] types Primitive type order + * @return Vertex buffer bindings + */ +VertexBufferBindings loadVertexBufferBindings(const std::vector<VertexAttribute> &attributes, + const BufferHandle &buffer, + const std::vector<PrimitiveType> &types); +/** @} */ -} +} // end namespace vkcv::asset diff --git a/modules/asset_loader/lib/fx-gltf b/modules/asset_loader/lib/fx-gltf index f4f18f2017a049a23748c9c9aad42ba2de20bfd5..7766c237ea81c0bb3759e78e5c0f22854843eef8 160000 --- a/modules/asset_loader/lib/fx-gltf +++ b/modules/asset_loader/lib/fx-gltf @@ -1 +1 @@ -Subproject commit f4f18f2017a049a23748c9c9aad42ba2de20bfd5 +Subproject commit 7766c237ea81c0bb3759e78e5c0f22854843eef8 diff --git a/modules/asset_loader/lib/json b/modules/asset_loader/lib/json index 0972f7ff0e651f09a306dba791cc42024b8642c1..bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d 160000 --- a/modules/asset_loader/lib/json +++ b/modules/asset_loader/lib/json @@ -1 +1 @@ -Subproject commit 0972f7ff0e651f09a306dba791cc42024b8642c1 +Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d diff --git a/modules/asset_loader/lib/stb b/modules/asset_loader/lib/stb index c9064e317699d2e495f36ba4f9ac037e88ee371a..8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55 160000 --- a/modules/asset_loader/lib/stb +++ b/modules/asset_loader/lib/stb @@ -1 +1 @@ -Subproject commit c9064e317699d2e495f36ba4f9ac037e88ee371a +Subproject commit 8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55 diff --git a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp index c21d0c9f70bc81561e1078b15b8372e6dd4730f5..ef83785e6a7280dc7e6e45a850915f2ad4fc56c1 100644 --- a/modules/asset_loader/src/vkcv/asset/asset_loader.cpp +++ b/modules/asset_loader/src/vkcv/asset/asset_loader.cpp @@ -1,366 +1,894 @@ #include "vkcv/asset/asset_loader.hpp" #include <iostream> -#include <string.h> // memcpy(3) -#include <stdlib.h> // calloc(3) +#include <cstring> // memcpy(3) +#include <set> +#include <cstdlib> // calloc(3) +#include <vulkan/vulkan.hpp> #include <fx/gltf.h> #include <stb_image.h> #include <vkcv/Logger.hpp> #include <algorithm> + namespace vkcv::asset { -/** -* convert the accessor type from the fx-gltf library to an unsigned int -* @param type -* @return unsigned integer representation -*/ -// TODO Return proper error code (we need to define those as macros or enums, -// will discuss during the next core meeting if that should happen on the scope -// of the vkcv framework or just this module) -uint8_t convertTypeToInt(const fx::gltf::Accessor::Type type) { - switch (type) { - case fx::gltf::Accessor::Type::None : - return 0; - case fx::gltf::Accessor::Type::Scalar : - return 1; - case fx::gltf::Accessor::Type::Vec2 : - return 2; - case fx::gltf::Accessor::Type::Vec3 : - return 3; - case fx::gltf::Accessor::Type::Vec4 : - return 4; - default: return 10; // TODO add cases for matrices (or maybe change the type in the struct itself) + /** + * This function unrolls nested exceptions via recursion and prints them + * @param e The exception being thrown + * @param path The path to the file that was responsible for the exception + */ + static void recurseExceptionPrint(const std::exception& e, const std::string &path) { + vkcv_log(LogLevel::ERROR, "Loading file %s: %s", path.c_str(), e.what()); + + try { + std::rethrow_if_nested(e); + } catch (const std::exception& nested) { + recurseExceptionPrint(nested, path); + } } -} -/** - * This function unrolls nested exceptions via recursion and prints them - * @param e error code - * @param path path to file that is responsible for error - */ -void print_what (const std::exception& e, const std::string &path) { - vkcv_log(LogLevel::ERROR, "Loading file %s: %s", - path.c_str(), e.what()); + /** + * Returns the component count for an accessor type of the fx-gltf library. + * @param type The accessor type + * @return An unsigned integer count + */ + static uint32_t getComponentCount(const fx::gltf::Accessor::Type type) { + switch (type) { + case fx::gltf::Accessor::Type::Scalar: + return 1; + case fx::gltf::Accessor::Type::Vec2: + return 2; + case fx::gltf::Accessor::Type::Vec3: + return 3; + case fx::gltf::Accessor::Type::Vec4: + case fx::gltf::Accessor::Type::Mat2: + return 4; + case fx::gltf::Accessor::Type::Mat3: + return 9; + case fx::gltf::Accessor::Type::Mat4: + return 16; + case fx::gltf::Accessor::Type::None: + default: + return 0; + } + } - try { - std::rethrow_if_nested(e); - } catch (const std::exception& nested) { - print_what(nested, path); + static uint32_t getComponentSize(ComponentType type) { + switch (type) { + case ComponentType::INT8: + case ComponentType::UINT8: + return 1; + case ComponentType::INT16: + case ComponentType::UINT16: + return 2; + case ComponentType::UINT32: + case ComponentType::FLOAT32: + return 4; + default: + return 0; + } } -} -/** Translate the component type used in the index accessor of fx-gltf to our - * enum for index type. The reason we have defined an incompatible enum that - * needs translation is that only a subset of component types is valid for - * indices and we want to catch these incompatibilities here. */ -enum IndexType getIndexType(const enum fx::gltf::Accessor::ComponentType &t) -{ - switch (t) { - case fx::gltf::Accessor::ComponentType::UnsignedByte: - return IndexType::UINT8; - case fx::gltf::Accessor::ComponentType::UnsignedShort: - return IndexType::UINT16; - case fx::gltf::Accessor::ComponentType::UnsignedInt: - return IndexType::UINT32; - default: - std::cerr << "ERROR: Index type not supported: " << - static_cast<uint16_t>(t) << std::endl; - return IndexType::UNDEFINED; + /** + * Translate the component type used in the index accessor of fx-gltf to our + * enum for index type. The reason we have defined an incompatible enum that + * needs translation is that only a subset of component types is valid for + * indices and we want to catch these incompatibilities here. + * @param t The component type + * @return The vkcv::IndexType enum representation + */ + enum IndexType getIndexType(const enum fx::gltf::Accessor::ComponentType &type) { + switch (type) { + case fx::gltf::Accessor::ComponentType::UnsignedByte: + return IndexType::UINT8; + case fx::gltf::Accessor::ComponentType::UnsignedShort: + return IndexType::UINT16; + case fx::gltf::Accessor::ComponentType::UnsignedInt: + return IndexType::UINT32; + default: + vkcv_log(LogLevel::ERROR, "Index type not supported: %u", static_cast<uint16_t>(type)); + return IndexType::UNDEFINED; + } } -} - -/** - * This function computes the modelMatrix out of the data given in the gltf file. It also checks, whether a modelMatrix was given. - * @param translation possible translation vector (default 0,0,0) - * @param scale possible scale vector (default 1,1,1) - * @param rotation possible rotation, given in quaternion (default 0,0,0,1) - * @param matrix possible modelmatrix (default identity) - * @return model Matrix as an array of floats - */ -std::array<float, 16> computeModelMatrix(std::array<float, 3> translation, std::array<float, 3> scale, std::array<float, 4> rotation, std::array<float, 16> matrix){ - std::array<float, 16> modelMatrix = {1,0,0,0, - 0,1,0,0, - 0,0,1,0, - 0,0,0,1}; - if (matrix != modelMatrix){ - return matrix; - } else { - // translation - modelMatrix[3] = translation[0]; - modelMatrix[7] = translation[1]; - modelMatrix[11] = translation[2]; - // rotation and scale - auto a = rotation[0]; - auto q1 = rotation[1]; - auto q2 = rotation[2]; - auto q3 = rotation[3]; - - modelMatrix[0] = (2 * (a * a + q1 * q1) - 1) * scale[0]; - modelMatrix[1] = (2 * (q1 * q2 - a * q3)) * scale[1]; - modelMatrix[2] = (2 * (q1 * q3 + a * q2)) * scale[2]; - - modelMatrix[4] = (2 * (q1 * q2 + a * q3)) * scale[0]; - modelMatrix[5] = (2 * (a * a + q2 * q2) - 1) * scale[1]; - modelMatrix[6] = (2 * (q2 * q3 - a * q1)) * scale[2]; - - modelMatrix[8] = (2 * (q1 * q3 - a * q2)) * scale[0]; - modelMatrix[9] = (2 * (q2 * q3 + a * q1)) * scale[1]; - modelMatrix[10] = (2 * (a * a + q3 * q3) - 1) * scale[2]; - - // flip y, because GLTF uses y up, but vulkan -y up - modelMatrix[5] *= -1; - - return modelMatrix; - } - -} - -bool materialHasTexture(const Material *const m, const PBRTextureTarget t) -{ - return m->textureMask & bitflag(t); -} - -int loadScene(const std::string &path, Scene &scene){ - fx::gltf::Document sceneObjects; - - try { - if (path.rfind(".glb", (path.length()-4)) != std::string::npos) { - sceneObjects = fx::gltf::LoadFromBinary(path); - } else { - sceneObjects = fx::gltf::LoadFromText(path); - } - } catch (const std::system_error &err) { - print_what(err, path); - return 0; - } catch (const std::exception &e) { - print_what(e, path); - return 0; - } - size_t pos = path.find_last_of("/"); - auto dir = path.substr(0, pos); - - // file has to contain at least one mesh - if (sceneObjects.meshes.size() == 0) return 0; - - fx::gltf::Accessor posAccessor; - std::vector<VertexAttribute> vertexAttributes; - std::vector<Material> materials; - std::vector<Texture> textures; - std::vector<Sampler> samplers; - std::vector<Mesh> meshes; - std::vector<VertexGroup> vertexGroups; - int groupCount = 0; - - Mesh mesh = {}; - - for(int i = 0; i < sceneObjects.meshes.size(); i++){ - std::vector<int> vertexGroupsIndices; - fx::gltf::Mesh const &objectMesh = sceneObjects.meshes[i]; - - for(int j = 0; j < objectMesh.primitives.size(); j++){ - fx::gltf::Primitive const &objectPrimitive = objectMesh.primitives[j]; - vertexAttributes.clear(); - vertexAttributes.reserve(objectPrimitive.attributes.size()); - - for (auto const & attrib : objectPrimitive.attributes) { - - fx::gltf::Accessor accessor = sceneObjects.accessors[attrib.second]; - VertexAttribute attribute; - - if (attrib.first == "POSITION") { - attribute.type = PrimitiveType::POSITION; - posAccessor = accessor; - } else if (attrib.first == "NORMAL") { - attribute.type = PrimitiveType::NORMAL; - } else if (attrib.first == "TEXCOORD_0") { - attribute.type = PrimitiveType::TEXCOORD_0; - } else if (attrib.first == "TEXCOORD_1") { - attribute.type = PrimitiveType::TEXCOORD_1; - } else { - return 0; - } - - attribute.offset = sceneObjects.bufferViews[accessor.bufferView].byteOffset; - attribute.length = sceneObjects.bufferViews[accessor.bufferView].byteLength; - attribute.stride = sceneObjects.bufferViews[accessor.bufferView].byteStride; - attribute.componentType = static_cast<ComponentType>(accessor.componentType); - - if (convertTypeToInt(accessor.type) != 10) { - attribute.componentCount = convertTypeToInt(accessor.type); - } else { - return 0; - } - - vertexAttributes.push_back(attribute); - } - - IndexType indexType; - std::vector<uint8_t> indexBufferData = {}; - if (objectPrimitive.indices >= 0){ // if there is no index buffer, -1 is returned from fx-gltf - const fx::gltf::Accessor &indexAccessor = sceneObjects.accessors[objectPrimitive.indices]; - const fx::gltf::BufferView &indexBufferView = sceneObjects.bufferViews[indexAccessor.bufferView]; - const fx::gltf::Buffer &indexBuffer = sceneObjects.buffers[indexBufferView.buffer]; - - indexBufferData.resize(indexBufferView.byteLength); - { - const size_t off = indexBufferView.byteOffset; - const void *const ptr = ((char*)indexBuffer.data.data()) + off; - if (!memcpy(indexBufferData.data(), ptr, indexBufferView.byteLength)) { - vkcv_log(LogLevel::ERROR, "Copying index buffer data"); - return 0; - } - } - - indexType = getIndexType(indexAccessor.componentType); - if (indexType == IndexType::UNDEFINED){ - vkcv_log(LogLevel::ERROR, "Index Type undefined."); - return 0; - } - } - - const fx::gltf::BufferView& vertexBufferView = sceneObjects.bufferViews[posAccessor.bufferView]; - const fx::gltf::Buffer& vertexBuffer = sceneObjects.buffers[vertexBufferView.buffer]; - - // only copy relevant part of vertex data - uint32_t relevantBufferOffset = std::numeric_limits<uint32_t>::max(); - uint32_t relevantBufferEnd = 0; - for (const auto &attribute : vertexAttributes) { - relevantBufferOffset = std::min(attribute.offset, relevantBufferOffset); - const uint32_t attributeEnd = attribute.offset + attribute.length; - relevantBufferEnd = std::max(relevantBufferEnd, attributeEnd); // TODO: need to incorporate stride? - } - const uint32_t relevantBufferSize = relevantBufferEnd - relevantBufferOffset; - - // FIXME: This only works when all vertex attributes are in one buffer - std::vector<uint8_t> vertexBufferData; - vertexBufferData.resize(relevantBufferSize); - { - const void *const ptr = ((char*)vertexBuffer.data.data()) + relevantBufferOffset; - if (!memcpy(vertexBufferData.data(), ptr, relevantBufferSize)) { - vkcv_log(LogLevel::ERROR, "Copying vertex buffer data"); - return 0; - } - } - - // make vertex attributes relative to copied section - for (auto &attribute : vertexAttributes) { - attribute.offset -= relevantBufferOffset; - } - - const size_t numVertexGroups = objectMesh.primitives.size(); - vertexGroups.reserve(numVertexGroups); - - vertexGroups.push_back({ - static_cast<PrimitiveMode>(objectPrimitive.mode), - sceneObjects.accessors[objectPrimitive.indices].count, - posAccessor.count, - {indexType, indexBufferData}, - {vertexBufferData, vertexAttributes}, - {posAccessor.min[0], posAccessor.min[1], posAccessor.min[2]}, - {posAccessor.max[0], posAccessor.max[1], posAccessor.max[2]}, - static_cast<uint8_t>(objectPrimitive.material) - }); - - vertexGroupsIndices.push_back(groupCount); - groupCount++; - } - mesh.name = sceneObjects.meshes[i].name; - mesh.vertexGroups = vertexGroupsIndices; - - meshes.push_back(mesh); - } - - for(int m = 0; m < sceneObjects.nodes.size(); m++) { - meshes[sceneObjects.nodes[m].mesh].modelMatrix = computeModelMatrix(sceneObjects.nodes[m].translation, - sceneObjects.nodes[m].scale, - sceneObjects.nodes[m].rotation, - sceneObjects.nodes[m].matrix); - } - - if (sceneObjects.textures.size() > 0){ - textures.reserve(sceneObjects.textures.size()); - - for(int k = 0; k < sceneObjects.textures.size(); k++){ - const fx::gltf::Texture &tex = sceneObjects.textures[k]; - const fx::gltf::Image &img = sceneObjects.images[tex.source]; - std::string img_uri = dir + "/" + img.uri; - int w, h, c; - uint8_t *data = stbi_load(img_uri.c_str(), &w, &h, &c, 4); - c = 4; // FIXME hardcoded to always have RGBA channel layout - if (!data) { - vkcv_log(LogLevel::ERROR, "Loading texture image data.") - return 0; - } - const size_t byteLen = w * h * c; + /** + * This function fills the array of vertex attributes of a VertexGroup (usually + * part of a vkcv::asset::Mesh) object based on the description of attributes + * for a fx::gltf::Primitive. + * + * @param src The description of attribute objects from the fx-gltf library + * @param gltf The main glTF document + * @param dst The array of vertex attributes stored in an asset::Mesh object + * @return ASSET_ERROR when at least one VertexAttribute could not be + * constructed properly, otherwise ASSET_SUCCESS + */ + static int loadVertexAttributes(const fx::gltf::Attributes &src, + const std::vector<fx::gltf::Accessor> &accessors, + const std::vector<fx::gltf::BufferView> &bufferViews, + std::vector<VertexAttribute> &dst) { + for (const auto &attrib : src) { + VertexAttribute att {}; + + if (attrib.first == "POSITION") { + att.type = PrimitiveType::POSITION; + } else if (attrib.first == "NORMAL") { + att.type = PrimitiveType::NORMAL; + } else if (attrib.first == "TANGENT") { + att.type = PrimitiveType::TANGENT; + } else if (attrib.first == "TEXCOORD_0") { + att.type = PrimitiveType::TEXCOORD_0; + } else if (attrib.first == "TEXCOORD_1") { + att.type = PrimitiveType::TEXCOORD_1; + } else if (attrib.first == "COLOR_0") { + att.type = PrimitiveType::COLOR_0; + } else if (attrib.first == "COLOR_1") { + att.type = PrimitiveType::COLOR_1; + } else if (attrib.first == "JOINTS_0") { + att.type = PrimitiveType::JOINTS_0; + } else if (attrib.first == "WEIGHTS_0") { + att.type = PrimitiveType::WEIGHTS_0; + } else { + att.type = PrimitiveType::UNDEFINED; + } + + if (att.type != PrimitiveType::UNDEFINED) { + const fx::gltf::Accessor &accessor = accessors[attrib.second]; + const fx::gltf::BufferView &buf = bufferViews[accessor.bufferView]; + + att.offset = buf.byteOffset; + att.length = buf.byteLength; + att.stride = buf.byteStride; + att.componentType = static_cast<ComponentType>(accessor.componentType); + att.componentCount = getComponentCount(accessor.type); + + /* Assume tightly packed stride as not explicitly provided */ + if (att.stride == 0) { + att.stride = att.componentCount * getComponentSize(att.componentType); + } + } + + if ((att.type == PrimitiveType::UNDEFINED) || + (att.componentCount == 0)) { + return ASSET_ERROR; + } + + dst.push_back(att); + } + + return ASSET_SUCCESS; + } - std::vector<uint8_t> imgdata; - imgdata.resize(byteLen); - if (!memcpy(imgdata.data(), data, byteLen)) { - vkcv_log(LogLevel::ERROR, "Copying texture image data") - free(data); - return 0; - } - free(data); + /** + * This function calculates the modelMatrix out of the data given in the gltf file. + * It also checks, whether a modelMatrix was given. + * + * @param translation possible translation vector (default 0,0,0) + * @param scale possible scale vector (default 1,1,1) + * @param rotation possible rotation, given in quaternion (default 0,0,0,1) + * @param matrix possible modelmatrix (default identity) + * @return model Matrix as an array of floats + */ + static std::array<float, 16> calculateModelMatrix(const std::array<float, 3>& translation, + const std::array<float, 3>& scale, + const std::array<float, 4>& rotation, + const std::array<float, 16>& matrix) { + std::array<float, 16> modelMatrix = { + 1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1 + }; + + if (matrix != modelMatrix){ + return matrix; + } else { + // translation + modelMatrix[3] = translation[0]; + modelMatrix[7] = translation[1]; + modelMatrix[11] = -translation[2]; // flip Z to convert from GLTF to Vulkan + + // rotation and scale + auto a = rotation[3]; + auto q1 = rotation[0]; + auto q2 = rotation[1]; + auto q3 = rotation[2]; + + auto s = 2 / (a * a + q1 * q1 + q2 * q2 + q3 * q3); + + auto s1 = scale[0]; + auto s2 = scale[1]; + auto s3 = -scale[2]; // flip Z to convert from GLTF to Vulkan + + modelMatrix[0] = (1 - s * (q2 * q2 + q3 * q3)) * s1; + modelMatrix[1] = ( s * (q1 * q2 - q3 * a)) * s2; + modelMatrix[2] = ( s * (q1 * q3 + q2 * a)) * s3; + + modelMatrix[4] = ( s * (q1 * q2 + q3 * a)) * s1; + modelMatrix[5] = (1 - s * (q1 * q1 + q3 * q3)) * s2; + modelMatrix[6] = ( s * (q2 * q3 - q1 * a)) * s3; + + modelMatrix[8] = ( s * (q1 * q3 - q2 * a)) * s1; + modelMatrix[9] = ( s * (q2 * q3 + q1 * a)) * s2; + modelMatrix[10] = (1 - s * (q1 * q1 + q2 * q2)) * s3; + + return modelMatrix; + } + } + + static std::array<float, 16> multiplyMatrix(const std::array<float, 16>& matrix, + const std::array<float, 16>& other) { + std::array<float, 16> result; + size_t i, j, k; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + float sum = 0.0f; + + for (k = 0; k < 4; k++) { + sum += matrix[i * 4 + k] * other[k * 4 + j]; + } + + result[i * 4 + j] = sum; + } + } + + return result; + } - textures.push_back({ - 0, - static_cast<uint8_t>(c), - static_cast<uint16_t>(w), - static_cast<uint16_t>(h), - imgdata - }); + bool Material::hasTexture(const PBRTextureTarget target) const { + return textureMask & bitflag(target); + } - } - } + /** + * This function translates a given fx-gltf-sampler-wrapping-mode-enum to its vulkan sampler-adress-mode counterpart. + * @param mode: wrapping mode of a sampler given as fx-gltf-enum + * @return int vulkan-enum representing the same wrapping mode + */ + static int translateSamplerMode(const fx::gltf::Sampler::WrappingMode mode) { + switch (mode) { + case fx::gltf::Sampler::WrappingMode::ClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case fx::gltf::Sampler::WrappingMode::MirroredRepeat: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case fx::gltf::Sampler::WrappingMode::Repeat: + default: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + } + } - if (sceneObjects.materials.size() > 0){ - materials.reserve(sceneObjects.materials.size()); + /** + * If the glTF doesn't define samplers, we use the defaults defined by fx-gltf. + * The following are details about the glTF/OpenGL to Vulkan translation. + * magFilter (VkFilter?): + * GL_NEAREST -> VK_FILTER_NEAREST + * GL_LINEAR -> VK_FILTER_LINEAR + * minFilter (VkFilter?): + * mipmapMode (VkSamplerMipmapMode?): + * Vulkans minFilter and mipmapMode combined correspond to OpenGLs + * GL_minFilter_MIPMAP_mipmapMode: + * GL_NEAREST_MIPMAP_NEAREST: + * minFilter=VK_FILTER_NEAREST + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_NEAREST + * GL_LINEAR_MIPMAP_NEAREST: + * minFilter=VK_FILTER_LINEAR + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_NEAREST + * GL_NEAREST_MIPMAP_LINEAR: + * minFilter=VK_FILTER_NEAREST + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_LINEAR + * GL_LINEAR_MIPMAP_LINEAR: + * minFilter=VK_FILTER_LINEAR + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_LINEAR + * The modes of GL_LINEAR and GL_NEAREST have to be emulated using + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_NEAREST with specific minLOD and maxLOD: + * GL_LINEAR: + * minFilter=VK_FILTER_LINEAR + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_NEAREST + * minLOD=0, maxLOD=0.25 + * GL_NEAREST: + * minFilter=VK_FILTER_NEAREST + * mipmapMode=VK_SAMPLER_MIPMAP_MODE_NEAREST + * minLOD=0, maxLOD=0.25 + * Setting maxLOD=0 causes magnification to always be performed (using + * the defined magFilter), this may be valid if the min- and magFilter + * are equal, otherwise it won't be the expected behaviour from OpenGL + * and glTF; instead using maxLod=0.25 allows the minFilter to be + * performed while still always rounding to the base level. + * With other modes, minLOD and maxLOD default to: + * minLOD=0 + * maxLOD=VK_LOD_CLAMP_NONE + * wrapping: + * gltf has wrapS, wrapT with {clampToEdge, MirroredRepeat, Repeat} while + * Vulkan has addressModeU, addressModeV, addressModeW with values + * VK_SAMPLER_ADDRESS_MODE_{REPEAT,MIRRORED_REPEAT,CLAMP_TO_EDGE, + * CAMP_TO_BORDER,MIRROR_CLAMP_TO_EDGE} + * Translation from glTF to Vulkan is straight forward for the 3 existing + * modes, default is repeat, the other modes aren't available. + */ + static vkcv::asset::Sampler loadSampler(const fx::gltf::Sampler &src) { + Sampler dst {}; + + dst.minLOD = 0; + dst.maxLOD = VK_LOD_CLAMP_NONE; + + switch (src.minFilter) { + case fx::gltf::Sampler::MinFilter::None: + case fx::gltf::Sampler::MinFilter::Nearest: + dst.minFilter = VK_FILTER_NEAREST; + dst.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + dst.maxLOD = 0.25; + break; + case fx::gltf::Sampler::MinFilter::Linear: + dst.minFilter = VK_FILTER_LINEAR; + dst.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + dst.maxLOD = 0.25; + break; + case fx::gltf::Sampler::MinFilter::NearestMipMapNearest: + dst.minFilter = VK_FILTER_NEAREST; + dst.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + break; + case fx::gltf::Sampler::MinFilter::LinearMipMapNearest: + dst.minFilter = VK_FILTER_LINEAR; + dst.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + break; + case fx::gltf::Sampler::MinFilter::NearestMipMapLinear: + dst.minFilter = VK_FILTER_NEAREST; + dst.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + break; + case fx::gltf::Sampler::MinFilter::LinearMipMapLinear: + dst.minFilter = VK_FILTER_LINEAR; + dst.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + break; + default: + break; + } + + switch (src.magFilter) { + case fx::gltf::Sampler::MagFilter::None: + case fx::gltf::Sampler::MagFilter::Nearest: + dst.magFilter = VK_FILTER_NEAREST; + break; + case fx::gltf::Sampler::MagFilter::Linear: + dst.magFilter = VK_FILTER_LINEAR; + break; + default: + break; + } + + dst.addressModeU = translateSamplerMode(src.wrapS); + dst.addressModeV = translateSamplerMode(src.wrapT); + + // There is no information about wrapping for a third axis in glTF and + // we have to hardcode this value. + dst.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + + return dst; + } - for (int l = 0; l < sceneObjects.materials.size(); l++){ - fx::gltf::Material material = sceneObjects.materials[l]; - // TODO I think we shouldn't set the index for a texture target if - // it isn't defined. So we need to test first if there is a normal - // texture before assigning material.normalTexture.index. - // About the bitmask: If a normal texture is there, modify the - // materials textureMask like this: - // mat.textureMask |= bitflag(asset::normal); - materials.push_back({ - 0, - material.pbrMetallicRoughness.baseColorTexture.index, - material.pbrMetallicRoughness.metallicRoughnessTexture.index, - material.normalTexture.index, - material.occlusionTexture.index, - material.emissiveTexture.index, - { - material.pbrMetallicRoughness.baseColorFactor[0], - material.pbrMetallicRoughness.baseColorFactor[1], - material.pbrMetallicRoughness.baseColorFactor[2], - material.pbrMetallicRoughness.baseColorFactor[3] - }, - material.pbrMetallicRoughness.metallicFactor, - material.pbrMetallicRoughness.roughnessFactor, - material.normalTexture.scale, - material.occlusionTexture.strength, - { - material.emissiveFactor[0], - material.emissiveFactor[1], - material.emissiveFactor[2] - } + /** + * Initializes vertex groups of a Mesh, including copying the data to + * index- and vertex-buffers. + */ + static int loadVertexGroups(const fx::gltf::Mesh &objectMesh, + const fx::gltf::Document &sceneObjects, + Scene &scene, Mesh &mesh) { + mesh.vertexGroups.reserve(objectMesh.primitives.size()); + + for (const auto &objectPrimitive : objectMesh.primitives) { + VertexGroup vertexGroup; + + vertexGroup.vertexBuffer.attributes.reserve( + objectPrimitive.attributes.size() + ); + + if (ASSET_SUCCESS != loadVertexAttributes( + objectPrimitive.attributes, + sceneObjects.accessors, + sceneObjects.bufferViews, + vertexGroup.vertexBuffer.attributes)) { + vkcv_log(LogLevel::ERROR, "Failed to get vertex attributes of '%s'", + mesh.name.c_str()); + return ASSET_ERROR; + } + + // The accessor for the position attribute is used for + // 1) getting the vertex buffer view which is only needed to get + // the vertex buffer + // 2) getting the vertex count for the VertexGroup + // 3) getting the min/max of the bounding box for the VertexGroup + fx::gltf::Accessor posAccessor; + bool noPosition = true; + + for (auto const& attrib : objectPrimitive.attributes) { + if (attrib.first == "POSITION") { + posAccessor = sceneObjects.accessors[attrib.second]; + noPosition = false; + break; + } + } + + if (noPosition) { + vkcv_log(LogLevel::ERROR, "Position attribute not found from '%s'", + mesh.name.c_str()); + return ASSET_ERROR; + } + + const fx::gltf::Accessor& indexAccessor = sceneObjects.accessors[objectPrimitive.indices]; + + int indexBufferURI; + if (objectPrimitive.indices >= 0) { // if there is no index buffer, -1 is returned from fx-gltf + const fx::gltf::BufferView& indexBufferView = sceneObjects.bufferViews[indexAccessor.bufferView]; + const fx::gltf::Buffer& indexBuffer = sceneObjects.buffers[indexBufferView.buffer]; + + // Because the buffers are already preloaded into the memory by the gltf-library, + // it makes no sense to load them later on manually again into memory. + vertexGroup.indexBuffer.data.resize(indexBufferView.byteLength); + memcpy(vertexGroup.indexBuffer.data.data(), + indexBuffer.data.data() + indexBufferView.byteOffset, + indexBufferView.byteLength); + } else { + indexBufferURI = -1; + } + + vertexGroup.indexBuffer.type = getIndexType(indexAccessor.componentType); + + if (IndexType::UNDEFINED == vertexGroup.indexBuffer.type) { + vkcv_log(LogLevel::ERROR, "Index Type undefined or not supported."); + return ASSET_ERROR; + } + + if (posAccessor.bufferView >= sceneObjects.bufferViews.size()) { + vkcv_log(LogLevel::ERROR, "Access to bufferView out of bounds: %d", + posAccessor.bufferView); + return ASSET_ERROR; + } + const fx::gltf::BufferView& vertexBufferView = sceneObjects.bufferViews[posAccessor.bufferView]; + if (vertexBufferView.buffer >= sceneObjects.buffers.size()) { + vkcv_log(LogLevel::ERROR, "Access to buffer out of bounds: %d", + vertexBufferView.buffer); + return ASSET_ERROR; + } + const fx::gltf::Buffer& vertexBuffer = sceneObjects.buffers[vertexBufferView.buffer]; + + // only copy relevant part of vertex data + uint32_t relevantBufferOffset = std::numeric_limits<uint32_t>::max(); + uint32_t relevantBufferEnd = 0; + + for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { + relevantBufferOffset = std::min(attribute.offset, relevantBufferOffset); + relevantBufferEnd = std::max(relevantBufferEnd, attribute.offset + attribute.length); + } + + const uint32_t relevantBufferSize = relevantBufferEnd - relevantBufferOffset; + + vertexGroup.vertexBuffer.data.resize(relevantBufferSize); + memcpy(vertexGroup.vertexBuffer.data.data(), + vertexBuffer.data.data() + relevantBufferOffset, + relevantBufferSize); + + // make vertex attributes relative to copied section + for (auto& attribute : vertexGroup.vertexBuffer.attributes) { + attribute.offset -= relevantBufferOffset; + } + + vertexGroup.mode = static_cast<PrimitiveMode>(objectPrimitive.mode); + vertexGroup.numIndices = sceneObjects.accessors[objectPrimitive.indices].count; + vertexGroup.numVertices = posAccessor.count; + + memcpy(&(vertexGroup.min), posAccessor.min.data(), sizeof(vertexGroup.min)); + memcpy(&(vertexGroup.max), posAccessor.max.data(), sizeof(vertexGroup.max)); + + vertexGroup.materialIndex = static_cast<uint8_t>(objectPrimitive.material); + + mesh.vertexGroups.push_back(static_cast<int>(scene.vertexGroups.size())); + scene.vertexGroups.push_back(vertexGroup); + } + + return ASSET_SUCCESS; + } - }); - } - } + /** + * Returns an integer with specific bits set corresponding to the + * textures that appear in the given material. This mask is used in the + * vkcv::asset::Material struct and can be tested via the hasTexture + * method. + */ + static uint16_t generateTextureMask(fx::gltf::Material &material) { + uint16_t textureMask = 0; + + if (material.pbrMetallicRoughness.baseColorTexture.index >= 0) { + textureMask |= bitflag(asset::PBRTextureTarget::baseColor); + } + if (material.pbrMetallicRoughness.metallicRoughnessTexture.index >= 0) { + textureMask |= bitflag(asset::PBRTextureTarget::metalRough); + } + if (material.normalTexture.index >= 0) { + textureMask |= bitflag(asset::PBRTextureTarget::normal); + } + if (material.occlusionTexture.index >= 0) { + textureMask |= bitflag(asset::PBRTextureTarget::occlusion); + } + if (material.emissiveTexture.index >= 0) { + textureMask |= bitflag(asset::PBRTextureTarget::emissive); + } + + return textureMask; + } - scene = { - meshes, - vertexGroups, - materials, - textures, - samplers - }; + int probeScene(const std::filesystem::path& path, Scene& scene) { + fx::gltf::Document sceneObjects; + + try { + if (path.extension() == ".glb") { + sceneObjects = fx::gltf::LoadFromBinary( + path.string(), + { + fx::gltf::detail::DefaultMaxBufferCount, + fx::gltf::detail::DefaultMaxMemoryAllocation * 16, + fx::gltf::detail::DefaultMaxMemoryAllocation * 8 + } + ); + } else { + sceneObjects = fx::gltf::LoadFromText( + path.string(), + { + fx::gltf::detail::DefaultMaxBufferCount, + fx::gltf::detail::DefaultMaxMemoryAllocation, + fx::gltf::detail::DefaultMaxMemoryAllocation * 8 + } + ); + } + } catch (const std::system_error& err) { + recurseExceptionPrint(err, path.string()); + return ASSET_ERROR; + } catch (const std::exception& e) { + recurseExceptionPrint(e, path.string()); + return ASSET_ERROR; + } + + const auto directory = path.parent_path(); + + scene.meshes.clear(); + scene.vertexGroups.clear(); + scene.materials.clear(); + scene.textures.clear(); + scene.samplers.clear(); + + // file has to contain at least one mesh + if (sceneObjects.meshes.empty()) { + vkcv_log(LogLevel::ERROR, "No meshes found! (%s)", path.c_str()); + return ASSET_ERROR; + } else { + scene.meshes.reserve(sceneObjects.meshes.size()); + + for (size_t i = 0; i < sceneObjects.meshes.size(); i++) { + Mesh mesh; + mesh.name = sceneObjects.meshes[i].name; + + if (loadVertexGroups(sceneObjects.meshes[i], sceneObjects, scene, mesh) != ASSET_SUCCESS) { + vkcv_log(LogLevel::ERROR, "Failed to load vertex groups of '%s'! (%s)", + mesh.name.c_str(), path.c_str()); + return ASSET_ERROR; + } + + scene.meshes.push_back(mesh); + } + + std::vector< std::array<float, 16> > matrices; + std::vector< int32_t > parents; + + matrices.reserve(sceneObjects.nodes.size()); + parents.resize(sceneObjects.nodes.size(), -1); + + for (size_t i = 0; i < sceneObjects.nodes.size(); i++) { + const auto &node = sceneObjects.nodes[i]; + + matrices.push_back(calculateModelMatrix( + node.translation, + node.scale, + node.rotation, + node.matrix + )); + + for (int32_t child : node.children) + if ((child >= 0) && (child < parents.size())) + parents[child] = static_cast<int32_t>(i); + } + + std::vector< std::array<float, 16> > final_matrices; + final_matrices.reserve(matrices.size()); + + for (size_t i = 0; i < matrices.size(); i++) { + std::vector<int> order; + order.push_back(static_cast<int32_t>(i)); + + while (parents[ order[order.size() - 1] ] >= 0) + order.push_back(parents[ order[order.size() - 1] ]); + + std::array<float, 16> matrix = matrices[ order[order.size() - 1] ]; + + for (size_t j = order.size() - 1; j > 0; j--) { + const auto id = order[j - 1]; + const std::array<float, 16> matrix_other = matrices[ id ]; + + matrix = multiplyMatrix(matrix, matrix_other); + } + + final_matrices.push_back(matrix); + } + + for (size_t i = 0; i < sceneObjects.nodes.size(); i++) { + const auto &node = sceneObjects.nodes[i]; + + if ((node.mesh >= 0) && (node.mesh < scene.meshes.size())) { + scene.meshes[node.mesh].modelMatrix = final_matrices[i]; + } + } + } + + if (sceneObjects.samplers.empty()) { + vkcv_log(LogLevel::WARNING, "No samplers found! (%s)", path.c_str()); + } else { + scene.samplers.reserve(sceneObjects.samplers.size()); + + for (const auto &samplerObject : sceneObjects.samplers) { + scene.samplers.push_back(loadSampler(samplerObject)); + } + } + + if (sceneObjects.textures.empty()) { + vkcv_log(LogLevel::WARNING, "No textures found! (%s)", path.c_str()); + } else { + scene.textures.reserve(sceneObjects.textures.size()); + + for (const auto& textureObject : sceneObjects.textures) { + Texture texture; + + if (textureObject.sampler < 0) { + texture.sampler = -1; + } else + if (static_cast<size_t>(textureObject.sampler) >= scene.samplers.size()) { + vkcv_log(LogLevel::ERROR, "Sampler of texture '%s' missing (%s)", + textureObject.name.c_str(), path.c_str()); + return ASSET_ERROR; + } else { + texture.sampler = textureObject.sampler; + } + + if ((textureObject.source < 0) || + (static_cast<size_t>(textureObject.source) >= sceneObjects.images.size())) { + vkcv_log(LogLevel::ERROR, "Failed to load texture '%s' (%s)", + textureObject.name.c_str(), path.c_str()); + return ASSET_ERROR; + } + + const auto& image = sceneObjects.images[textureObject.source]; + + if (image.uri.empty()) { + const fx::gltf::BufferView bufferView = sceneObjects.bufferViews[image.bufferView]; + + texture.path.clear(); + texture.data.resize(bufferView.byteLength); + memcpy(texture.data.data(), + sceneObjects.buffers[bufferView.buffer].data.data() + bufferView.byteOffset, + bufferView.byteLength); + } else { + texture.path = directory / image.uri; + } + + scene.textures.push_back(texture); + } + } + + if (sceneObjects.materials.empty()) { + vkcv_log(LogLevel::WARNING, "No materials found! (%s)", path.c_str()); + } else { + scene.materials.reserve(sceneObjects.materials.size()); + + for (auto material : sceneObjects.materials) { + scene.materials.push_back({ + generateTextureMask(material), + material.pbrMetallicRoughness.baseColorTexture.index, + material.pbrMetallicRoughness.metallicRoughnessTexture.index, + material.normalTexture.index, + material.occlusionTexture.index, + material.emissiveTexture.index, + { + material.pbrMetallicRoughness.baseColorFactor[0], + material.pbrMetallicRoughness.baseColorFactor[1], + material.pbrMetallicRoughness.baseColorFactor[2], + material.pbrMetallicRoughness.baseColorFactor[3] + }, + material.pbrMetallicRoughness.metallicFactor, + material.pbrMetallicRoughness.roughnessFactor, + material.normalTexture.scale, + material.occlusionTexture.strength, + { + material.emissiveFactor[0], + material.emissiveFactor[1], + material.emissiveFactor[2] + } + }); + } + } + + return ASSET_SUCCESS; + } + + /** + * Loads and decodes the textures data based on the textures file path. + * The path member is the only one that has to be initialized before + * calling this function, the others (width, height, channels, data) + * are set by this function and the sampler is of no concern here. + */ + static int loadTextureData(Texture& texture) { + if ((texture.width > 0) && (texture.height > 0) && (texture.channels > 0) && + (!texture.data.empty())) { + return ASSET_SUCCESS; // Texture data was loaded already! + } + + uint8_t* data; + + if (texture.path.empty()) { + data = stbi_load_from_memory( + reinterpret_cast<uint8_t*>(texture.data.data()), + static_cast<int>(texture.data.size()), + &texture.width, + &texture.height, + &texture.channels, 4 + ); + } else { + data = stbi_load( + texture.path.string().c_str(), + &texture.width, + &texture.height, + &texture.channels, 4 + ); + } + + if (!data) { + vkcv_log(LogLevel::ERROR, "Texture could not be loaded from '%s'", + texture.path.c_str()); + + texture.width = 0; + texture.height = 0; + texture.channels = 0; + return ASSET_ERROR; + } + + texture.data.resize(texture.width * texture.height * 4); + memcpy(texture.data.data(), data, texture.data.size()); + stbi_image_free(data); + + return ASSET_SUCCESS; + } - return 1; -} + int loadMesh(Scene &scene, int index) { + if ((index < 0) || (static_cast<size_t>(index) >= scene.meshes.size())) { + vkcv_log(LogLevel::ERROR, "Mesh index out of range: %d", index); + return ASSET_ERROR; + } + + const Mesh &mesh = scene.meshes[index]; + + for (const auto& vg : mesh.vertexGroups) { + const VertexGroup &vertexGroup = scene.vertexGroups[vg]; + const Material& material = scene.materials[vertexGroup.materialIndex]; + + if (material.hasTexture(PBRTextureTarget::baseColor)) { + const int result = loadTextureData(scene.textures[material.baseColor]); + if (ASSET_SUCCESS != result) { + vkcv_log(LogLevel::ERROR, "Failed loading baseColor texture of mesh '%s'", + mesh.name.c_str()) + return result; + } + } + + if (material.hasTexture(PBRTextureTarget::metalRough)) { + const int result = loadTextureData(scene.textures[material.metalRough]); + if (ASSET_SUCCESS != result) { + vkcv_log(LogLevel::ERROR, "Failed loading metalRough texture of mesh '%s'", + mesh.name.c_str()) + return result; + } + } + + if (material.hasTexture(PBRTextureTarget::normal)) { + const int result = loadTextureData(scene.textures[material.normal]); + if (ASSET_SUCCESS != result) { + vkcv_log(LogLevel::ERROR, "Failed loading normal texture of mesh '%s'", + mesh.name.c_str()) + return result; + } + } + + if (material.hasTexture(PBRTextureTarget::occlusion)) { + const int result = loadTextureData(scene.textures[material.occlusion]); + if (ASSET_SUCCESS != result) { + vkcv_log(LogLevel::ERROR, "Failed loading occlusion texture of mesh '%s'", + mesh.name.c_str()) + return result; + } + } + + if (material.hasTexture(PBRTextureTarget::emissive)) { + const int result = loadTextureData(scene.textures[material.emissive]); + if (ASSET_SUCCESS != result) { + vkcv_log(LogLevel::ERROR, "Failed loading emissive texture of mesh '%s'", + mesh.name.c_str()) + return result; + } + } + } + + return ASSET_SUCCESS; + } + + int loadScene(const std::filesystem::path &path, Scene &scene) { + int result = probeScene(path, scene); + size_t i; + + if (result != ASSET_SUCCESS) { + vkcv_log(LogLevel::ERROR, "Loading scene failed '%s'", + path.c_str()); + return result; + } + + /* Preloading the textures of the scene to improve performance */ + #pragma omp parallel for shared(scene.textures) private(i) + for (i = 0; i < scene.textures.size(); i++) { + loadTextureData(scene.textures[i]); + } + + for (i = 0; i < scene.meshes.size(); i++) { + result = loadMesh(scene, static_cast<int>(i)); + + if (result != ASSET_SUCCESS) { + vkcv_log(LogLevel::ERROR, "Loading mesh with index %d failed '%s'", + static_cast<int>(i), path.c_str()); + return result; + } + } + + return ASSET_SUCCESS; + } + + Texture loadTexture(const std::filesystem::path& path) { + Texture texture; + texture.path = path; + texture.sampler = -1; + + if (loadTextureData(texture) != ASSET_SUCCESS) { + texture.path.clear(); + texture.w = texture.h = texture.channels = 0; + texture.data.clear(); + } + return texture; + } + + VertexBufferBindings loadVertexBufferBindings(const std::vector<VertexAttribute> &attributes, + const BufferHandle &buffer, + const std::vector<PrimitiveType> &types) { + VertexBufferBindings bindings; + + for (const auto& type : types) { + const VertexAttribute* attribute = nullptr; + + for (const auto& attr : attributes) { + if (type == attr.type) { + attribute = &(attr); + break; + } + } + + if (!attribute) { + vkcv_log(LogLevel::ERROR, "Missing primitive type in vertex attributes"); + break; + } + + bindings.push_back(vkcv::vertexBufferBinding(buffer, attribute->offset)); + } + + return bindings; + } } diff --git a/modules/camera/CMakeLists.txt b/modules/camera/CMakeLists.txt index 41617da3905a118a7f963a95f2dd71d259e0fd70..0bc5ce6cbbab55abebce376820f2ba398267cdd5 100644 --- a/modules/camera/CMakeLists.txt +++ b/modules/camera/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(vkcv_camera) # setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(vkcv_camera_source ${PROJECT_SOURCE_DIR}/src) @@ -23,19 +23,33 @@ set(vkcv_camera_sources ) # adding source files to the project -add_library(vkcv_camera STATIC ${vkcv_camera_sources}) +add_library(vkcv_camera ${vkcv_build_attribute} ${vkcv_camera_sources}) # Setup some path variables to load libraries set(vkcv_camera_lib lib) set(vkcv_camera_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_camera_lib}) -include(config/GLM.cmake) - -target_link_libraries(vkcv_camera PUBLIC ${vkcv_camera_libraries} vkcv) +target_link_libraries(vkcv_camera PUBLIC + ${vkcv_camera_libraries} + vkcv + vkcv_geometry +) -target_include_directories(vkcv_camera SYSTEM BEFORE PRIVATE ${vkcv_camera_includes} ${vkcv_include}) +target_include_directories(vkcv_camera SYSTEM BEFORE PRIVATE + ${vkcv_camera_includes} + ${vkcv_include} + ${vkcv_includes} + ${vkcv_geometry_include} +) # add the own include directory for public headers target_include_directories(vkcv_camera BEFORE PUBLIC ${vkcv_camera_include} ${vkcv_camera_includes}) - target_compile_definitions(vkcv_camera PUBLIC ${vkcv_camera_definitions}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_camera_include}) + list(APPEND vkcv_modules_libraries vkcv_camera) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/camera/README.md b/modules/camera/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c88253a1fc343337d1b2b0178306ed9c04505fed --- /dev/null +++ b/modules/camera/README.md @@ -0,0 +1,7 @@ +# Camera + +A VkCV module to manage cameras and their handle view and projection matrices + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__camera.html) to this module. diff --git a/modules/camera/config/GLM.cmake b/modules/camera/config/GLM.cmake deleted file mode 100644 index efd6444451100b912aa0b5b4a532dc8f448b0b40..0000000000000000000000000000000000000000 --- a/modules/camera/config/GLM.cmake +++ /dev/null @@ -1,21 +0,0 @@ - -find_package(glm QUIET) - -if (glm_FOUND) - list(APPEND vkcv_camera_includes ${GLM_INCLUDE_DIRS}) - list(APPEND vkcv_camera_libraries glm) - - list(APPEND vkcv_camera_definitions GLM_DEPTH_ZERO_TO_ONE) - list(APPEND vkcv_camera_definitions GLM_FORCE_LEFT_HANDED) -else() - if (EXISTS "${vkcv_camera_lib_path}/glm") - add_subdirectory(${vkcv_camera_lib}/glm) - - list(APPEND vkcv_camera_libraries glm) - - list(APPEND vkcv_camera_definitions GLM_DEPTH_ZERO_TO_ONE) - list(APPEND vkcv_camera_definitions GLM_FORCE_LEFT_HANDED) - else() - message(WARNING "GLM is required..! Update the submodules!") - endif () -endif () diff --git a/modules/camera/include/vkcv/camera/Camera.hpp b/modules/camera/include/vkcv/camera/Camera.hpp index ce32d3f8a0c6ee3e0dd882f24a9ac2d12c14a024..b02e9cfb2d40ae6c2500a85572f894c64652eafd 100644 --- a/modules/camera/include/vkcv/camera/Camera.hpp +++ b/modules/camera/include/vkcv/camera/Camera.hpp @@ -1,11 +1,25 @@ #pragma once +/** + * @authors Vanessa Karolek, Josch Morgenstern, Sebastian Gaida, Katharina Krämer, Tobias Frisch, Alexander Gauggel + * @file include/vkcv/camera/Camera.hpp + * @brief Camera class of the camera module for the vkcv framework. + */ #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_access.hpp> +#include <glm/vec3.hpp> +#include <glm/gtc/type_ptr.hpp> +#include <glm/mat4x4.hpp> namespace vkcv::camera { + /** + * @defgroup vkcv_camera Camera Module + * A module to manage intrinsic and extrinsic parameters with camera instances. + * @{ + */ + /** * @brief Used to create a camera which governs the view and projection matrices. */ @@ -20,9 +34,6 @@ namespace vkcv::camera { glm::vec3 m_up; glm::vec3 m_position; glm::vec3 m_center; - - float m_pitch; - float m_yaw; /** * @brief Sets the view matrix of the camera to @p view @@ -58,9 +69,10 @@ namespace vkcv::camera { void setPerspective(float fov, float ratio, float near, float far); /** - * @brief Gets the view matrix of the camera + * @brief Returns the view matrix of the camera * @return The view matrix of the camera */ + [[nodiscard]] const glm::mat4& getView() const; /** @@ -72,28 +84,31 @@ namespace vkcv::camera { void lookAt(const glm::vec3& position, const glm::vec3& center, const glm::vec3& up); /** - * @brief Gets the current projection of the camera + * @brief Returns the current projection of the camera * @return The current projection matrix */ + [[nodiscard]] const glm::mat4& getProjection() const; /** - * @brief Gets the model-view-projection matrix of the camera with y-axis-correction applied + * @brief Returns the model-view-projection matrix of the camera with y-axis-correction applied * @return The model-view-projection matrix */ + [[nodiscard]] glm::mat4 getMVP() const; /** - * @brief Gets the near and far bounds of the view frustum of the camera. + * @brief Returns the near and far bounds of the view frustum of the camera. * @param[out] near The near bound of the view frustum * @param[out] far The far bound of the view frustum */ void getNearFar(float &near, float &far) const; /** - * @brief Gets the current field of view of the camera in radians + * @brief Returns the current field of view of the camera in radians * @return[in] The current field of view in radians */ + [[nodiscard]] float getFov() const; /** @@ -103,9 +118,10 @@ namespace vkcv::camera { void setFov(float fov); /** - * @brief Gets the current aspect ratio of the camera + * @brief Returns the current aspect ratio of the camera * @return The current aspect ratio of the camera */ + [[nodiscard]] float getRatio() const; /** @@ -122,9 +138,10 @@ namespace vkcv::camera { void setNearFar(float near, float far); /** - * @brief Gets the current front vector of the camera in world space + * @brief Returns the current front vector of the camera in world space * @return The current front vector of the camera */ + [[nodiscard]] glm::vec3 getFront() const; /** @@ -134,9 +151,10 @@ namespace vkcv::camera { void setFront(const glm::vec3& front); /** - * @brief Gets the current position of the camera in world space + * @brief Returns the current position of the camera in world space * @return The current position of the camera in world space */ + [[nodiscard]] const glm::vec3& getPosition() const; /** @@ -146,9 +164,10 @@ namespace vkcv::camera { void setPosition( const glm::vec3& position ); /** - * @brief Gets the center point. + * @brief Returns the center point. * @return The center point. */ + [[nodiscard]] const glm::vec3& getCenter() const; /** @@ -156,11 +175,26 @@ namespace vkcv::camera { * @param[in] center The new center point. */ void setCenter(const glm::vec3& center); + + /** + * @brief Returns the angles of the camera. + * @param[out] pitch The pitch value in radians + * @param[out] yaw The yaw value in radians + */ + void getAngles(float& pitch, float& yaw); + + /** + * @brief Sets the angles of the camera. + * @param pitch The new pitch value in radians + * @param yaw The new yaw value in radians + */ + void setAngles(float pitch, float yaw); /** - * @brief Gets the pitch value of the camera in degrees. + * @brief Returns the pitch value of the camera in degrees. * @return The pitch value in degrees. */ + [[nodiscard]] float getPitch() const; /** @@ -170,9 +204,10 @@ namespace vkcv::camera { void setPitch(float pitch); /** - * @brief Gets the yaw value of the camera in degrees. + * @brief Returns the yaw value of the camera in degrees. * @return The yaw value in degrees. */ + [[nodiscard]] float getYaw() const; /** @@ -182,9 +217,10 @@ namespace vkcv::camera { void setYaw(float yaw); /** - * @brief Gets the up vector. + * @brief Returns the up vector. * @return The up vector. */ + [[nodiscard]] const glm::vec3& getUp() const; /** @@ -192,6 +228,9 @@ namespace vkcv::camera { * @param[in] up The new up vector. */ void setUp(const glm::vec3 &up); + }; + /** @} */ + } diff --git a/modules/camera/include/vkcv/camera/CameraController.hpp b/modules/camera/include/vkcv/camera/CameraController.hpp index 90fc97401851851194ec89a10757bbfb1453990d..0b5a17faf178a01f6e1460fb5b86bff901576c68 100644 --- a/modules/camera/include/vkcv/camera/CameraController.hpp +++ b/modules/camera/include/vkcv/camera/CameraController.hpp @@ -1,16 +1,27 @@ #pragma once +/** + * @authors Vanessa Karolek, Josch Morgenstern, Tobias Frisch + * @file include/vkcv/camera/CameraController.hpp + * @brief CameraController class of the camera module for the vkcv framework. A camera object is controlled by a camera + * controller object. Using inheritance of this base class a camera controller object defines a specific control + * behaviour of the camera object in the scene. + */ #include "Camera.hpp" #include "vkcv/Window.hpp" namespace vkcv::camera { + /** + * @addtogroup vkcv_camera + * @{ + */ + /** * @brief Used as a base class for defining camera controller classes with different behaviors, e.g. the - * #PilotCameraController. + * PilotCameraController. */ class CameraController { - public: /** @@ -69,4 +80,6 @@ namespace vkcv::camera { virtual void gamepadCallback(int gamepadIndex, Camera &camera, double frametime) = 0; }; + /** @} */ + } \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/CameraHandle.hpp b/modules/camera/include/vkcv/camera/CameraHandle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b681f93cb0e753af8d383f890ccc263ed650f998 --- /dev/null +++ b/modules/camera/include/vkcv/camera/CameraHandle.hpp @@ -0,0 +1,31 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file include/vkcv/camera/CameraHandle.hpp + * @brief CameraHandle class of the camera module for the vkcv framework. + */ + +#include <vkcv/Handles.hpp> + +namespace vkcv::camera { + + /** + * @addtogroup vkcv_camera + * @{ + */ + + class CameraManager; + + /** + * @brief Handle class for cameras. + */ + class CameraHandle : public Handle { + friend class CameraManager; + + private: + using Handle::Handle; + }; + + /** @} */ + +} \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp index aa8880e4040a93d8d04a73f1c1f972033a4e8eab..01b11eedcffcbbf588fa93c48f4762e4f4adbdc6 100644 --- a/modules/camera/include/vkcv/camera/CameraManager.hpp +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -1,23 +1,28 @@ #pragma once - +/** + * @authors Vanessa Karolek, Josch Morgenstern, Sebastian Gaida, Katharina Krämer, Tobias Frisch, Alexander Gauggel + * @file include/vkcv/camera/CameraManager.hpp + * @brief CameraManager class of the camera module for the vkcv framework. The camera manager manages several camera + * controller objects. Camera objects can be created and bound to a specific camera controller via this class. + */ + +#include "CameraHandle.hpp" +#include "ControllerType.hpp" #include "PilotCameraController.hpp" #include "TrackballCameraController.hpp" #include "CameraController.hpp" + #include "vkcv/Window.hpp" + #include <GLFW/glfw3.h> #include <functional> namespace vkcv::camera { /** - * @brief Used for specifying existing types of camera controllers when adding a new controller object to the - * #CameraManager. + * @addtogroup vkcv_camera + * @{ */ - enum class ControllerType { - NONE, - PILOT, - TRACKBALL, - }; /** * @brief Used for managing an arbitrary amount of camera controllers. @@ -34,7 +39,7 @@ namespace vkcv::camera { Window& m_window; std::vector<Camera> m_cameras; std::vector<ControllerType> m_cameraControllerTypes; - uint32_t m_activeCameraIndex; + uint64_t m_activeCameraIndex; PilotCameraController m_pilotController; TrackballCameraController m_trackController; @@ -98,26 +103,11 @@ namespace vkcv::camera { void gamepadCallback(int gamepadIndex); /** - * @brief Gets a camera controller object of specified @p controllerType. + * @brief Returns a camera controller object of specified @p controllerType. * @param[in] controllerType The type of the camera controller. * @return The specified camera controller object. */ CameraController& getControllerByType(ControllerType controllerType); - - /** - * @briof A method to get the currently active controller for the active camera. - * @return Reference to the active #CameraController - */ - CameraController& getActiveController(); - - /** - * @brief Returns 'true' if the camera has a controller. - * - * @param cameraIndex - * @return true - * @return false - */ - bool cameraHasController(uint32_t cameraIndex); public: @@ -125,7 +115,7 @@ namespace vkcv::camera { * @brief The constructor of the #CameraManager. * @param[in] window The window. */ - CameraManager(Window &window); + explicit CameraManager(Window &window); /** * @brief The destructor of the #CameraManager. Destroying the #CameraManager leads to deletion of all stored @@ -137,67 +127,74 @@ namespace vkcv::camera { * @brief Adds a new camera object to the #CameraManager and binds it to a camera controller object of specified * @p controllerType. * @param[in] controllerType The type of the camera controller. - * @return The index of the newly created camera object. + * @return The handle of the newly created camera object. */ - uint32_t addCamera(ControllerType controllerType = ControllerType::NONE); + CameraHandle addCamera(ControllerType controllerType = ControllerType::NONE); /** * @brief Adds a new camera object to the #CameraManager and binds it to a camera controller object of specified * @p controllerType. * @param[in] controllerType The type of the camera controller. * @param[in] camera The new camera object. - * @return The index of the newly bound camera object. + * @return The handle of the newly bound camera object. */ - uint32_t addCamera(ControllerType controllerType, const Camera& camera); + CameraHandle addCamera(ControllerType controllerType, const Camera& camera); /** - * @brief Gets the stored camera object located at @p cameraIndex. - * @param[in] cameraIndex The camera index. - * @return The camera object at @p cameraIndex. - * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + * @brief Returns the stored camera object located by @p cameraHandle. + * @param[in] cameraHandle The camera handle. + * @return The camera object by @p cameraHandle. + * @throws std::runtime_error If @p cameraHandle is not a valid camera handle. */ - Camera& getCamera(uint32_t cameraIndex); + [[nodiscard]] + Camera& getCamera(const CameraHandle& cameraHandle); /** - * @brief Gets the stored camera object set as the active camera. + * @brief Returns the stored camera object set as the active camera. * @return The active camera. */ + [[nodiscard]] Camera& getActiveCamera(); /** - * @brief Sets the stored camera object located at @p cameraIndex as the active camera. - * @param[in] cameraIndex The camera index. - * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + * @brief Sets the stored camera object located at @p cameraHandle as the active camera. + * @param[in] cameraHandle The camera handle. + * @throws std::runtime_error If @p cameraHandle is not a valid camera handle. */ - void setActiveCamera(uint32_t cameraIndex); + void setActiveCamera(const CameraHandle& cameraHandle); /** - * @brief Gets the index of the stored active camera object. - * @return The active camera index. + * @brief Returns the handle of the stored active camera object. + * @return The active camera handle. */ - uint32_t getActiveCameraIndex() const; + [[nodiscard]] + CameraHandle getActiveCameraHandle() const; /** - * @brief Binds a stored camera object located at @p cameraIndex to a camera controller of specified + * @brief Binds a stored camera object located by @p cameraHandle to a camera controller of specified * @p controllerType. - * @param[in] cameraIndex The camera index. + * @param[in] cameraHandle The camera handle. * @param[in] controllerType The type of the camera controller. - * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + * @throws std::runtime_error If @p cameraHandle is not a valid camera handle. */ - void setControllerType(uint32_t cameraIndex, ControllerType controllerType); + void setControllerType(const CameraHandle& cameraHandle, ControllerType controllerType); /** - * @brief Gets the currently bound camera controller type of the stored camera object located at @p cameraIndex. - * @param[in] cameraIndex The camera index. + * @brief Returns the currently bound camera controller type of the stored camera object located by @p cameraHandle. + * @param[in] cameraHandle The camera handle. * @return The type of the camera controller of the specified camera object. - * @throws std::runtime_error If @p cameraIndex is not a valid camera index. + * @throws std::runtime_error If @p cameraHandle is not a valid camera handle. */ - ControllerType getControllerType(uint32_t cameraIndex); - + [[nodiscard]] + ControllerType getControllerType(const CameraHandle& cameraHandle); + /** * @brief Updates all stored camera controllers in respect to @p deltaTime. * @param[in] deltaTime The time that has passed since last update. */ void update(double deltaTime); }; + + /** @} */ + } diff --git a/modules/camera/include/vkcv/camera/ControllerType.hpp b/modules/camera/include/vkcv/camera/ControllerType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3e85c1f9749ced9680030e95d08a1c6bc39aafb3 --- /dev/null +++ b/modules/camera/include/vkcv/camera/ControllerType.hpp @@ -0,0 +1,27 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file include/vkcv/camera/ControllerType.hpp + * @brief ControllerType enum of the camera module for the vkcv framework. + */ + +namespace vkcv::camera { + + /** + * @addtogroup vkcv_camera + * @{ + */ + + /** + * @brief Used for specifying existing types of camera controllers when adding a new controller object to the + * #CameraManager. + */ + enum class ControllerType { + NONE, + PILOT, + TRACKBALL + }; + + /** @} */ + +} diff --git a/modules/camera/include/vkcv/camera/PilotCameraController.hpp b/modules/camera/include/vkcv/camera/PilotCameraController.hpp index 2b64cdc0dd3045714aba7b3b7c6241af2337c706..40ccd56bd3033b7e831bfc1f8d24b5a646eb0efd 100644 --- a/modules/camera/include/vkcv/camera/PilotCameraController.hpp +++ b/modules/camera/include/vkcv/camera/PilotCameraController.hpp @@ -1,9 +1,20 @@ #pragma once +/** + * @authors Vanessa Karolek, Josch Morgenstern, Tobias Frisch + * @file include/vkcv/camera/PilotCameraController.hpp + * @brief PilotCameraController class of the camera module for the vkcv framework. This class inherits from the base + * class @#CameraController and enables camera objects to be moved freely within the scene. + */ #include <vkcv/camera/CameraController.hpp> namespace vkcv::camera { + /** + * @addtogroup vkcv_camera + * @{ + */ + /** * @brief Used to move around a camera object in world space. */ @@ -29,42 +40,6 @@ namespace vkcv::camera { float m_fov_min; float m_fov_max; - /** - * @brief Indicates forward movement of the camera depending on the performed @p action. - * @param[in] action The performed action. - */ - void moveForward(int action); - - /** - * @brief Indicates backward movement of the camera depending on the performed @p action. - * @param[in] action The performed action. - */ - void moveBackward(int action); - - /** - * @brief Indicates left movement of the camera depending on the performed @p action. - * @param[in] action The performed action. - */ - void moveLeft(int action); - - /** - * @brief Indicates right movement of the camera depending on the performed @p action. - * @param[in] action The performed action. - */ - void moveRight(int action); - - /** - * @brief Indicates upward movement of the camera depending on the performed @p action. - * @param[in] action The performed action. - */ - void moveUpward(int action); - - /** - * @brief Indicates downward movement of the camera depending on the performed @p action. - * @param[in] action The performed action. - */ - void moveDownward(int action); - public: /** @@ -98,7 +73,7 @@ namespace vkcv::camera { * @param[in] deltaTime The time that has passed since last update. * @param[in] camera The camera object. */ - void updateCamera(double deltaTime, Camera &camera); + void updateCamera(double deltaTime, Camera &camera) override; /** * @brief A callback function for key events. Currently, 3D camera movement via W, A, S, D, E, Q are supported. @@ -108,7 +83,7 @@ namespace vkcv::camera { * @param[in] mods The modifier bits. * @param[in] camera The camera object. */ - void keyCallback(int key, int scancode, int action, int mods, Camera &camera); + void keyCallback(int key, int scancode, int action, int mods, Camera &camera) override; /** * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view @@ -117,7 +92,7 @@ namespace vkcv::camera { * @param[in] offsetY The offset in vertical direction. * @param[in] camera The camera object. */ - void scrollCallback(double offsetX, double offsetY, Camera &camera); + void scrollCallback(double offsetX, double offsetY, Camera &camera) override; /** * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the camera, @@ -126,7 +101,7 @@ namespace vkcv::camera { * @param[in] y The vertical mouse position * @param[in] camera The camera object. */ - void mouseMoveCallback(double x, double y, Camera &camera); + void mouseMoveCallback(double x, double y, Camera &camera) override; /** * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the @@ -136,7 +111,7 @@ namespace vkcv::camera { * @param[in] mods The modifier bits * @param[in] camera The camera object. */ - void mouseButtonCallback(int button, int action, int mods, Camera &camera); + void mouseButtonCallback(int button, int action, int mods, Camera &camera) override; /** * @brief A callback function for gamepad input events. @@ -144,7 +119,9 @@ namespace vkcv::camera { * @param camera The camera object. * @param frametime The current frametime. */ - void gamepadCallback(int gamepadIndex, Camera &camera, double frametime); + void gamepadCallback(int gamepadIndex, Camera &camera, double frametime) override; }; + /** @} */ + } \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp index 4166bda9f6cb62e4c8f1b650557b00c6ec94b2a1..5df56fd839f1c654541baaa75bd3d9553cdedd62 100644 --- a/modules/camera/include/vkcv/camera/TrackballCameraController.hpp +++ b/modules/camera/include/vkcv/camera/TrackballCameraController.hpp @@ -1,19 +1,30 @@ #pragma once +/** + * @authors Vanessa Karolek, Josch Morgenstern, Sebastian Gaida,Tobias Frisch + * @file include/vkcv/camera/TrackballCameraController.hpp + * @brief TrackballCameraController class of the camera module for the vkcv framework. This class inherits from the base + * class @#CameraController and enables camera objects to be orbited around a specific center point. + */ #include "CameraController.hpp" namespace vkcv::camera { + /** + * @addtogroup vkcv_camera + * @{ + */ + /** * @brief Used to orbit a camera around its center point. */ class TrackballCameraController final : public CameraController { private: bool m_rotationActive; - float m_cameraSpeed; float m_scrollSensitivity; - float m_radius; + float m_pitch; + float m_yaw; /** * @brief Updates the current radius of @p camera in respect to the @p offset. @@ -34,12 +45,6 @@ namespace vkcv::camera { */ ~TrackballCameraController() = default; - /** - * @brief Sets @p radius as the new radius for orbiting around the camera's center point. - * @param[in] radius The new radius. - */ - void setRadius(const float radius); - /** * @brief Pans the view of @p camera according to the pitch and yaw values and additional offsets @p xOffset * and @p yOffset. @@ -54,7 +59,7 @@ namespace vkcv::camera { * @param[in] deltaTime The time that has passed since last update. * @param[in] camera The camera object */ - void updateCamera(double deltaTime, Camera &camera); + void updateCamera(double deltaTime, Camera &camera) override; /** * @brief A callback function for key events. Currently, the trackball camera does not support camera movement. @@ -65,7 +70,7 @@ namespace vkcv::camera { * @param[in] mods The modifier bits. * @param[in] camera The camera object. */ - void keyCallback(int key, int scancode, int action, int mods, Camera &camera); + void keyCallback(int key, int scancode, int action, int mods, Camera &camera) override; /** * @brief A callback function for mouse scrolling events. Currently, this leads to changes in the field of view @@ -74,7 +79,7 @@ namespace vkcv::camera { * @param[in] offsetY The offset in vertical direction. * @param[in] camera The camera object. */ - void scrollCallback(double offsetX, double offsetY, Camera &camera); + void scrollCallback(double offsetX, double offsetY, Camera &camera) override; /** * @brief A callback function for mouse movement events. Currently, this leads to panning the view of the @@ -83,7 +88,7 @@ namespace vkcv::camera { * @param[in] yoffset The vertical mouse position. * @param[in] camera The camera object. */ - void mouseMoveCallback(double xoffset, double yoffset, Camera &camera); + void mouseMoveCallback(double xoffset, double yoffset, Camera &camera) override; /** * @brief A callback function for mouse button events. Currently, the right mouse button enables panning the @@ -93,7 +98,7 @@ namespace vkcv::camera { * @param[in] mods The modifier bits. * @param[in] camera The camera object. */ - void mouseButtonCallback(int button, int action, int mods, Camera &camera); + void mouseButtonCallback(int button, int action, int mods, Camera &camera) override; /** * @brief A callback function for gamepad input events. @@ -101,7 +106,9 @@ namespace vkcv::camera { * @param camera The camera object. * @param frametime The current frametime. */ - void gamepadCallback(int gamepadIndex, Camera &camera, double frametime); + void gamepadCallback(int gamepadIndex, Camera &camera, double frametime) override; }; + /** @} */ + } \ No newline at end of file diff --git a/modules/camera/lib/glm b/modules/camera/lib/glm deleted file mode 160000 index 66062497b104ca7c297321bd0e970869b1e6ece5..0000000000000000000000000000000000000000 --- a/modules/camera/lib/glm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 66062497b104ca7c297321bd0e970869b1e6ece5 diff --git a/modules/camera/src/vkcv/camera/Camera.cpp b/modules/camera/src/vkcv/camera/Camera.cpp index 18bf94463a0e2c4cb7d64526f4c30835cb451eb2..4b3ae9274733a070463f553bd79183789d0701b6 100644 --- a/modules/camera/src/vkcv/camera/Camera.cpp +++ b/modules/camera/src/vkcv/camera/Camera.cpp @@ -1,6 +1,6 @@ #include "vkcv/camera/Camera.hpp" -#include <math.h> +#include <cmath> namespace vkcv::camera { @@ -10,8 +10,6 @@ namespace vkcv::camera { glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f) ); - - setFront(glm::normalize(m_center - m_position)); } Camera::~Camera() = default; @@ -37,27 +35,27 @@ namespace vkcv::camera { m_view = view; } + const glm::mat4 y_correction( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + const glm::mat4& Camera::getProjection() const { return m_projection; } void Camera::setProjection(const glm::mat4& projection) { - m_projection = projection; + m_projection = y_correction * projection; } glm::mat4 Camera::getMVP() const { - const glm::mat4 y_correction ( - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, -1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - ); - - return y_correction * m_projection * m_view; + return m_projection * m_view; } float Camera::getFov() const { - const float tanHalfFovy = 1.0f / m_projection[1][1]; + const float tanHalfFovy = -1.0f / m_projection[1][1]; float halfFovy = std::atan(tanHalfFovy); if (halfFovy < 0) { @@ -73,7 +71,7 @@ namespace vkcv::camera { float Camera::getRatio() const { const float aspectProduct = 1.0f / m_projection[0][0]; - const float tanHalfFovy = 1.0f / m_projection[1][1]; + const float tanHalfFovy = -1.0f / m_projection[1][1]; return aspectProduct / tanHalfFovy; } @@ -93,16 +91,11 @@ namespace vkcv::camera { } glm::vec3 Camera::getFront() const { - glm::vec3 direction; - direction.x = std::sin(glm::radians(m_yaw)) * std::cos(glm::radians(m_pitch)); - direction.y = std::sin(glm::radians(m_pitch)); - direction.z = std::cos(glm::radians(m_yaw)) * std::cos(glm::radians(m_pitch)); - return glm::normalize(direction); + return glm::normalize(m_center - m_position); } void Camera::setFront(const glm::vec3 &front) { - m_pitch = std::atan2(front.y, std::sqrt(front.x * front.x + front.z * front.z)); - m_yaw = std::atan2(front.x, front.z); + setCenter(m_position + front); } const glm::vec3& Camera::getPosition() const { @@ -128,21 +121,47 @@ namespace vkcv::camera { void Camera::setUp(const glm::vec3 &up) { lookAt(m_position, m_center, up); } - - float Camera::getPitch() const { - return m_pitch; + + void Camera::getAngles(float& pitch, float& yaw) { + const auto front = getFront(); + + pitch = std::atan2(front[1], std::sqrt( + front[0] * front[0] + front[2] * front[2] + )); + + yaw = std::atan2(front[0], front[2]); + } + + void Camera::setAngles(float pitch, float yaw) { + float cosPitch = std::cos(pitch); + + setFront(glm::vec3( + std::sin(yaw) * cosPitch, + std::sin(pitch), + std::cos(yaw) * cosPitch + )); + } + + float Camera::getPitch() const { + const auto front = getFront(); + + return glm::degrees(std::atan2(front[1], std::sqrt( + front[0] * front[0] + front[2] * front[2] + ))); } void Camera::setPitch(float pitch) { - m_pitch = pitch; + setAngles(glm::radians(pitch), glm::radians(getYaw())); } float Camera::getYaw() const { - return m_yaw; + const auto front = getFront(); + + return glm::degrees(std::atan2(front[0], front[2])); } void Camera::setYaw(float yaw) { - m_yaw = yaw; + setAngles(glm::radians(getPitch()), glm::radians(yaw)); } -} \ No newline at end of file +} diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp index c0525d849095f07b6a6988e26c21dc32674f08f0..ad5de753a17e7f207cd3e538cb3b96a6c2753083 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -35,59 +35,84 @@ namespace vkcv::camera { void CameraManager::resizeCallback(int width, int height) { if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_ICONIFIED) == GLFW_FALSE) { - for (size_t i = 0; i < m_cameras.size(); i++) { - getCamera(i).setRatio(static_cast<float>(width) / static_cast<float>(height));; + for (auto& camera : m_cameras) { + camera.setRatio(static_cast<float>(width) / static_cast<float>(height));; } } } - void CameraManager::mouseButtonCallback(int button, int action, int mods){ - if(button == GLFW_MOUSE_BUTTON_2 && action == GLFW_PRESS){ + void CameraManager::mouseButtonCallback(int button, int action, int mods) { + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if ((button == GLFW_MOUSE_BUTTON_2) && (action == GLFW_PRESS)) { glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); - } - else if(button == GLFW_MOUSE_BUTTON_2 && action == GLFW_RELEASE){ + } else + if ((button == GLFW_MOUSE_BUTTON_2) && (action == GLFW_RELEASE)) { glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); } - getActiveController().mouseButtonCallback(button, action, mods, getActiveCamera()); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).mouseButtonCallback(button, action, mods, getActiveCamera()); } - void CameraManager::mouseMoveCallback(double x, double y){ - auto xoffset = static_cast<float>(x - m_lastX); - auto yoffset = static_cast<float>(y - m_lastY); + void CameraManager::mouseMoveCallback(double x, double y) { + const ControllerType type = getControllerType(getActiveCameraHandle()); + + auto xoffset = static_cast<float>(x - m_lastX) / m_window.getWidth(); + auto yoffset = static_cast<float>(y - m_lastY) / m_window.getHeight(); m_lastX = x; m_lastY = y; - getActiveController().mouseMoveCallback(xoffset, yoffset, getActiveCamera()); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).mouseMoveCallback(xoffset, yoffset, getActiveCamera()); } void CameraManager::scrollCallback(double offsetX, double offsetY) { - getActiveController().scrollCallback(offsetX, offsetY, getActiveCamera()); + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).scrollCallback(offsetX, offsetY, getActiveCamera()); } void CameraManager::keyCallback(int key, int scancode, int action, int mods) { - switch (action) { - case GLFW_RELEASE: - switch (key) { - case GLFW_KEY_TAB: - if (m_activeCameraIndex + 1 == m_cameras.size()) { - m_activeCameraIndex = 0; - } - else { - m_activeCameraIndex++; - } - return; - case GLFW_KEY_ESCAPE: - glfwSetWindowShouldClose(m_window.getWindow(), 1); - return; - default: - break; - } - default: - getActiveController().keyCallback(key, scancode, action, mods, getActiveCamera()); - break; + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if (action == GLFW_RELEASE) { + switch (key) { + case GLFW_KEY_TAB: + if (m_activeCameraIndex + 1 == m_cameras.size()) { + m_activeCameraIndex = 0; + } else { + m_activeCameraIndex++; + } + return; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(m_window.getWindow(), 1); + return; + default: + break; + } } + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).keyCallback(key, scancode, action, mods, getActiveCamera()); } void CameraManager::gamepadCallback(int gamepadIndex) { + const ControllerType type = getControllerType(getActiveCameraHandle()); + // handle camera switching GLFWgamepadstate gamepadState; glfwGetGamepadState(gamepadIndex, &gamepadState); @@ -96,81 +121,82 @@ namespace vkcv::camera { if (time - m_inputDelayTimer > 0.2) { int switchDirection = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] - gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT]; m_activeCameraIndex += switchDirection; - if (std::greater<int>{}(m_activeCameraIndex, m_cameras.size() - 1)) { + + if (std::greater<int>{}(m_activeCameraIndex, m_cameras.size() - 1)) { m_activeCameraIndex = 0; - } - else if (std::less<int>{}(m_activeCameraIndex, 0)) { + } else + if (std::less<int>{}(m_activeCameraIndex, 0)) { m_activeCameraIndex = m_cameras.size() - 1; } + uint32_t triggered = abs(switchDirection); m_inputDelayTimer = (1-triggered)*m_inputDelayTimer + triggered * time; // Only reset timer, if dpad was pressed - is this cheaper than if-clause? } - - getActiveController().gamepadCallback(gamepadIndex, getActiveCamera(), m_frameTime); // handle camera rotation, translation - } - - CameraController& CameraManager::getActiveController() { - const ControllerType type = getControllerType(getActiveCameraIndex()); - return getControllerByType(type); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).gamepadCallback(gamepadIndex, getActiveCamera(), m_frameTime); // handle camera rotation, translation } - uint32_t CameraManager::addCamera(ControllerType controllerType) { + CameraHandle CameraManager::addCamera(ControllerType controllerType) { const float ratio = static_cast<float>(m_window.getWidth()) / static_cast<float>(m_window.getHeight()); Camera camera; camera.setPerspective(glm::radians(60.0f), ratio, 0.1f, 10.0f); return addCamera(controllerType, camera); } - - uint32_t CameraManager::addCamera(ControllerType controllerType, const Camera &camera) { + + CameraHandle CameraManager::addCamera(ControllerType controllerType, const Camera &camera) { const uint32_t index = static_cast<uint32_t>(m_cameras.size()); m_cameras.push_back(camera); m_cameraControllerTypes.push_back(controllerType); - return index; + return CameraHandle(index); } - Camera& CameraManager::getCamera(uint32_t cameraIndex) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + Camera& CameraManager::getCamera(const CameraHandle& cameraHandle) { + if (cameraHandle.getId() < 0 || cameraHandle.getId() >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return getActiveCamera(); } - return m_cameras[cameraIndex]; + return m_cameras[cameraHandle.getId()]; } Camera& CameraManager::getActiveCamera() { - return m_cameras[getActiveCameraIndex()]; + return m_cameras[m_activeCameraIndex]; } - void CameraManager::setActiveCamera(uint32_t cameraIndex) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + void CameraManager::setActiveCamera(const CameraHandle& cameraHandle) { + if (cameraHandle.getId() < 0 || cameraHandle.getId() >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return; } - m_activeCameraIndex = cameraIndex; + m_activeCameraIndex = cameraHandle.getId(); } - uint32_t CameraManager::getActiveCameraIndex() const { - return m_activeCameraIndex; + CameraHandle CameraManager::getActiveCameraHandle() const { + return CameraHandle(m_activeCameraIndex); } - void CameraManager::setControllerType(uint32_t cameraIndex, ControllerType controllerType) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + void CameraManager::setControllerType(const CameraHandle& cameraHandle, ControllerType controllerType) { + if (cameraHandle.getId() < 0 || cameraHandle.getId() >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return; } - m_cameraControllerTypes[cameraIndex] = controllerType; + m_cameraControllerTypes[cameraHandle.getId()] = controllerType; } - ControllerType CameraManager::getControllerType(uint32_t cameraIndex) { - if (cameraIndex < 0 || cameraIndex > m_cameras.size() - 1) { + ControllerType CameraManager::getControllerType(const CameraHandle& cameraHandle) { + if (cameraHandle.getId() < 0 || cameraHandle.getId() >= m_cameras.size()) { vkcv_log(LogLevel::ERROR, "Invalid camera index: The index must range from 0 to %lu", m_cameras.size()); return ControllerType::NONE; } - return m_cameraControllerTypes[cameraIndex]; + return m_cameraControllerTypes[cameraHandle.getId()]; } CameraController& CameraManager::getControllerByType(ControllerType controllerType) { @@ -184,14 +210,18 @@ namespace vkcv::camera { } } - bool CameraManager::cameraHasController(uint32_t cameraIndex) { - return (m_cameraControllerTypes[cameraIndex] != ControllerType::NONE); - } - void CameraManager::update(double deltaTime) { - m_frameTime = deltaTime; - if ((glfwGetWindowAttrib(m_window.getWindow(), GLFW_FOCUSED) == GLFW_TRUE) && cameraHasController(getActiveCameraIndex())) { - getActiveController().updateCamera(deltaTime, getActiveCamera()); + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if (type != ControllerType::NONE) { + m_frameTime = deltaTime; + } else { + m_frameTime = 0.0; + return; + } + + if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_FOCUSED) == GLFW_TRUE) { + getControllerByType(type).updateCamera(m_frameTime, getActiveCamera()); } } diff --git a/modules/camera/src/vkcv/camera/PilotCameraController.cpp b/modules/camera/src/vkcv/camera/PilotCameraController.cpp index 5460858ab48d81252787b3c0141dd72982faca7d..37a79fe1012d6b81f12e50dba0e0edb6b76390e7 100644 --- a/modules/camera/src/vkcv/camera/PilotCameraController.cpp +++ b/modules/camera/src/vkcv/camera/PilotCameraController.cpp @@ -50,12 +50,11 @@ namespace vkcv::camera { } // handle yaw rotation - float yaw = camera.getYaw() + static_cast<float>(xOffset); - yaw += 360.0f * (yaw < -180.0f) - 360.0f * (yaw > 180.0f); + float yaw = camera.getYaw() + static_cast<float>(xOffset) * 90.0f * m_cameraSpeed; camera.setYaw(yaw); // handle pitch rotation - float pitch = camera.getPitch() - static_cast<float>(yOffset); + float pitch = camera.getPitch() - static_cast<float>(yOffset) * 90.0f * m_cameraSpeed; pitch = glm::clamp(pitch, -89.0f, 89.0f); camera.setPitch(pitch); } @@ -83,22 +82,22 @@ namespace vkcv::camera { void PilotCameraController::keyCallback(int key, int scancode, int action, int mods, Camera &camera) { switch (key) { case GLFW_KEY_W: - moveForward(action); + m_forward = static_cast<bool>(action); break; case GLFW_KEY_S: - moveBackward(action); + m_backward = static_cast<bool>(action); break; case GLFW_KEY_A: - moveLeft(action); + m_left = static_cast<bool>(action); break; case GLFW_KEY_D: - moveRight(action); + m_right = static_cast<bool>(action); break; case GLFW_KEY_E: - moveUpward(action); + m_upward = static_cast<bool>(action); break; case GLFW_KEY_Q: - moveDownward(action); + m_downward = static_cast<bool>(action); break; default: break; @@ -110,31 +109,25 @@ namespace vkcv::camera { } void PilotCameraController::mouseMoveCallback(double xoffset, double yoffset, Camera &camera) { - if(!m_rotationActive){ - return; - } - - float sensitivity = 0.05f; - xoffset *= sensitivity; - yoffset *= sensitivity; + xoffset *= static_cast<float>(m_rotationActive); + yoffset *= static_cast<float>(m_rotationActive); - panView(xoffset , yoffset, camera); + panView(xoffset, yoffset, camera); } void PilotCameraController::mouseButtonCallback(int button, int action, int mods, Camera &camera) { - if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == false && action == GLFW_PRESS){ - m_rotationActive = true; - } - else if(button == GLFW_MOUSE_BUTTON_2 && m_rotationActive == true && action == GLFW_RELEASE){ - m_rotationActive = false; - } + if (button == GLFW_MOUSE_BUTTON_2) { + if (m_rotationActive != (action == GLFW_PRESS)) { + m_rotationActive = (action == GLFW_PRESS); + } + } } void PilotCameraController::gamepadCallback(int gamepadIndex, Camera &camera, double frametime) { GLFWgamepadstate gamepadState; glfwGetGamepadState(gamepadIndex, &gamepadState); - float sensitivity = 100.0f; + float sensitivity = 1.0f; double threshold = 0.1; // handle rotations @@ -163,29 +156,4 @@ namespace vkcv::camera { * -copysign(1.0, stickLeftX); } - - void PilotCameraController::moveForward(int action){ - m_forward = static_cast<bool>(action); - } - - void PilotCameraController::moveBackward(int action){ - m_backward = static_cast<bool>(action); - } - - void PilotCameraController::moveLeft(int action){ - m_left = static_cast<bool>(action); - } - - void PilotCameraController::moveRight(int action){ - m_right = static_cast<bool>(action); - } - - void PilotCameraController::moveUpward(int action){ - m_upward = static_cast<bool>(action); - } - - void PilotCameraController::moveDownward(int action){ - m_downward = static_cast<bool>(action); - } - } \ No newline at end of file diff --git a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp index cdd66cdb7fdd650d5112fe7bb4738f1fcded7783..5e89ce1a7b2fec5c203573cf4b58b45025e608f6 100644 --- a/modules/camera/src/vkcv/camera/TrackballCameraController.cpp +++ b/modules/camera/src/vkcv/camera/TrackballCameraController.cpp @@ -5,82 +5,78 @@ namespace vkcv::camera { TrackballCameraController::TrackballCameraController() { m_rotationActive = false; - m_radius = 3.0f; m_cameraSpeed = 2.5f; m_scrollSensitivity = 0.2f; - } - - void TrackballCameraController::setRadius(const float radius) { - m_radius = 0.1f * (radius < 0.1f) + radius * (1 - (radius < 0.1f)); + m_pitch = 0.0f; + m_yaw = 0.0f; } void TrackballCameraController::panView(double xOffset, double yOffset, Camera &camera) { - // update only if there is (valid) input - if (xOffset == 0.0 && yOffset == 0.0) { - return; - } - - // handle yaw rotation - float yaw = camera.getYaw() + static_cast<float>(xOffset) * m_cameraSpeed; - yaw += 360.0f * (yaw < 0.0f) - 360.0f * (yaw > 360.0f); - camera.setYaw(yaw); - - // handle pitch rotation - float pitch = camera.getPitch() + static_cast<float>(yOffset) * m_cameraSpeed; - pitch += 360.0f * (pitch < 0.0f) - 360.0f * (pitch > 360.0f); - camera.setPitch(pitch); - } + // update only if there is (valid) input + if (xOffset == 0.0 && yOffset == 0.0) { + return; + } + + m_yaw += static_cast<float>(xOffset) * 90.0f * m_cameraSpeed; + m_pitch += static_cast<float>(yOffset) * 90.0f * m_cameraSpeed; + } void TrackballCameraController::updateRadius(double offset, Camera &camera) { // update only if there is (valid) input if (offset == 0.0) { return; } - - glm::vec3 cameraPosition = camera.getPosition(); - glm::vec3 cameraCenter = camera.getCenter(); - float radius = glm::length(cameraCenter - cameraPosition); // get current camera radius - setRadius(radius - static_cast<float>(offset) * m_scrollSensitivity); + + camera.setPosition( + camera.getPosition() + + camera.getFront() * static_cast<float>(offset) + ); } void TrackballCameraController::updateCamera(double deltaTime, Camera &camera) { - float yaw = camera.getYaw(); - float pitch = camera.getPitch(); - - const glm::vec3 yAxis = glm::vec3(0.0f, 1.0f, 0.0f); - const glm::vec3 xAxis = glm::vec3(1.0f, 0.0f, 0.0f); + const auto center = camera.getCenter(); + const auto distance = center - camera.getPosition(); + const float radius = glm::length(distance); + + glm::vec3 front = distance / radius; + glm::vec3 up = camera.getUp(); + glm::vec3 left; - const glm::mat4 rotationY = glm::rotate(glm::mat4(1.0f), glm::radians(yaw), yAxis); - const glm::mat4 rotationX = glm::rotate(rotationY, -glm::radians(pitch), xAxis); - const glm::vec3 translation = glm::vec3( - rotationX * glm::vec4(0.0f, 0.0f, m_radius, 0.0f) + const auto rotationY = glm::rotate( + glm::identity<glm::mat4>(), + glm::radians(m_yaw), + up ); - - const glm::vec3 center = camera.getCenter(); - const glm::vec3 position = center + translation; - const glm::vec3 up = glm::vec3( - rotationX * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f) + + front = glm::vec3(rotationY * glm::vec4(front, 0.0f)); + left = glm::normalize(glm::cross(up, front)); + + const auto rotationX = glm::rotate( + rotationY, + glm::radians(m_pitch), + left ); + + up = glm::vec3(rotationX * glm::vec4(up, 0.0f)); + front = glm::normalize(glm::cross(up, left)); - camera.lookAt(position, center, up); - } + m_yaw = 0.0f; + m_pitch = 0.0f; + + camera.lookAt(center + front * radius, center, up); + } void TrackballCameraController::keyCallback(int key, int scancode, int action, int mods, Camera &camera) {} void TrackballCameraController::scrollCallback(double offsetX, double offsetY, Camera &camera) { - updateRadius(offsetY, camera); + updateRadius(offsetY * m_scrollSensitivity, camera); } void TrackballCameraController::mouseMoveCallback(double xoffset, double yoffset, Camera &camera) { - if(!m_rotationActive){ - return; - } - - float sensitivity = 0.025f; - xoffset *= sensitivity; - yoffset *= sensitivity; + xoffset *= static_cast<float>(m_rotationActive); + yoffset *= static_cast<float>(m_rotationActive); - panView(xoffset , yoffset, camera); + panView(xoffset, yoffset, camera); } void TrackballCameraController::mouseButtonCallback(int button, int action, int mods, Camera &camera) { @@ -96,23 +92,23 @@ namespace vkcv::camera { GLFWgamepadstate gamepadState; glfwGetGamepadState(gamepadIndex, &gamepadState); - float sensitivity = 100.0f; + float sensitivity = 1.0f; double threshold = 0.1; // handle rotations - double stickRightX = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]); - double stickRightY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]); + auto stickRightX = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X]); + auto stickRightY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y]); - double rightXVal = glm::clamp((abs(stickRightX)-threshold), 0.0, 1.0) - * std::copysign(1.0, stickRightX) * sensitivity * frametime; - double rightYVal = glm::clamp((abs(stickRightY)-threshold), 0.0, 1.0) - * std::copysign(1.0, stickRightY) * sensitivity * frametime; + double rightXVal = glm::clamp((glm::abs(stickRightX)-threshold), 0.0, 1.0) + * glm::sign(stickRightX) * sensitivity * frametime; + double rightYVal = glm::clamp((glm::abs(stickRightY)-threshold), 0.0, 1.0) + * glm::sign(stickRightY) * sensitivity * frametime; panView(rightXVal, rightYVal, camera); // handle translation - double stickLeftY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]); - double leftYVal = glm::clamp((abs(stickLeftY)-threshold), 0.0, 1.0) - * std::copysign(1.0, stickLeftY) * sensitivity * frametime; + auto stickLeftY = static_cast<double>(gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y]); + double leftYVal = glm::clamp((glm::abs(stickLeftY)-threshold), 0.0, 1.0) + * glm::sign(stickLeftY) * sensitivity * frametime; updateRadius(-leftYVal, camera); } } \ No newline at end of file diff --git a/modules/effects/CMakeLists.txt b/modules/effects/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ea350fd4492c201bd247a40b94273829c1654a89 --- /dev/null +++ b/modules/effects/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_effects) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_effects_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_effects_include ${PROJECT_SOURCE_DIR}/include) + +set(vkcv_effects_sources + ${vkcv_effects_include}/vkcv/effects/Effect.hpp + ${vkcv_effects_source}/vkcv/effects/Effect.cpp + + ${vkcv_effects_include}/vkcv/effects/BloomAndFlaresEffect.hpp + ${vkcv_effects_source}/vkcv/effects/BloomAndFlaresEffect.cpp +) + +set(vkcv_effects_shaders ${PROJECT_SOURCE_DIR}/shaders) + +include_shader(${vkcv_effects_shaders}/bloomDownsample.comp ${vkcv_effects_include} ${vkcv_effects_source}) +include_shader(${vkcv_effects_shaders}/bloomFlaresComposite.comp ${vkcv_effects_include} ${vkcv_effects_source}) +include_shader(${vkcv_effects_shaders}/bloomUpsample.comp ${vkcv_effects_include} ${vkcv_effects_source}) +include_shader(${vkcv_effects_shaders}/lensFlares.comp ${vkcv_effects_include} ${vkcv_effects_source}) + +list(APPEND vkcv_effects_sources ${vkcv_effects_source}/bloomDownsample.comp.cxx) +list(APPEND vkcv_effects_sources ${vkcv_effects_source}/bloomFlaresComposite.comp.cxx) +list(APPEND vkcv_effects_sources ${vkcv_effects_source}/bloomUpsample.comp.cxx) +list(APPEND vkcv_effects_sources ${vkcv_effects_source}/lensFlares.comp.cxx) + +list(APPEND vkcv_effects_sources ${vkcv_effects_include}/bloomDownsample.comp.hxx) +list(APPEND vkcv_effects_sources ${vkcv_effects_include}/bloomFlaresComposite.comp.hxx) +list(APPEND vkcv_effects_sources ${vkcv_effects_include}/bloomUpsample.comp.hxx) +list(APPEND vkcv_effects_sources ${vkcv_effects_include}/lensFlares.comp.hxx) + +# adding source files to the project +add_library(vkcv_effects ${vkcv_build_attribute} ${vkcv_effects_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_effects ${vkcv_effects_libraries} vkcv vkcv_shader_compiler vkcv_camera vkcv_asset_loader) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_effects SYSTEM BEFORE PRIVATE ${vkcv_effects_includes} ${vkcv_include} ${vkcv_includes} ${vkcv_shader_compiler_include} ${vkcv_camera_include} {vkcv_asset_loader_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_effects BEFORE PUBLIC ${vkcv_effects_include}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_effects_include}) + list(APPEND vkcv_modules_libraries vkcv_effects) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/effects/README.md b/modules/effects/README.md new file mode 100644 index 0000000000000000000000000000000000000000..87639f4bff19fd8151d359b36c6ff34960782e3e --- /dev/null +++ b/modules/effects/README.md @@ -0,0 +1,15 @@ +# Effects + +A VkCV module to add post-processing effects to images in realtime after rendering + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|--------------------|-------------------| +| | | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__effects.html) to this module. diff --git a/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp b/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f4a9d03d8afcaf3532570a0807b246a0d641e02d --- /dev/null +++ b/modules/effects/include/vkcv/effects/BloomAndFlaresEffect.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include <vector> +#include <vkcv/camera/Camera.hpp> + +#include "Effect.hpp" + +namespace vkcv::effects { + + class BloomAndFlaresEffect : public Effect { + private: + bool m_advanced; + + ComputePipelineHandle m_downsamplePipeline; + ComputePipelineHandle m_upsamplePipeline; + ComputePipelineHandle m_lensFlaresPipeline; + ComputePipelineHandle m_compositePipeline; + + DescriptorSetLayoutHandle m_downsampleDescriptorSetLayout; + std::vector<DescriptorSetHandle> m_downsampleDescriptorSets; + + DescriptorSetLayoutHandle m_upsampleDescriptorSetLayout; + std::vector<DescriptorSetHandle> m_upsampleDescriptorSets; + std::vector<DescriptorSetHandle> m_flaresDescriptorSets; + + DescriptorSetLayoutHandle m_lensFlaresDescriptorSetLayout; + DescriptorSetHandle m_lensFlaresDescriptorSet; + + DescriptorSetLayoutHandle m_compositeDescriptorSetLayout; + DescriptorSetHandle m_compositeDescriptorSet; + + ImageHandle m_blurImage; + ImageHandle m_flaresImage; + + SamplerHandle m_linearSampler; + SamplerHandle m_radialLutSampler; + + ImageHandle m_radialLut; + ImageHandle m_lensDirt; + + glm::vec3 m_cameraDirection; + uint32_t m_upsampleLimit; + + void recordDownsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &sample, + const std::vector<DescriptorSetHandle> &mipDescriptorSets); + + void recordUpsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &sample, + const std::vector<DescriptorSetHandle> &mipDescriptorSets); + + void recordLensFlares(const CommandStreamHandle &cmdStream, + uint32_t mipLevel); + + void recordComposition(const CommandStreamHandle &cmdStream, + const ImageHandle &output); + + public: + BloomAndFlaresEffect(Core& core, + bool advanced = false); + + void recordEffect(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &output) override; + + void updateCameraDirection(const camera::Camera &camera); + + void setUpsamplingLimit(uint32_t limit); + + }; + +} diff --git a/modules/effects/include/vkcv/effects/Effect.hpp b/modules/effects/include/vkcv/effects/Effect.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d068fb59404080aa421e49f6a183844e731c1c5e --- /dev/null +++ b/modules/effects/include/vkcv/effects/Effect.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include <vkcv/Core.hpp> +#include <vkcv/Handles.hpp> + +namespace vkcv::effects { + + class Effect { + protected: + Core& m_core; + + public: + explicit Effect(Core& core); + + ~Effect() = default; + + virtual void recordEffect(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) = 0; + + }; + +} diff --git a/projects/bloom/resources/shaders/downsample.comp b/modules/effects/shaders/bloomDownsample.comp similarity index 100% rename from projects/bloom/resources/shaders/downsample.comp rename to modules/effects/shaders/bloomDownsample.comp diff --git a/modules/effects/shaders/bloomFlaresComposite.comp b/modules/effects/shaders/bloomFlaresComposite.comp new file mode 100644 index 0000000000000000000000000000000000000000..21d67393b634c8e639c0b81669e96070c380e6f6 --- /dev/null +++ b/modules/effects/shaders/bloomFlaresComposite.comp @@ -0,0 +1,96 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(set=0, binding=0) uniform texture2D blurImage; +layout(set=0, binding=1) uniform texture2D lensImage; +layout(set=0, binding=2) uniform sampler linearSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D colorBuffer; + +#ifdef ADVANCED_FEATURES +layout(set=0, binding=4) uniform texture2D radialLUT; +layout(set=0, binding=5) uniform sampler radialLUTSampler; +layout(set=0, binding=6) uniform texture2D dirtTexture; +#endif + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#ifdef ADVANCED_FEATURES + +layout( push_constant ) uniform constants{ + vec3 cameraForward; +}; + +float starburst(vec2 uv){ + vec2 toCenter = vec2(0.5) - uv; + float d2 = dot(toCenter, toCenter); + float falloff = clamp(pow(d2 * 2, 2.5), 0, 1); + + float cosTheta = acos(normalize(toCenter).x) * sign(toCenter.y); + cosTheta *= 4; + + float thetaOffset = cameraForward.x + cameraForward.y; + thetaOffset *= 10; + cosTheta += thetaOffset; + + float burst = texture(sampler2D(radialLUT, radialLUTSampler), vec2(cosTheta, 0.5)).r; + burst = pow(burst, 2); + return mix(1, burst, falloff); +} + +float getLensDirtWeight(vec2 uv){ + vec2 targetTextureRes = imageSize(colorBuffer); + float targetAspectRatio = targetTextureRes.x / targetTextureRes.y; + + vec2 dirtTextureRes = textureSize(sampler2D(dirtTexture, linearSampler), 0); + float dirtAspectRatio = dirtTextureRes.x / dirtTextureRes.y; + + uv.x *= targetAspectRatio / dirtAspectRatio; + float dirt = texture(sampler2D(dirtTexture, radialLUTSampler), uv).r; + float dirtStrength = 0.4f; + + // manually looked up in gimp, must be adjusted when changing dirt texture + float dirtMean = 0.132; + // make sure no energy is lost + // otherwise bloom is darkened when the dirt increases + dirt /= dirtMean; + + return mix(1, dirt, dirtStrength); +} + +#endif + +void main() +{ + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){ + return; + } + + ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixel_size = vec2(1.0f) / imageSize(colorBuffer); + vec2 UV = pixel_coord.xy * pixel_size; + + vec4 composite_color = vec4(0.0f); + + vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb; + vec3 lens_color = texture(sampler2D(lensImage, linearSampler), UV).rgb; + vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb; + + // composite blur and lens features + float bloom_weight = 0.06f; + float lens_weight = 0.02f; + float main_weight = 1 - (bloom_weight + lens_weight); + +#ifdef ADVANCED_FEATURES + lens_color *= starburst(UV); + + float lensDirtWeight = getLensDirtWeight(UV); + bloom_weight *= lensDirtWeight; + lens_weight *= lensDirtWeight; +#endif + + composite_color.rgb = blur_color * bloom_weight + + lens_color * lens_weight + + main_color * main_weight; + + imageStore(colorBuffer, pixel_coord, composite_color); +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/upsample.comp b/modules/effects/shaders/bloomUpsample.comp similarity index 100% rename from projects/bloom/resources/shaders/upsample.comp rename to modules/effects/shaders/bloomUpsample.comp diff --git a/projects/bloom/resources/shaders/lensFlares.comp b/modules/effects/shaders/lensFlares.comp similarity index 79% rename from projects/bloom/resources/shaders/lensFlares.comp rename to modules/effects/shaders/lensFlares.comp index ce27d8850b709f61332d467914ddc944dc63109f..afcad375c1cd3e8f547ad2386b6f1d7bdfdfa85a 100644 --- a/projects/bloom/resources/shaders/lensFlares.comp +++ b/modules/effects/shaders/lensFlares.comp @@ -12,7 +12,7 @@ vec3 sampleColorChromaticAberration(vec2 _uv) vec2 toCenter = (vec2(0.5) - _uv); vec3 colorScales = vec3(-1, 0, 1); - float aberrationScale = 0.1; + float aberrationScale = 0.15; vec3 scaleFactors = colorScales * aberrationScale; float r = texture(sampler2D(blurBuffer, linearSampler), _uv + toCenter * scaleFactors.r).r; @@ -26,7 +26,7 @@ vec3 ghost_vectors(vec2 _uv) { vec2 ghost_vec = (vec2(0.5f) - _uv); - const uint c_ghost_count = 64; + const uint c_ghost_count = 8; const float c_ghost_spacing = length(ghost_vec) / c_ghost_count; ghost_vec *= c_ghost_spacing; @@ -53,18 +53,19 @@ vec3 ghost_vectors(vec2 _uv) vec3 halo(vec2 _uv) { - const float c_aspect_ratio = float(imageSize(lensBuffer).x) / float(imageSize(lensBuffer).y); - const float c_radius = 0.6f; - const float c_halo_thickness = 0.1f; + float c_aspect_ratio = float(imageSize(lensBuffer).x) / float(imageSize(lensBuffer).y); + c_aspect_ratio *= 0.55; + const float c_radius = 0.5f; + const float c_halo_thickness = 0.15f; - vec2 halo_vec = vec2(0.5) - _uv; - //halo_vec.x /= c_aspect_ratio; - halo_vec = normalize(halo_vec); - //halo_vec.x *= c_aspect_ratio; + vec2 halo_vec = vec2(0.5) - _uv; + halo_vec.x /= c_aspect_ratio; + halo_vec = normalize(halo_vec); + halo_vec.x *= c_aspect_ratio; - //vec2 w_uv = (_uv - vec2(0.5, 0.0)) * vec2(c_aspect_ratio, 1.0) + vec2(0.5, 0.0); - vec2 w_uv = _uv; + vec2 w_uv = (_uv - vec2(0.5, 0.0)) * vec2(c_aspect_ratio, 1.0) + vec2(0.5, 0.0); + //vec2 w_uv = _uv; float d = distance(w_uv, vec2(0.5)); // distance to center float distance_to_halo = abs(d - c_radius); @@ -75,11 +76,7 @@ vec3 halo(vec2 _uv) float distance_to_border = c_halo_thickness - distance_to_halo; halo_weight = distance_to_border / c_halo_thickness; - //halo_weight = clamp((halo_weight / 0.4f), 0.0f, 1.0f); - halo_weight = pow(halo_weight, 2.0f); - - - //halo_weight = 1.0f; + halo_weight = pow(clamp(halo_weight + 0.1, 0, 1), 2); } return sampleColorChromaticAberration(_uv + halo_vec) * halo_weight; diff --git a/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..133b380095abc6c729ef94da675cc9f394475ab4 --- /dev/null +++ b/modules/effects/src/vkcv/effects/BloomAndFlaresEffect.cpp @@ -0,0 +1,545 @@ + +#include "vkcv/effects/BloomAndFlaresEffect.hpp" + +#include <vkcv/PushConstants.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/Sampler.hpp> + +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/asset/asset_loader.hpp> + +#include "bloomDownsample.comp.hxx" +#include "bloomFlaresComposite.comp.hxx" +#include "bloomUpsample.comp.hxx" +#include "lensFlares.comp.hxx" + +namespace vkcv::effects { + + static DescriptorBindings getDescriptorBindings() { + DescriptorBindings descriptorBindings = {}; + + auto binding_0 = DescriptorBinding { + 0, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_1 = DescriptorBinding { + 1, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_2 = DescriptorBinding{ + 2, + DescriptorType::IMAGE_STORAGE, + 1, + ShaderStage::COMPUTE, + false + }; + + descriptorBindings.insert(std::make_pair(0, binding_0)); + descriptorBindings.insert(std::make_pair(1, binding_1)); + descriptorBindings.insert(std::make_pair(2, binding_2)); + + return descriptorBindings; + } + + static DescriptorBindings getCompositeDescriptorBindings(bool advanced) { + DescriptorBindings descriptorBindings = {}; + + auto binding_0 = DescriptorBinding { + 0, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_1 = DescriptorBinding { + 1, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_2 = DescriptorBinding{ + 2, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_3 = DescriptorBinding{ + 3, + DescriptorType::IMAGE_STORAGE, + 1, + ShaderStage::COMPUTE, + false + }; + + descriptorBindings.insert(std::make_pair(0, binding_0)); + descriptorBindings.insert(std::make_pair(1, binding_1)); + descriptorBindings.insert(std::make_pair(2, binding_2)); + descriptorBindings.insert(std::make_pair(3, binding_3)); + + if (advanced) { + auto binding_4 = DescriptorBinding{ + 4, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_5 = DescriptorBinding{ + 5, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_6 = DescriptorBinding{ + 6, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + descriptorBindings.insert(std::make_pair(4, binding_4)); + descriptorBindings.insert(std::make_pair(5, binding_5)); + descriptorBindings.insert(std::make_pair(6, binding_6)); + } + + return descriptorBindings; + } + + static ImageHandle loadTexture(Core &core, + const std::string &texturePath) { + const auto texture = vkcv::asset::loadTexture(texturePath); + + auto image = vkcv::image( + core, + vk::Format::eR8G8B8A8Unorm, + texture.width, + texture.height + ); + + image.fill(texture.data.data(), texture.data.size()); + return image.getHandle(); + } + + static ComputePipelineHandle compilePipeline(Core &core, + const std::string &shaderSource, + const DescriptorSetLayoutHandle &descriptorSetLayout, + bool advanced = false) { + vkcv::shader::GLSLCompiler compiler; + + if (advanced) { + compiler.setDefine("ADVANCED_FEATURES", "1"); + } + + ShaderProgram program; + compiler.compileSource( + vkcv::ShaderStage::COMPUTE, + shaderSource.c_str(), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }, + "" + ); + + ComputePipelineHandle pipeline = core.createComputePipeline({ + program, { descriptorSetLayout } + }); + + return pipeline; + } + + BloomAndFlaresEffect::BloomAndFlaresEffect(Core &core, + bool advanced) : + Effect(core), + m_advanced(advanced), + + m_downsamplePipeline(), + m_upsamplePipeline(), + m_lensFlaresPipeline(), + m_compositePipeline(), + + m_downsampleDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_downsampleDescriptorSets({}), + + m_upsampleDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_upsampleDescriptorSets({}), + m_flaresDescriptorSets({}), + + m_lensFlaresDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_lensFlaresDescriptorSet(m_core.createDescriptorSet(m_lensFlaresDescriptorSetLayout)), + + m_compositeDescriptorSetLayout(), + m_compositeDescriptorSet(), + + m_blurImage(), + m_flaresImage(), + + m_linearSampler(samplerLinear(m_core, true)), + + m_radialLutSampler(), + + m_radialLut(), + m_lensDirt(), + + m_cameraDirection(), + m_upsampleLimit(5) { + m_downsamplePipeline = compilePipeline( + m_core, + BLOOMDOWNSAMPLE_COMP_SHADER, + m_downsampleDescriptorSetLayout + ); + + m_upsamplePipeline = compilePipeline( + m_core, + BLOOMUPSAMPLE_COMP_SHADER, + m_upsampleDescriptorSetLayout + ); + + m_lensFlaresPipeline = compilePipeline( + m_core, + LENSFLARES_COMP_SHADER, + m_lensFlaresDescriptorSetLayout + ); + + m_compositeDescriptorSetLayout = m_core.createDescriptorSetLayout( + getCompositeDescriptorBindings(m_advanced) + ); + + m_compositeDescriptorSet = m_core.createDescriptorSet( + m_compositeDescriptorSetLayout + ); + + m_compositePipeline = compilePipeline( + m_core, + BLOOMFLARESCOMPOSITE_COMP_SHADER, + m_compositeDescriptorSetLayout, + m_advanced + ); + + if (m_advanced) { + m_radialLutSampler = samplerLinear(m_core); + m_radialLut = loadTexture(m_core, "assets/RadialLUT.png"); + m_lensDirt = loadTexture(m_core, "assets/lensDirt.jpg"); + } + } + + static uint32_t calcDispatchSize(float sampleSizeDim, uint32_t threadGroupWorkRegionDim) { + return std::max<uint32_t>( + static_cast<uint32_t>(std::ceil( + sampleSizeDim / static_cast<float>(threadGroupWorkRegionDim) + )), + 1 + ); + } + + void BloomAndFlaresEffect::recordDownsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &sample, + const std::vector<DescriptorSetHandle> &mipDescriptorSets) { + const uint32_t sampleWidth = m_core.getImageWidth(sample); + const uint32_t sampleHeight = m_core.getImageHeight(sample); + + m_core.prepareImageForSampling(cmdStream, input); + m_core.prepareImageForStorage(cmdStream, m_blurImage); + + for (uint32_t mipLevel = 0; mipLevel < mipDescriptorSets.size(); mipLevel++) { + // mip descriptor writes + DescriptorWrites mipDownsampleWrites; + + if (mipLevel > 0) { + mipDownsampleWrites.writeSampledImage(0, sample, mipLevel - 1, true); + } else { + mipDownsampleWrites.writeSampledImage(0, input); + } + + mipDownsampleWrites.writeSampler(1, m_linearSampler); + mipDownsampleWrites.writeStorageImage(2, sample, mipLevel); + + m_core.writeDescriptorSet(mipDescriptorSets[mipLevel], mipDownsampleWrites); + + float mipDivisor = 1.0f; + + for (uint32_t i = 0; i < mipLevel; i++) { + mipDivisor *= 2.0f; + } + + const auto downsampleSizeX = static_cast<float>(sampleWidth) / mipDivisor; + const auto downsampleSizeY = static_cast<float>(sampleHeight) / mipDivisor; + + static const uint32_t threadGroupWorkRegionDim = 8; + + DispatchSize dispatch ( + calcDispatchSize(downsampleSizeX, threadGroupWorkRegionDim), + calcDispatchSize(downsampleSizeY, threadGroupWorkRegionDim) + ); + + // mip blur dispatch + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_downsamplePipeline, + dispatch, + { useDescriptorSet(0, mipDescriptorSets[mipLevel]) }, + PushConstants(0) + ); + + // image barrier between mips + m_core.recordImageMemoryBarrier(cmdStream, sample); + } + } + + void BloomAndFlaresEffect::recordUpsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &sample, + const std::vector<DescriptorSetHandle> &mipDescriptorSets) { + // upsample dispatch + m_core.prepareImageForStorage(cmdStream, sample); + + const uint32_t sampleWidth = m_core.getImageWidth(sample); + const uint32_t sampleHeight = m_core.getImageHeight(sample); + + // upsample dispatch for each mip map + for(uint32_t mipLevel = mipDescriptorSets.size(); mipLevel > 0; mipLevel--) { + // mip descriptor writes + DescriptorWrites mipUpsampleWrites; + mipUpsampleWrites.writeSampledImage(0, sample, mipLevel, true); + mipUpsampleWrites.writeSampler(1, m_linearSampler); + mipUpsampleWrites.writeStorageImage(2, sample, mipLevel - 1); + + m_core.writeDescriptorSet(mipDescriptorSets[mipLevel - 1], mipUpsampleWrites); + + float mipDivisor = 1.0f; + + for (uint32_t i = 0; i < mipLevel - 1; i++) { + mipDivisor *= 2.0f; + } + + const auto upsampleSizeX = static_cast<float>(sampleWidth) / mipDivisor; + const auto upsampleSizeY = static_cast<float>(sampleHeight) / mipDivisor; + + static const uint32_t threadGroupWorkRegionDim = 8; + + DispatchSize dispatch ( + calcDispatchSize(upsampleSizeX, threadGroupWorkRegionDim), + calcDispatchSize(upsampleSizeY, threadGroupWorkRegionDim) + ); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_upsamplePipeline, + dispatch, + { useDescriptorSet(0, mipDescriptorSets[mipLevel - 1]) }, + PushConstants(0) + ); + + // image barrier between mips + m_core.recordImageMemoryBarrier(cmdStream, sample); + } + } + + void BloomAndFlaresEffect::recordLensFlares(const CommandStreamHandle &cmdStream, + uint32_t mipLevel) { + // lens feature generation descriptor writes + m_core.prepareImageForSampling(cmdStream, m_blurImage); + m_core.prepareImageForStorage(cmdStream, m_flaresImage); + + const uint32_t flaresWidth = m_core.getImageWidth(m_flaresImage); + const uint32_t flaresHeight = m_core.getImageHeight(m_flaresImage); + + DescriptorWrites lensFlaresWrites; + lensFlaresWrites.writeSampledImage(0, m_blurImage, 0); + lensFlaresWrites.writeSampler(1, m_linearSampler); + lensFlaresWrites.writeStorageImage(2, m_flaresImage, mipLevel); + + m_core.writeDescriptorSet(m_lensFlaresDescriptorSet, lensFlaresWrites); + + const auto sampleSizeX = static_cast<float>(flaresWidth); + const auto sampleSizeY = static_cast<float>(flaresHeight); + + float mipDivisor = 1.0f; + + for (uint32_t i = 0; i < mipLevel - 1; i++) { + mipDivisor *= 2.0f; + } + + static const uint32_t threadGroupWorkRegionDim = 8; + + // lens feature generation dispatch + DispatchSize dispatch ( + calcDispatchSize(sampleSizeX / mipDivisor, threadGroupWorkRegionDim), + calcDispatchSize(sampleSizeY / mipDivisor, threadGroupWorkRegionDim) + ); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_lensFlaresPipeline, + dispatch, + { useDescriptorSet(0, m_lensFlaresDescriptorSet) }, + PushConstants(0) + ); + } + + void BloomAndFlaresEffect::recordComposition(const CommandStreamHandle &cmdStream, + const ImageHandle &output) { + const uint32_t outputWidth = m_core.getImageWidth(output); + const uint32_t outputHeight = m_core.getImageHeight(output); + + m_core.prepareImageForSampling(cmdStream, m_blurImage); + m_core.prepareImageForSampling(cmdStream, m_flaresImage); + m_core.prepareImageForStorage(cmdStream, output); + + // bloom composite descriptor write + vkcv::DescriptorWrites compositeWrites; + + if (m_advanced) { + compositeWrites.writeSampledImage( + 0, m_blurImage + ).writeSampledImage( + 1, m_flaresImage + ).writeSampledImage( + 4, m_radialLut + ).writeSampledImage( + 6, m_lensDirt + ); + + compositeWrites.writeSampler( + 2, m_linearSampler + ).writeSampler( + 5, m_radialLutSampler + ); + + compositeWrites.writeStorageImage(3, output); + } else { + compositeWrites.writeSampledImage( + 0, m_blurImage + ).writeSampledImage( + 1, m_flaresImage + ); + + compositeWrites.writeSampler(2, m_linearSampler); + + compositeWrites.writeStorageImage(3, output); + } + + m_core.writeDescriptorSet(m_compositeDescriptorSet, compositeWrites); + + const auto sampleWidth = static_cast<float>(outputWidth); + const auto sampleHeight = static_cast<float>(outputHeight); + + static const uint32_t threadGroupWorkRegionDim = 8; + + DispatchSize dispatch ( + calcDispatchSize(sampleWidth, threadGroupWorkRegionDim), + calcDispatchSize(sampleHeight, threadGroupWorkRegionDim) + ); + + PushConstants pushConstants = vkcv::pushConstants<glm::vec3>(); + pushConstants.appendDrawcall(m_cameraDirection); + + // bloom composite dispatch + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_compositePipeline, + dispatch, + { useDescriptorSet(0, m_compositeDescriptorSet) }, + m_advanced? pushConstants : PushConstants(0) + ); + } + + void BloomAndFlaresEffect::recordEffect(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &output) { + m_core.recordBeginDebugLabel(cmdStream, "vkcv::post_processing::BloomAndFlaresEffect", { + 0.0f, 1.0f, 1.0f, 1.0f + }); + + const auto halfWidth = static_cast<uint32_t>(std::ceil( + static_cast<float>(m_core.getImageWidth(output)) * 0.5f + )); + + const auto halfHeight = static_cast<uint32_t>(std::ceil( + static_cast<float>(m_core.getImageHeight(output)) * 0.5f + )); + + vkcv::ImageConfig imageConfig (halfWidth, halfHeight); + imageConfig.setSupportingStorage(true); + + if ((!m_blurImage) || + (halfWidth != m_core.getImageWidth(m_blurImage)) || + (halfHeight != m_core.getImageHeight(m_blurImage))) { + m_blurImage = m_core.createImage( + m_core.getImageFormat(output), + imageConfig, + true + ); + + m_downsampleDescriptorSets.clear(); + m_upsampleDescriptorSets.clear(); + + const uint32_t mipLevels = m_core.getImageMipLevels(m_blurImage); + + for (uint32_t i = 0; i < mipLevels; i++) { + m_downsampleDescriptorSets.push_back(m_core.createDescriptorSet( + m_downsampleDescriptorSetLayout + )); + } + + for (uint32_t i = 0; i < std::min<uint32_t>(m_upsampleLimit, mipLevels); i++) { + m_upsampleDescriptorSets.push_back(m_core.createDescriptorSet( + m_upsampleDescriptorSetLayout + )); + } + } + + if ((!m_flaresImage) || + (halfWidth != m_core.getImageWidth(m_flaresImage)) || + (halfHeight != m_core.getImageHeight(m_flaresImage))) { + m_flaresImage = m_core.createImage( + m_core.getImageFormat(output), + imageConfig, + true + ); + + m_flaresDescriptorSets.clear(); + + const uint32_t mipLevels = m_core.getImageMipLevels(m_flaresImage); + + for (uint32_t i = 0; i < std::min<uint32_t>(2, mipLevels); i++) { + m_flaresDescriptorSets.push_back(m_core.createDescriptorSet( + m_upsampleDescriptorSetLayout + )); + } + } + + recordDownsampling(cmdStream, input, m_blurImage, m_downsampleDescriptorSets); + recordUpsampling(cmdStream, m_blurImage, m_upsampleDescriptorSets); + recordLensFlares(cmdStream, m_flaresDescriptorSets.size()); + recordUpsampling(cmdStream, m_flaresImage, m_flaresDescriptorSets); + recordComposition(cmdStream, output); + + m_core.recordEndDebugLabel(cmdStream); + } + + void BloomAndFlaresEffect::updateCameraDirection(const camera::Camera &camera) { + m_cameraDirection = camera.getFront(); + } + + void BloomAndFlaresEffect::setUpsamplingLimit(uint32_t limit) { + m_upsampleLimit = limit; + } + +} diff --git a/modules/effects/src/vkcv/effects/Effect.cpp b/modules/effects/src/vkcv/effects/Effect.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3246db15144bcafcc253df34f358594c85868647 --- /dev/null +++ b/modules/effects/src/vkcv/effects/Effect.cpp @@ -0,0 +1,8 @@ + +#include "vkcv/effects/Effect.hpp" + +namespace vkcv::effects { + + Effect::Effect(Core &core) : m_core(core) {} + +} diff --git a/modules/geometry/CMakeLists.txt b/modules/geometry/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ffadd9c60733aa6cadda3837e3f6a7ab4d26f3e3 --- /dev/null +++ b/modules/geometry/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_geometry) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_geometry_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_geometry_include ${PROJECT_SOURCE_DIR}/include) + +set(vkcv_geometry_sources + ${vkcv_geometry_include}/vkcv/geometry/Geometry.hpp + ${vkcv_geometry_source}/vkcv/geometry/Geometry.cpp + ${vkcv_geometry_include}/vkcv/geometry/Volume.hpp + ${vkcv_geometry_source}/vkcv/geometry/Volume.cpp + ${vkcv_geometry_include}/vkcv/geometry/Circular.hpp + ${vkcv_geometry_source}/vkcv/geometry/Circular.cpp + ${vkcv_geometry_include}/vkcv/geometry/Sphere.hpp + ${vkcv_geometry_source}/vkcv/geometry/Sphere.cpp + ${vkcv_geometry_include}/vkcv/geometry/Cuboid.hpp + ${vkcv_geometry_source}/vkcv/geometry/Cuboid.cpp + ${vkcv_geometry_include}/vkcv/geometry/Cylinder.hpp + ${vkcv_geometry_source}/vkcv/geometry/Cylinder.cpp + ${vkcv_geometry_include}/vkcv/geometry/Teapot.hpp + ${vkcv_geometry_source}/vkcv/geometry/Teapot.cpp +) + +# adding source files to the project +add_library(vkcv_geometry ${vkcv_build_attribute} ${vkcv_geometry_sources}) + +# Setup some path variables to load libraries +set(vkcv_geometry_lib lib) +set(vkcv_geometry_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_geometry_lib}) + +include(config/GLM.cmake) + +target_link_libraries(vkcv_geometry PUBLIC + ${vkcv_geometry_libraries} + vkcv +) + +target_include_directories(vkcv_geometry SYSTEM BEFORE PRIVATE + ${vkcv_geometry_includes} + ${vkcv_include} + ${vkcv_includes} +) + +# add the own include directory for public headers +target_include_directories(vkcv_geometry BEFORE PUBLIC ${vkcv_geometry_include} ${vkcv_geometry_includes}) +target_compile_definitions(vkcv_geometry PUBLIC ${vkcv_geometry_definitions}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_geometry_include}) + list(APPEND vkcv_modules_libraries vkcv_geometry) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/geometry/README.md b/modules/geometry/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3148a94f9c8d782cd4f1a127fd6b8f94e43d9b60 --- /dev/null +++ b/modules/geometry/README.md @@ -0,0 +1,15 @@ +# Geometry + +A VkCV module to use basic geometry for rendering. + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|--------------------------------------|---| +| [GLM](https://github.com/g-truc/glm) | ✅ | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__geometry.html) to this module. diff --git a/modules/geometry/config/GLM.cmake b/modules/geometry/config/GLM.cmake new file mode 100644 index 0000000000000000000000000000000000000000..6a1e463ba94de14fcc707270671e535df1e45a2f --- /dev/null +++ b/modules/geometry/config/GLM.cmake @@ -0,0 +1,23 @@ + +find_package(glm QUIET) + +if (glm_FOUND) + list(APPEND vkcv_geometry_includes ${GLM_INCLUDE_DIRS}) + list(APPEND vkcv_geometry_libraries glm) +else() + use_git_submodule("${vkcv_geometry_lib_path}/glm" glm_status) + + if (${glm_status}) + add_subdirectory(${vkcv_geometry_lib}/glm) + + list(APPEND vkcv_geometry_includes ${vkcv_geometry_lib_path}/glm) + list(APPEND vkcv_geometry_libraries glm) + endif () +endif () + +list(APPEND vkcv_geometry_definitions GLM_DEPTH_ZERO_TO_ONE) +list(APPEND vkcv_geometry_definitions GLM_FORCE_LEFT_HANDED) + +if ((WIN32) AND (${CMAKE_SIZEOF_VOID_P} MATCHES 4)) + list(APPEND vkcv_geometry_definitions GLM_ENABLE_EXPERIMENTAL) +endif() diff --git a/modules/geometry/include/vkcv/geometry/Circular.hpp b/modules/geometry/include/vkcv/geometry/Circular.hpp new file mode 100644 index 0000000000000000000000000000000000000000..09165f9d8e984144bd9c2e21e8cfbbd2d32d1010 --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Circular.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include <cstdlib> + +namespace vkcv::geometry { + + /** + * @addtogroup vkcv_geometry + * @{ + */ + + /** + * A basic class to provide attributes for circular geometry. + */ + class Circular { + private: + /** + * Radius of the circular part of the geometry. + */ + float m_radius; + + /** + * Resolution in case of generating the geometry in a + * discrete way. + */ + size_t m_resolution; + + public: + /** + * Constructor creating circular geometry by a given + * radius and a resolution also provides a default. + * + * @param[in] radius Radius of the circular geometry + * @param[in] resoltion Resolution of the circular geometry + */ + explicit Circular(float radius, size_t resoltion = 10); + + /** + * Copy-constructor of a circular geometry. + * + * @param[in] other Other circular geometry + */ + Circular(const Circular& other) = default; + + /** + * Move-constructor of a circular geometry. + * + * @param[in] other Other circular geometry + */ + Circular(Circular&& other) = default; + + /** + * Destructor of a circular geometry. + */ + ~Circular() = default; + + /** + * Copy-operator of a circular geometry. + * + * @param[in] other Other circular geometry + * @return Reference to this circular geometry + */ + Circular& operator=(const Circular& other) = default; + + /** + * Move-operator of a circular geometry. + * + * @param[in] other Other circular geometry + * @return Reference to this circular geometry + */ + Circular& operator=(Circular&& other) = default; + + /** + * Return the radius of the circular part of the geometry. + * + * @return Radius of the circular geometry + */ + [[nodiscard]] + float getRadius() const; + + /** + * Set the radius of the circular part of the geometry. + * + * @param[in] radius Radius of the circular geometry + */ + void setRadius(float radius); + + /** + * Return the resolution of the geometry for discrete + * generation. + * + * @return Resolution of the circular geometry + */ + [[nodiscard]] + size_t getResolution() const; + + /** + * Set the resolution of the geometry for any discrete + * generation. + * + * @param[in] resolution Resolution of the circular geometry + */ + void setResolution(size_t resolution); + + }; + + /** @} */ + +} \ No newline at end of file diff --git a/modules/geometry/include/vkcv/geometry/Cuboid.hpp b/modules/geometry/include/vkcv/geometry/Cuboid.hpp new file mode 100644 index 0000000000000000000000000000000000000000..411152513eb6a4c5d7c368c1defa70258ff9ac9c --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Cuboid.hpp @@ -0,0 +1,123 @@ +#pragma once + +#include "Volume.hpp" + +namespace vkcv::geometry { + + /** + * @addtogroup vkcv_geometry + * @{ + */ + + /** + * A class to use geometry in form of a cuboid. + */ + class Cuboid : public Volume { + private: + /** + * Size of the cuboid in 3D-space. + */ + glm::vec3 m_size; + + public: + /** + * Constructor creating cuboid by a given position + * and size as 3D vectors. + * + * @param[in] position Position of the cuboid as 3D vector + * @param[in] size Size of the cuboid as 3D vector + */ + Cuboid(const glm::vec3& position, const glm::vec3& size); + + /** + * Constructor creating cube by a given position + * as 3D vector and uniform size. + * + * @param[in] position Position of the cube as 3D vector + * @param[in] size Uniform size of the cube + */ + Cuboid(const glm::vec3& position, float size); + + /** + * Copy-constructor of a cuboid. + * + * @param[in] other Other cuboid + */ + Cuboid(const Cuboid& other) = default; + + /** + * Move-constructor of a cuboid. + * + * @param[in] other Other cuboid + */ + Cuboid(Cuboid&& other) = default; + + /** + * Destructor of a cuboid. + */ + ~Cuboid() = default; + + /** + * Copy-operator of a cuboid. + * + * @param[in] other Other cuboid + * @return Reference to this cuboid + */ + Cuboid& operator=(const Cuboid& other) = default; + + /** + * Move-operator of a cuboid. + * + * @param[in] other Other cuboid + * @return Reference to this cuboid + */ + Cuboid& operator=(Cuboid&& other) = default; + + /** + * Return the size of a cuboid as 3D vector. + * + * @return Size of the cuboid as 3D vector + */ + [[nodiscard]] + const glm::vec3& getSize() const; + + /** + * Set the size of a cuboid to a specific + * 3D vector. + * + * @param[in] position Size as 3D vector + */ + void setSize(const glm::vec3& size); + + /** + * Returns the signed distance from a point to the closest + * surface of the cuboid. + * + * The result is negative if the point is contained by the + * cuboid. + * + * @param[in] point Point as 3D vector + * @return Signed distance from point to surface + */ + [[nodiscard]] + float distanceTo(const glm::vec3& point) override; + + /** + * Generates a vertex data structure, which can be + * used for rendering via draw calls, containing + * the cuboid in a discrete form. + * + * The vertex data will store positions, normals and + * UV-coordinates as vertex attributes. + * + * @param[in,out] core Core instance + * @return Vertex data with generated geometry + */ + [[nodiscard]] + VertexData generateVertexData(Core& core) const override; + + }; + + /** @} */ + +} diff --git a/modules/geometry/include/vkcv/geometry/Cylinder.hpp b/modules/geometry/include/vkcv/geometry/Cylinder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fe6ccc74b9ade02b4a8b7bc530557315aceb3703 --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Cylinder.hpp @@ -0,0 +1,115 @@ +#pragma once + +#include "Circular.hpp" +#include "Volume.hpp" + +namespace vkcv::geometry { + + /** + * @addtogroup vkcv_geometry + * @{ + */ + + /** + * A class to use geometry in form of a cylinder. + */ + class Cylinder : public Volume, public Circular { + private: + /** + * Height of the cylinder (Y-axis) + */ + float m_height; + + public: + /** + * Constructor creating cylinder by a given position + * as 3D vector, a given height and a radius. + * + * @param[in] position Position of the cylinder as 3D vector + * @param[in] height Height of the cylinder + * @param[in] radius Radius of the cylinder + */ + Cylinder(const glm::vec3& position, float height, float radius); + + /** + * Copy-constructor of a cylinder. + * + * @param[in] other Other cylinder + */ + Cylinder(const Cylinder& other) = default; + + /** + * Move-constructor of a cylinder. + * + * @param[in] other Other cylinder + */ + Cylinder(Cylinder&& other) = default; + + /** + * Destructor of a cylinder. + */ + ~Cylinder() = default; + + /** + * Copy-operator of a cylinder. + * + * @param[in] other Other cylinder + * @return Reference to this cylinder + */ + Cylinder& operator=(const Cylinder& other) = default; + + /** + * Move-operator of a cylinder. + * + * @param[in] other Other cylinder + * @return Reference to this cylinder + */ + Cylinder& operator=(Cylinder&& other) = default; + + /** + * Return the height of the cylinder. + * + * @return Height of the cylinder + */ + [[nodiscard]] + float getHeight() const; + + /** + * Set the height of the cylinder. + * + * @param[in] height Height of the cylinder + */ + void setHeight(float height); + + /** + * Returns the signed distance from a point to the closest + * surface of the cylinder. + * + * The result is negative if the point is contained by the + * cylinder. + * + * @param[in] point Point as 3D vector + * @return Signed distance from point to surface + */ + [[nodiscard]] + float distanceTo(const glm::vec3& point) override; + + /** + * Generates a vertex data structure, which can be + * used for rendering via draw calls, containing + * the cylinder in a discrete form. + * + * The vertex data will store positions, normals and + * UV-coordinates as vertex attributes. + * + * @param[in,out] core Core instance + * @return Vertex data with generated geometry + */ + [[nodiscard]] + VertexData generateVertexData(Core& core) const override; + + }; + + /** @} */ + +} \ No newline at end of file diff --git a/modules/geometry/include/vkcv/geometry/Geometry.hpp b/modules/geometry/include/vkcv/geometry/Geometry.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cf2b6eb5132e46883a81dc6d45eae18f51f48bea --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Geometry.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/VertexData.hpp> + +#include <glm/glm.hpp> + +namespace vkcv::geometry { + + /** + * @defgroup vkcv_geometry Geometry Module + * A module to use basic geometry for rendering. + * @{ + */ + + /** + * A basic class to provide attributes for any kind of geometry. + */ + class Geometry { + private: + /** + * Position of the geometry in 3D-coordinates. + */ + glm::vec3 m_position; + + protected: + /** + * Generate tangent from positions and uv-coordinates + * for a given triangle. + * + * @param[in] positions Array of positions + * @param[in] uvs Array of uv-coordinates + * @return Calculated tangent + */ + [[nodiscard]] + glm::vec3 generateTangent(const std::array<glm::vec3, 3>& positions, + const std::array<glm::vec2, 3>& uvs) const; + + public: + /** + * Constructor creating geometry by a given position + * as 3D vector. + * + * @param[in] position Position of the geometry as 3D vector + */ + explicit Geometry(const glm::vec3& position); + + /** + * Copy-constructor of a geometry. + * + * @param[in] other Other geometry + */ + Geometry(const Geometry& other) = default; + + /** + * Move-constructor of a geometry. + * + * @param[in] other Other geometry + */ + Geometry(Geometry&& other) = default; + + /** + * Destructor of a geometry. + */ + ~Geometry() = default; + + /** + * Copy-operator of a geometry. + * + * @param[in] other Other geometry + * @return Reference to this geometry + */ + Geometry& operator=(const Geometry& other) = default; + + /** + * Move-operator of a geometry. + * + * @param[in] other Other geometry + * @return Reference to this geometry + */ + Geometry& operator=(Geometry&& other) = default; + + /** + * Return the position of a geometry as 3D vector. + * + * @return Position of the geometry as 3D vector + */ + [[nodiscard]] + const glm::vec3& getPosition() const; + + /** + * Set the position of a geometry to a specific + * 3D vector. + * + * @param[in] position Position as 3D vector + */ + void setPosition(const glm::vec3& position); + + /** + * Generates a vertex data structure, which can be + * used for rendering via draw calls, containing + * the geometry in a discrete form. + * + * The vertex data will store positions, normals and + * UV-coordinates as vertex attributes. + * + * @param[in,out] core Core instance + * @return Vertex data with generated geometry + */ + [[nodiscard]] + virtual VertexData generateVertexData(Core& core) const = 0; + + }; + + /** @} */ + +} diff --git a/modules/geometry/include/vkcv/geometry/Sphere.hpp b/modules/geometry/include/vkcv/geometry/Sphere.hpp new file mode 100644 index 0000000000000000000000000000000000000000..173d1f157dfd4e3987e278a1516dc08ba2f7cf9b --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Sphere.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include "Circular.hpp" +#include "Volume.hpp" + +namespace vkcv::geometry { + + /** + * @addtogroup vkcv_geometry + * @{ + */ + + /** + * A class to use geometry in form of a sphere. + */ + class Sphere : public Volume, public Circular { + public: + /** + * Constructor creating sphere by a given position + * as 3D vector and a radius. + * + * @param[in] position Position of the sphere as 3D vector + * @param[in] radius Radius of the sphere + */ + Sphere(const glm::vec3& position, float radius); + + /** + * Copy-constructor of a sphere. + * + * @param[in] other Other sphere + */ + Sphere(const Sphere& other) = default; + + /** + * Move-constructor of a sphere. + * + * @param[in] other Other sphere + */ + Sphere(Sphere&& other) = default; + + /** + * Destructor of a sphere. + */ + ~Sphere() = default; + + /** + * Copy-operator of a sphere. + * + * @param[in] other Other sphere + * @return Reference to this sphere + */ + Sphere& operator=(const Sphere& other) = default; + + /** + * Move-operator of a sphere. + * + * @param[in] other Other sphere + * @return Reference to this sphere + */ + Sphere& operator=(Sphere&& other) = default; + + /** + * Returns the signed distance from a point to the closest + * surface of the sphere. + * + * The result is negative if the point is contained by the + * sphere. + * + * @param[in] point Point as 3D vector + * @return Signed distance from point to surface + */ + [[nodiscard]] + float distanceTo(const glm::vec3& point) override; + + /** + * Generates a vertex data structure, which can be + * used for rendering via draw calls, containing + * the sphere in a discrete form. + * + * The vertex data will store positions, normals and + * UV-coordinates as vertex attributes. + * + * @param[in,out] core Core instance + * @return Vertex data with generated geometry + */ + [[nodiscard]] + VertexData generateVertexData(Core& core) const override; + + }; + + /** @} */ + +} diff --git a/modules/geometry/include/vkcv/geometry/Teapot.hpp b/modules/geometry/include/vkcv/geometry/Teapot.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ef0f973dee8a617573cf91274afc51f4a499b5f0 --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Teapot.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include "Geometry.hpp" + +namespace vkcv::geometry { + + /** + * @addtogroup vkcv_geometry + * @{ + */ + + /** + * A class to use geometry in form of a teapot. + */ + class Teapot : public Geometry { + private: + /** + * Uniform scale of the teapot. + */ + float m_scale; + + public: + /** + * Constructor creating teapot by a given position + * as 3D vector and uniform scale. + * + * @param[in] position Position of the teapot as 3D vector + * @param[in] scale Uniform scale of the teapot + */ + explicit Teapot(const glm::vec3& position, float scale); + + /** + * Copy-constructor of a teapot. + * + * @param[in] other Other teapot + */ + Teapot(const Teapot& other) = default; + + /** + * Move-constructor of a teapot. + * + * @param[in] other Other teapot + */ + Teapot(Teapot&& other) = default; + + /** + * Destructor of a teapot. + */ + ~Teapot() = default; + + /** + * Copy-operator of a teapot. + * + * @param[in] other Other teapot + * @return Reference to this teapot + */ + Teapot& operator=(const Teapot& other) = default; + + /** + * Move-operator of a teapot. + * + * @param[in] other Other teapot + * @return Reference to this teapot + */ + Teapot& operator=(Teapot&& other) = default; + + /** + * Return the uniform scale of a teapot. + * + * @return Uniform scale of the teapot + */ + [[nodiscard]] + float getScale() const; + + /** + * Set the uniform scale of a teapot. + * + * @param scale Uniform scale + */ + void setScale(float scale); + + /** + * Generates a vertex data structure, which can be + * used for rendering via draw calls, containing + * the teapot in a discrete form. + * + * The vertex data will store positions, normals and + * UV-coordinates as vertex attributes. + * + * @param[in,out] core Core instance + * @return Vertex data with generated geometry + */ + [[nodiscard]] + VertexData generateVertexData(Core& core) const override; + + }; + + /** @} */ + +} diff --git a/modules/geometry/include/vkcv/geometry/Volume.hpp b/modules/geometry/include/vkcv/geometry/Volume.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5705b6687492f4425e7ac0dfd060a9575606704a --- /dev/null +++ b/modules/geometry/include/vkcv/geometry/Volume.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include "Geometry.hpp" + +namespace vkcv::geometry { + + /** + * @addtogroup vkcv_geometry + * @{ + */ + + /** + * A basic class to provide functions for any volumetric geometry. + */ + class Volume : public Geometry { + private: + public: + /** + * Constructor creating volumetric geometry by a given + * position as 3D vector. + * + * @param[in] position Position of the geometry as 3D vector + */ + explicit Volume(const glm::vec3& position); + + /** + * Copy-constructor of a volumetric geometry. + * + * @param[in] other Other volumetric geometry + */ + Volume(const Volume& other) = default; + + /** + * Move-constructor of a volumetric geometry. + * + * @param[in] other Other volumetric geometry + */ + Volume(Volume&& other) = default; + + /** + * Destructor of a volumetric geometry. + */ + ~Volume() = default; + + /** + * Copy-operator of a volumetric geometry. + * + * @param[in] other Other volumetric geometry + * @return Reference to this volumetric geometry + */ + Volume& operator=(const Volume& other) = default; + + /** + * Move-operator of a volumetric geometry. + * + * @param[in] other Other volumetric geometry + * @return Reference to this volumetric geometry + */ + Volume& operator=(Volume&& other) = default; + + /** + * Returns the signed distance from a point to the closest + * surface of the volumetric geometry. + * + * The result is negative if the point is contained by the + * volumetric geometry. + * + * @param[in] point Point as 3D vector + * @return Signed distance from point to surface + */ + [[nodiscard]] + virtual float distanceTo(const glm::vec3& point) = 0; + + /** + * Returns as boolean value whether a point is contained + * by a volumetric geometry. + * + * @param[in] point Point as 3D vector + * @return true if the point is contained, other false. + */ + [[nodiscard]] + bool contains(const glm::vec3& point); + + }; + + /** @} */ + +} diff --git a/modules/geometry/lib/glm b/modules/geometry/lib/glm new file mode 160000 index 0000000000000000000000000000000000000000..cc98465e3508535ba8c7f6208df934c156a018dc --- /dev/null +++ b/modules/geometry/lib/glm @@ -0,0 +1 @@ +Subproject commit cc98465e3508535ba8c7f6208df934c156a018dc diff --git a/modules/geometry/src/vkcv/geometry/Circular.cpp b/modules/geometry/src/vkcv/geometry/Circular.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f546b2cf75c517971ce314de73b72e2ce990c7f7 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Circular.cpp @@ -0,0 +1,25 @@ + +#include "vkcv/geometry/Circular.hpp" + +namespace vkcv::geometry { + + Circular::Circular(float radius, size_t resolution) + : m_radius(radius), m_resolution(resolution) {} + + float Circular::getRadius() const { + return m_radius; + } + + void Circular::setRadius(float radius) { + m_radius = radius; + } + + size_t Circular::getResolution() const { + return m_resolution; + } + + void Circular::setResolution(size_t resolution) { + m_resolution = resolution; + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Cuboid.cpp b/modules/geometry/src/vkcv/geometry/Cuboid.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73a701b2826a7694201304b029c91af3e7191e5c --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Cuboid.cpp @@ -0,0 +1,261 @@ + +#include "vkcv/geometry/Cuboid.hpp" + +namespace vkcv::geometry { + + Cuboid::Cuboid(const glm::vec3 &position, const glm::vec3 &size) + : Volume(position), m_size(size) {} + + Cuboid::Cuboid(const glm::vec3 &position, float size) + : Cuboid(position, glm::vec3(size)) {} + + const glm::vec3 &Cuboid::getSize() const { + return m_size; + } + + void Cuboid::setSize(const glm::vec3 &size) { + m_size = size; + } + + float Cuboid::distanceTo(const glm::vec3 &point) { + const auto offset = (point - getPosition()); + const auto distance = (glm::abs(offset) - getSize() * 0.5f); + const auto inside = glm::lessThanEqual(distance, glm::vec3(0.0f)); + + if (glm::all(inside)) { + return glm::max(glm::max(distance.x, distance.y), distance.z); + } else { + return glm::length(glm::vec3(glm::not_(inside)) * distance); + } + } + + VertexData Cuboid::generateVertexData(vkcv::Core &core) const { + std::array<float, 72> cuboidPositions = { + -0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, +0.5f, + -0.5f, +0.5f, -0.5f, + -0.5f, +0.5f, +0.5f, + +0.5f, -0.5f, -0.5f, + +0.5f, -0.5f, +0.5f, + +0.5f, +0.5f, -0.5f, + +0.5f, +0.5f, +0.5f, + + -0.5f, -0.5f, -0.5f, + +0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, +0.5f, + +0.5f, -0.5f, +0.5f, + -0.5f, +0.5f, -0.5f, + +0.5f, +0.5f, -0.5f, + -0.5f, +0.5f, +0.5f, + +0.5f, +0.5f, +0.5f, + + -0.5f, -0.5f, -0.5f, + -0.5f, +0.5f, -0.5f, + +0.5f, -0.5f, -0.5f, + +0.5f, +0.5f, -0.5f, + -0.5f, -0.5f, +0.5f, + -0.5f, +0.5f, +0.5f, + +0.5f, -0.5f, +0.5f, + +0.5f, +0.5f, +0.5f + }; + + const std::array<float, 72> cuboidNormals = { + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, + +1.0f, 0.0f, 0.0f, + +1.0f, 0.0f, 0.0f, + +1.0f, 0.0f, 0.0f, + +1.0f, 0.0f, 0.0f, + + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, +1.0f, 0.0f, + 0.0f, +1.0f, 0.0f, + 0.0f, +1.0f, 0.0f, + 0.0f, +1.0f, 0.0f, + + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, +1.0f, + 0.0f, 0.0f, +1.0f, + 0.0f, 0.0f, +1.0f, + 0.0f, 0.0f, +1.0f + }; + + const std::array<float, 48> cuboidUVCoords = { + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + }; + + const std::array<uint8_t, 36> cuboidIndices = { + 0, 1, 3, + 0, 3, 2, + 4, 6, 7, + 4, 7, 5, + + 8, 9, 11, + 8, 11, 10, + 12, 14, 15, + 12, 15, 13, + + 16, 17, 19, + 16, 19, 18, + 20, 22, 23, + 20, 23, 21 + }; + + std::vector<glm::vec3> cuboidTangents; + cuboidTangents.resize(24, glm::vec3(0.0f)); + + std::vector<size_t> cuboidTangentWeights; + cuboidTangentWeights.resize(cuboidTangents.size(), 0); + + for (size_t i = 0; i < cuboidIndices.size(); i += 3) { + const auto index0 = cuboidIndices[i + 0]; + const auto index1 = cuboidIndices[i + 1]; + const auto index2 = cuboidIndices[i + 2]; + + const std::array<glm::vec3, 3> positions = { + glm::vec3( + cuboidPositions[index0 * 3 + 0], + cuboidPositions[index0 * 3 + 1], + cuboidPositions[index0 * 3 + 2] + ), + glm::vec3( + cuboidPositions[index1 * 3 + 0], + cuboidPositions[index1 * 3 + 1], + cuboidPositions[index1 * 3 + 2] + ), + glm::vec3( + cuboidPositions[index2 * 3 + 0], + cuboidPositions[index2 * 3 + 1], + cuboidPositions[index2 * 3 + 2] + ) + }; + + const std::array<glm::vec2, 3> uvs = { + glm::vec2( + cuboidUVCoords[index0 * 3 + 0], + cuboidUVCoords[index0 * 3 + 1] + ), + glm::vec2( + cuboidUVCoords[index1 * 3 + 0], + cuboidUVCoords[index1 * 3 + 1] + ), + glm::vec2( + cuboidUVCoords[index2 * 3 + 0], + cuboidUVCoords[index2 * 3 + 1] + ) + }; + + const glm::vec3 tangent = generateTangent(positions, uvs); + + cuboidTangents[index0] += tangent; + cuboidTangents[index1] += tangent; + cuboidTangents[index2] += tangent; + + cuboidTangentWeights[index0]++; + cuboidTangentWeights[index1]++; + cuboidTangentWeights[index2]++; + } + + for (size_t i = 0; i < cuboidTangents.size(); i++) { + if (cuboidTangentWeights[i] <= 0) { + continue; + } + + cuboidTangents[i] /= cuboidTangentWeights[i]; + } + + const auto& position = getPosition(); + const auto& size = getSize(); + + for (size_t i = 0; i < 24; i++) { + cuboidPositions[i * 3 + 0] = cuboidPositions[i * 3 + 0] * size.x + position.x; + cuboidPositions[i * 3 + 1] = cuboidPositions[i * 3 + 1] * size.y + position.y; + cuboidPositions[i * 3 + 2] = cuboidPositions[i * 3 + 2] * size.z + position.z; + } + + auto positionBuffer = buffer<float>(core, BufferType::VERTEX, cuboidPositions.size()); + positionBuffer.fill(cuboidPositions); + + auto normalBuffer = buffer<float>(core, BufferType::VERTEX, cuboidNormals.size()); + normalBuffer.fill(cuboidNormals); + + auto uvBuffer = buffer<float>(core, BufferType::VERTEX, cuboidUVCoords.size()); + uvBuffer.fill(cuboidUVCoords); + + auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cuboidTangents.size()); + tangentBuffer.fill(cuboidTangents); + + VertexData data ({ + vkcv::vertexBufferBinding(positionBuffer.getHandle()), + vkcv::vertexBufferBinding(normalBuffer.getHandle()), + vkcv::vertexBufferBinding(uvBuffer.getHandle()), + vkcv::vertexBufferBinding(tangentBuffer.getHandle()) + }); + + const auto& featureManager = core.getContext().getFeatureManager(); + + const bool index8Bit = featureManager.checkFeatures<vk::PhysicalDeviceIndexTypeUint8FeaturesEXT>( + vk::StructureType::ePhysicalDeviceIndexTypeUint8FeaturesEXT, + [](const vk::PhysicalDeviceIndexTypeUint8FeaturesEXT& features) { + return features.indexTypeUint8; + } + ); + + if (index8Bit) { + auto indexBuffer = buffer<uint8_t>(core, BufferType::INDEX, cuboidIndices.size()); + indexBuffer.fill(cuboidIndices); + + data.setIndexBuffer(indexBuffer.getHandle(), IndexBitCount::Bit8); + data.setCount(indexBuffer.getCount()); + } else { + std::vector<uint16_t> cuboidIndices16; + cuboidIndices16.resize(cuboidIndices.size()); + + for (size_t i = 0; i < cuboidIndices16.size(); i++) { + cuboidIndices16[i] = static_cast<uint16_t>(cuboidIndices[i]); + } + + auto indexBuffer = buffer<uint16_t>(core, BufferType::INDEX, cuboidIndices16.size()); + indexBuffer.fill(cuboidIndices16); + + data.setIndexBuffer(indexBuffer.getHandle()); + data.setCount(indexBuffer.getCount()); + } + + return data; + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Cylinder.cpp b/modules/geometry/src/vkcv/geometry/Cylinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0583c4c134922f16ed00d8ff36f81c027051bb90 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Cylinder.cpp @@ -0,0 +1,192 @@ + +#include "vkcv/geometry/Cylinder.hpp" + +#include <numbers> + +namespace vkcv::geometry { + + Cylinder::Cylinder(const glm::vec3 &position, float height, float radius) + : Volume(position), Circular(radius), m_height(height) {} + + float Cylinder::getHeight() const { + return m_height; + } + + void Cylinder::setHeight(float height) { + m_height = height; + } + + float Cylinder::distanceTo(const glm::vec3 &point) { + const auto& position = getPosition(); + + const auto verticalDistance = glm::abs(position.y - point.y) - getHeight(); + const auto circularDistance = glm::distance( + glm::vec2(position.x, position.z), + glm::vec2(point.x, point.z) + ) - getRadius(); + + if (circularDistance <= 0.0f) { + return glm::max(verticalDistance, circularDistance); + } else + if (verticalDistance <= 0.0f) { + return circularDistance; + } else { + return glm::length(glm::vec2(verticalDistance, circularDistance)); + } + } + + VertexData Cylinder::generateVertexData(vkcv::Core &core) const { + const auto& position = getPosition(); + const auto radius = getRadius(); + const auto height = getHeight(); + const auto resolution = getResolution(); + + const size_t vertexCount = (resolution * 4 + 2); + + std::vector<glm::vec3> cylinderVertices; + std::vector<glm::vec3> cylinderNormals; + std::vector<glm::vec2> cylinderUVCoords; + + cylinderVertices.reserve(vertexCount); + cylinderNormals.reserve(vertexCount); + cylinderUVCoords.reserve(vertexCount); + + std::vector<uint32_t> cylinderIndices; + cylinderIndices.reserve(resolution * 12); + + size_t i, j; + float u, v; + float phi; + float sinPhi, cosPhi; + float x, y, z; + + for (j = 0; j < 2; j++) { + v = static_cast<float>(j); + + x = position.x; + y = position.y + height * (v - 0.5f); + z = position.z; + + cylinderVertices.push_back(glm::vec3(x, y, z)); + cylinderNormals.push_back(glm::vec3(0.0f, v * 2.0f - 1.0f, 0.0f)); + cylinderUVCoords.push_back(glm::vec2(0.5f, 0.5f)); + } + + size_t offset = 0; + + for (i = 0; i < resolution; i++) { + u = static_cast<float>(i) / static_cast<float>(resolution - 1); + phi = 2.0f * std::numbers::pi_v<float> * u; + + sinPhi = std::sin(phi); + cosPhi = std::cos(phi); + + x = position.x + radius * sinPhi; + z = position.z + radius * cosPhi; + + for (j = 0; j < 2; j++) { + v = static_cast<float>(j); + + y = position.y + height * (v - 0.5f); + + cylinderVertices.push_back(glm::vec3(x, y, z)); + cylinderNormals.push_back(glm::vec3(0.0f, v * 2.0f - 1.0f, 0.0f)); + cylinderUVCoords.push_back(glm::vec2((sinPhi + 1.0f) * 0.5f, (cosPhi + 1.0f) * 0.5f)); + + cylinderVertices.push_back(glm::vec3(x, y, z)); + cylinderNormals.push_back(glm::vec3(sinPhi, 0.0f, cosPhi)); + cylinderUVCoords.push_back(glm::vec2(u, v)); + + if (j == 1) { + cylinderIndices.push_back(2 + (offset + j * 2) % (vertexCount - 2)); + cylinderIndices.push_back(2 + (offset + j * 2 + 4) % (vertexCount - 2)); + cylinderIndices.push_back(j); + + cylinderIndices.push_back(2 + (offset + j * 2 + 5) % (vertexCount - 2)); + cylinderIndices.push_back(2 + (offset + j * 2 + 1) % (vertexCount - 2)); + cylinderIndices.push_back(2 + (offset + j * 2 + 3) % (vertexCount - 2)); + } else { + cylinderIndices.push_back(2 + (offset + j * 2 + 4) % (vertexCount - 2)); + cylinderIndices.push_back(2 + (offset + j * 2) % (vertexCount - 2)); + cylinderIndices.push_back(j); + + cylinderIndices.push_back(2 + (offset + j * 2 + 1) % (vertexCount - 2)); + cylinderIndices.push_back(2 + (offset + j * 2 + 5) % (vertexCount - 2)); + cylinderIndices.push_back(2 + (offset + j * 2 + 3) % (vertexCount - 2)); + } + } + + offset += 4; + } + + std::vector<glm::vec3> cylinderTangents; + cylinderTangents.resize(cylinderVertices.size(), glm::vec3(0.0f)); + + std::vector<size_t> cylinderTangentWeights; + cylinderTangentWeights.resize(cylinderTangents.size(), 0); + + for (i = 0; i < cylinderIndices.size(); i += 3) { + const auto index0 = cylinderIndices[i + 0]; + const auto index1 = cylinderIndices[i + 1]; + const auto index2 = cylinderIndices[i + 2]; + + const std::array<glm::vec3, 3> positions = { + cylinderVertices[index0], + cylinderVertices[index1], + cylinderVertices[index2] + }; + + const std::array<glm::vec2, 3> uvs = { + cylinderUVCoords[index0], + cylinderUVCoords[index1], + cylinderUVCoords[index2] + }; + + const glm::vec3 tangent = generateTangent(positions, uvs); + + cylinderTangents[index0] += tangent; + cylinderTangents[index1] += tangent; + cylinderTangents[index2] += tangent; + + cylinderTangentWeights[index0]++; + cylinderTangentWeights[index1]++; + cylinderTangentWeights[index2]++; + } + + for (i = 0; i < cylinderTangents.size(); i++) { + if (cylinderTangentWeights[i] <= 0) { + continue; + } + + cylinderTangents[i] /= cylinderTangentWeights[i]; + } + + auto positionBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cylinderVertices.size()); + positionBuffer.fill(cylinderVertices); + + auto normalBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cylinderNormals.size()); + normalBuffer.fill(cylinderNormals); + + auto uvBuffer = buffer<glm::vec2>(core, BufferType::VERTEX, cylinderUVCoords.size()); + uvBuffer.fill(cylinderUVCoords); + + auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, cylinderTangents.size()); + tangentBuffer.fill(cylinderTangents); + + auto indexBuffer = buffer<uint32_t>(core, BufferType::INDEX, cylinderIndices.size()); + indexBuffer.fill(cylinderIndices); + + VertexData data ({ + vkcv::vertexBufferBinding(positionBuffer.getHandle()), + vkcv::vertexBufferBinding(normalBuffer.getHandle()), + vkcv::vertexBufferBinding(uvBuffer.getHandle()), + vkcv::vertexBufferBinding(tangentBuffer.getHandle()) + }); + + data.setIndexBuffer(indexBuffer.getHandle(), IndexBitCount::Bit32); + data.setCount(indexBuffer.getCount()); + + return data; + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Geometry.cpp b/modules/geometry/src/vkcv/geometry/Geometry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..feaa84a04f2e180b08fd92977d6494ac997af6b2 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Geometry.cpp @@ -0,0 +1,31 @@ + +#include "vkcv/geometry/Geometry.hpp" + +namespace vkcv::geometry { + + Geometry::Geometry(const glm::vec3 &position) + : m_position(position) {} + + const glm::vec3 &Geometry::getPosition() const { + return m_position; + } + + void Geometry::setPosition(const glm::vec3 &position) { + m_position = position; + } + + glm::vec3 Geometry::generateTangent(const std::array<glm::vec3, 3>& positions, + const std::array<glm::vec2, 3>& uvs) const { + auto delta1 = positions[1] - positions[0]; + auto delta2 = positions[2] - positions[0]; + + auto deltaUV1 = uvs[1] - uvs[0]; + auto deltaUV2 = uvs[2] - uvs[0]; + + return glm::normalize(glm::vec3( + delta1 * deltaUV2.y - + delta2 * deltaUV1.y + )); + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Sphere.cpp b/modules/geometry/src/vkcv/geometry/Sphere.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d516a1dad4e11651e4ad6d6c056dcf9442d29f18 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Sphere.cpp @@ -0,0 +1,152 @@ + +#include "vkcv/geometry/Sphere.hpp" + +#include <numbers> + +namespace vkcv::geometry { + + Sphere::Sphere(const glm::vec3& position, float radius) + : Volume(position), Circular(radius) {} + + float Sphere::distanceTo(const glm::vec3 &point) { + return glm::distance(getPosition(), point) - getRadius(); + } + + VertexData Sphere::generateVertexData(vkcv::Core &core) const { + const auto& position = getPosition(); + const auto radius = getRadius(); + const auto resolution = getResolution(); + + const size_t vertexCount = resolution * (resolution + 1); + + std::vector<glm::vec3> sphereVertices; + std::vector<glm::vec3> sphereNormals; + std::vector<glm::vec2> sphereUVCoords; + + sphereVertices.reserve(vertexCount); + sphereNormals.reserve(vertexCount); + sphereUVCoords.reserve(vertexCount); + + std::vector<uint32_t> sphereIndices; + sphereIndices.reserve((resolution - 1) * resolution * 6); + + size_t i, j; + float u, v; + float phi, theta; + float sinTheta; + float x, y, z; + + for (i = 0; i < resolution; i++) { + v = static_cast<float>(i) / static_cast<float>(resolution - 1); + theta = std::numbers::pi_v<float> * v; + sinTheta = std::sin(theta); + + y = position.y + radius * std::cos(theta); + + for (j = 0; j < resolution; j++) { + u = static_cast<float>(j) / static_cast<float>(resolution); + phi = 2.0f * std::numbers::pi_v<float> * u; + + x = position.x + radius * sinTheta * std::sin(phi); + z = position.z + radius * sinTheta * std::cos(phi); + + sphereVertices.push_back(glm::vec3(x, y, z)); + sphereNormals.push_back(glm::vec3(x, y, z) / radius); + sphereUVCoords.push_back(glm::vec2(u, 1.0f - v)); + } + + x = position.x; + z = position.z + radius * sinTheta; + + sphereVertices.push_back(glm::vec3(x, y, z)); + sphereNormals.push_back(glm::vec3(x, y, z) / radius); + sphereUVCoords.push_back(glm::vec2(1.0f, 1.0f - v)); + } + + size_t offset = 0; + + for (i = 1; i < resolution; i++) { + for (j = 0; j < resolution; j++) { + sphereIndices.push_back((offset + j) % vertexCount); + sphereIndices.push_back((offset + resolution + j + 1) % vertexCount); + sphereIndices.push_back((offset + resolution + j + 2) % vertexCount); + + sphereIndices.push_back((offset + resolution + j + 2) % vertexCount); + sphereIndices.push_back((offset + j + 1) % vertexCount); + sphereIndices.push_back((offset + j) % vertexCount); + } + + offset += (resolution + 1); + } + + std::vector<glm::vec3> sphereTangents; + sphereTangents.resize(sphereVertices.size(), glm::vec3(0.0f)); + + std::vector<size_t> sphereTangentWeights; + sphereTangentWeights.resize(sphereTangents.size(), 0); + + for (i = 0; i < sphereIndices.size(); i += 3) { + const auto index0 = sphereIndices[i + 0]; + const auto index1 = sphereIndices[i + 1]; + const auto index2 = sphereIndices[i + 2]; + + const std::array<glm::vec3, 3> positions = { + sphereVertices[index0], + sphereVertices[index1], + sphereVertices[index2] + }; + + const std::array<glm::vec2, 3> uvs = { + sphereUVCoords[index0], + sphereUVCoords[index1], + sphereUVCoords[index2] + }; + + const glm::vec3 tangent = generateTangent(positions, uvs); + + sphereTangents[index0] += tangent; + sphereTangents[index1] += tangent; + sphereTangents[index2] += tangent; + + sphereTangentWeights[index0]++; + sphereTangentWeights[index1]++; + sphereTangentWeights[index2]++; + } + + for (i = 0; i < sphereTangents.size(); i++) { + if (sphereTangentWeights[i] <= 0) { + continue; + } + + sphereTangents[i] /= sphereTangentWeights[i]; + } + + auto positionBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, sphereVertices.size()); + positionBuffer.fill(sphereVertices); + + auto normalBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, sphereNormals.size()); + normalBuffer.fill(sphereNormals); + + auto uvBuffer = buffer<glm::vec2>(core, BufferType::VERTEX, sphereUVCoords.size()); + uvBuffer.fill(sphereUVCoords); + + auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, sphereTangents.size()); + tangentBuffer.fill(sphereTangents); + + auto indexBuffer = buffer<uint32_t>(core, BufferType::INDEX, sphereIndices.size()); + indexBuffer.fill(sphereIndices); + + VertexData data ({ + vkcv::vertexBufferBinding(positionBuffer.getHandle()), + vkcv::vertexBufferBinding(normalBuffer.getHandle()), + vkcv::vertexBufferBinding(uvBuffer.getHandle()), + vkcv::vertexBufferBinding(tangentBuffer.getHandle()) + }); + + data.setIndexBuffer(indexBuffer.getHandle(), IndexBitCount::Bit32); + data.setCount(indexBuffer.getCount()); + + return data; + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Teapot.cpp b/modules/geometry/src/vkcv/geometry/Teapot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7becdef5941769c79da0b0b17a45ec3e5d0a1cb6 --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Teapot.cpp @@ -0,0 +1,14949 @@ + +#include "vkcv/geometry/Teapot.hpp" + +namespace vkcv::geometry { + + Teapot::Teapot(const glm::vec3 &position, float scale) + : Geometry(position), m_scale(scale) {} + + float Teapot::getScale() const { + return m_scale; + } + + void Teapot::setScale(float scale) { + m_scale = scale; + } + + VertexData Teapot::generateVertexData(Core& core) const { + const size_t vertexCount = 3872; + const size_t indexCount = 19200; + + std::array<float, (vertexCount * 3)> teapotVertices = { + 0.69999999f, 0.45000005f, 0.00000001f, + 0.69098389f, 0.44999996f, 0.11485600f, + 0.66483200f, 0.45000011f, 0.22332802f, + 0.62288797f, 0.45000002f, 0.32407200f, + 0.56649601f, 0.45000008f, 0.41574404f, + 0.49699998f, 0.45000005f, 0.49700001f, + 0.41574395f, 0.45000005f, 0.56649601f, + 0.32407200f, 0.45000008f, 0.62288797f, + 0.22332799f, 0.45000005f, 0.66483200f, + 0.11485596f, 0.45000005f, 0.69098401f, + 0.00000000f, 0.45000005f, 0.69999999f, + 0.69296241f, 0.46771872f, 0.00000001f, + 0.68403697f, 0.46771869f, 0.11370128f, + 0.65814799f, 0.46771878f, 0.22108276f, + 0.61662567f, 0.46771872f, 0.32081389f, + 0.56080067f, 0.46771872f, 0.41156429f, + 0.49200332f, 0.46771872f, 0.49200332f, + 0.41156423f, 0.46771872f, 0.56080067f, + 0.32081389f, 0.46771872f, 0.61662567f, + 0.22108275f, 0.46771872f, 0.65814799f, + 0.11370124f, 0.46771872f, 0.68403703f, + 0.00000000f, 0.46771872f, 0.69296241f, + 0.69020003f, 0.48150003f, 0.00000001f, + 0.68131018f, 0.48149997f, 0.11324803f, + 0.65552443f, 0.48150006f, 0.22020143f, + 0.61416763f, 0.48150000f, 0.31953502f, + 0.55856514f, 0.48150009f, 0.40992361f, + 0.49004203f, 0.48150003f, 0.49004203f, + 0.40992361f, 0.48150006f, 0.55856514f, + 0.31953505f, 0.48150003f, 0.61416763f, + 0.22020143f, 0.48150003f, 0.65552437f, + 0.11324799f, 0.48150003f, 0.68131030f, + 0.00000000f, 0.48150003f, 0.69020003f, + 0.69111252f, 0.49134374f, 0.00000001f, + 0.68221092f, 0.49134374f, 0.11339775f, + 0.65639102f, 0.49134377f, 0.22049254f, + 0.61497957f, 0.49134377f, 0.31995746f, + 0.55930358f, 0.49134377f, 0.41046557f, + 0.49068987f, 0.49134374f, 0.49068987f, + 0.41046554f, 0.49134374f, 0.55930358f, + 0.31995746f, 0.49134374f, 0.61497957f, + 0.22049253f, 0.49134377f, 0.65639102f, + 0.11339770f, 0.49134374f, 0.68221098f, + 0.00000000f, 0.49134374f, 0.69111252f, + 0.69510001f, 0.49725008f, 0.00000001f, + 0.68614709f, 0.49725005f, 0.11405202f, + 0.66017824f, 0.49725014f, 0.22176473f, + 0.61852777f, 0.49725008f, 0.32180351f, + 0.56253058f, 0.49725008f, 0.41283381f, + 0.49352103f, 0.49725008f, 0.49352100f, + 0.41283381f, 0.49725008f, 0.56253058f, + 0.32180351f, 0.49725008f, 0.61852777f, + 0.22176471f, 0.49725008f, 0.66017818f, + 0.11405198f, 0.49725008f, 0.68614715f, + 0.00000000f, 0.49725008f, 0.69510001f, + 0.70156252f, 0.49921876f, 0.00000001f, + 0.69252634f, 0.49921870f, 0.11511238f, + 0.66631603f, 0.49921876f, 0.22382653f, + 0.62427837f, 0.49921873f, 0.32479537f, + 0.56776059f, 0.49921879f, 0.41667202f, + 0.49810940f, 0.49921876f, 0.49810940f, + 0.41667199f, 0.49921879f, 0.56776053f, + 0.32479542f, 0.49921876f, 0.62427843f, + 0.22382650f, 0.49921876f, 0.66631603f, + 0.11511234f, 0.49921876f, 0.69252640f, + 0.00000000f, 0.49921876f, 0.70156252f, + 0.70989996f, 0.49725002f, 0.00000001f, + 0.70075637f, 0.49724996f, 0.11648039f, + 0.67423457f, 0.49725008f, 0.22648652f, + 0.63169742f, 0.49724999f, 0.32865530f, + 0.57450789f, 0.49725002f, 0.42162383f, + 0.50402898f, 0.49725002f, 0.50402898f, + 0.42162377f, 0.49725002f, 0.57450783f, + 0.32865530f, 0.49725002f, 0.63169742f, + 0.22648649f, 0.49725005f, 0.67423463f, + 0.11648035f, 0.49725002f, 0.70075643f, + 0.00000000f, 0.49725002f, 0.70989996f, + 0.71951252f, 0.49134377f, 0.00000001f, + 0.71024513f, 0.49134374f, 0.11805762f, + 0.68336421f, 0.49134380f, 0.22955328f, + 0.64025098f, 0.49134377f, 0.33310553f, + 0.58228713f, 0.49134383f, 0.42733288f, + 0.51085389f, 0.49134377f, 0.51085389f, + 0.42733288f, 0.49134377f, 0.58228713f, + 0.33310553f, 0.49134377f, 0.64025104f, + 0.22955328f, 0.49134380f, 0.68336427f, + 0.11805756f, 0.49134377f, 0.71024519f, + 0.00000000f, 0.49134377f, 0.71951252f, + 0.72979999f, 0.48150003f, 0.00000001f, + 0.72040009f, 0.48149997f, 0.11974559f, + 0.69313490f, 0.48150006f, 0.23283541f, + 0.64940518f, 0.48150000f, 0.33786824f, + 0.59061253f, 0.48150009f, 0.43344283f, + 0.51815796f, 0.48150003f, 0.51815796f, + 0.43344280f, 0.48150006f, 0.59061259f, + 0.33786821f, 0.48150003f, 0.64940524f, + 0.23283540f, 0.48150003f, 0.69313484f, + 0.11974555f, 0.48150003f, 0.72040015f, + 0.00000000f, 0.48150003f, 0.72979999f, + 0.74016249f, 0.46771878f, 0.00000001f, + 0.73062909f, 0.46771872f, 0.12144586f, + 0.70297670f, 0.46771881f, 0.23614147f, + 0.65862614f, 0.46771878f, 0.34266564f, + 0.59899879f, 0.46771878f, 0.43959734f, + 0.52551538f, 0.46771878f, 0.52551538f, + 0.43959731f, 0.46771878f, 0.59899873f, + 0.34266564f, 0.46771878f, 0.65862620f, + 0.23614144f, 0.46771878f, 0.70297670f, + 0.12144582f, 0.46771878f, 0.73062921f, + 0.00000000f, 0.46771878f, 0.74016249f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.74033999f, 0.44999996f, 0.12306000f, + 0.71232003f, 0.45000011f, 0.23928000f, + 0.66737998f, 0.45000002f, 0.34721997f, + 0.60696000f, 0.45000008f, 0.44544002f, + 0.53250003f, 0.45000005f, 0.53250003f, + 0.44543999f, 0.45000005f, 0.60696000f, + 0.34722000f, 0.45000008f, 0.66738003f, + 0.23927999f, 0.45000005f, 0.71231997f, + 0.12305996f, 0.45000005f, 0.74033999f, + 0.00000000f, 0.45000005f, 0.75000000f, + 0.00000000f, 0.45000005f, -0.69999999f, + 0.11485599f, 0.44999996f, -0.69098389f, + 0.22332802f, 0.45000011f, -0.66483200f, + 0.32407200f, 0.45000002f, -0.62288797f, + 0.41574404f, 0.45000008f, -0.56649601f, + 0.49700001f, 0.45000005f, -0.49699998f, + 0.56649601f, 0.45000005f, -0.41574395f, + 0.62288797f, 0.45000008f, -0.32407200f, + 0.66483200f, 0.45000005f, -0.22332799f, + 0.69098401f, 0.45000005f, -0.11485595f, + 0.69999999f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.46771872f, -0.69296241f, + 0.11370128f, 0.46771869f, -0.68403697f, + 0.22108276f, 0.46771878f, -0.65814799f, + 0.32081389f, 0.46771872f, -0.61662567f, + 0.41156429f, 0.46771872f, -0.56080067f, + 0.49200332f, 0.46771872f, -0.49200332f, + 0.56080067f, 0.46771872f, -0.41156423f, + 0.61662567f, 0.46771872f, -0.32081389f, + 0.65814799f, 0.46771872f, -0.22108275f, + 0.68403703f, 0.46771872f, -0.11370123f, + 0.69296241f, 0.46771872f, 0.00000001f, + 0.00000000f, 0.48150003f, -0.69020003f, + 0.11324802f, 0.48149997f, -0.68131018f, + 0.22020143f, 0.48150006f, -0.65552443f, + 0.31953502f, 0.48150000f, -0.61416763f, + 0.40992361f, 0.48150009f, -0.55856514f, + 0.49004203f, 0.48150003f, -0.49004203f, + 0.55856514f, 0.48150006f, -0.40992361f, + 0.61416763f, 0.48150003f, -0.31953505f, + 0.65552437f, 0.48150003f, -0.22020143f, + 0.68131030f, 0.48150003f, -0.11324798f, + 0.69020003f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.49134374f, -0.69111252f, + 0.11339774f, 0.49134374f, -0.68221092f, + 0.22049254f, 0.49134377f, -0.65639102f, + 0.31995746f, 0.49134377f, -0.61497957f, + 0.41046557f, 0.49134377f, -0.55930358f, + 0.49068987f, 0.49134374f, -0.49068987f, + 0.55930358f, 0.49134374f, -0.41046554f, + 0.61497957f, 0.49134374f, -0.31995746f, + 0.65639102f, 0.49134377f, -0.22049253f, + 0.68221098f, 0.49134374f, -0.11339770f, + 0.69111252f, 0.49134374f, 0.00000001f, + 0.00000000f, 0.49725008f, -0.69510001f, + 0.11405201f, 0.49725005f, -0.68614709f, + 0.22176473f, 0.49725014f, -0.66017824f, + 0.32180351f, 0.49725008f, -0.61852777f, + 0.41283381f, 0.49725008f, -0.56253058f, + 0.49352100f, 0.49725008f, -0.49352103f, + 0.56253058f, 0.49725008f, -0.41283381f, + 0.61852777f, 0.49725008f, -0.32180351f, + 0.66017818f, 0.49725008f, -0.22176471f, + 0.68614715f, 0.49725008f, -0.11405197f, + 0.69510001f, 0.49725008f, 0.00000001f, + 0.00000000f, 0.49921876f, -0.70156252f, + 0.11511238f, 0.49921870f, -0.69252634f, + 0.22382653f, 0.49921876f, -0.66631603f, + 0.32479537f, 0.49921873f, -0.62427837f, + 0.41667202f, 0.49921879f, -0.56776059f, + 0.49810940f, 0.49921876f, -0.49810940f, + 0.56776053f, 0.49921879f, -0.41667199f, + 0.62427843f, 0.49921876f, -0.32479542f, + 0.66631603f, 0.49921876f, -0.22382650f, + 0.69252640f, 0.49921876f, -0.11511233f, + 0.70156252f, 0.49921876f, 0.00000001f, + 0.00000000f, 0.49725002f, -0.70989996f, + 0.11648039f, 0.49724996f, -0.70075637f, + 0.22648652f, 0.49725008f, -0.67423457f, + 0.32865530f, 0.49724999f, -0.63169742f, + 0.42162383f, 0.49725002f, -0.57450789f, + 0.50402898f, 0.49725002f, -0.50402898f, + 0.57450783f, 0.49725002f, -0.42162377f, + 0.63169742f, 0.49725002f, -0.32865530f, + 0.67423463f, 0.49725005f, -0.22648649f, + 0.70075643f, 0.49725002f, -0.11648034f, + 0.70989996f, 0.49725002f, 0.00000001f, + 0.00000000f, 0.49134377f, -0.71951252f, + 0.11805761f, 0.49134374f, -0.71024513f, + 0.22955328f, 0.49134380f, -0.68336421f, + 0.33310553f, 0.49134377f, -0.64025098f, + 0.42733288f, 0.49134383f, -0.58228713f, + 0.51085389f, 0.49134377f, -0.51085389f, + 0.58228713f, 0.49134377f, -0.42733288f, + 0.64025104f, 0.49134377f, -0.33310553f, + 0.68336427f, 0.49134380f, -0.22955328f, + 0.71024519f, 0.49134377f, -0.11805756f, + 0.71951252f, 0.49134377f, 0.00000001f, + 0.00000000f, 0.48150003f, -0.72979999f, + 0.11974558f, 0.48149997f, -0.72040009f, + 0.23283541f, 0.48150006f, -0.69313490f, + 0.33786824f, 0.48150000f, -0.64940518f, + 0.43344283f, 0.48150009f, -0.59061253f, + 0.51815796f, 0.48150003f, -0.51815796f, + 0.59061259f, 0.48150006f, -0.43344280f, + 0.64940524f, 0.48150003f, -0.33786821f, + 0.69313484f, 0.48150003f, -0.23283540f, + 0.72040015f, 0.48150003f, -0.11974554f, + 0.72979999f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.46771878f, -0.74016249f, + 0.12144586f, 0.46771872f, -0.73062909f, + 0.23614147f, 0.46771881f, -0.70297670f, + 0.34266564f, 0.46771878f, -0.65862614f, + 0.43959734f, 0.46771878f, -0.59899879f, + 0.52551538f, 0.46771878f, -0.52551538f, + 0.59899873f, 0.46771878f, -0.43959731f, + 0.65862620f, 0.46771878f, -0.34266564f, + 0.70297670f, 0.46771878f, -0.23614144f, + 0.73062921f, 0.46771878f, -0.12144581f, + 0.74016249f, 0.46771878f, 0.00000001f, + 0.00000000f, 0.45000005f, -0.75000000f, + 0.12305999f, 0.44999996f, -0.74033999f, + 0.23928000f, 0.45000011f, -0.71232003f, + 0.34721997f, 0.45000002f, -0.66737998f, + 0.44544002f, 0.45000008f, -0.60696000f, + 0.53250003f, 0.45000005f, -0.53250003f, + 0.60696000f, 0.45000005f, -0.44543999f, + 0.66738003f, 0.45000008f, -0.34722000f, + 0.71231997f, 0.45000005f, -0.23927999f, + 0.74033999f, 0.45000005f, -0.12305995f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.45000005f, 0.69999999f, + -0.11485599f, 0.44999996f, 0.69098389f, + -0.22332802f, 0.45000011f, 0.66483200f, + -0.32407200f, 0.45000002f, 0.62288797f, + -0.41574404f, 0.45000008f, 0.56649601f, + -0.49700001f, 0.45000005f, 0.49699998f, + -0.56649601f, 0.45000005f, 0.41574395f, + -0.62288797f, 0.45000008f, 0.32407200f, + -0.66483200f, 0.45000005f, 0.22332799f, + -0.69098401f, 0.45000005f, 0.11485597f, + -0.69999999f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.46771872f, 0.69296241f, + -0.11370128f, 0.46771869f, 0.68403697f, + -0.22108276f, 0.46771878f, 0.65814799f, + -0.32081389f, 0.46771872f, 0.61662567f, + -0.41156429f, 0.46771872f, 0.56080067f, + -0.49200332f, 0.46771872f, 0.49200332f, + -0.56080067f, 0.46771872f, 0.41156423f, + -0.61662567f, 0.46771872f, 0.32081389f, + -0.65814799f, 0.46771872f, 0.22108275f, + -0.68403703f, 0.46771872f, 0.11370125f, + -0.69296241f, 0.46771872f, 0.00000001f, + 0.00000000f, 0.48150003f, 0.69020003f, + -0.11324802f, 0.48149997f, 0.68131018f, + -0.22020143f, 0.48150006f, 0.65552443f, + -0.31953502f, 0.48150000f, 0.61416763f, + -0.40992361f, 0.48150009f, 0.55856514f, + -0.49004203f, 0.48150003f, 0.49004203f, + -0.55856514f, 0.48150006f, 0.40992361f, + -0.61416763f, 0.48150003f, 0.31953505f, + -0.65552437f, 0.48150003f, 0.22020143f, + -0.68131030f, 0.48150003f, 0.11324800f, + -0.69020003f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.49134374f, 0.69111252f, + -0.11339774f, 0.49134374f, 0.68221092f, + -0.22049254f, 0.49134377f, 0.65639102f, + -0.31995746f, 0.49134377f, 0.61497957f, + -0.41046557f, 0.49134377f, 0.55930358f, + -0.49068987f, 0.49134374f, 0.49068987f, + -0.55930358f, 0.49134374f, 0.41046554f, + -0.61497957f, 0.49134374f, 0.31995746f, + -0.65639102f, 0.49134377f, 0.22049253f, + -0.68221098f, 0.49134374f, 0.11339771f, + -0.69111252f, 0.49134374f, 0.00000001f, + 0.00000000f, 0.49725008f, 0.69510001f, + -0.11405201f, 0.49725005f, 0.68614709f, + -0.22176473f, 0.49725014f, 0.66017824f, + -0.32180351f, 0.49725008f, 0.61852777f, + -0.41283381f, 0.49725008f, 0.56253058f, + -0.49352100f, 0.49725008f, 0.49352103f, + -0.56253058f, 0.49725008f, 0.41283381f, + -0.61852777f, 0.49725008f, 0.32180351f, + -0.66017818f, 0.49725008f, 0.22176471f, + -0.68614715f, 0.49725008f, 0.11405198f, + -0.69510001f, 0.49725008f, 0.00000001f, + 0.00000000f, 0.49921876f, 0.70156252f, + -0.11511238f, 0.49921870f, 0.69252634f, + -0.22382653f, 0.49921876f, 0.66631603f, + -0.32479537f, 0.49921873f, 0.62427837f, + -0.41667202f, 0.49921879f, 0.56776059f, + -0.49810940f, 0.49921876f, 0.49810940f, + -0.56776053f, 0.49921879f, 0.41667199f, + -0.62427843f, 0.49921876f, 0.32479542f, + -0.66631603f, 0.49921876f, 0.22382650f, + -0.69252640f, 0.49921876f, 0.11511235f, + -0.70156252f, 0.49921876f, 0.00000001f, + 0.00000000f, 0.49725002f, 0.70989996f, + -0.11648039f, 0.49724996f, 0.70075637f, + -0.22648652f, 0.49725008f, 0.67423457f, + -0.32865530f, 0.49724999f, 0.63169742f, + -0.42162383f, 0.49725002f, 0.57450789f, + -0.50402898f, 0.49725002f, 0.50402898f, + -0.57450783f, 0.49725002f, 0.42162377f, + -0.63169742f, 0.49725002f, 0.32865530f, + -0.67423463f, 0.49725005f, 0.22648649f, + -0.70075643f, 0.49725002f, 0.11648036f, + -0.70989996f, 0.49725002f, 0.00000001f, + 0.00000000f, 0.49134377f, 0.71951252f, + -0.11805761f, 0.49134374f, 0.71024513f, + -0.22955328f, 0.49134380f, 0.68336421f, + -0.33310553f, 0.49134377f, 0.64025098f, + -0.42733288f, 0.49134383f, 0.58228713f, + -0.51085389f, 0.49134377f, 0.51085389f, + -0.58228713f, 0.49134377f, 0.42733288f, + -0.64025104f, 0.49134377f, 0.33310553f, + -0.68336427f, 0.49134380f, 0.22955328f, + -0.71024519f, 0.49134377f, 0.11805757f, + -0.71951252f, 0.49134377f, 0.00000001f, + 0.00000000f, 0.48150003f, 0.72979999f, + -0.11974558f, 0.48149997f, 0.72040009f, + -0.23283541f, 0.48150006f, 0.69313490f, + -0.33786824f, 0.48150000f, 0.64940518f, + -0.43344283f, 0.48150009f, 0.59061253f, + -0.51815796f, 0.48150003f, 0.51815796f, + -0.59061259f, 0.48150006f, 0.43344280f, + -0.64940524f, 0.48150003f, 0.33786821f, + -0.69313484f, 0.48150003f, 0.23283540f, + -0.72040015f, 0.48150003f, 0.11974555f, + -0.72979999f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.46771878f, 0.74016249f, + -0.12144586f, 0.46771872f, 0.73062909f, + -0.23614147f, 0.46771881f, 0.70297670f, + -0.34266564f, 0.46771878f, 0.65862614f, + -0.43959734f, 0.46771878f, 0.59899879f, + -0.52551538f, 0.46771878f, 0.52551538f, + -0.59899873f, 0.46771878f, 0.43959731f, + -0.65862620f, 0.46771878f, 0.34266564f, + -0.70297670f, 0.46771878f, 0.23614144f, + -0.73062921f, 0.46771878f, 0.12144583f, + -0.74016249f, 0.46771878f, 0.00000001f, + 0.00000000f, 0.45000005f, 0.75000000f, + -0.12305999f, 0.44999996f, 0.74033999f, + -0.23928000f, 0.45000011f, 0.71232003f, + -0.34721997f, 0.45000002f, 0.66737998f, + -0.44544002f, 0.45000008f, 0.60696000f, + -0.53250003f, 0.45000005f, 0.53250003f, + -0.60696000f, 0.45000005f, 0.44543999f, + -0.66738003f, 0.45000008f, 0.34722000f, + -0.71231997f, 0.45000005f, 0.23927999f, + -0.74033999f, 0.45000005f, 0.12305997f, + -0.75000000f, 0.45000005f, 0.00000001f, + -0.69999999f, 0.45000005f, 0.00000001f, + -0.69098389f, 0.44999996f, -0.11485598f, + -0.66483200f, 0.45000011f, -0.22332802f, + -0.62288797f, 0.45000002f, -0.32407200f, + -0.56649601f, 0.45000008f, -0.41574404f, + -0.49699998f, 0.45000005f, -0.49700001f, + -0.41574395f, 0.45000005f, -0.56649601f, + -0.32407200f, 0.45000008f, -0.62288797f, + -0.22332799f, 0.45000005f, -0.66483200f, + -0.11485596f, 0.45000005f, -0.69098401f, + 0.00000000f, 0.45000005f, -0.69999999f, + -0.69296241f, 0.46771872f, 0.00000001f, + -0.68403697f, 0.46771869f, -0.11370127f, + -0.65814799f, 0.46771878f, -0.22108276f, + -0.61662567f, 0.46771872f, -0.32081389f, + -0.56080067f, 0.46771872f, -0.41156429f, + -0.49200332f, 0.46771872f, -0.49200332f, + -0.41156423f, 0.46771872f, -0.56080067f, + -0.32081389f, 0.46771872f, -0.61662567f, + -0.22108275f, 0.46771872f, -0.65814799f, + -0.11370124f, 0.46771872f, -0.68403703f, + 0.00000000f, 0.46771872f, -0.69296241f, + -0.69020003f, 0.48150003f, 0.00000001f, + -0.68131018f, 0.48149997f, -0.11324801f, + -0.65552443f, 0.48150006f, -0.22020143f, + -0.61416763f, 0.48150000f, -0.31953502f, + -0.55856514f, 0.48150009f, -0.40992361f, + -0.49004203f, 0.48150003f, -0.49004203f, + -0.40992361f, 0.48150006f, -0.55856514f, + -0.31953505f, 0.48150003f, -0.61416763f, + -0.22020143f, 0.48150003f, -0.65552437f, + -0.11324799f, 0.48150003f, -0.68131030f, + 0.00000000f, 0.48150003f, -0.69020003f, + -0.69111252f, 0.49134374f, 0.00000001f, + -0.68221092f, 0.49134374f, -0.11339773f, + -0.65639102f, 0.49134377f, -0.22049254f, + -0.61497957f, 0.49134377f, -0.31995746f, + -0.55930358f, 0.49134377f, -0.41046557f, + -0.49068987f, 0.49134374f, -0.49068987f, + -0.41046554f, 0.49134374f, -0.55930358f, + -0.31995746f, 0.49134374f, -0.61497957f, + -0.22049253f, 0.49134377f, -0.65639102f, + -0.11339770f, 0.49134374f, -0.68221098f, + 0.00000000f, 0.49134374f, -0.69111252f, + -0.69510001f, 0.49725008f, 0.00000001f, + -0.68614709f, 0.49725005f, -0.11405201f, + -0.66017824f, 0.49725014f, -0.22176473f, + -0.61852777f, 0.49725008f, -0.32180351f, + -0.56253058f, 0.49725008f, -0.41283381f, + -0.49352103f, 0.49725008f, -0.49352100f, + -0.41283381f, 0.49725008f, -0.56253058f, + -0.32180351f, 0.49725008f, -0.61852777f, + -0.22176471f, 0.49725008f, -0.66017818f, + -0.11405198f, 0.49725008f, -0.68614715f, + 0.00000000f, 0.49725008f, -0.69510001f, + -0.70156252f, 0.49921876f, 0.00000001f, + -0.69252634f, 0.49921870f, -0.11511236f, + -0.66631603f, 0.49921876f, -0.22382653f, + -0.62427837f, 0.49921873f, -0.32479537f, + -0.56776059f, 0.49921879f, -0.41667202f, + -0.49810940f, 0.49921876f, -0.49810940f, + -0.41667199f, 0.49921879f, -0.56776053f, + -0.32479542f, 0.49921876f, -0.62427843f, + -0.22382650f, 0.49921876f, -0.66631603f, + -0.11511234f, 0.49921876f, -0.69252640f, + 0.00000000f, 0.49921876f, -0.70156252f, + -0.70989996f, 0.49725002f, 0.00000001f, + -0.70075637f, 0.49724996f, -0.11648037f, + -0.67423457f, 0.49725008f, -0.22648652f, + -0.63169742f, 0.49724999f, -0.32865530f, + -0.57450789f, 0.49725002f, -0.42162383f, + -0.50402898f, 0.49725002f, -0.50402898f, + -0.42162377f, 0.49725002f, -0.57450783f, + -0.32865530f, 0.49725002f, -0.63169742f, + -0.22648649f, 0.49725005f, -0.67423463f, + -0.11648035f, 0.49725002f, -0.70075643f, + 0.00000000f, 0.49725002f, -0.70989996f, + -0.71951252f, 0.49134377f, 0.00000001f, + -0.71024513f, 0.49134374f, -0.11805760f, + -0.68336421f, 0.49134380f, -0.22955328f, + -0.64025098f, 0.49134377f, -0.33310553f, + -0.58228713f, 0.49134383f, -0.42733288f, + -0.51085389f, 0.49134377f, -0.51085389f, + -0.42733288f, 0.49134377f, -0.58228713f, + -0.33310553f, 0.49134377f, -0.64025104f, + -0.22955328f, 0.49134380f, -0.68336427f, + -0.11805756f, 0.49134377f, -0.71024519f, + 0.00000000f, 0.49134377f, -0.71951252f, + -0.72979999f, 0.48150003f, 0.00000001f, + -0.72040009f, 0.48149997f, -0.11974557f, + -0.69313490f, 0.48150006f, -0.23283541f, + -0.64940518f, 0.48150000f, -0.33786824f, + -0.59061253f, 0.48150009f, -0.43344283f, + -0.51815796f, 0.48150003f, -0.51815796f, + -0.43344280f, 0.48150006f, -0.59061259f, + -0.33786821f, 0.48150003f, -0.64940524f, + -0.23283540f, 0.48150003f, -0.69313484f, + -0.11974555f, 0.48150003f, -0.72040015f, + 0.00000000f, 0.48150003f, -0.72979999f, + -0.74016249f, 0.46771878f, 0.00000001f, + -0.73062909f, 0.46771872f, -0.12144585f, + -0.70297670f, 0.46771881f, -0.23614147f, + -0.65862614f, 0.46771878f, -0.34266564f, + -0.59899879f, 0.46771878f, -0.43959734f, + -0.52551538f, 0.46771878f, -0.52551538f, + -0.43959731f, 0.46771878f, -0.59899873f, + -0.34266564f, 0.46771878f, -0.65862620f, + -0.23614144f, 0.46771878f, -0.70297670f, + -0.12144582f, 0.46771878f, -0.73062921f, + 0.00000000f, 0.46771878f, -0.74016249f, + -0.75000000f, 0.45000005f, 0.00000001f, + -0.74033999f, 0.44999996f, -0.12305998f, + -0.71232003f, 0.45000011f, -0.23928000f, + -0.66737998f, 0.45000002f, -0.34721997f, + -0.60696000f, 0.45000008f, -0.44544002f, + -0.53250003f, 0.45000005f, -0.53250003f, + -0.44543999f, 0.45000005f, -0.60696000f, + -0.34722000f, 0.45000008f, -0.66738003f, + -0.23927999f, 0.45000005f, -0.71231997f, + -0.12305996f, 0.45000005f, -0.74033999f, + 0.00000000f, 0.45000005f, -0.75000000f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.74033999f, 0.44999996f, 0.12306000f, + 0.71232003f, 0.45000011f, 0.23928000f, + 0.66737998f, 0.45000002f, 0.34721997f, + 0.60696000f, 0.45000008f, 0.44544002f, + 0.53250003f, 0.45000005f, 0.53250003f, + 0.44543999f, 0.45000005f, 0.60696000f, + 0.34722000f, 0.45000008f, 0.66738003f, + 0.23927999f, 0.45000005f, 0.71231997f, + 0.12305996f, 0.45000005f, 0.74033999f, + 0.00000000f, 0.45000005f, 0.75000000f, + 0.78737491f, 0.37128749f, 0.00000000f, + 0.77723348f, 0.37128747f, 0.12919247f, + 0.74781722f, 0.37128752f, 0.25120407f, + 0.70063770f, 0.37128749f, 0.36452308f, + 0.63720679f, 0.37128752f, 0.46763775f, + 0.55903620f, 0.37128749f, 0.55903620f, + 0.46763766f, 0.37128749f, 0.63720679f, + 0.36452311f, 0.37128749f, 0.70063770f, + 0.25120407f, 0.37128752f, 0.74781728f, + 0.12919241f, 0.37128749f, 0.77723348f, + 0.00000000f, 0.37128749f, 0.78737491f, + 0.82400006f, 0.29280004f, 0.00000000f, + 0.81338686f, 0.29280004f, 0.13520193f, + 0.78260237f, 0.29280004f, 0.26288900f, + 0.73322815f, 0.29280004f, 0.38147905f, + 0.66684681f, 0.29280004f, 0.48939016f, + 0.58504003f, 0.29280001f, 0.58504003f, + 0.48939011f, 0.29280004f, 0.66684675f, + 0.38147908f, 0.29280007f, 0.73322821f, + 0.26288897f, 0.29280004f, 0.78260231f, + 0.13520187f, 0.29280004f, 0.81338698f, + 0.00000000f, 0.29280004f, 0.82400006f, + 0.85912502f, 0.21476251f, 0.00000000f, + 0.84805942f, 0.21476249f, 0.14096522f, + 0.81596267f, 0.21476252f, 0.27409527f, + 0.76448381f, 0.21476249f, 0.39774051f, + 0.69527274f, 0.21476252f, 0.51025152f, + 0.60997874f, 0.21476251f, 0.60997874f, + 0.51025152f, 0.21476251f, 0.69527274f, + 0.39774054f, 0.21476251f, 0.76448381f, + 0.27409524f, 0.21476251f, 0.81596261f, + 0.14096518f, 0.21476251f, 0.84805954f, + 0.00000000f, 0.21476251f, 0.85912502f, + 0.89200008f, 0.13740003f, 0.00000000f, + 0.88051099f, 0.13740002f, 0.14635937f, + 0.84718603f, 0.13740005f, 0.28458369f, + 0.79373735f, 0.13740002f, 0.41296035f, + 0.72187787f, 0.13740005f, 0.52977669f, + 0.63332003f, 0.13740002f, 0.63332003f, + 0.52977669f, 0.13740002f, 0.72187781f, + 0.41296035f, 0.13740002f, 0.79373741f, + 0.28458369f, 0.13740000f, 0.84718597f, + 0.14635932f, 0.13740000f, 0.88051111f, + 0.00000000f, 0.13740000f, 0.89200008f, + 0.92187500f, 0.06093751f, 0.00000000f, + 0.91000110f, 0.06093750f, 0.15126126f, + 0.87556010f, 0.06093751f, 0.29411504f, + 0.82032120f, 0.06093750f, 0.42679128f, + 0.74605507f, 0.06093750f, 0.54752004f, + 0.65453124f, 0.06093751f, 0.65453124f, + 0.54751998f, 0.06093750f, 0.74605501f, + 0.42679128f, 0.06093750f, 0.82032126f, + 0.29411501f, 0.06093750f, 0.87556005f, + 0.15126120f, 0.06093750f, 0.91000128f, + 0.00000000f, 0.06093750f, 0.92187500f, + 0.94800001f, -0.01440001f, -0.00000000f, + 0.93578976f, -0.01440001f, 0.15554784f, + 0.90037251f, -0.01440001f, 0.30244994f, + 0.84356833f, -0.01440001f, 0.43888608f, + 0.76719749f, -0.01440001f, 0.56303620f, + 0.67308003f, -0.01440001f, 0.67308003f, + 0.56303614f, -0.01440002f, 0.76719743f, + 0.43888611f, -0.01440002f, 0.84356833f, + 0.30244991f, -0.01440002f, 0.90037251f, + 0.15554780f, -0.01440002f, 0.93578976f, + 0.00000000f, -0.01440002f, 0.94800001f, + 0.96962506f, -0.08838750f, -0.00000000f, + 0.95713621f, -0.08838749f, 0.15909605f, + 0.92091113f, -0.08838750f, 0.30934918f, + 0.86281121f, -0.08838750f, 0.44889757f, + 0.78469825f, -0.08838750f, 0.57587969f, + 0.68843377f, -0.08838750f, 0.68843377f, + 0.57587969f, -0.08838750f, 0.78469819f, + 0.44889760f, -0.08838750f, 0.86281121f, + 0.30934915f, -0.08838750f, 0.92091107f, + 0.15909600f, -0.08838750f, 0.95713627f, + 0.00000000f, -0.08838750f, 0.96962506f, + 0.98600000f, -0.16080001f, -0.00000000f, + 0.97330022f, -0.16080000f, 0.16178288f, + 0.93646342f, -0.16080002f, 0.31457347f, + 0.87738222f, -0.16080001f, 0.45647857f, + 0.79795015f, -0.16080002f, 0.58560514f, + 0.70006001f, -0.16080001f, 0.70006001f, + 0.58560514f, -0.16080001f, 0.79795015f, + 0.45647860f, -0.16080001f, 0.87738228f, + 0.31457344f, -0.16080001f, 0.93646336f, + 0.16178282f, -0.16080001f, 0.97330034f, + 0.00000000f, -0.16080001f, 0.98600000f, + 0.99637496f, -0.23141253f, -0.00000000f, + 0.98354161f, -0.23141250f, 0.16348520f, + 0.94631720f, -0.23141254f, 0.31788349f, + 0.88661432f, -0.23141253f, 0.46128178f, + 0.80634636f, -0.23141254f, 0.59176707f, + 0.70742619f, -0.23141253f, 0.70742619f, + 0.59176701f, -0.23141253f, 0.80634630f, + 0.46128178f, -0.23141253f, 0.88661432f, + 0.31788346f, -0.23141253f, 0.94631708f, + 0.16348514f, -0.23141253f, 0.98354173f, + 0.00000000f, -0.23141253f, 0.99637496f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.98711991f, -0.29999998f, 0.16407999f, + 0.94976002f, -0.30000004f, 0.31904000f, + 0.88984001f, -0.30000004f, 0.46296000f, + 0.80928004f, -0.30000001f, 0.59392005f, + 0.71000004f, -0.30000001f, 0.71000004f, + 0.59391999f, -0.30000001f, 0.80928004f, + 0.46296003f, -0.30000001f, 0.88984001f, + 0.31904000f, -0.30000001f, 0.94976002f, + 0.16407993f, -0.30000001f, 0.98712003f, + 0.00000000f, -0.30000001f, 1.00000000f, + 0.00000000f, 0.45000005f, -0.75000000f, + 0.12305999f, 0.44999996f, -0.74033999f, + 0.23928000f, 0.45000011f, -0.71232003f, + 0.34721997f, 0.45000002f, -0.66737998f, + 0.44544002f, 0.45000008f, -0.60696000f, + 0.53250003f, 0.45000005f, -0.53250003f, + 0.60696000f, 0.45000005f, -0.44543999f, + 0.66738003f, 0.45000008f, -0.34722000f, + 0.71231997f, 0.45000005f, -0.23927999f, + 0.74033999f, 0.45000005f, -0.12305995f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.37128749f, -0.78737491f, + 0.12919247f, 0.37128747f, -0.77723348f, + 0.25120407f, 0.37128752f, -0.74781722f, + 0.36452308f, 0.37128749f, -0.70063770f, + 0.46763775f, 0.37128752f, -0.63720679f, + 0.55903620f, 0.37128749f, -0.55903620f, + 0.63720679f, 0.37128749f, -0.46763766f, + 0.70063770f, 0.37128749f, -0.36452311f, + 0.74781728f, 0.37128752f, -0.25120407f, + 0.77723348f, 0.37128749f, -0.12919241f, + 0.78737491f, 0.37128749f, 0.00000000f, + 0.00000000f, 0.29280004f, -0.82400006f, + 0.13520193f, 0.29280004f, -0.81338686f, + 0.26288900f, 0.29280004f, -0.78260237f, + 0.38147905f, 0.29280004f, -0.73322815f, + 0.48939016f, 0.29280004f, -0.66684681f, + 0.58504003f, 0.29280001f, -0.58504003f, + 0.66684675f, 0.29280004f, -0.48939011f, + 0.73322821f, 0.29280007f, -0.38147908f, + 0.78260231f, 0.29280004f, -0.26288897f, + 0.81338698f, 0.29280004f, -0.13520187f, + 0.82400006f, 0.29280004f, 0.00000000f, + 0.00000000f, 0.21476251f, -0.85912502f, + 0.14096522f, 0.21476249f, -0.84805942f, + 0.27409527f, 0.21476252f, -0.81596267f, + 0.39774051f, 0.21476249f, -0.76448381f, + 0.51025152f, 0.21476252f, -0.69527274f, + 0.60997874f, 0.21476251f, -0.60997874f, + 0.69527274f, 0.21476251f, -0.51025152f, + 0.76448381f, 0.21476251f, -0.39774054f, + 0.81596261f, 0.21476251f, -0.27409524f, + 0.84805954f, 0.21476251f, -0.14096518f, + 0.85912502f, 0.21476251f, 0.00000000f, + 0.00000000f, 0.13740005f, -0.89200008f, + 0.14635937f, 0.13740005f, -0.88051099f, + 0.28458369f, 0.13740005f, -0.84718603f, + 0.41296035f, 0.13740005f, -0.79373735f, + 0.52977669f, 0.13740005f, -0.72187787f, + 0.63332003f, 0.13740003f, -0.63332003f, + 0.72187781f, 0.13740003f, -0.52977669f, + 0.79373741f, 0.13740005f, -0.41296035f, + 0.84718597f, 0.13740003f, -0.28458369f, + 0.88051111f, 0.13740003f, -0.14635932f, + 0.89200008f, 0.13740003f, 0.00000000f, + 0.00000000f, 0.06093752f, -0.92187500f, + 0.15126126f, 0.06093751f, -0.91000110f, + 0.29411504f, 0.06093752f, -0.87556010f, + 0.42679128f, 0.06093752f, -0.82032120f, + 0.54752004f, 0.06093752f, -0.74605507f, + 0.65453124f, 0.06093752f, -0.65453124f, + 0.74605501f, 0.06093752f, -0.54751998f, + 0.82032126f, 0.06093751f, -0.42679128f, + 0.87556005f, 0.06093752f, -0.29411501f, + 0.91000128f, 0.06093751f, -0.15126120f, + 0.92187500f, 0.06093751f, 0.00000000f, + 0.00000000f, -0.01440000f, -0.94800001f, + 0.15554784f, -0.01440000f, -0.93578976f, + 0.30244994f, -0.01440000f, -0.90037251f, + 0.43888608f, -0.01440000f, -0.84356833f, + 0.56303620f, -0.01440000f, -0.76719749f, + 0.67308003f, -0.01440000f, -0.67308003f, + 0.76719743f, -0.01440001f, -0.56303614f, + 0.84356833f, -0.01440001f, -0.43888611f, + 0.90037251f, -0.01440001f, -0.30244991f, + 0.93578976f, -0.01440001f, -0.15554780f, + 0.94800001f, -0.01440001f, -0.00000000f, + 0.00000000f, -0.08838749f, -0.96962506f, + 0.15909605f, -0.08838748f, -0.95713621f, + 0.30934918f, -0.08838750f, -0.92091113f, + 0.44889757f, -0.08838750f, -0.86281121f, + 0.57587969f, -0.08838750f, -0.78469825f, + 0.68843377f, -0.08838749f, -0.68843377f, + 0.78469819f, -0.08838750f, -0.57587969f, + 0.86281121f, -0.08838750f, -0.44889760f, + 0.92091107f, -0.08838750f, -0.30934915f, + 0.95713627f, -0.08838749f, -0.15909600f, + 0.96962506f, -0.08838750f, -0.00000000f, + 0.00000000f, -0.16080000f, -0.98600000f, + 0.16178288f, -0.16079998f, -0.97330022f, + 0.31457347f, -0.16080001f, -0.93646342f, + 0.45647857f, -0.16080000f, -0.87738222f, + 0.58560514f, -0.16080001f, -0.79795015f, + 0.70006001f, -0.16080000f, -0.70006001f, + 0.79795015f, -0.16080000f, -0.58560514f, + 0.87738228f, -0.16080001f, -0.45647860f, + 0.93646336f, -0.16080001f, -0.31457344f, + 0.97330034f, -0.16080001f, -0.16178282f, + 0.98600000f, -0.16080001f, -0.00000000f, + 0.00000000f, -0.23141253f, -0.99637496f, + 0.16348520f, -0.23141250f, -0.98354161f, + 0.31788349f, -0.23141254f, -0.94631720f, + 0.46128178f, -0.23141253f, -0.88661432f, + 0.59176707f, -0.23141254f, -0.80634636f, + 0.70742619f, -0.23141253f, -0.70742619f, + 0.80634630f, -0.23141253f, -0.59176701f, + 0.88661432f, -0.23141253f, -0.46128178f, + 0.94631708f, -0.23141253f, -0.31788346f, + 0.98354173f, -0.23141253f, -0.16348514f, + 0.99637496f, -0.23141253f, -0.00000000f, + 0.00000000f, -0.30000001f, -1.00000000f, + 0.16407999f, -0.29999998f, -0.98711991f, + 0.31904000f, -0.30000004f, -0.94976002f, + 0.46296000f, -0.30000004f, -0.88984001f, + 0.59392005f, -0.30000001f, -0.80928004f, + 0.71000004f, -0.30000001f, -0.71000004f, + 0.80928004f, -0.30000001f, -0.59391999f, + 0.88984001f, -0.30000001f, -0.46296003f, + 0.94976002f, -0.30000001f, -0.31904000f, + 0.98712003f, -0.30000001f, -0.16407993f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.00000000f, 0.45000005f, 0.75000000f, + -0.12305999f, 0.44999996f, 0.74033999f, + -0.23928000f, 0.45000011f, 0.71232003f, + -0.34721997f, 0.45000002f, 0.66737998f, + -0.44544002f, 0.45000008f, 0.60696000f, + -0.53250003f, 0.45000005f, 0.53250003f, + -0.60696000f, 0.45000005f, 0.44543999f, + -0.66738003f, 0.45000008f, 0.34722000f, + -0.71231997f, 0.45000005f, 0.23927999f, + -0.74033999f, 0.45000005f, 0.12305997f, + -0.75000000f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.37128749f, 0.78737491f, + -0.12919247f, 0.37128747f, 0.77723348f, + -0.25120407f, 0.37128752f, 0.74781722f, + -0.36452308f, 0.37128749f, 0.70063770f, + -0.46763775f, 0.37128752f, 0.63720679f, + -0.55903620f, 0.37128749f, 0.55903620f, + -0.63720679f, 0.37128749f, 0.46763766f, + -0.70063770f, 0.37128749f, 0.36452311f, + -0.74781728f, 0.37128752f, 0.25120407f, + -0.77723348f, 0.37128749f, 0.12919241f, + -0.78737491f, 0.37128749f, 0.00000000f, + 0.00000000f, 0.29280004f, 0.82400006f, + -0.13520193f, 0.29280004f, 0.81338686f, + -0.26288900f, 0.29280004f, 0.78260237f, + -0.38147905f, 0.29280004f, 0.73322815f, + -0.48939016f, 0.29280004f, 0.66684681f, + -0.58504003f, 0.29280001f, 0.58504003f, + -0.66684675f, 0.29280004f, 0.48939011f, + -0.73322821f, 0.29280007f, 0.38147908f, + -0.78260231f, 0.29280004f, 0.26288897f, + -0.81338698f, 0.29280004f, 0.13520187f, + -0.82400006f, 0.29280004f, 0.00000000f, + 0.00000000f, 0.21476251f, 0.85912502f, + -0.14096522f, 0.21476249f, 0.84805942f, + -0.27409527f, 0.21476252f, 0.81596267f, + -0.39774051f, 0.21476249f, 0.76448381f, + -0.51025152f, 0.21476252f, 0.69527274f, + -0.60997874f, 0.21476251f, 0.60997874f, + -0.69527274f, 0.21476251f, 0.51025152f, + -0.76448381f, 0.21476251f, 0.39774054f, + -0.81596261f, 0.21476251f, 0.27409524f, + -0.84805954f, 0.21476251f, 0.14096518f, + -0.85912502f, 0.21476251f, 0.00000000f, + 0.00000000f, 0.13740000f, 0.89200008f, + -0.14635937f, 0.13740000f, 0.88051099f, + -0.28458369f, 0.13740002f, 0.84718603f, + -0.41296035f, 0.13740002f, 0.79373735f, + -0.52977669f, 0.13740002f, 0.72187787f, + -0.63332003f, 0.13740002f, 0.63332003f, + -0.72187781f, 0.13740002f, 0.52977669f, + -0.79373741f, 0.13740003f, 0.41296035f, + -0.84718597f, 0.13740003f, 0.28458369f, + -0.88051111f, 0.13740003f, 0.14635932f, + -0.89200008f, 0.13740003f, 0.00000000f, + 0.00000000f, 0.06093750f, 0.92187500f, + -0.15126126f, 0.06093750f, 0.91000110f, + -0.29411504f, 0.06093751f, 0.87556010f, + -0.42679128f, 0.06093750f, 0.82032120f, + -0.54752004f, 0.06093750f, 0.74605507f, + -0.65453124f, 0.06093750f, 0.65453124f, + -0.74605501f, 0.06093750f, 0.54751998f, + -0.82032126f, 0.06093751f, 0.42679128f, + -0.87556005f, 0.06093751f, 0.29411501f, + -0.91000128f, 0.06093751f, 0.15126120f, + -0.92187500f, 0.06093751f, 0.00000000f, + 0.00000000f, -0.01440002f, 0.94800001f, + -0.15554784f, -0.01440002f, 0.93578976f, + -0.30244994f, -0.01440002f, 0.90037251f, + -0.43888608f, -0.01440002f, 0.84356833f, + -0.56303620f, -0.01440002f, 0.76719749f, + -0.67308003f, -0.01440001f, 0.67308003f, + -0.76719743f, -0.01440001f, 0.56303614f, + -0.84356833f, -0.01440001f, 0.43888611f, + -0.90037251f, -0.01440001f, 0.30244991f, + -0.93578976f, -0.01440001f, 0.15554780f, + -0.94800001f, -0.01440001f, -0.00000000f, + 0.00000000f, -0.08838750f, 0.96962506f, + -0.15909605f, -0.08838750f, 0.95713621f, + -0.30934918f, -0.08838750f, 0.92091113f, + -0.44889757f, -0.08838750f, 0.86281121f, + -0.57587969f, -0.08838751f, 0.78469825f, + -0.68843377f, -0.08838750f, 0.68843377f, + -0.78469819f, -0.08838750f, 0.57587969f, + -0.86281121f, -0.08838750f, 0.44889760f, + -0.92091107f, -0.08838750f, 0.30934915f, + -0.95713627f, -0.08838750f, 0.15909600f, + -0.96962506f, -0.08838750f, -0.00000000f, + 0.00000000f, -0.16080001f, 0.98600000f, + -0.16178288f, -0.16080000f, 0.97330022f, + -0.31457347f, -0.16080002f, 0.93646342f, + -0.45647857f, -0.16080001f, 0.87738222f, + -0.58560514f, -0.16080002f, 0.79795015f, + -0.70006001f, -0.16080001f, 0.70006001f, + -0.79795015f, -0.16080001f, 0.58560514f, + -0.87738228f, -0.16080001f, 0.45647860f, + -0.93646336f, -0.16080001f, 0.31457344f, + -0.97330034f, -0.16080001f, 0.16178282f, + -0.98600000f, -0.16080001f, -0.00000000f, + 0.00000000f, -0.23141253f, 0.99637496f, + -0.16348520f, -0.23141250f, 0.98354161f, + -0.31788349f, -0.23141254f, 0.94631720f, + -0.46128178f, -0.23141253f, 0.88661432f, + -0.59176707f, -0.23141254f, 0.80634636f, + -0.70742619f, -0.23141253f, 0.70742619f, + -0.80634630f, -0.23141253f, 0.59176701f, + -0.88661432f, -0.23141253f, 0.46128178f, + -0.94631708f, -0.23141253f, 0.31788346f, + -0.98354173f, -0.23141253f, 0.16348514f, + -0.99637496f, -0.23141253f, -0.00000000f, + 0.00000000f, -0.30000001f, 1.00000000f, + -0.16407999f, -0.29999998f, 0.98711991f, + -0.31904000f, -0.30000004f, 0.94976002f, + -0.46296000f, -0.30000004f, 0.88984001f, + -0.59392005f, -0.30000001f, 0.80928004f, + -0.71000004f, -0.30000001f, 0.71000004f, + -0.80928004f, -0.30000001f, 0.59391999f, + -0.88984001f, -0.30000001f, 0.46296003f, + -0.94976002f, -0.30000001f, 0.31904000f, + -0.98712003f, -0.30000001f, 0.16407993f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.75000000f, 0.45000005f, 0.00000001f, + -0.74033999f, 0.44999996f, -0.12305998f, + -0.71232003f, 0.45000011f, -0.23928000f, + -0.66737998f, 0.45000002f, -0.34721997f, + -0.60696000f, 0.45000008f, -0.44544002f, + -0.53250003f, 0.45000005f, -0.53250003f, + -0.44543999f, 0.45000005f, -0.60696000f, + -0.34722000f, 0.45000008f, -0.66738003f, + -0.23927999f, 0.45000005f, -0.71231997f, + -0.12305996f, 0.45000005f, -0.74033999f, + 0.00000000f, 0.45000005f, -0.75000000f, + -0.78737491f, 0.37128749f, 0.00000000f, + -0.77723348f, 0.37128747f, -0.12919247f, + -0.74781722f, 0.37128752f, -0.25120407f, + -0.70063770f, 0.37128749f, -0.36452308f, + -0.63720679f, 0.37128752f, -0.46763775f, + -0.55903620f, 0.37128749f, -0.55903620f, + -0.46763766f, 0.37128749f, -0.63720679f, + -0.36452311f, 0.37128749f, -0.70063770f, + -0.25120407f, 0.37128752f, -0.74781728f, + -0.12919241f, 0.37128749f, -0.77723348f, + 0.00000000f, 0.37128749f, -0.78737491f, + -0.82400006f, 0.29280004f, 0.00000000f, + -0.81338686f, 0.29280004f, -0.13520193f, + -0.78260237f, 0.29280004f, -0.26288900f, + -0.73322815f, 0.29280004f, -0.38147905f, + -0.66684681f, 0.29280004f, -0.48939016f, + -0.58504003f, 0.29280001f, -0.58504003f, + -0.48939011f, 0.29280004f, -0.66684675f, + -0.38147908f, 0.29280007f, -0.73322821f, + -0.26288897f, 0.29280004f, -0.78260231f, + -0.13520187f, 0.29280004f, -0.81338698f, + 0.00000000f, 0.29280004f, -0.82400006f, + -0.85912502f, 0.21476251f, 0.00000000f, + -0.84805942f, 0.21476249f, -0.14096522f, + -0.81596267f, 0.21476252f, -0.27409527f, + -0.76448381f, 0.21476249f, -0.39774051f, + -0.69527274f, 0.21476252f, -0.51025152f, + -0.60997874f, 0.21476251f, -0.60997874f, + -0.51025152f, 0.21476251f, -0.69527274f, + -0.39774054f, 0.21476251f, -0.76448381f, + -0.27409524f, 0.21476251f, -0.81596261f, + -0.14096518f, 0.21476251f, -0.84805954f, + 0.00000000f, 0.21476251f, -0.85912502f, + -0.89200008f, 0.13740003f, 0.00000000f, + -0.88051099f, 0.13740003f, -0.14635937f, + -0.84718603f, 0.13740005f, -0.28458369f, + -0.79373735f, 0.13740005f, -0.41296035f, + -0.72187787f, 0.13740005f, -0.52977669f, + -0.63332003f, 0.13740005f, -0.63332003f, + -0.52977669f, 0.13740005f, -0.72187781f, + -0.41296035f, 0.13740005f, -0.79373741f, + -0.28458369f, 0.13740005f, -0.84718597f, + -0.14635932f, 0.13740005f, -0.88051111f, + 0.00000000f, 0.13740005f, -0.89200008f, + -0.92187500f, 0.06093751f, 0.00000000f, + -0.91000110f, 0.06093751f, -0.15126126f, + -0.87556010f, 0.06093752f, -0.29411504f, + -0.82032120f, 0.06093751f, -0.42679128f, + -0.74605507f, 0.06093752f, -0.54752004f, + -0.65453124f, 0.06093752f, -0.65453124f, + -0.54751998f, 0.06093752f, -0.74605501f, + -0.42679128f, 0.06093752f, -0.82032126f, + -0.29411501f, 0.06093752f, -0.87556005f, + -0.15126120f, 0.06093752f, -0.91000128f, + 0.00000000f, 0.06093752f, -0.92187500f, + -0.94800001f, -0.01440001f, -0.00000000f, + -0.93578976f, -0.01440001f, -0.15554784f, + -0.90037251f, -0.01440001f, -0.30244994f, + -0.84356833f, -0.01440001f, -0.43888608f, + -0.76719749f, -0.01440001f, -0.56303620f, + -0.67308003f, -0.01440000f, -0.67308003f, + -0.56303614f, -0.01440000f, -0.76719743f, + -0.43888611f, -0.01440000f, -0.84356833f, + -0.30244991f, -0.01440000f, -0.90037251f, + -0.15554780f, -0.01440000f, -0.93578976f, + 0.00000000f, -0.01440000f, -0.94800001f, + -0.96962506f, -0.08838750f, -0.00000000f, + -0.95713621f, -0.08838749f, -0.15909605f, + -0.92091113f, -0.08838750f, -0.30934918f, + -0.86281121f, -0.08838750f, -0.44889757f, + -0.78469825f, -0.08838750f, -0.57587969f, + -0.68843377f, -0.08838749f, -0.68843377f, + -0.57587969f, -0.08838749f, -0.78469819f, + -0.44889760f, -0.08838749f, -0.86281121f, + -0.30934915f, -0.08838749f, -0.92091107f, + -0.15909600f, -0.08838749f, -0.95713627f, + 0.00000000f, -0.08838749f, -0.96962506f, + -0.98600000f, -0.16080001f, -0.00000000f, + -0.97330022f, -0.16080000f, -0.16178288f, + -0.93646342f, -0.16080001f, -0.31457347f, + -0.87738222f, -0.16080000f, -0.45647857f, + -0.79795015f, -0.16080001f, -0.58560514f, + -0.70006001f, -0.16080001f, -0.70006001f, + -0.58560514f, -0.16080000f, -0.79795015f, + -0.45647860f, -0.16080001f, -0.87738228f, + -0.31457344f, -0.16080000f, -0.93646336f, + -0.16178282f, -0.16080000f, -0.97330034f, + 0.00000000f, -0.16080000f, -0.98600000f, + -0.99637496f, -0.23141253f, -0.00000000f, + -0.98354161f, -0.23141250f, -0.16348520f, + -0.94631720f, -0.23141254f, -0.31788349f, + -0.88661432f, -0.23141253f, -0.46128178f, + -0.80634636f, -0.23141254f, -0.59176707f, + -0.70742619f, -0.23141253f, -0.70742619f, + -0.59176701f, -0.23141253f, -0.80634630f, + -0.46128178f, -0.23141253f, -0.88661432f, + -0.31788346f, -0.23141253f, -0.94631708f, + -0.16348514f, -0.23141253f, -0.98354173f, + 0.00000000f, -0.23141253f, -0.99637496f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.98711991f, -0.29999998f, -0.16407999f, + -0.94976002f, -0.30000004f, -0.31904000f, + -0.88984001f, -0.30000004f, -0.46296000f, + -0.80928004f, -0.30000001f, -0.59392005f, + -0.71000004f, -0.30000001f, -0.71000004f, + -0.59391999f, -0.30000001f, -0.80928004f, + -0.46296003f, -0.30000001f, -0.88984001f, + -0.31904000f, -0.30000001f, -0.94976002f, + -0.16407993f, -0.30000001f, -0.98712003f, + 0.00000000f, -0.30000001f, -1.00000000f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.98711991f, -0.29999998f, 0.16407999f, + 0.94976002f, -0.30000004f, 0.31904000f, + 0.88984001f, -0.30000004f, 0.46296000f, + 0.80928004f, -0.30000001f, 0.59392005f, + 0.71000004f, -0.30000001f, 0.71000004f, + 0.59391999f, -0.30000001f, 0.80928004f, + 0.46296003f, -0.30000001f, 0.88984001f, + 0.31904000f, -0.30000001f, 0.94976002f, + 0.16407993f, -0.30000001f, 0.98712003f, + 0.00000000f, -0.30000001f, 1.00000000f, + 0.99299991f, -0.36416247f, -0.00000000f, + 0.98021001f, -0.36416242f, 0.16293143f, + 0.94311166f, -0.36416250f, 0.31680670f, + 0.88361102f, -0.36416245f, 0.45971927f, + 0.80361503f, -0.36416247f, 0.58976257f, + 0.70502996f, -0.36416247f, 0.70502996f, + 0.58976251f, -0.36416250f, 0.80361497f, + 0.45971930f, -0.36416247f, 0.88361108f, + 0.31680670f, -0.36416250f, 0.94311166f, + 0.16293137f, -0.36416247f, 0.98021007f, + 0.00000000f, -0.36416247f, 0.99299991f, + 0.97400004f, -0.42180002f, -0.00000001f, + 0.96145481f, -0.42179996f, 0.15981390f, + 0.92506635f, -0.42180005f, 0.31074497f, + 0.86670411f, -0.42180002f, 0.45092306f, + 0.78823876f, -0.42180011f, 0.57847816f, + 0.69154000f, -0.42180008f, 0.69154000f, + 0.57847810f, -0.42180005f, 0.78823876f, + 0.45092303f, -0.42180008f, 0.86670417f, + 0.31074494f, -0.42180008f, 0.92506629f, + 0.15981385f, -0.42180008f, 0.96145493f, + 0.00000000f, -0.42180005f, 0.97400004f, + 0.94600004f, -0.47313750f, -0.00000001f, + 0.93381548f, -0.47313747f, 0.15521967f, + 0.89847302f, -0.47313756f, 0.30181187f, + 0.84178865f, -0.47313750f, 0.43796018f, + 0.76557899f, -0.47313753f, 0.56184840f, + 0.67166001f, -0.47313753f, 0.67166001f, + 0.56184828f, -0.47313753f, 0.76557899f, + 0.43796021f, -0.47313753f, 0.84178865f, + 0.30181184f, -0.47313753f, 0.89847302f, + 0.15521961f, -0.47313753f, 0.93381560f, + 0.00000000f, -0.47313753f, 0.94600004f, + 0.91200000f, -0.51840001f, -0.00000001f, + 0.90025330f, -0.51839995f, 0.14964095f, + 0.86618114f, -0.51840007f, 0.29096451f, + 0.81153411f, -0.51840001f, 0.42221951f, + 0.73806345f, -0.51840007f, 0.54165506f, + 0.64752001f, -0.51840007f, 0.64752001f, + 0.54165506f, -0.51840007f, 0.73806340f, + 0.42221954f, -0.51840007f, 0.81153411f, + 0.29096448f, -0.51840007f, 0.86618114f, + 0.14964090f, -0.51840007f, 0.90025342f, + 0.00000000f, -0.51840007f, 0.91200000f, + 0.87500000f, -0.55781251f, -0.00000001f, + 0.86372989f, -0.55781251f, 0.14356998f, + 0.83104002f, -0.55781251f, 0.27916002f, + 0.77860999f, -0.55781251f, 0.40509003f, + 0.70812005f, -0.55781257f, 0.51968002f, + 0.62125003f, -0.55781251f, 0.62125003f, + 0.51967996f, -0.55781251f, 0.70812005f, + 0.40509003f, -0.55781257f, 0.77860999f, + 0.27915999f, -0.55781251f, 0.83104002f, + 0.14356995f, -0.55781251f, 0.86373001f, + 0.00000000f, -0.55781251f, 0.87500000f, + 0.83800000f, -0.59160000f, -0.00000001f, + 0.82720655f, -0.59159994f, 0.13749902f, + 0.79589891f, -0.59160000f, 0.26735553f, + 0.74568588f, -0.59160000f, 0.38796049f, + 0.67817670f, -0.59160006f, 0.49770495f, + 0.59498000f, -0.59160000f, 0.59497994f, + 0.49770495f, -0.59160000f, 0.67817664f, + 0.38796049f, -0.59160006f, 0.74568594f, + 0.26735550f, -0.59160000f, 0.79589891f, + 0.13749899f, -0.59160000f, 0.82720655f, + 0.00000000f, -0.59160000f, 0.83800000f, + 0.80400008f, -0.61998749f, -0.00000001f, + 0.79364455f, -0.61998743f, 0.13192031f, + 0.76360714f, -0.61998749f, 0.25650817f, + 0.71543139f, -0.61998749f, 0.37221986f, + 0.65066117f, -0.61998749f, 0.47751173f, + 0.57084000f, -0.61998749f, 0.57084000f, + 0.47751170f, -0.61998749f, 0.65066117f, + 0.37221986f, -0.61998749f, 0.71543145f, + 0.25650814f, -0.61998749f, 0.76360714f, + 0.13192026f, -0.61998749f, 0.79364455f, + 0.00000000f, -0.61998749f, 0.80400008f, + 0.77600002f, -0.64320004f, -0.00000001f, + 0.76600504f, -0.64319998f, 0.12732607f, + 0.73701382f, -0.64320010f, 0.24757506f, + 0.69051588f, -0.64320004f, 0.35925698f, + 0.62800133f, -0.64320010f, 0.46088195f, + 0.55096000f, -0.64320004f, 0.55096000f, + 0.46088192f, -0.64320004f, 0.62800133f, + 0.35925698f, -0.64320004f, 0.69051588f, + 0.24757503f, -0.64320004f, 0.73701382f, + 0.12732603f, -0.64320004f, 0.76600516f, + 0.00000000f, -0.64320004f, 0.77600002f, + 0.75699997f, -0.66146249f, -0.00000001f, + 0.74724966f, -0.66146243f, 0.12420854f, + 0.71896833f, -0.66146255f, 0.24151328f, + 0.67360884f, -0.66146243f, 0.35046071f, + 0.61262494f, -0.66146255f, 0.44959745f, + 0.53746998f, -0.66146249f, 0.53746998f, + 0.44959742f, -0.66146249f, 0.61262494f, + 0.35046071f, -0.66146249f, 0.67360890f, + 0.24151327f, -0.66146255f, 0.71896827f, + 0.12420851f, -0.66146249f, 0.74724984f, + 0.00000000f, -0.66146249f, 0.75699997f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.74033999f, -0.67499995f, 0.12305998f, + 0.71232003f, -0.67500007f, 0.23928000f, + 0.66737998f, -0.67500001f, 0.34721997f, + 0.60696000f, -0.67500007f, 0.44544002f, + 0.53250003f, -0.67500007f, 0.53250003f, + 0.44543999f, -0.67500001f, 0.60696000f, + 0.34722000f, -0.67500001f, 0.66738003f, + 0.23927999f, -0.67500001f, 0.71231997f, + 0.12305996f, -0.67500001f, 0.74033999f, + 0.00000000f, -0.67500001f, 0.75000000f, + 0.00000000f, -0.30000001f, -1.00000000f, + 0.16407999f, -0.29999998f, -0.98711991f, + 0.31904000f, -0.30000004f, -0.94976002f, + 0.46296000f, -0.30000004f, -0.88984001f, + 0.59392005f, -0.30000001f, -0.80928004f, + 0.71000004f, -0.30000001f, -0.71000004f, + 0.80928004f, -0.30000001f, -0.59391999f, + 0.88984001f, -0.30000001f, -0.46296003f, + 0.94976002f, -0.30000001f, -0.31904000f, + 0.98712003f, -0.30000001f, -0.16407993f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.00000000f, -0.36416247f, -0.99299991f, + 0.16293143f, -0.36416242f, -0.98021001f, + 0.31680670f, -0.36416250f, -0.94311166f, + 0.45971927f, -0.36416245f, -0.88361102f, + 0.58976257f, -0.36416247f, -0.80361503f, + 0.70502996f, -0.36416247f, -0.70502996f, + 0.80361497f, -0.36416250f, -0.58976251f, + 0.88361108f, -0.36416247f, -0.45971930f, + 0.94311166f, -0.36416250f, -0.31680670f, + 0.98021007f, -0.36416247f, -0.16293137f, + 0.99299991f, -0.36416247f, -0.00000000f, + 0.00000000f, -0.42180002f, -0.97400004f, + 0.15981390f, -0.42179996f, -0.96145481f, + 0.31074497f, -0.42180005f, -0.92506635f, + 0.45092306f, -0.42180002f, -0.86670411f, + 0.57847816f, -0.42180005f, -0.78823876f, + 0.69154000f, -0.42180002f, -0.69154000f, + 0.78823876f, -0.42180002f, -0.57847810f, + 0.86670417f, -0.42180002f, -0.45092303f, + 0.92506629f, -0.42180002f, -0.31074494f, + 0.96145493f, -0.42180002f, -0.15981385f, + 0.97400004f, -0.42180002f, -0.00000001f, + 0.00000000f, -0.47313750f, -0.94600004f, + 0.15521967f, -0.47313747f, -0.93381548f, + 0.30181187f, -0.47313756f, -0.89847302f, + 0.43796018f, -0.47313750f, -0.84178865f, + 0.56184840f, -0.47313753f, -0.76557899f, + 0.67166001f, -0.47313750f, -0.67166001f, + 0.76557899f, -0.47313750f, -0.56184828f, + 0.84178865f, -0.47313750f, -0.43796021f, + 0.89847302f, -0.47313750f, -0.30181184f, + 0.93381560f, -0.47313750f, -0.15521961f, + 0.94600004f, -0.47313750f, -0.00000001f, + 0.00000000f, -0.51840001f, -0.91200000f, + 0.14964095f, -0.51839995f, -0.90025330f, + 0.29096451f, -0.51840001f, -0.86618114f, + 0.42221951f, -0.51840001f, -0.81153411f, + 0.54165506f, -0.51840007f, -0.73806345f, + 0.64752001f, -0.51840001f, -0.64752001f, + 0.73806340f, -0.51840001f, -0.54165506f, + 0.81153411f, -0.51840007f, -0.42221954f, + 0.86618114f, -0.51840007f, -0.29096448f, + 0.90025342f, -0.51840001f, -0.14964090f, + 0.91200000f, -0.51840001f, -0.00000001f, + 0.00000000f, -0.55781251f, -0.87500000f, + 0.14356999f, -0.55781251f, -0.86372989f, + 0.27916002f, -0.55781251f, -0.83104002f, + 0.40509003f, -0.55781251f, -0.77860999f, + 0.51968002f, -0.55781257f, -0.70812005f, + 0.62125003f, -0.55781251f, -0.62125003f, + 0.70812005f, -0.55781251f, -0.51967996f, + 0.77860999f, -0.55781257f, -0.40509003f, + 0.83104002f, -0.55781251f, -0.27915999f, + 0.86373001f, -0.55781251f, -0.14356995f, + 0.87500000f, -0.55781251f, -0.00000001f, + 0.00000000f, -0.59160000f, -0.83800000f, + 0.13749902f, -0.59159994f, -0.82720655f, + 0.26735553f, -0.59160000f, -0.79589891f, + 0.38796049f, -0.59160000f, -0.74568588f, + 0.49770495f, -0.59160006f, -0.67817670f, + 0.59497994f, -0.59160000f, -0.59498000f, + 0.67817664f, -0.59160000f, -0.49770495f, + 0.74568594f, -0.59160006f, -0.38796049f, + 0.79589891f, -0.59160000f, -0.26735550f, + 0.82720655f, -0.59160000f, -0.13749899f, + 0.83800000f, -0.59160000f, -0.00000001f, + 0.00000000f, -0.61998749f, -0.80400008f, + 0.13192032f, -0.61998743f, -0.79364455f, + 0.25650817f, -0.61998749f, -0.76360714f, + 0.37221986f, -0.61998749f, -0.71543139f, + 0.47751173f, -0.61998749f, -0.65066117f, + 0.57084000f, -0.61998749f, -0.57084000f, + 0.65066117f, -0.61998749f, -0.47751170f, + 0.71543145f, -0.61998749f, -0.37221986f, + 0.76360714f, -0.61998749f, -0.25650814f, + 0.79364455f, -0.61998749f, -0.13192026f, + 0.80400008f, -0.61998749f, -0.00000001f, + 0.00000000f, -0.64320004f, -0.77600002f, + 0.12732607f, -0.64319998f, -0.76600504f, + 0.24757506f, -0.64320010f, -0.73701382f, + 0.35925698f, -0.64320004f, -0.69051588f, + 0.46088195f, -0.64320010f, -0.62800133f, + 0.55096000f, -0.64320004f, -0.55096000f, + 0.62800133f, -0.64320004f, -0.46088192f, + 0.69051588f, -0.64320004f, -0.35925698f, + 0.73701382f, -0.64320004f, -0.24757503f, + 0.76600516f, -0.64320004f, -0.12732603f, + 0.77600002f, -0.64320004f, -0.00000001f, + 0.00000000f, -0.66146249f, -0.75699997f, + 0.12420855f, -0.66146243f, -0.74724966f, + 0.24151328f, -0.66146255f, -0.71896833f, + 0.35046071f, -0.66146243f, -0.67360884f, + 0.44959745f, -0.66146255f, -0.61262494f, + 0.53746998f, -0.66146249f, -0.53746998f, + 0.61262494f, -0.66146249f, -0.44959742f, + 0.67360890f, -0.66146249f, -0.35046071f, + 0.71896827f, -0.66146255f, -0.24151327f, + 0.74724984f, -0.66146249f, -0.12420852f, + 0.75699997f, -0.66146249f, -0.00000001f, + 0.00000000f, -0.67500001f, -0.75000000f, + 0.12305999f, -0.67499995f, -0.74033999f, + 0.23928000f, -0.67500007f, -0.71232003f, + 0.34721997f, -0.67500001f, -0.66737998f, + 0.44544002f, -0.67500007f, -0.60696000f, + 0.53250003f, -0.67500007f, -0.53250003f, + 0.60696000f, -0.67500001f, -0.44543999f, + 0.66738003f, -0.67500001f, -0.34722000f, + 0.71231997f, -0.67500001f, -0.23927999f, + 0.74033999f, -0.67500001f, -0.12305997f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.00000000f, -0.30000001f, 1.00000000f, + -0.16407999f, -0.29999998f, 0.98711991f, + -0.31904000f, -0.30000004f, 0.94976002f, + -0.46296000f, -0.30000004f, 0.88984001f, + -0.59392005f, -0.30000001f, 0.80928004f, + -0.71000004f, -0.30000001f, 0.71000004f, + -0.80928004f, -0.30000001f, 0.59391999f, + -0.88984001f, -0.30000001f, 0.46296003f, + -0.94976002f, -0.30000001f, 0.31904000f, + -0.98712003f, -0.30000001f, 0.16407993f, + -1.00000000f, -0.30000001f, -0.00000000f, + 0.00000000f, -0.36416247f, 0.99299991f, + -0.16293143f, -0.36416242f, 0.98021001f, + -0.31680670f, -0.36416250f, 0.94311166f, + -0.45971927f, -0.36416245f, 0.88361102f, + -0.58976257f, -0.36416247f, 0.80361503f, + -0.70502996f, -0.36416247f, 0.70502996f, + -0.80361497f, -0.36416250f, 0.58976251f, + -0.88361108f, -0.36416247f, 0.45971930f, + -0.94311166f, -0.36416250f, 0.31680670f, + -0.98021007f, -0.36416247f, 0.16293137f, + -0.99299991f, -0.36416247f, -0.00000000f, + 0.00000000f, -0.42180005f, 0.97400004f, + -0.15981390f, -0.42179999f, 0.96145481f, + -0.31074497f, -0.42180005f, 0.92506635f, + -0.45092306f, -0.42180002f, 0.86670411f, + -0.57847816f, -0.42180011f, 0.78823876f, + -0.69154000f, -0.42180008f, 0.69154000f, + -0.78823876f, -0.42180002f, 0.57847810f, + -0.86670417f, -0.42180005f, 0.45092303f, + -0.92506629f, -0.42180005f, 0.31074494f, + -0.96145493f, -0.42180002f, 0.15981385f, + -0.97400004f, -0.42180002f, -0.00000001f, + 0.00000000f, -0.47313753f, 0.94600004f, + -0.15521967f, -0.47313747f, 0.93381548f, + -0.30181187f, -0.47313756f, 0.89847302f, + -0.43796018f, -0.47313750f, 0.84178865f, + -0.56184840f, -0.47313753f, 0.76557899f, + -0.67166001f, -0.47313750f, 0.67166001f, + -0.76557899f, -0.47313753f, 0.56184828f, + -0.84178865f, -0.47313753f, 0.43796021f, + -0.89847302f, -0.47313750f, 0.30181184f, + -0.93381560f, -0.47313750f, 0.15521961f, + -0.94600004f, -0.47313750f, -0.00000001f, + 0.00000000f, -0.51840007f, 0.91200000f, + -0.14964095f, -0.51839995f, 0.90025330f, + -0.29096451f, -0.51840007f, 0.86618114f, + -0.42221951f, -0.51840007f, 0.81153411f, + -0.54165506f, -0.51840007f, 0.73806345f, + -0.64752001f, -0.51840007f, 0.64752001f, + -0.73806340f, -0.51840007f, 0.54165506f, + -0.81153411f, -0.51840007f, 0.42221954f, + -0.86618114f, -0.51840007f, 0.29096448f, + -0.90025342f, -0.51840001f, 0.14964090f, + -0.91200000f, -0.51840001f, -0.00000001f, + 0.00000000f, -0.55781251f, 0.87500000f, + -0.14356999f, -0.55781251f, 0.86372989f, + -0.27916002f, -0.55781251f, 0.83104002f, + -0.40509003f, -0.55781251f, 0.77860999f, + -0.51968002f, -0.55781257f, 0.70812005f, + -0.62125003f, -0.55781251f, 0.62125003f, + -0.70812005f, -0.55781251f, 0.51967996f, + -0.77860999f, -0.55781257f, 0.40509003f, + -0.83104002f, -0.55781251f, 0.27915999f, + -0.86373001f, -0.55781251f, 0.14356995f, + -0.87500000f, -0.55781251f, -0.00000001f, + 0.00000000f, -0.59160000f, 0.83800000f, + -0.13749902f, -0.59159994f, 0.82720655f, + -0.26735553f, -0.59160000f, 0.79589891f, + -0.38796049f, -0.59160000f, 0.74568588f, + -0.49770495f, -0.59160006f, 0.67817670f, + -0.59497994f, -0.59160000f, 0.59498000f, + -0.67817664f, -0.59160000f, 0.49770495f, + -0.74568594f, -0.59160006f, 0.38796049f, + -0.79589891f, -0.59160000f, 0.26735550f, + -0.82720655f, -0.59160000f, 0.13749899f, + -0.83800000f, -0.59160000f, -0.00000001f, + 0.00000000f, -0.61998749f, 0.80400008f, + -0.13192032f, -0.61998743f, 0.79364455f, + -0.25650817f, -0.61998749f, 0.76360714f, + -0.37221986f, -0.61998749f, 0.71543139f, + -0.47751173f, -0.61998749f, 0.65066117f, + -0.57084000f, -0.61998749f, 0.57084000f, + -0.65066117f, -0.61998749f, 0.47751170f, + -0.71543145f, -0.61998749f, 0.37221986f, + -0.76360714f, -0.61998749f, 0.25650814f, + -0.79364455f, -0.61998749f, 0.13192026f, + -0.80400008f, -0.61998749f, -0.00000001f, + 0.00000000f, -0.64320004f, 0.77600002f, + -0.12732607f, -0.64319998f, 0.76600504f, + -0.24757506f, -0.64320010f, 0.73701382f, + -0.35925698f, -0.64320004f, 0.69051588f, + -0.46088195f, -0.64320010f, 0.62800133f, + -0.55096000f, -0.64320004f, 0.55096000f, + -0.62800133f, -0.64320004f, 0.46088192f, + -0.69051588f, -0.64320004f, 0.35925698f, + -0.73701382f, -0.64320004f, 0.24757503f, + -0.76600516f, -0.64320004f, 0.12732603f, + -0.77600002f, -0.64320004f, -0.00000001f, + 0.00000000f, -0.66146249f, 0.75699997f, + -0.12420855f, -0.66146243f, 0.74724966f, + -0.24151328f, -0.66146255f, 0.71896833f, + -0.35046071f, -0.66146243f, 0.67360884f, + -0.44959745f, -0.66146255f, 0.61262494f, + -0.53746998f, -0.66146249f, 0.53746998f, + -0.61262494f, -0.66146249f, 0.44959742f, + -0.67360890f, -0.66146249f, 0.35046071f, + -0.71896827f, -0.66146255f, 0.24151327f, + -0.74724984f, -0.66146249f, 0.12420850f, + -0.75699997f, -0.66146249f, -0.00000001f, + 0.00000000f, -0.67500001f, 0.75000000f, + -0.12305999f, -0.67499995f, 0.74033999f, + -0.23928000f, -0.67500007f, 0.71232003f, + -0.34721997f, -0.67500001f, 0.66737998f, + -0.44544002f, -0.67500007f, 0.60696000f, + -0.53250003f, -0.67500007f, 0.53250003f, + -0.60696000f, -0.67500001f, 0.44543999f, + -0.66738003f, -0.67500001f, 0.34722000f, + -0.71231997f, -0.67500001f, 0.23927999f, + -0.74033999f, -0.67500001f, 0.12305995f, + -0.75000000f, -0.67500001f, -0.00000001f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.98711991f, -0.29999998f, -0.16407999f, + -0.94976002f, -0.30000004f, -0.31904000f, + -0.88984001f, -0.30000004f, -0.46296000f, + -0.80928004f, -0.30000001f, -0.59392005f, + -0.71000004f, -0.30000001f, -0.71000004f, + -0.59391999f, -0.30000001f, -0.80928004f, + -0.46296003f, -0.30000001f, -0.88984001f, + -0.31904000f, -0.30000001f, -0.94976002f, + -0.16407993f, -0.30000001f, -0.98712003f, + 0.00000000f, -0.30000001f, -1.00000000f, + -0.99299991f, -0.36416247f, -0.00000000f, + -0.98021001f, -0.36416242f, -0.16293143f, + -0.94311166f, -0.36416250f, -0.31680670f, + -0.88361102f, -0.36416245f, -0.45971927f, + -0.80361503f, -0.36416247f, -0.58976257f, + -0.70502996f, -0.36416247f, -0.70502996f, + -0.58976251f, -0.36416250f, -0.80361497f, + -0.45971930f, -0.36416247f, -0.88361108f, + -0.31680670f, -0.36416250f, -0.94311166f, + -0.16293137f, -0.36416247f, -0.98021007f, + 0.00000000f, -0.36416247f, -0.99299991f, + -0.97400004f, -0.42180002f, -0.00000001f, + -0.96145481f, -0.42179996f, -0.15981390f, + -0.92506635f, -0.42180005f, -0.31074497f, + -0.86670411f, -0.42180002f, -0.45092306f, + -0.78823876f, -0.42180005f, -0.57847816f, + -0.69154000f, -0.42180002f, -0.69154000f, + -0.57847810f, -0.42180002f, -0.78823876f, + -0.45092303f, -0.42180002f, -0.86670417f, + -0.31074494f, -0.42180002f, -0.92506629f, + -0.15981385f, -0.42180002f, -0.96145493f, + 0.00000000f, -0.42180002f, -0.97400004f, + -0.94600004f, -0.47313750f, -0.00000001f, + -0.93381548f, -0.47313747f, -0.15521967f, + -0.89847302f, -0.47313756f, -0.30181187f, + -0.84178865f, -0.47313750f, -0.43796018f, + -0.76557899f, -0.47313753f, -0.56184840f, + -0.67166001f, -0.47313750f, -0.67166001f, + -0.56184828f, -0.47313750f, -0.76557899f, + -0.43796021f, -0.47313750f, -0.84178865f, + -0.30181184f, -0.47313750f, -0.89847302f, + -0.15521961f, -0.47313750f, -0.93381560f, + 0.00000000f, -0.47313750f, -0.94600004f, + -0.91200000f, -0.51840001f, -0.00000001f, + -0.90025330f, -0.51839995f, -0.14964096f, + -0.86618114f, -0.51840001f, -0.29096451f, + -0.81153411f, -0.51840001f, -0.42221951f, + -0.73806345f, -0.51840007f, -0.54165506f, + -0.64752001f, -0.51840001f, -0.64752001f, + -0.54165506f, -0.51840001f, -0.73806340f, + -0.42221954f, -0.51840007f, -0.81153411f, + -0.29096448f, -0.51840007f, -0.86618114f, + -0.14964090f, -0.51840001f, -0.90025342f, + 0.00000000f, -0.51840001f, -0.91200000f, + -0.87500000f, -0.55781251f, -0.00000001f, + -0.86372989f, -0.55781251f, -0.14357001f, + -0.83104002f, -0.55781251f, -0.27916002f, + -0.77860999f, -0.55781251f, -0.40509003f, + -0.70812005f, -0.55781257f, -0.51968002f, + -0.62125003f, -0.55781251f, -0.62125003f, + -0.51967996f, -0.55781251f, -0.70812005f, + -0.40509003f, -0.55781257f, -0.77860999f, + -0.27915999f, -0.55781251f, -0.83104002f, + -0.14356995f, -0.55781251f, -0.86373001f, + 0.00000000f, -0.55781251f, -0.87500000f, + -0.83800000f, -0.59160000f, -0.00000001f, + -0.82720655f, -0.59159994f, -0.13749903f, + -0.79589891f, -0.59160000f, -0.26735553f, + -0.74568588f, -0.59160000f, -0.38796049f, + -0.67817670f, -0.59160006f, -0.49770495f, + -0.59498000f, -0.59160000f, -0.59497994f, + -0.49770495f, -0.59160000f, -0.67817664f, + -0.38796049f, -0.59160006f, -0.74568594f, + -0.26735550f, -0.59160000f, -0.79589891f, + -0.13749899f, -0.59160000f, -0.82720655f, + 0.00000000f, -0.59160000f, -0.83800000f, + -0.80400008f, -0.61998749f, -0.00000001f, + -0.79364455f, -0.61998743f, -0.13192032f, + -0.76360714f, -0.61998749f, -0.25650817f, + -0.71543139f, -0.61998749f, -0.37221986f, + -0.65066117f, -0.61998749f, -0.47751173f, + -0.57084000f, -0.61998749f, -0.57084000f, + -0.47751170f, -0.61998749f, -0.65066117f, + -0.37221986f, -0.61998749f, -0.71543145f, + -0.25650814f, -0.61998749f, -0.76360714f, + -0.13192026f, -0.61998749f, -0.79364455f, + 0.00000000f, -0.61998749f, -0.80400008f, + -0.77600002f, -0.64320004f, -0.00000001f, + -0.76600504f, -0.64319998f, -0.12732607f, + -0.73701382f, -0.64320010f, -0.24757506f, + -0.69051588f, -0.64320004f, -0.35925698f, + -0.62800133f, -0.64320010f, -0.46088195f, + -0.55096000f, -0.64320004f, -0.55096000f, + -0.46088192f, -0.64320004f, -0.62800133f, + -0.35925698f, -0.64320004f, -0.69051588f, + -0.24757503f, -0.64320004f, -0.73701382f, + -0.12732603f, -0.64320004f, -0.76600516f, + 0.00000000f, -0.64320004f, -0.77600002f, + -0.75699997f, -0.66146249f, -0.00000001f, + -0.74724966f, -0.66146243f, -0.12420855f, + -0.71896833f, -0.66146255f, -0.24151328f, + -0.67360884f, -0.66146243f, -0.35046071f, + -0.61262494f, -0.66146255f, -0.44959745f, + -0.53746998f, -0.66146249f, -0.53746998f, + -0.44959742f, -0.66146249f, -0.61262494f, + -0.35046071f, -0.66146249f, -0.67360890f, + -0.24151327f, -0.66146255f, -0.71896827f, + -0.12420851f, -0.66146249f, -0.74724984f, + 0.00000000f, -0.66146249f, -0.75699997f, + -0.75000000f, -0.67500001f, -0.00000001f, + -0.74033999f, -0.67499995f, -0.12306000f, + -0.71232003f, -0.67500007f, -0.23928000f, + -0.66737998f, -0.67500001f, -0.34721997f, + -0.60696000f, -0.67500007f, -0.44544002f, + -0.53250003f, -0.67500007f, -0.53250003f, + -0.44543999f, -0.67500001f, -0.60696000f, + -0.34722000f, -0.67500001f, -0.66738003f, + -0.23927999f, -0.67500001f, -0.71231997f, + -0.12305996f, -0.67500001f, -0.74033999f, + 0.00000000f, -0.67500001f, -0.75000000f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.09730000f, 0.82072502f, 0.00000001f, + 0.09605332f, 0.82072490f, 0.01602404f, + 0.09243499f, 0.82072508f, 0.03113591f, + 0.08662736f, 0.82072502f, 0.04515318f, + 0.07881293f, 0.82072508f, 0.05789340f, + 0.06917413f, 0.82072502f, 0.06917413f, + 0.05789339f, 0.82072502f, 0.07881294f, + 0.04515317f, 0.82072508f, 0.08662736f, + 0.03113590f, 0.82072502f, 0.09243499f, + 0.01602403f, 0.82072502f, 0.09605335f, + 0.00000000f, 0.82072502f, 0.09730001f, + 0.15440002f, 0.80880016f, 0.00000001f, + 0.15242170f, 0.80880004f, 0.02542728f, + 0.14667983f, 0.80880022f, 0.04940725f, + 0.13746388f, 0.80880022f, 0.07165039f, + 0.12506345f, 0.80880028f, 0.09186716f, + 0.10976801f, 0.80880016f, 0.10976802f, + 0.09186715f, 0.80880016f, 0.12506345f, + 0.07165038f, 0.80880022f, 0.13746390f, + 0.04940724f, 0.80880022f, 0.14667983f, + 0.02542726f, 0.80880016f, 0.15242171f, + 0.00000000f, 0.80880016f, 0.15440002f, + 0.17909999f, 0.79057503f, 0.00000001f, + 0.17680508f, 0.79057491f, 0.02949390f, + 0.17014435f, 0.79057509f, 0.05730941f, + 0.15945368f, 0.79057509f, 0.08311062f, + 0.14506906f, 0.79057503f, 0.10656159f, + 0.12732637f, 0.79057503f, 0.12732637f, + 0.10656157f, 0.79057503f, 0.14506905f, + 0.08311062f, 0.79057503f, 0.15945369f, + 0.05730940f, 0.79057503f, 0.17014435f, + 0.02949388f, 0.79057503f, 0.17680509f, + 0.00000000f, 0.79057503f, 0.17909999f, + 0.17920001f, 0.76740009f, 0.00000001f, + 0.17690356f, 0.76740003f, 0.02950812f, + 0.17023848f, 0.76740015f, 0.05733787f, + 0.15954098f, 0.76740009f, 0.08315296f, + 0.14514741f, 0.76740015f, 0.10661709f, + 0.12739401f, 0.76740009f, 0.12739401f, + 0.10661709f, 0.76740009f, 0.14514741f, + 0.08315295f, 0.76740009f, 0.15954098f, + 0.05733785f, 0.76740015f, 0.17023847f, + 0.02950810f, 0.76740009f, 0.17690358f, + 0.00000000f, 0.76740009f, 0.17920001f, + 0.16250001f, 0.74062496f, 0.00000001f, + 0.16041712f, 0.74062496f, 0.02675413f, + 0.15437202f, 0.74062502f, 0.05198801f, + 0.14466989f, 0.74062496f, 0.07539638f, + 0.13161601f, 0.74062496f, 0.09667401f, + 0.11551563f, 0.74062496f, 0.11551563f, + 0.09667400f, 0.74062496f, 0.13161600f, + 0.07539637f, 0.74062502f, 0.14466989f, + 0.05198800f, 0.74062502f, 0.15437201f, + 0.02675411f, 0.74062496f, 0.16041712f, + 0.00000000f, 0.74062496f, 0.16250001f, + 0.13680001f, 0.71160007f, 0.00000001f, + 0.13504578f, 0.71160001f, 0.02251614f, + 0.12995483f, 0.71160012f, 0.04375527f, + 0.12178454f, 0.71160007f, 0.06345994f, + 0.11079245f, 0.71160012f, 0.08137268f, + 0.09723600f, 0.71160007f, 0.09723601f, + 0.08137267f, 0.71160007f, 0.11079245f, + 0.06345994f, 0.71160007f, 0.12178455f, + 0.04375526f, 0.71160007f, 0.12995481f, + 0.02251612f, 0.71160007f, 0.13504580f, + 0.00000000f, 0.71160007f, 0.13680001f, + 0.10990001f, 0.68167502f, 0.00000001f, + 0.10848958f, 0.68167496f, 0.01807833f, + 0.10439678f, 0.68167502f, 0.03513508f, + 0.09782913f, 0.68167496f, 0.05096266f, + 0.08899431f, 0.68167502f, 0.06535347f, + 0.07809988f, 0.68167502f, 0.07809989f, + 0.06535345f, 0.68167502f, 0.08899432f, + 0.05096266f, 0.68167502f, 0.09782915f, + 0.03513507f, 0.68167502f, 0.10439678f, + 0.01807831f, 0.68167502f, 0.10848960f, + 0.00000000f, 0.68167502f, 0.10990001f, + 0.08960000f, 0.65219998f, 0.00000001f, + 0.08844854f, 0.65219992f, 0.01472490f, + 0.08510771f, 0.65220004f, 0.02862286f, + 0.07974781f, 0.65219998f, 0.04152356f, + 0.07253914f, 0.65219998f, 0.05325671f, + 0.06365200f, 0.65219998f, 0.06365201f, + 0.05325670f, 0.65219998f, 0.07253915f, + 0.04152355f, 0.65219998f, 0.07974782f, + 0.02862285f, 0.65219998f, 0.08510773f, + 0.01472489f, 0.65219998f, 0.08844855f, + 0.00000000f, 0.65219998f, 0.08960001f, + 0.08370000f, 0.62452501f, 0.00000001f, + 0.08262266f, 0.62452495f, 0.01374006f, + 0.07949751f, 0.62452501f, 0.02671403f, + 0.07448471f, 0.62452501f, 0.03876166f, + 0.06774452f, 0.62452507f, 0.04972278f, + 0.05943713f, 0.62452501f, 0.05943713f, + 0.04972277f, 0.62452501f, 0.06774452f, + 0.03876166f, 0.62452501f, 0.07448472f, + 0.02671402f, 0.62452501f, 0.07949752f, + 0.01374005f, 0.62452501f, 0.08262268f, + 0.00000000f, 0.62452501f, 0.08370001f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.09871199f, 0.59999996f, 0.01640801f, + 0.09497602f, 0.60000008f, 0.03190401f, + 0.08898400f, 0.60000008f, 0.04629601f, + 0.08092801f, 0.60000002f, 0.05939201f, + 0.07100000f, 0.60000002f, 0.07100001f, + 0.05939200f, 0.60000002f, 0.08092801f, + 0.04629600f, 0.60000002f, 0.08898401f, + 0.03190400f, 0.60000002f, 0.09497601f, + 0.01640799f, 0.60000002f, 0.09871200f, + 0.00000000f, 0.60000002f, 0.10000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82072502f, -0.09729999f, + 0.01602403f, 0.82072490f, -0.09605332f, + 0.03113590f, 0.82072508f, -0.09243497f, + 0.04515317f, 0.82072502f, -0.08662734f, + 0.05789339f, 0.82072508f, -0.07881292f, + 0.06917413f, 0.82072502f, -0.06917411f, + 0.07881293f, 0.82072502f, -0.05789338f, + 0.08662736f, 0.82072508f, -0.04515316f, + 0.09243498f, 0.82072502f, -0.03113589f, + 0.09605334f, 0.82072502f, -0.01602402f, + 0.09730000f, 0.82072502f, 0.00000001f, + 0.00000000f, 0.80880016f, -0.15440002f, + 0.02542727f, 0.80880004f, -0.15242170f, + 0.04940724f, 0.80880022f, -0.14667983f, + 0.07165038f, 0.80880022f, -0.13746388f, + 0.09186715f, 0.80880028f, -0.12506345f, + 0.10976800f, 0.80880016f, -0.10976801f, + 0.12506345f, 0.80880016f, -0.09186714f, + 0.13746388f, 0.80880022f, -0.07165038f, + 0.14667982f, 0.80880022f, -0.04940723f, + 0.15242171f, 0.80880016f, -0.02542725f, + 0.15440002f, 0.80880016f, 0.00000001f, + 0.00000000f, 0.79057503f, -0.17909999f, + 0.02949389f, 0.79057491f, -0.17680508f, + 0.05730940f, 0.79057509f, -0.17014435f, + 0.08311061f, 0.79057509f, -0.15945368f, + 0.10656158f, 0.79057503f, -0.14506905f, + 0.12732637f, 0.79057503f, -0.12732637f, + 0.14506905f, 0.79057503f, -0.10656157f, + 0.15945369f, 0.79057503f, -0.08311061f, + 0.17014435f, 0.79057503f, -0.05730939f, + 0.17680509f, 0.79057503f, -0.02949387f, + 0.17909999f, 0.79057503f, 0.00000001f, + 0.00000000f, 0.76740009f, -0.17920001f, + 0.02950811f, 0.76740003f, -0.17690356f, + 0.05733786f, 0.76740015f, -0.17023848f, + 0.08315294f, 0.76740009f, -0.15954097f, + 0.10661709f, 0.76740015f, -0.14514740f, + 0.12739401f, 0.76740009f, -0.12739399f, + 0.14514741f, 0.76740009f, -0.10661709f, + 0.15954098f, 0.76740009f, -0.08315295f, + 0.17023847f, 0.76740015f, -0.05733785f, + 0.17690358f, 0.76740009f, -0.02950809f, + 0.17920001f, 0.76740009f, 0.00000001f, + 0.00000000f, 0.74062496f, -0.16250001f, + 0.02675412f, 0.74062496f, -0.16041712f, + 0.05198800f, 0.74062502f, -0.15437202f, + 0.07539637f, 0.74062496f, -0.14466989f, + 0.09667401f, 0.74062496f, -0.13161601f, + 0.11551563f, 0.74062496f, -0.11551563f, + 0.13161600f, 0.74062496f, -0.09667400f, + 0.14466989f, 0.74062502f, -0.07539637f, + 0.15437201f, 0.74062502f, -0.05198799f, + 0.16041712f, 0.74062496f, -0.02675411f, + 0.16250001f, 0.74062496f, 0.00000001f, + 0.00000000f, 0.71160007f, -0.13679999f, + 0.02251613f, 0.71160001f, -0.13504578f, + 0.04375527f, 0.71160012f, -0.12995481f, + 0.06345994f, 0.71160007f, -0.12178454f, + 0.08137268f, 0.71160012f, -0.11079244f, + 0.09723600f, 0.71160007f, -0.09723599f, + 0.11079245f, 0.71160007f, -0.08137266f, + 0.12178455f, 0.71160007f, -0.06345993f, + 0.12995481f, 0.71160007f, -0.04375526f, + 0.13504580f, 0.71160007f, -0.02251611f, + 0.13680001f, 0.71160007f, 0.00000001f, + 0.00000000f, 0.68167502f, -0.10990000f, + 0.01807832f, 0.68167496f, -0.10848958f, + 0.03513507f, 0.68167502f, -0.10439678f, + 0.05096266f, 0.68167496f, -0.09782913f, + 0.06535346f, 0.68167502f, -0.08899431f, + 0.07809988f, 0.68167502f, -0.07809987f, + 0.08899431f, 0.68167502f, -0.06535345f, + 0.09782915f, 0.68167502f, -0.05096265f, + 0.10439678f, 0.68167502f, -0.03513506f, + 0.10848960f, 0.68167502f, -0.01807830f, + 0.10990001f, 0.68167502f, 0.00000001f, + 0.00000000f, 0.65219998f, -0.08960000f, + 0.01472490f, 0.65219992f, -0.08844854f, + 0.02862285f, 0.65220004f, -0.08510771f, + 0.04152355f, 0.65219998f, -0.07974780f, + 0.05325671f, 0.65219998f, -0.07253914f, + 0.06365199f, 0.65219998f, -0.06365199f, + 0.07253914f, 0.65219998f, -0.05325670f, + 0.07974781f, 0.65219998f, -0.04152355f, + 0.08510771f, 0.65219998f, -0.02862284f, + 0.08844854f, 0.65219998f, -0.01472488f, + 0.08960000f, 0.65219998f, 0.00000001f, + 0.00000000f, 0.62452501f, -0.08369999f, + 0.01374006f, 0.62452495f, -0.08262266f, + 0.02671402f, 0.62452501f, -0.07949750f, + 0.03876166f, 0.62452501f, -0.07448471f, + 0.04972277f, 0.62452507f, -0.06774451f, + 0.05943713f, 0.62452501f, -0.05943712f, + 0.06774452f, 0.62452501f, -0.04972276f, + 0.07448471f, 0.62452501f, -0.03876166f, + 0.07949751f, 0.62452501f, -0.02671401f, + 0.08262268f, 0.62452501f, -0.01374004f, + 0.08370000f, 0.62452501f, 0.00000001f, + 0.00000000f, 0.60000002f, -0.09999999f, + 0.01640800f, 0.59999996f, -0.09871199f, + 0.03190400f, 0.60000008f, -0.09497599f, + 0.04629600f, 0.60000008f, -0.08898398f, + 0.05939200f, 0.60000002f, -0.08092800f, + 0.07100000f, 0.60000002f, -0.07099999f, + 0.08092801f, 0.60000002f, -0.05939199f, + 0.08898400f, 0.60000002f, -0.04629600f, + 0.09497601f, 0.60000002f, -0.03190399f, + 0.09871200f, 0.60000002f, -0.01640799f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82072502f, 0.09730001f, + -0.01602403f, 0.82072490f, 0.09605334f, + -0.03113590f, 0.82072508f, 0.09243499f, + -0.04515317f, 0.82072502f, 0.08662736f, + -0.05789339f, 0.82072508f, 0.07881294f, + -0.06917413f, 0.82072502f, 0.06917413f, + -0.07881293f, 0.82072502f, 0.05789340f, + -0.08662736f, 0.82072508f, 0.04515318f, + -0.09243498f, 0.82072502f, 0.03113591f, + -0.09605334f, 0.82072502f, 0.01602404f, + -0.09730000f, 0.82072502f, 0.00000001f, + 0.00000000f, 0.80880016f, 0.15440002f, + -0.02542727f, 0.80880004f, 0.15242170f, + -0.04940724f, 0.80880022f, 0.14667983f, + -0.07165038f, 0.80880022f, 0.13746390f, + -0.09186715f, 0.80880028f, 0.12506345f, + -0.10976800f, 0.80880016f, 0.10976802f, + -0.12506345f, 0.80880016f, 0.09186715f, + -0.13746388f, 0.80880022f, 0.07165039f, + -0.14667982f, 0.80880022f, 0.04940725f, + -0.15242171f, 0.80880016f, 0.02542727f, + -0.15440002f, 0.80880016f, 0.00000001f, + 0.00000000f, 0.79057503f, 0.17909999f, + -0.02949389f, 0.79057491f, 0.17680508f, + -0.05730940f, 0.79057509f, 0.17014435f, + -0.08311061f, 0.79057509f, 0.15945368f, + -0.10656158f, 0.79057503f, 0.14506906f, + -0.12732637f, 0.79057503f, 0.12732637f, + -0.14506905f, 0.79057503f, 0.10656157f, + -0.15945369f, 0.79057503f, 0.08311062f, + -0.17014435f, 0.79057503f, 0.05730941f, + -0.17680509f, 0.79057503f, 0.02949389f, + -0.17909999f, 0.79057503f, 0.00000001f, + 0.00000000f, 0.76740009f, 0.17920001f, + -0.02950811f, 0.76740003f, 0.17690358f, + -0.05733786f, 0.76740015f, 0.17023848f, + -0.08315294f, 0.76740009f, 0.15954098f, + -0.10661709f, 0.76740015f, 0.14514741f, + -0.12739401f, 0.76740009f, 0.12739401f, + -0.14514741f, 0.76740009f, 0.10661709f, + -0.15954098f, 0.76740009f, 0.08315295f, + -0.17023847f, 0.76740015f, 0.05733786f, + -0.17690358f, 0.76740009f, 0.02950811f, + -0.17920001f, 0.76740009f, 0.00000001f, + 0.00000000f, 0.74062496f, 0.16250001f, + -0.02675412f, 0.74062496f, 0.16041712f, + -0.05198800f, 0.74062502f, 0.15437202f, + -0.07539637f, 0.74062496f, 0.14466989f, + -0.09667401f, 0.74062496f, 0.13161601f, + -0.11551563f, 0.74062496f, 0.11551563f, + -0.13161600f, 0.74062496f, 0.09667400f, + -0.14466989f, 0.74062502f, 0.07539638f, + -0.15437201f, 0.74062502f, 0.05198800f, + -0.16041712f, 0.74062496f, 0.02675412f, + -0.16250001f, 0.74062496f, 0.00000001f, + 0.00000000f, 0.71160007f, 0.13680001f, + -0.02251613f, 0.71160001f, 0.13504578f, + -0.04375527f, 0.71160012f, 0.12995483f, + -0.06345994f, 0.71160007f, 0.12178455f, + -0.08137268f, 0.71160012f, 0.11079246f, + -0.09723600f, 0.71160007f, 0.09723601f, + -0.11079245f, 0.71160007f, 0.08137267f, + -0.12178455f, 0.71160007f, 0.06345994f, + -0.12995481f, 0.71160007f, 0.04375527f, + -0.13504580f, 0.71160007f, 0.02251613f, + -0.13680001f, 0.71160007f, 0.00000001f, + 0.00000000f, 0.68167502f, 0.10990001f, + -0.01807832f, 0.68167496f, 0.10848960f, + -0.03513507f, 0.68167502f, 0.10439679f, + -0.05096266f, 0.68167496f, 0.09782915f, + -0.06535346f, 0.68167502f, 0.08899432f, + -0.07809988f, 0.68167502f, 0.07809988f, + -0.08899431f, 0.68167502f, 0.06535346f, + -0.09782915f, 0.68167502f, 0.05096267f, + -0.10439678f, 0.68167502f, 0.03513508f, + -0.10848960f, 0.68167502f, 0.01807832f, + -0.10990001f, 0.68167502f, 0.00000001f, + 0.00000000f, 0.65219998f, 0.08960001f, + -0.01472490f, 0.65219992f, 0.08844855f, + -0.02862285f, 0.65220004f, 0.08510773f, + -0.04152355f, 0.65219998f, 0.07974782f, + -0.05325671f, 0.65219998f, 0.07253915f, + -0.06365199f, 0.65219998f, 0.06365201f, + -0.07253914f, 0.65219998f, 0.05325671f, + -0.07974781f, 0.65219998f, 0.04152356f, + -0.08510771f, 0.65219998f, 0.02862286f, + -0.08844854f, 0.65219998f, 0.01472490f, + -0.08960000f, 0.65219998f, 0.00000001f, + 0.00000000f, 0.62452501f, 0.08370001f, + -0.01374006f, 0.62452495f, 0.08262267f, + -0.02671402f, 0.62452501f, 0.07949752f, + -0.03876166f, 0.62452501f, 0.07448472f, + -0.04972277f, 0.62452507f, 0.06774452f, + -0.05943713f, 0.62452501f, 0.05943713f, + -0.06774452f, 0.62452501f, 0.04972278f, + -0.07448471f, 0.62452501f, 0.03876167f, + -0.07949751f, 0.62452501f, 0.02671402f, + -0.08262268f, 0.62452501f, 0.01374006f, + -0.08370000f, 0.62452501f, 0.00000001f, + 0.00000000f, 0.60000002f, 0.10000001f, + -0.01640800f, 0.59999996f, 0.09871200f, + -0.03190400f, 0.60000008f, 0.09497602f, + -0.04629600f, 0.60000008f, 0.08898401f, + -0.05939200f, 0.60000002f, 0.08092801f, + -0.07100000f, 0.60000002f, 0.07100001f, + -0.08092801f, 0.60000002f, 0.05939201f, + -0.08898400f, 0.60000002f, 0.04629601f, + -0.09497601f, 0.60000002f, 0.03190401f, + -0.09871200f, 0.60000002f, 0.01640800f, + -0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + -0.09730000f, 0.82072502f, 0.00000001f, + -0.09605332f, 0.82072490f, -0.01602402f, + -0.09243499f, 0.82072508f, -0.03113589f, + -0.08662736f, 0.82072502f, -0.04515316f, + -0.07881293f, 0.82072508f, -0.05789338f, + -0.06917413f, 0.82072502f, -0.06917411f, + -0.05789339f, 0.82072502f, -0.07881292f, + -0.04515317f, 0.82072508f, -0.08662735f, + -0.03113590f, 0.82072502f, -0.09243497f, + -0.01602403f, 0.82072502f, -0.09605333f, + 0.00000000f, 0.82072502f, -0.09729999f, + -0.15440002f, 0.80880016f, 0.00000001f, + -0.15242170f, 0.80880004f, -0.02542726f, + -0.14667983f, 0.80880022f, -0.04940723f, + -0.13746388f, 0.80880022f, -0.07165037f, + -0.12506345f, 0.80880028f, -0.09186714f, + -0.10976801f, 0.80880016f, -0.10976800f, + -0.09186715f, 0.80880016f, -0.12506345f, + -0.07165038f, 0.80880022f, -0.13746388f, + -0.04940724f, 0.80880022f, -0.14667982f, + -0.02542726f, 0.80880016f, -0.15242171f, + 0.00000000f, 0.80880016f, -0.15440002f, + -0.17909999f, 0.79057503f, 0.00000001f, + -0.17680508f, 0.79057491f, -0.02949388f, + -0.17014435f, 0.79057509f, -0.05730940f, + -0.15945368f, 0.79057509f, -0.08311061f, + -0.14506906f, 0.79057503f, -0.10656158f, + -0.12732637f, 0.79057503f, -0.12732637f, + -0.10656157f, 0.79057503f, -0.14506905f, + -0.08311062f, 0.79057503f, -0.15945369f, + -0.05730940f, 0.79057503f, -0.17014435f, + -0.02949388f, 0.79057503f, -0.17680509f, + 0.00000000f, 0.79057503f, -0.17909999f, + -0.17920001f, 0.76740009f, 0.00000001f, + -0.17690356f, 0.76740003f, -0.02950810f, + -0.17023848f, 0.76740015f, -0.05733785f, + -0.15954098f, 0.76740009f, -0.08315294f, + -0.14514741f, 0.76740015f, -0.10661709f, + -0.12739401f, 0.76740009f, -0.12739401f, + -0.10661709f, 0.76740009f, -0.14514740f, + -0.08315295f, 0.76740009f, -0.15954098f, + -0.05733785f, 0.76740015f, -0.17023847f, + -0.02950810f, 0.76740009f, -0.17690358f, + 0.00000000f, 0.76740009f, -0.17920001f, + -0.16250001f, 0.74062496f, 0.00000001f, + -0.16041712f, 0.74062496f, -0.02675412f, + -0.15437202f, 0.74062502f, -0.05198800f, + -0.14466989f, 0.74062496f, -0.07539637f, + -0.13161601f, 0.74062496f, -0.09667400f, + -0.11551563f, 0.74062496f, -0.11551563f, + -0.09667400f, 0.74062496f, -0.13161600f, + -0.07539637f, 0.74062502f, -0.14466989f, + -0.05198800f, 0.74062502f, -0.15437201f, + -0.02675411f, 0.74062496f, -0.16041712f, + 0.00000000f, 0.74062496f, -0.16250001f, + -0.13680001f, 0.71160007f, 0.00000001f, + -0.13504578f, 0.71160001f, -0.02251612f, + -0.12995483f, 0.71160012f, -0.04375526f, + -0.12178454f, 0.71160007f, -0.06345993f, + -0.11079245f, 0.71160012f, -0.08137267f, + -0.09723600f, 0.71160007f, -0.09723599f, + -0.08137267f, 0.71160007f, -0.11079245f, + -0.06345994f, 0.71160007f, -0.12178454f, + -0.04375526f, 0.71160007f, -0.12995481f, + -0.02251612f, 0.71160007f, -0.13504578f, + 0.00000000f, 0.71160007f, -0.13679999f, + -0.10990001f, 0.68167502f, 0.00000001f, + -0.10848958f, 0.68167496f, -0.01807831f, + -0.10439678f, 0.68167502f, -0.03513507f, + -0.09782913f, 0.68167496f, -0.05096265f, + -0.08899431f, 0.68167502f, -0.06535345f, + -0.07809988f, 0.68167502f, -0.07809987f, + -0.06535345f, 0.68167502f, -0.08899430f, + -0.05096266f, 0.68167502f, -0.09782913f, + -0.03513507f, 0.68167502f, -0.10439677f, + -0.01807831f, 0.68167502f, -0.10848959f, + 0.00000000f, 0.68167502f, -0.10990000f, + -0.08960000f, 0.65219998f, 0.00000001f, + -0.08844854f, 0.65219992f, -0.01472489f, + -0.08510771f, 0.65220004f, -0.02862284f, + -0.07974781f, 0.65219998f, -0.04152355f, + -0.07253914f, 0.65219998f, -0.05325670f, + -0.06365200f, 0.65219998f, -0.06365199f, + -0.05325670f, 0.65219998f, -0.07253914f, + -0.04152355f, 0.65219998f, -0.07974781f, + -0.02862285f, 0.65219998f, -0.08510771f, + -0.01472489f, 0.65219998f, -0.08844854f, + 0.00000000f, 0.65219998f, -0.08960000f, + -0.08370000f, 0.62452501f, 0.00000001f, + -0.08262266f, 0.62452495f, -0.01374005f, + -0.07949751f, 0.62452501f, -0.02671401f, + -0.07448471f, 0.62452501f, -0.03876165f, + -0.06774452f, 0.62452507f, -0.04972276f, + -0.05943713f, 0.62452501f, -0.05943712f, + -0.04972277f, 0.62452501f, -0.06774451f, + -0.03876166f, 0.62452501f, -0.07448471f, + -0.02671402f, 0.62452501f, -0.07949750f, + -0.01374005f, 0.62452501f, -0.08262267f, + 0.00000000f, 0.62452501f, -0.08369999f, + -0.10000000f, 0.60000002f, 0.00000001f, + -0.09871199f, 0.59999996f, -0.01640799f, + -0.09497602f, 0.60000008f, -0.03190399f, + -0.08898400f, 0.60000008f, -0.04629599f, + -0.08092801f, 0.60000002f, -0.05939199f, + -0.07100000f, 0.60000002f, -0.07099999f, + -0.05939200f, 0.60000002f, -0.08092800f, + -0.04629600f, 0.60000002f, -0.08898400f, + -0.03190400f, 0.60000002f, -0.09497599f, + -0.01640799f, 0.60000002f, -0.09871200f, + 0.00000000f, 0.60000002f, -0.09999999f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.09871199f, 0.59999996f, 0.01640801f, + 0.09497602f, 0.60000008f, 0.03190401f, + 0.08898400f, 0.60000008f, 0.04629601f, + 0.08092801f, 0.60000002f, 0.05939201f, + 0.07100000f, 0.60000002f, 0.07100001f, + 0.05939200f, 0.60000002f, 0.08092801f, + 0.04629600f, 0.60000002f, 0.08898401f, + 0.03190400f, 0.60000002f, 0.09497601f, + 0.01640799f, 0.60000002f, 0.09871200f, + 0.00000000f, 0.60000002f, 0.10000001f, + 0.13969998f, 0.57959992f, 0.00000001f, + 0.13790064f, 0.57959986f, 0.02292198f, + 0.13268146f, 0.57959998f, 0.04456990f, + 0.12431064f, 0.57959992f, 0.06467552f, + 0.11305641f, 0.57959998f, 0.08297063f, + 0.09918699f, 0.57959992f, 0.09918701f, + 0.08297062f, 0.57959992f, 0.11305644f, + 0.06467551f, 0.57959998f, 0.12431066f, + 0.04456988f, 0.57959992f, 0.13268149f, + 0.02292197f, 0.57959992f, 0.13790068f, + 0.00000000f, 0.57959992f, 0.13970001f, + 0.19560002f, 0.56280005f, 0.00000001f, + 0.19308068f, 0.56279999f, 0.03209405f, + 0.18577307f, 0.56280011f, 0.06240424f, + 0.17405272f, 0.56280005f, 0.09055499f, + 0.15829518f, 0.56280005f, 0.11617076f, + 0.13887602f, 0.56280005f, 0.13887601f, + 0.11617076f, 0.56280005f, 0.15829518f, + 0.09055499f, 0.56280005f, 0.17405272f, + 0.06240423f, 0.56280005f, 0.18577307f, + 0.03209404f, 0.56280005f, 0.19308069f, + 0.00000000f, 0.56280005f, 0.19560002f, + 0.26289999f, 0.54869998f, 0.00000001f, + 0.25951380f, 0.54869998f, 0.04313663f, + 0.24969190f, 0.54869998f, 0.08387563f, + 0.23393893f, 0.54870003f, 0.12171219f, + 0.21275970f, 0.54869998f, 0.15614158f, + 0.18665901f, 0.54869998f, 0.18665899f, + 0.15614155f, 0.54869998f, 0.21275972f, + 0.12171219f, 0.54869998f, 0.23393893f, + 0.08387562f, 0.54869998f, 0.24969190f, + 0.04313662f, 0.54869998f, 0.25951385f, + 0.00000000f, 0.54869998f, 0.26289999f, + 0.33680001f, 0.53640002f, 0.00000001f, + 0.33246198f, 0.53639996f, 0.05526215f, + 0.31987920f, 0.53640002f, 0.10745268f, + 0.29969811f, 0.53640002f, 0.15592493f, + 0.27256551f, 0.53640002f, 0.20003228f, + 0.23912801f, 0.53640002f, 0.23912801f, + 0.20003226f, 0.53640002f, 0.27256551f, + 0.15592495f, 0.53640008f, 0.29969811f, + 0.10745268f, 0.53640008f, 0.31987917f, + 0.05526213f, 0.53640002f, 0.33246201f, + 0.00000000f, 0.53640002f, 0.33680001f, + 0.41250002f, 0.52499998f, 0.00000001f, + 0.40718701f, 0.52499992f, 0.06768300f, + 0.39177606f, 0.52499998f, 0.13160400f, + 0.36705902f, 0.52499998f, 0.19097100f, + 0.33382803f, 0.52499998f, 0.24499202f, + 0.29287499f, 0.52499998f, 0.29287499f, + 0.24499199f, 0.52499998f, 0.33382803f, + 0.19097102f, 0.52499998f, 0.36705902f, + 0.13160400f, 0.52499998f, 0.39177603f, + 0.06768297f, 0.52499998f, 0.40718701f, + 0.00000000f, 0.52499998f, 0.41250002f, + 0.48519999f, 0.51359999f, 0.00000001f, + 0.47895056f, 0.51359999f, 0.07961162f, + 0.46082360f, 0.51359999f, 0.15479822f, + 0.43175036f, 0.51359999f, 0.22462820f, + 0.39266267f, 0.51360005f, 0.28817001f, + 0.34449199f, 0.51359999f, 0.34449199f, + 0.28816998f, 0.51359999f, 0.39266264f, + 0.22462821f, 0.51359999f, 0.43175036f, + 0.15479821f, 0.51359999f, 0.46082354f, + 0.07961158f, 0.51359999f, 0.47895062f, + 0.00000000f, 0.51359999f, 0.48519999f, + 0.55009997f, 0.50129998f, 0.00000001f, + 0.54301465f, 0.50129992f, 0.09026041f, + 0.52246296f, 0.50129998f, 0.17550392f, + 0.48950094f, 0.50129998f, 0.25467429f, + 0.44518495f, 0.50129998f, 0.32671541f, + 0.39057100f, 0.50129998f, 0.39057100f, + 0.32671538f, 0.50129998f, 0.44518495f, + 0.25467432f, 0.50129998f, 0.48950097f, + 0.17550389f, 0.50129998f, 0.52246296f, + 0.09026037f, 0.50129998f, 0.54301471f, + 0.00000000f, 0.50129998f, 0.55009997f, + 0.60240000f, 0.48720002f, 0.00000001f, + 0.59464109f, 0.48719996f, 0.09884179f, + 0.57213551f, 0.48720005f, 0.19218971f, + 0.53603959f, 0.48719999f, 0.27888709f, + 0.48751029f, 0.48720002f, 0.35777742f, + 0.42770401f, 0.48720002f, 0.42770401f, + 0.35777742f, 0.48720002f, 0.48751026f, + 0.27888709f, 0.48720005f, 0.53603959f, + 0.19218969f, 0.48720002f, 0.57213545f, + 0.09884176f, 0.48720002f, 0.59464109f, + 0.00000000f, 0.48720002f, 0.60240000f, + 0.63730001f, 0.47040004f, 0.00000001f, + 0.62909156f, 0.47039998f, 0.10456818f, + 0.60528207f, 0.47040007f, 0.20332420f, + 0.56709504f, 0.47040001f, 0.29504442f, + 0.51575416f, 0.47040004f, 0.37850523f, + 0.45248300f, 0.47040004f, 0.45248300f, + 0.37850523f, 0.47040004f, 0.51575416f, + 0.29504442f, 0.47040004f, 0.56709504f, + 0.20332420f, 0.47040004f, 0.60528207f, + 0.10456815f, 0.47040004f, 0.62909162f, + 0.00000000f, 0.47040004f, 0.63730001f, + 0.64999998f, 0.45000005f, 0.00000001f, + 0.64162791f, 0.44999996f, 0.10665201f, + 0.61734402f, 0.45000011f, 0.20737600f, + 0.57839590f, 0.45000002f, 0.30092400f, + 0.52603197f, 0.45000008f, 0.38604802f, + 0.46149999f, 0.45000005f, 0.46149999f, + 0.38604796f, 0.45000005f, 0.52603197f, + 0.30092400f, 0.45000008f, 0.57839596f, + 0.20737599f, 0.45000005f, 0.61734402f, + 0.10665196f, 0.45000005f, 0.64162797f, + 0.00000000f, 0.45000005f, 0.64999998f, + 0.00000000f, 0.60000002f, -0.09999999f, + 0.01640800f, 0.59999996f, -0.09871199f, + 0.03190400f, 0.60000008f, -0.09497599f, + 0.04629600f, 0.60000008f, -0.08898398f, + 0.05939200f, 0.60000002f, -0.08092800f, + 0.07100000f, 0.60000002f, -0.07099999f, + 0.08092801f, 0.60000002f, -0.05939199f, + 0.08898400f, 0.60000002f, -0.04629600f, + 0.09497601f, 0.60000002f, -0.03190399f, + 0.09871200f, 0.60000002f, -0.01640799f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.57959992f, -0.13969998f, + 0.02292197f, 0.57959986f, -0.13790064f, + 0.04456988f, 0.57959998f, -0.13268146f, + 0.06467551f, 0.57959992f, -0.12431064f, + 0.08297062f, 0.57959998f, -0.11305641f, + 0.09918699f, 0.57959992f, -0.09918699f, + 0.11305641f, 0.57959992f, -0.08297061f, + 0.12431064f, 0.57959998f, -0.06467551f, + 0.13268146f, 0.57959992f, -0.04456988f, + 0.13790065f, 0.57959992f, -0.02292196f, + 0.13969998f, 0.57959992f, 0.00000001f, + 0.00000000f, 0.56280005f, -0.19560000f, + 0.03209405f, 0.56279999f, -0.19308066f, + 0.06240423f, 0.56280011f, -0.18577306f, + 0.09055499f, 0.56280005f, -0.17405270f, + 0.11617076f, 0.56280005f, -0.15829517f, + 0.13887601f, 0.56280005f, -0.13887601f, + 0.15829518f, 0.56280005f, -0.11617075f, + 0.17405272f, 0.56280005f, -0.09055498f, + 0.18577307f, 0.56280005f, -0.06240422f, + 0.19308069f, 0.56280005f, -0.03209403f, + 0.19560002f, 0.56280005f, 0.00000001f, + 0.00000000f, 0.54869998f, -0.26289999f, + 0.04313663f, 0.54869998f, -0.25951380f, + 0.08387563f, 0.54869998f, -0.24969190f, + 0.12171219f, 0.54870003f, -0.23393893f, + 0.15614158f, 0.54869998f, -0.21275970f, + 0.18665899f, 0.54869998f, -0.18665899f, + 0.21275972f, 0.54869998f, -0.15614155f, + 0.23393893f, 0.54869998f, -0.12171219f, + 0.24969190f, 0.54869998f, -0.08387561f, + 0.25951385f, 0.54869998f, -0.04313661f, + 0.26289999f, 0.54869998f, 0.00000001f, + 0.00000000f, 0.53640002f, -0.33680001f, + 0.05526214f, 0.53639996f, -0.33246198f, + 0.10745268f, 0.53640002f, -0.31987920f, + 0.15592493f, 0.53640002f, -0.29969811f, + 0.20003228f, 0.53640002f, -0.27256551f, + 0.23912801f, 0.53640002f, -0.23912799f, + 0.27256551f, 0.53640002f, -0.20003225f, + 0.29969811f, 0.53640008f, -0.15592495f, + 0.31987917f, 0.53640008f, -0.10745267f, + 0.33246201f, 0.53640002f, -0.05526212f, + 0.33680001f, 0.53640002f, 0.00000001f, + 0.00000000f, 0.52499998f, -0.41250002f, + 0.06768300f, 0.52499992f, -0.40718701f, + 0.13160400f, 0.52499998f, -0.39177606f, + 0.19097100f, 0.52499998f, -0.36705902f, + 0.24499202f, 0.52499998f, -0.33382803f, + 0.29287499f, 0.52499998f, -0.29287499f, + 0.33382803f, 0.52499998f, -0.24499199f, + 0.36705902f, 0.52499998f, -0.19097102f, + 0.39177603f, 0.52499998f, -0.13160400f, + 0.40718701f, 0.52499998f, -0.06768297f, + 0.41250002f, 0.52499998f, 0.00000001f, + 0.00000000f, 0.51359999f, -0.48519999f, + 0.07961161f, 0.51359999f, -0.47895056f, + 0.15479822f, 0.51359999f, -0.46082360f, + 0.22462820f, 0.51359999f, -0.43175036f, + 0.28817001f, 0.51360005f, -0.39266267f, + 0.34449199f, 0.51359999f, -0.34449199f, + 0.39266264f, 0.51359999f, -0.28816998f, + 0.43175036f, 0.51359999f, -0.22462821f, + 0.46082354f, 0.51359999f, -0.15479821f, + 0.47895062f, 0.51359999f, -0.07961158f, + 0.48519999f, 0.51359999f, 0.00000001f, + 0.00000000f, 0.50129998f, -0.55009997f, + 0.09026040f, 0.50129992f, -0.54301465f, + 0.17550392f, 0.50129998f, -0.52246296f, + 0.25467429f, 0.50129998f, -0.48950094f, + 0.32671541f, 0.50129998f, -0.44518495f, + 0.39057100f, 0.50129998f, -0.39057100f, + 0.44518495f, 0.50129998f, -0.32671538f, + 0.48950097f, 0.50129998f, -0.25467432f, + 0.52246296f, 0.50129998f, -0.17550389f, + 0.54301471f, 0.50129998f, -0.09026036f, + 0.55009997f, 0.50129998f, 0.00000001f, + 0.00000000f, 0.48720002f, -0.60240000f, + 0.09884179f, 0.48719996f, -0.59464109f, + 0.19218971f, 0.48720005f, -0.57213551f, + 0.27888709f, 0.48719999f, -0.53603959f, + 0.35777742f, 0.48720002f, -0.48751029f, + 0.42770401f, 0.48720002f, -0.42770401f, + 0.48751026f, 0.48720002f, -0.35777742f, + 0.53603959f, 0.48720005f, -0.27888709f, + 0.57213545f, 0.48720002f, -0.19218969f, + 0.59464109f, 0.48720002f, -0.09884175f, + 0.60240000f, 0.48720002f, 0.00000001f, + 0.00000000f, 0.47040004f, -0.63730001f, + 0.10456818f, 0.47039998f, -0.62909156f, + 0.20332420f, 0.47040007f, -0.60528207f, + 0.29504442f, 0.47040001f, -0.56709504f, + 0.37850523f, 0.47040004f, -0.51575416f, + 0.45248300f, 0.47040004f, -0.45248300f, + 0.51575416f, 0.47040004f, -0.37850523f, + 0.56709504f, 0.47040004f, -0.29504442f, + 0.60528207f, 0.47040004f, -0.20332420f, + 0.62909162f, 0.47040004f, -0.10456815f, + 0.63730001f, 0.47040004f, 0.00000001f, + 0.00000000f, 0.45000005f, -0.64999998f, + 0.10665200f, 0.44999996f, -0.64162791f, + 0.20737600f, 0.45000011f, -0.61734402f, + 0.30092400f, 0.45000002f, -0.57839590f, + 0.38604802f, 0.45000008f, -0.52603197f, + 0.46149999f, 0.45000005f, -0.46149999f, + 0.52603197f, 0.45000005f, -0.38604796f, + 0.57839596f, 0.45000008f, -0.30092400f, + 0.61734402f, 0.45000005f, -0.20737599f, + 0.64162797f, 0.45000005f, -0.10665195f, + 0.64999998f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.60000002f, 0.10000001f, + -0.01640800f, 0.59999996f, 0.09871200f, + -0.03190400f, 0.60000008f, 0.09497602f, + -0.04629600f, 0.60000008f, 0.08898401f, + -0.05939200f, 0.60000002f, 0.08092801f, + -0.07100000f, 0.60000002f, 0.07100001f, + -0.08092801f, 0.60000002f, 0.05939201f, + -0.08898400f, 0.60000002f, 0.04629601f, + -0.09497601f, 0.60000002f, 0.03190401f, + -0.09871200f, 0.60000002f, 0.01640800f, + -0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.57959992f, 0.13970001f, + -0.02292197f, 0.57959986f, 0.13790067f, + -0.04456988f, 0.57959998f, 0.13268149f, + -0.06467551f, 0.57959992f, 0.12431065f, + -0.08297062f, 0.57959998f, 0.11305643f, + -0.09918699f, 0.57959992f, 0.09918701f, + -0.11305641f, 0.57959992f, 0.08297063f, + -0.12431064f, 0.57959998f, 0.06467552f, + -0.13268146f, 0.57959992f, 0.04456989f, + -0.13790065f, 0.57959992f, 0.02292197f, + -0.13969998f, 0.57959992f, 0.00000001f, + 0.00000000f, 0.56280005f, 0.19560002f, + -0.03209405f, 0.56279999f, 0.19308068f, + -0.06240423f, 0.56280011f, 0.18577307f, + -0.09055499f, 0.56280005f, 0.17405272f, + -0.11617076f, 0.56280005f, 0.15829518f, + -0.13887601f, 0.56280005f, 0.13887602f, + -0.15829518f, 0.56280005f, 0.11617076f, + -0.17405272f, 0.56280005f, 0.09055499f, + -0.18577307f, 0.56280005f, 0.06240423f, + -0.19308069f, 0.56280005f, 0.03209404f, + -0.19560002f, 0.56280005f, 0.00000001f, + 0.00000000f, 0.54869998f, 0.26289999f, + -0.04313663f, 0.54869998f, 0.25951380f, + -0.08387563f, 0.54869998f, 0.24969190f, + -0.12171219f, 0.54870003f, 0.23393893f, + -0.15614158f, 0.54869998f, 0.21275970f, + -0.18665899f, 0.54869998f, 0.18665901f, + -0.21275972f, 0.54869998f, 0.15614155f, + -0.23393893f, 0.54869998f, 0.12171219f, + -0.24969190f, 0.54869998f, 0.08387562f, + -0.25951385f, 0.54869998f, 0.04313662f, + -0.26289999f, 0.54869998f, 0.00000001f, + 0.00000000f, 0.53640002f, 0.33680001f, + -0.05526214f, 0.53639996f, 0.33246198f, + -0.10745268f, 0.53640002f, 0.31987920f, + -0.15592493f, 0.53640002f, 0.29969811f, + -0.20003228f, 0.53640002f, 0.27256551f, + -0.23912801f, 0.53640002f, 0.23912801f, + -0.27256551f, 0.53640002f, 0.20003226f, + -0.29969811f, 0.53640008f, 0.15592495f, + -0.31987917f, 0.53640008f, 0.10745268f, + -0.33246201f, 0.53640002f, 0.05526213f, + -0.33680001f, 0.53640002f, 0.00000001f, + 0.00000000f, 0.52499998f, 0.41250002f, + -0.06768300f, 0.52499992f, 0.40718701f, + -0.13160400f, 0.52499998f, 0.39177606f, + -0.19097100f, 0.52499998f, 0.36705902f, + -0.24499202f, 0.52499998f, 0.33382803f, + -0.29287499f, 0.52499998f, 0.29287499f, + -0.33382803f, 0.52499998f, 0.24499199f, + -0.36705902f, 0.52499998f, 0.19097102f, + -0.39177603f, 0.52499998f, 0.13160400f, + -0.40718701f, 0.52499998f, 0.06768298f, + -0.41250002f, 0.52499998f, 0.00000001f, + 0.00000000f, 0.51359999f, 0.48519999f, + -0.07961161f, 0.51359999f, 0.47895056f, + -0.15479822f, 0.51359999f, 0.46082360f, + -0.22462820f, 0.51359999f, 0.43175036f, + -0.28817001f, 0.51360005f, 0.39266267f, + -0.34449199f, 0.51359999f, 0.34449199f, + -0.39266264f, 0.51359999f, 0.28816998f, + -0.43175036f, 0.51359999f, 0.22462821f, + -0.46082354f, 0.51359999f, 0.15479821f, + -0.47895062f, 0.51359999f, 0.07961159f, + -0.48519999f, 0.51359999f, 0.00000001f, + 0.00000000f, 0.50129998f, 0.55009997f, + -0.09026040f, 0.50129992f, 0.54301465f, + -0.17550392f, 0.50129998f, 0.52246296f, + -0.25467429f, 0.50129998f, 0.48950094f, + -0.32671541f, 0.50129998f, 0.44518495f, + -0.39057100f, 0.50129998f, 0.39057100f, + -0.44518495f, 0.50129998f, 0.32671538f, + -0.48950097f, 0.50129998f, 0.25467432f, + -0.52246296f, 0.50129998f, 0.17550389f, + -0.54301471f, 0.50129998f, 0.09026038f, + -0.55009997f, 0.50129998f, 0.00000001f, + 0.00000000f, 0.48720002f, 0.60240000f, + -0.09884179f, 0.48719996f, 0.59464109f, + -0.19218971f, 0.48720005f, 0.57213551f, + -0.27888709f, 0.48719999f, 0.53603959f, + -0.35777742f, 0.48720002f, 0.48751029f, + -0.42770401f, 0.48720002f, 0.42770401f, + -0.48751026f, 0.48720002f, 0.35777742f, + -0.53603959f, 0.48720005f, 0.27888709f, + -0.57213545f, 0.48720002f, 0.19218969f, + -0.59464109f, 0.48720002f, 0.09884176f, + -0.60240000f, 0.48720002f, 0.00000001f, + 0.00000000f, 0.47040004f, 0.63730001f, + -0.10456818f, 0.47039998f, 0.62909156f, + -0.20332420f, 0.47040007f, 0.60528207f, + -0.29504442f, 0.47040001f, 0.56709504f, + -0.37850523f, 0.47040004f, 0.51575416f, + -0.45248300f, 0.47040004f, 0.45248300f, + -0.51575416f, 0.47040004f, 0.37850523f, + -0.56709504f, 0.47040004f, 0.29504442f, + -0.60528207f, 0.47040004f, 0.20332420f, + -0.62909162f, 0.47040004f, 0.10456816f, + -0.63730001f, 0.47040004f, 0.00000001f, + 0.00000000f, 0.45000005f, 0.64999998f, + -0.10665200f, 0.44999996f, 0.64162791f, + -0.20737600f, 0.45000011f, 0.61734402f, + -0.30092400f, 0.45000002f, 0.57839590f, + -0.38604802f, 0.45000008f, 0.52603197f, + -0.46149999f, 0.45000005f, 0.46149999f, + -0.52603197f, 0.45000005f, 0.38604796f, + -0.57839596f, 0.45000008f, 0.30092400f, + -0.61734402f, 0.45000005f, 0.20737599f, + -0.64162797f, 0.45000005f, 0.10665197f, + -0.64999998f, 0.45000005f, 0.00000001f, + -0.10000000f, 0.60000002f, 0.00000001f, + -0.09871199f, 0.59999996f, -0.01640799f, + -0.09497602f, 0.60000008f, -0.03190399f, + -0.08898400f, 0.60000008f, -0.04629599f, + -0.08092801f, 0.60000002f, -0.05939199f, + -0.07100000f, 0.60000002f, -0.07099999f, + -0.05939200f, 0.60000002f, -0.08092800f, + -0.04629600f, 0.60000002f, -0.08898400f, + -0.03190400f, 0.60000002f, -0.09497599f, + -0.01640799f, 0.60000002f, -0.09871200f, + 0.00000000f, 0.60000002f, -0.09999999f, + -0.13969998f, 0.57959992f, 0.00000001f, + -0.13790064f, 0.57959986f, -0.02292197f, + -0.13268146f, 0.57959998f, -0.04456988f, + -0.12431064f, 0.57959992f, -0.06467550f, + -0.11305641f, 0.57959998f, -0.08297062f, + -0.09918699f, 0.57959992f, -0.09918699f, + -0.08297062f, 0.57959992f, -0.11305641f, + -0.06467551f, 0.57959998f, -0.12431063f, + -0.04456988f, 0.57959992f, -0.13268146f, + -0.02292197f, 0.57959992f, -0.13790065f, + 0.00000000f, 0.57959992f, -0.13969998f, + -0.19560002f, 0.56280005f, 0.00000001f, + -0.19308068f, 0.56279999f, -0.03209404f, + -0.18577307f, 0.56280011f, -0.06240422f, + -0.17405272f, 0.56280005f, -0.09055497f, + -0.15829518f, 0.56280005f, -0.11617076f, + -0.13887602f, 0.56280005f, -0.13887601f, + -0.11617076f, 0.56280005f, -0.15829517f, + -0.09055499f, 0.56280005f, -0.17405272f, + -0.06240423f, 0.56280005f, -0.18577306f, + -0.03209404f, 0.56280005f, -0.19308066f, + 0.00000000f, 0.56280005f, -0.19560000f, + -0.26289999f, 0.54869998f, 0.00000001f, + -0.25951380f, 0.54869998f, -0.04313662f, + -0.24969190f, 0.54869998f, -0.08387562f, + -0.23393893f, 0.54870003f, -0.12171219f, + -0.21275970f, 0.54869998f, -0.15614156f, + -0.18665901f, 0.54869998f, -0.18665899f, + -0.15614155f, 0.54869998f, -0.21275972f, + -0.12171219f, 0.54869998f, -0.23393893f, + -0.08387562f, 0.54869998f, -0.24969190f, + -0.04313662f, 0.54869998f, -0.25951385f, + 0.00000000f, 0.54869998f, -0.26289999f, + -0.33680001f, 0.53640002f, 0.00000001f, + -0.33246198f, 0.53639996f, -0.05526214f, + -0.31987920f, 0.53640002f, -0.10745268f, + -0.29969811f, 0.53640002f, -0.15592493f, + -0.27256551f, 0.53640002f, -0.20003226f, + -0.23912801f, 0.53640002f, -0.23912801f, + -0.20003226f, 0.53640002f, -0.27256551f, + -0.15592495f, 0.53640008f, -0.29969811f, + -0.10745268f, 0.53640008f, -0.31987917f, + -0.05526213f, 0.53640002f, -0.33246201f, + 0.00000000f, 0.53640002f, -0.33680001f, + -0.41250002f, 0.52499998f, 0.00000001f, + -0.40718701f, 0.52499992f, -0.06768300f, + -0.39177606f, 0.52499998f, -0.13160400f, + -0.36705902f, 0.52499998f, -0.19097100f, + -0.33382803f, 0.52499998f, -0.24499202f, + -0.29287499f, 0.52499998f, -0.29287499f, + -0.24499199f, 0.52499998f, -0.33382803f, + -0.19097102f, 0.52499998f, -0.36705902f, + -0.13160400f, 0.52499998f, -0.39177603f, + -0.06768297f, 0.52499998f, -0.40718701f, + 0.00000000f, 0.52499998f, -0.41250002f, + -0.48519999f, 0.51359999f, 0.00000001f, + -0.47895056f, 0.51359999f, -0.07961161f, + -0.46082360f, 0.51359999f, -0.15479822f, + -0.43175036f, 0.51359999f, -0.22462820f, + -0.39266267f, 0.51360005f, -0.28817001f, + -0.34449199f, 0.51359999f, -0.34449199f, + -0.28816998f, 0.51359999f, -0.39266264f, + -0.22462821f, 0.51359999f, -0.43175036f, + -0.15479821f, 0.51359999f, -0.46082354f, + -0.07961158f, 0.51359999f, -0.47895062f, + 0.00000000f, 0.51359999f, -0.48519999f, + -0.55009997f, 0.50129998f, 0.00000001f, + -0.54301465f, 0.50129992f, -0.09026039f, + -0.52246296f, 0.50129998f, -0.17550392f, + -0.48950094f, 0.50129998f, -0.25467429f, + -0.44518495f, 0.50129998f, -0.32671541f, + -0.39057100f, 0.50129998f, -0.39057100f, + -0.32671538f, 0.50129998f, -0.44518495f, + -0.25467432f, 0.50129998f, -0.48950097f, + -0.17550389f, 0.50129998f, -0.52246296f, + -0.09026037f, 0.50129998f, -0.54301471f, + 0.00000000f, 0.50129998f, -0.55009997f, + -0.60240000f, 0.48720002f, 0.00000001f, + -0.59464109f, 0.48719996f, -0.09884178f, + -0.57213551f, 0.48720005f, -0.19218971f, + -0.53603959f, 0.48719999f, -0.27888709f, + -0.48751029f, 0.48720002f, -0.35777742f, + -0.42770401f, 0.48720002f, -0.42770401f, + -0.35777742f, 0.48720002f, -0.48751026f, + -0.27888709f, 0.48720005f, -0.53603959f, + -0.19218969f, 0.48720002f, -0.57213545f, + -0.09884176f, 0.48720002f, -0.59464109f, + 0.00000000f, 0.48720002f, -0.60240000f, + -0.63730001f, 0.47040004f, 0.00000001f, + -0.62909156f, 0.47039998f, -0.10456817f, + -0.60528207f, 0.47040007f, -0.20332420f, + -0.56709504f, 0.47040001f, -0.29504442f, + -0.51575416f, 0.47040004f, -0.37850523f, + -0.45248300f, 0.47040004f, -0.45248300f, + -0.37850523f, 0.47040004f, -0.51575416f, + -0.29504442f, 0.47040004f, -0.56709504f, + -0.20332420f, 0.47040004f, -0.60528207f, + -0.10456815f, 0.47040004f, -0.62909162f, + 0.00000000f, 0.47040004f, -0.63730001f, + -0.64999998f, 0.45000005f, 0.00000001f, + -0.64162791f, 0.44999996f, -0.10665199f, + -0.61734402f, 0.45000011f, -0.20737600f, + -0.57839590f, 0.45000002f, -0.30092400f, + -0.52603197f, 0.45000008f, -0.38604802f, + -0.46149999f, 0.45000005f, -0.46149999f, + -0.38604796f, 0.45000005f, -0.52603197f, + -0.30092400f, 0.45000008f, -0.57839596f, + -0.20737599f, 0.45000005f, -0.61734402f, + -0.10665196f, 0.45000005f, -0.64162797f, + 0.00000000f, 0.45000005f, -0.64999998f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.74891251f, 0.19413748f, + 0.03185407f, -0.74891245f, 0.19163698f, + 0.06193763f, -0.74891251f, 0.18438403f, + 0.08987789f, -0.74891245f, 0.17275129f, + 0.11530213f, -0.74891257f, 0.15711159f, + 0.13783762f, -0.74891251f, 0.13783762f, + 0.15711159f, -0.74891251f, 0.11530213f, + 0.17275131f, -0.74891257f, 0.08987789f, + 0.18438402f, -0.74891257f, 0.06193761f, + 0.19163698f, -0.74891251f, 0.03185406f, + 0.19413748f, -0.74891251f, -0.00000001f, + 0.00000000f, -0.74580008f, 0.35160002f, + 0.05769053f, -0.74579996f, 0.34707141f, + 0.11217448f, -0.74580014f, 0.33393562f, + 0.16277674f, -0.74580008f, 0.31286776f, + 0.20882230f, -0.74580008f, 0.28454286f, + 0.24963601f, -0.74580008f, 0.24963601f, + 0.28454286f, -0.74580008f, 0.20882228f, + 0.31286776f, -0.74580008f, 0.16277674f, + 0.33393565f, -0.74580008f, 0.11217446f, + 0.34707141f, -0.74580002f, 0.05769050f, + 0.35160002f, -0.74580008f, -0.00000001f, + 0.00000000f, -0.74088752f, 0.47621247f, + 0.07813694f, -0.74088746f, 0.47007880f, + 0.15193085f, -0.74088758f, 0.45228758f, + 0.22046733f, -0.74088752f, 0.42375290f, + 0.28283215f, -0.74088758f, 0.38538927f, + 0.33811086f, -0.74088752f, 0.33811086f, + 0.38538924f, -0.74088752f, 0.28283212f, + 0.42375290f, -0.74088752f, 0.22046733f, + 0.45228755f, -0.74088752f, 0.15193082f, + 0.47007886f, -0.74088752f, 0.07813691f, + 0.47621247f, -0.74088752f, -0.00000001f, + 0.00000000f, -0.73440003f, 0.57179999f, + 0.09382094f, -0.73439997f, 0.56443512f, + 0.18242709f, -0.73440009f, 0.54307282f, + 0.26472056f, -0.73440003f, 0.50881052f, + 0.33960345f, -0.73440003f, 0.46274635f, + 0.40597799f, -0.73440003f, 0.40597799f, + 0.46274632f, -0.73440009f, 0.33960345f, + 0.50881052f, -0.73440003f, 0.26472053f, + 0.54307282f, -0.73440003f, 0.18242708f, + 0.56443524f, -0.73440003f, 0.09382091f, + 0.57179999f, -0.73440003f, -0.00000001f, + 0.00000000f, -0.72656250f, 0.64218748f, + 0.10537013f, -0.72656244f, 0.63391608f, + 0.20488352f, -0.72656256f, 0.60992402f, + 0.29730710f, -0.72656256f, 0.57144409f, + 0.38140801f, -0.72656256f, 0.51970953f, + 0.45595312f, -0.72656250f, 0.45595312f, + 0.51970953f, -0.72656250f, 0.38140798f, + 0.57144415f, -0.72656250f, 0.29730713f, + 0.60992396f, -0.72656250f, 0.20488349f, + 0.63391614f, -0.72656250f, 0.10537008f, + 0.64218748f, -0.72656250f, -0.00000001f, + 0.00000000f, -0.71759999f, 0.69119996f, + 0.11341208f, -0.71759993f, 0.68229729f, + 0.22052045f, -0.71760005f, 0.65647405f, + 0.31999791f, -0.71759999f, 0.61505735f, + 0.41051748f, -0.71759999f, 0.55937433f, + 0.49075195f, -0.71759999f, 0.49075198f, + 0.55937433f, -0.71759999f, 0.41051745f, + 0.61505735f, -0.71759999f, 0.31999797f, + 0.65647411f, -0.71759999f, 0.22052044f, + 0.68229735f, -0.71759999f, 0.11341204f, + 0.69119996f, -0.71759999f, -0.00000001f, + 0.00000000f, -0.70773757f, 0.72266257f, + 0.11857446f, -0.70773745f, 0.71335465f, + 0.23055826f, -0.70773757f, 0.68635601f, + 0.33456385f, -0.70773745f, 0.64305401f, + 0.42920375f, -0.70773751f, 0.58483636f, + 0.51309043f, -0.70773751f, 0.51309037f, + 0.58483636f, -0.70773751f, 0.42920372f, + 0.64305407f, -0.70773757f, 0.33456385f, + 0.68635601f, -0.70773751f, 0.23055825f, + 0.71335471f, -0.70773751f, 0.11857441f, + 0.72266257f, -0.70773751f, -0.00000001f, + 0.00000000f, -0.69720000f, 0.74039996f, + 0.12148482f, -0.69719994f, 0.73086351f, + 0.23621722f, -0.69720006f, 0.70320231f, + 0.34277558f, -0.69720000f, 0.65883750f, + 0.43973836f, -0.69720006f, 0.59919089f, + 0.52568400f, -0.69719994f, 0.52568400f, + 0.59919089f, -0.69720006f, 0.43973833f, + 0.65883750f, -0.69720000f, 0.34277558f, + 0.70320225f, -0.69720006f, 0.23621720f, + 0.73086363f, -0.69720000f, 0.12148478f, + 0.74039996f, -0.69720000f, -0.00000001f, + 0.00000000f, -0.68621248f, 0.74823749f, + 0.12277080f, -0.68621248f, 0.73860013f, + 0.23871772f, -0.68621254f, 0.71064609f, + 0.34640405f, -0.68621248f, 0.66581166f, + 0.44439322f, -0.68621248f, 0.60553366f, + 0.53124863f, -0.68621248f, 0.53124863f, + 0.60553366f, -0.68621248f, 0.44439319f, + 0.66581166f, -0.68621254f, 0.34640405f, + 0.71064603f, -0.68621254f, 0.23871769f, + 0.73860019f, -0.68621248f, 0.12277076f, + 0.74823749f, -0.68621248f, -0.00000001f, + 0.00000000f, -0.67500001f, 0.75000000f, + 0.12305999f, -0.67499995f, 0.74033999f, + 0.23928000f, -0.67500007f, 0.71232003f, + 0.34721997f, -0.67500001f, 0.66737998f, + 0.44544002f, -0.67500007f, 0.60696000f, + 0.53250003f, -0.67500007f, 0.53250003f, + 0.60696000f, -0.67500001f, 0.44543999f, + 0.66738003f, -0.67500001f, 0.34722000f, + 0.71231997f, -0.67500001f, 0.23927999f, + 0.74033999f, -0.67500001f, 0.12305995f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.19413748f, -0.74891251f, -0.00000001f, + 0.19163698f, -0.74891245f, -0.03185408f, + 0.18438403f, -0.74891251f, -0.06193763f, + 0.17275129f, -0.74891245f, -0.08987790f, + 0.15711159f, -0.74891257f, -0.11530215f, + 0.13783762f, -0.74891251f, -0.13783762f, + 0.11530213f, -0.74891251f, -0.15711159f, + 0.08987789f, -0.74891257f, -0.17275131f, + 0.06193762f, -0.74891257f, -0.18438402f, + 0.03185407f, -0.74891251f, -0.19163698f, + 0.00000000f, -0.74891251f, -0.19413748f, + 0.35160002f, -0.74580008f, -0.00000001f, + 0.34707141f, -0.74579996f, -0.05769053f, + 0.33393562f, -0.74580014f, -0.11217449f, + 0.31286776f, -0.74580008f, -0.16277674f, + 0.28454286f, -0.74580008f, -0.20882230f, + 0.24963601f, -0.74580008f, -0.24963601f, + 0.20882228f, -0.74580008f, -0.28454286f, + 0.16277674f, -0.74580008f, -0.31286776f, + 0.11217447f, -0.74580008f, -0.33393565f, + 0.05769051f, -0.74580002f, -0.34707141f, + 0.00000000f, -0.74580008f, -0.35160002f, + 0.47621247f, -0.74088752f, -0.00000001f, + 0.47007880f, -0.74088746f, -0.07813694f, + 0.45228758f, -0.74088758f, -0.15193085f, + 0.42375290f, -0.74088752f, -0.22046733f, + 0.38538927f, -0.74088758f, -0.28283215f, + 0.33811086f, -0.74088752f, -0.33811086f, + 0.28283212f, -0.74088752f, -0.38538924f, + 0.22046733f, -0.74088752f, -0.42375290f, + 0.15193082f, -0.74088752f, -0.45228755f, + 0.07813691f, -0.74088752f, -0.47007886f, + 0.00000000f, -0.74088752f, -0.47621247f, + 0.57179999f, -0.73440003f, -0.00000001f, + 0.56443512f, -0.73439997f, -0.09382095f, + 0.54307282f, -0.73440009f, -0.18242709f, + 0.50881052f, -0.73440003f, -0.26472056f, + 0.46274635f, -0.73440003f, -0.33960345f, + 0.40597799f, -0.73440003f, -0.40597799f, + 0.33960345f, -0.73440009f, -0.46274632f, + 0.26472053f, -0.73440003f, -0.50881052f, + 0.18242708f, -0.73440003f, -0.54307282f, + 0.09382091f, -0.73440003f, -0.56443524f, + 0.00000000f, -0.73440003f, -0.57179999f, + 0.64218748f, -0.72656250f, -0.00000001f, + 0.63391608f, -0.72656244f, -0.10537013f, + 0.60992402f, -0.72656256f, -0.20488352f, + 0.57144409f, -0.72656256f, -0.29730710f, + 0.51970953f, -0.72656256f, -0.38140801f, + 0.45595312f, -0.72656250f, -0.45595312f, + 0.38140798f, -0.72656250f, -0.51970953f, + 0.29730713f, -0.72656250f, -0.57144415f, + 0.20488349f, -0.72656250f, -0.60992396f, + 0.10537009f, -0.72656250f, -0.63391614f, + 0.00000000f, -0.72656250f, -0.64218748f, + 0.69119996f, -0.71759999f, -0.00000001f, + 0.68229729f, -0.71759993f, -0.11341209f, + 0.65647405f, -0.71760005f, -0.22052045f, + 0.61505735f, -0.71759999f, -0.31999791f, + 0.55937433f, -0.71759999f, -0.41051748f, + 0.49075198f, -0.71759999f, -0.49075195f, + 0.41051745f, -0.71759999f, -0.55937433f, + 0.31999797f, -0.71759999f, -0.61505735f, + 0.22052044f, -0.71759999f, -0.65647411f, + 0.11341204f, -0.71759999f, -0.68229735f, + 0.00000000f, -0.71759999f, -0.69119996f, + 0.72266257f, -0.70773751f, -0.00000001f, + 0.71335465f, -0.70773745f, -0.11857446f, + 0.68635601f, -0.70773757f, -0.23055826f, + 0.64305401f, -0.70773745f, -0.33456385f, + 0.58483636f, -0.70773751f, -0.42920375f, + 0.51309037f, -0.70773751f, -0.51309043f, + 0.42920372f, -0.70773751f, -0.58483636f, + 0.33456385f, -0.70773751f, -0.64305407f, + 0.23055825f, -0.70773751f, -0.68635601f, + 0.11857442f, -0.70773751f, -0.71335471f, + 0.00000000f, -0.70773751f, -0.72266257f, + 0.74039996f, -0.69720000f, -0.00000001f, + 0.73086351f, -0.69719994f, -0.12148483f, + 0.70320231f, -0.69720006f, -0.23621722f, + 0.65883750f, -0.69720000f, -0.34277558f, + 0.59919089f, -0.69720006f, -0.43973836f, + 0.52568400f, -0.69719994f, -0.52568400f, + 0.43973833f, -0.69720006f, -0.59919089f, + 0.34277558f, -0.69720000f, -0.65883750f, + 0.23621720f, -0.69720006f, -0.70320225f, + 0.12148479f, -0.69720000f, -0.73086363f, + 0.00000000f, -0.69720000f, -0.74039996f, + 0.74823749f, -0.68621248f, -0.00000001f, + 0.73860013f, -0.68621248f, -0.12277081f, + 0.71064609f, -0.68621254f, -0.23871772f, + 0.66581166f, -0.68621248f, -0.34640405f, + 0.60553366f, -0.68621248f, -0.44439322f, + 0.53124863f, -0.68621248f, -0.53124863f, + 0.44439319f, -0.68621248f, -0.60553366f, + 0.34640405f, -0.68621254f, -0.66581166f, + 0.23871769f, -0.68621254f, -0.71064603f, + 0.12277076f, -0.68621248f, -0.73860019f, + 0.00000000f, -0.68621248f, -0.74823749f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.74033999f, -0.67499995f, -0.12306000f, + 0.71232003f, -0.67500007f, -0.23928000f, + 0.66737998f, -0.67500001f, -0.34721997f, + 0.60696000f, -0.67500007f, -0.44544002f, + 0.53250003f, -0.67500007f, -0.53250003f, + 0.44543999f, -0.67500001f, -0.60696000f, + 0.34722000f, -0.67500001f, -0.66738003f, + 0.23927999f, -0.67500001f, -0.71231997f, + 0.12305996f, -0.67500001f, -0.74033999f, + 0.00000000f, -0.67500001f, -0.75000000f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + -0.19413748f, -0.74891251f, -0.00000001f, + -0.19163698f, -0.74891245f, 0.03185407f, + -0.18438403f, -0.74891251f, 0.06193762f, + -0.17275129f, -0.74891245f, 0.08987788f, + -0.15711159f, -0.74891257f, 0.11530213f, + -0.13783762f, -0.74891251f, 0.13783762f, + -0.11530213f, -0.74891251f, 0.15711159f, + -0.08987789f, -0.74891257f, 0.17275131f, + -0.06193762f, -0.74891257f, 0.18438402f, + -0.03185407f, -0.74891251f, 0.19163698f, + 0.00000000f, -0.74891251f, 0.19413748f, + -0.35160002f, -0.74580008f, -0.00000001f, + -0.34707141f, -0.74579996f, 0.05769052f, + -0.33393562f, -0.74580014f, 0.11217447f, + -0.31286776f, -0.74580008f, 0.16277674f, + -0.28454286f, -0.74580008f, 0.20882230f, + -0.24963601f, -0.74580008f, 0.24963601f, + -0.20882228f, -0.74580008f, 0.28454286f, + -0.16277674f, -0.74580008f, 0.31286776f, + -0.11217447f, -0.74580008f, 0.33393565f, + -0.05769051f, -0.74580002f, 0.34707141f, + 0.00000000f, -0.74580008f, 0.35160002f, + -0.47621247f, -0.74088752f, -0.00000001f, + -0.47007880f, -0.74088746f, 0.07813693f, + -0.45228758f, -0.74088758f, 0.15193084f, + -0.42375290f, -0.74088752f, 0.22046733f, + -0.38538927f, -0.74088758f, 0.28283215f, + -0.33811086f, -0.74088752f, 0.33811086f, + -0.28283212f, -0.74088752f, 0.38538924f, + -0.22046733f, -0.74088752f, 0.42375290f, + -0.15193082f, -0.74088752f, 0.45228755f, + -0.07813691f, -0.74088752f, 0.47007886f, + 0.00000000f, -0.74088752f, 0.47621247f, + -0.57179999f, -0.73440003f, -0.00000001f, + -0.56443512f, -0.73439997f, 0.09382094f, + -0.54307282f, -0.73440009f, 0.18242709f, + -0.50881052f, -0.73440003f, 0.26472056f, + -0.46274635f, -0.73440003f, 0.33960345f, + -0.40597799f, -0.73440003f, 0.40597799f, + -0.33960345f, -0.73440009f, 0.46274632f, + -0.26472053f, -0.73440003f, 0.50881052f, + -0.18242708f, -0.73440003f, 0.54307282f, + -0.09382091f, -0.73440003f, 0.56443524f, + 0.00000000f, -0.73440003f, 0.57179999f, + -0.64218748f, -0.72656250f, -0.00000001f, + -0.63391608f, -0.72656244f, 0.10537011f, + -0.60992402f, -0.72656256f, 0.20488352f, + -0.57144409f, -0.72656256f, 0.29730710f, + -0.51970953f, -0.72656256f, 0.38140801f, + -0.45595312f, -0.72656250f, 0.45595312f, + -0.38140798f, -0.72656250f, 0.51970953f, + -0.29730713f, -0.72656250f, 0.57144415f, + -0.20488349f, -0.72656250f, 0.60992396f, + -0.10537009f, -0.72656250f, 0.63391614f, + 0.00000000f, -0.72656250f, 0.64218748f, + -0.69119996f, -0.71759999f, -0.00000001f, + -0.68229729f, -0.71759993f, 0.11341207f, + -0.65647405f, -0.71760005f, 0.22052045f, + -0.61505735f, -0.71759999f, 0.31999791f, + -0.55937433f, -0.71759999f, 0.41051748f, + -0.49075198f, -0.71759999f, 0.49075195f, + -0.41051745f, -0.71759999f, 0.55937433f, + -0.31999797f, -0.71759999f, 0.61505735f, + -0.22052044f, -0.71759999f, 0.65647411f, + -0.11341204f, -0.71759999f, 0.68229735f, + 0.00000000f, -0.71759999f, 0.69119996f, + -0.72266257f, -0.70773751f, -0.00000001f, + -0.71335465f, -0.70773745f, 0.11857444f, + -0.68635601f, -0.70773757f, 0.23055826f, + -0.64305401f, -0.70773751f, 0.33456385f, + -0.58483636f, -0.70773751f, 0.42920375f, + -0.51309037f, -0.70773751f, 0.51309043f, + -0.42920372f, -0.70773751f, 0.58483636f, + -0.33456385f, -0.70773757f, 0.64305407f, + -0.23055825f, -0.70773757f, 0.68635601f, + -0.11857442f, -0.70773757f, 0.71335471f, + 0.00000000f, -0.70773757f, 0.72266257f, + -0.74039996f, -0.69720000f, -0.00000001f, + -0.73086351f, -0.69719994f, 0.12148482f, + -0.70320231f, -0.69720006f, 0.23621722f, + -0.65883750f, -0.69720000f, 0.34277558f, + -0.59919089f, -0.69720006f, 0.43973836f, + -0.52568400f, -0.69719994f, 0.52568400f, + -0.43973833f, -0.69720006f, 0.59919089f, + -0.34277558f, -0.69720000f, 0.65883750f, + -0.23621720f, -0.69720006f, 0.70320225f, + -0.12148479f, -0.69720000f, 0.73086363f, + 0.00000000f, -0.69720000f, 0.74039996f, + -0.74823749f, -0.68621248f, -0.00000001f, + -0.73860013f, -0.68621248f, 0.12277079f, + -0.71064609f, -0.68621254f, 0.23871772f, + -0.66581166f, -0.68621248f, 0.34640405f, + -0.60553366f, -0.68621248f, 0.44439322f, + -0.53124863f, -0.68621248f, 0.53124863f, + -0.44439319f, -0.68621248f, 0.60553366f, + -0.34640405f, -0.68621254f, 0.66581166f, + -0.23871769f, -0.68621254f, 0.71064603f, + -0.12277076f, -0.68621248f, 0.73860019f, + 0.00000000f, -0.68621248f, 0.74823749f, + -0.75000000f, -0.67500001f, -0.00000001f, + -0.74033999f, -0.67499995f, 0.12305998f, + -0.71232003f, -0.67500007f, 0.23928000f, + -0.66737998f, -0.67500001f, 0.34721997f, + -0.60696000f, -0.67500007f, 0.44544002f, + -0.53250003f, -0.67500007f, 0.53250003f, + -0.44543999f, -0.67500001f, 0.60696000f, + -0.34722000f, -0.67500001f, 0.66738003f, + -0.23927999f, -0.67500001f, 0.71231997f, + -0.12305996f, -0.67500001f, 0.74033999f, + 0.00000000f, -0.67500001f, 0.75000000f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.74891251f, -0.19413748f, + -0.03185407f, -0.74891245f, -0.19163698f, + -0.06193763f, -0.74891251f, -0.18438403f, + -0.08987789f, -0.74891245f, -0.17275129f, + -0.11530213f, -0.74891257f, -0.15711159f, + -0.13783762f, -0.74891251f, -0.13783762f, + -0.15711159f, -0.74891251f, -0.11530213f, + -0.17275131f, -0.74891257f, -0.08987790f, + -0.18438402f, -0.74891257f, -0.06193763f, + -0.19163698f, -0.74891251f, -0.03185407f, + -0.19413748f, -0.74891251f, -0.00000001f, + 0.00000000f, -0.74580008f, -0.35160002f, + -0.05769053f, -0.74579996f, -0.34707141f, + -0.11217448f, -0.74580014f, -0.33393562f, + -0.16277674f, -0.74580008f, -0.31286776f, + -0.20882230f, -0.74580008f, -0.28454286f, + -0.24963601f, -0.74580008f, -0.24963601f, + -0.28454286f, -0.74580008f, -0.20882228f, + -0.31286776f, -0.74580008f, -0.16277674f, + -0.33393565f, -0.74580008f, -0.11217447f, + -0.34707141f, -0.74580002f, -0.05769052f, + -0.35160002f, -0.74580008f, -0.00000001f, + 0.00000000f, -0.74088752f, -0.47621247f, + -0.07813694f, -0.74088746f, -0.47007880f, + -0.15193085f, -0.74088758f, -0.45228758f, + -0.22046733f, -0.74088752f, -0.42375290f, + -0.28283215f, -0.74088758f, -0.38538927f, + -0.33811086f, -0.74088752f, -0.33811086f, + -0.38538924f, -0.74088752f, -0.28283212f, + -0.42375290f, -0.74088752f, -0.22046733f, + -0.45228755f, -0.74088752f, -0.15193082f, + -0.47007886f, -0.74088752f, -0.07813692f, + -0.47621247f, -0.74088752f, -0.00000001f, + 0.00000000f, -0.73440003f, -0.57179999f, + -0.09382094f, -0.73439997f, -0.56443512f, + -0.18242709f, -0.73440009f, -0.54307282f, + -0.26472056f, -0.73440003f, -0.50881052f, + -0.33960345f, -0.73440003f, -0.46274635f, + -0.40597799f, -0.73440003f, -0.40597799f, + -0.46274632f, -0.73440009f, -0.33960345f, + -0.50881052f, -0.73440003f, -0.26472053f, + -0.54307282f, -0.73440003f, -0.18242708f, + -0.56443524f, -0.73440003f, -0.09382092f, + -0.57179999f, -0.73440003f, -0.00000001f, + 0.00000000f, -0.72656250f, -0.64218748f, + -0.10537013f, -0.72656244f, -0.63391608f, + -0.20488352f, -0.72656256f, -0.60992402f, + -0.29730710f, -0.72656256f, -0.57144409f, + -0.38140801f, -0.72656256f, -0.51970953f, + -0.45595312f, -0.72656250f, -0.45595312f, + -0.51970953f, -0.72656250f, -0.38140798f, + -0.57144415f, -0.72656250f, -0.29730713f, + -0.60992396f, -0.72656250f, -0.20488349f, + -0.63391614f, -0.72656250f, -0.10537010f, + -0.64218748f, -0.72656250f, -0.00000001f, + 0.00000000f, -0.71759999f, -0.69119996f, + -0.11341208f, -0.71759993f, -0.68229729f, + -0.22052045f, -0.71760005f, -0.65647405f, + -0.31999791f, -0.71759999f, -0.61505735f, + -0.41051748f, -0.71759999f, -0.55937433f, + -0.49075195f, -0.71759999f, -0.49075198f, + -0.55937433f, -0.71759999f, -0.41051745f, + -0.61505735f, -0.71759999f, -0.31999797f, + -0.65647411f, -0.71759999f, -0.22052044f, + -0.68229735f, -0.71759999f, -0.11341205f, + -0.69119996f, -0.71759999f, -0.00000001f, + 0.00000000f, -0.70773751f, -0.72266257f, + -0.11857446f, -0.70773745f, -0.71335465f, + -0.23055826f, -0.70773757f, -0.68635601f, + -0.33456385f, -0.70773745f, -0.64305401f, + -0.42920375f, -0.70773751f, -0.58483636f, + -0.51309043f, -0.70773751f, -0.51309037f, + -0.58483636f, -0.70773751f, -0.42920372f, + -0.64305407f, -0.70773751f, -0.33456385f, + -0.68635601f, -0.70773751f, -0.23055825f, + -0.71335471f, -0.70773751f, -0.11857443f, + -0.72266257f, -0.70773751f, -0.00000001f, + 0.00000000f, -0.69720000f, -0.74039996f, + -0.12148482f, -0.69719994f, -0.73086351f, + -0.23621722f, -0.69720006f, -0.70320231f, + -0.34277558f, -0.69720000f, -0.65883750f, + -0.43973836f, -0.69720006f, -0.59919089f, + -0.52568400f, -0.69719994f, -0.52568400f, + -0.59919089f, -0.69720006f, -0.43973833f, + -0.65883750f, -0.69720000f, -0.34277558f, + -0.70320225f, -0.69720006f, -0.23621720f, + -0.73086363f, -0.69720000f, -0.12148479f, + -0.74039996f, -0.69720000f, -0.00000001f, + 0.00000000f, -0.68621248f, -0.74823749f, + -0.12277080f, -0.68621248f, -0.73860013f, + -0.23871772f, -0.68621254f, -0.71064609f, + -0.34640405f, -0.68621248f, -0.66581166f, + -0.44439322f, -0.68621248f, -0.60553366f, + -0.53124863f, -0.68621248f, -0.53124863f, + -0.60553366f, -0.68621248f, -0.44439319f, + -0.66581166f, -0.68621254f, -0.34640405f, + -0.71064603f, -0.68621254f, -0.23871769f, + -0.73860019f, -0.68621248f, -0.12277077f, + -0.74823749f, -0.68621248f, -0.00000001f, + 0.00000000f, -0.67500001f, -0.75000000f, + -0.12305999f, -0.67499995f, -0.74033999f, + -0.23928000f, -0.67500007f, -0.71232003f, + -0.34721997f, -0.67500001f, -0.66737998f, + -0.44544002f, -0.67500007f, -0.60696000f, + -0.53250003f, -0.67500007f, -0.53250003f, + -0.60696000f, -0.67500001f, -0.44543999f, + -0.66738003f, -0.67500001f, -0.34722000f, + -0.71231997f, -0.67500001f, -0.23927999f, + -0.74033999f, -0.67500001f, -0.12305997f, + -0.75000000f, -0.67500001f, -0.00000001f, + -0.80000001f, 0.26250005f, 0.00000000f, + -0.79859996f, 0.26565003f, 0.04050000f, + -0.79480010f, 0.27420005f, 0.07200000f, + -0.78920001f, 0.28680006f, 0.09450001f, + -0.78240001f, 0.30210006f, 0.10800001f, + -0.77499998f, 0.31875002f, 0.11250000f, + -0.76760000f, 0.33540002f, 0.10800000f, + -0.76080006f, 0.35070002f, 0.09450001f, + -0.75519997f, 0.36330000f, 0.07200000f, + -0.75139999f, 0.37185001f, 0.04049999f, + -0.75000000f, 0.37500000f, 0.00000000f, + -0.90044993f, 0.26238751f, 0.00000000f, + -0.90022725f, 0.26553434f, 0.04050000f, + -0.89962322f, 0.27407584f, 0.07200000f, + -0.89873272f, 0.28666320f, 0.09449999f, + -0.89765161f, 0.30194792f, 0.10800000f, + -0.89647496f, 0.31858125f, 0.11250000f, + -0.89529836f, 0.33521461f, 0.10800000f, + -0.89421713f, 0.35049930f, 0.09449999f, + -0.89332676f, 0.36308670f, 0.07200000f, + -0.89272249f, 0.37162814f, 0.04049999f, + -0.89249992f, 0.37477499f, 0.00000000f, + -0.99160004f, 0.26160005f, 0.00000000f, + -0.99239522f, 0.26472485f, 0.04050001f, + -0.99455369f, 0.27320647f, 0.07200002f, + -0.99773449f, 0.28570563f, 0.09450001f, + -1.00159693f, 0.30088326f, 0.10800002f, + -1.00580013f, 0.31740004f, 0.11250001f, + -1.01000333f, 0.33391684f, 0.10800001f, + -1.01386571f, 0.34909445f, 0.09450001f, + -1.01704645f, 0.36159366f, 0.07200001f, + -1.01920485f, 0.37007523f, 0.04050000f, + -1.02000010f, 0.37320003f, 0.00000000f, + -1.07315004f, 0.25946254f, 0.00000000f, + -1.07481182f, 0.26252747f, 0.04050001f, + -1.07932246f, 0.27084666f, 0.07200002f, + -1.08596969f, 0.28310645f, 0.09450001f, + -1.09404123f, 0.29799330f, 0.10800002f, + -1.10282505f, 0.31419379f, 0.11250001f, + -1.11160886f, 0.33039424f, 0.10800001f, + -1.11968040f, 0.34528112f, 0.09450001f, + -1.12632775f, 0.35754091f, 0.07200001f, + -1.13083827f, 0.36586004f, 0.04050000f, + -1.13250005f, 0.36892501f, 0.00000000f, + -1.14480007f, 0.25530005f, 0.00000000f, + -1.14718556f, 0.25824845f, 0.04050000f, + -1.15366089f, 0.26625127f, 0.07200000f, + -1.16320336f, 0.27804485f, 0.09450001f, + -1.17479050f, 0.29236567f, 0.10800001f, + -1.18740010f, 0.30795002f, 0.11250000f, + -1.20000958f, 0.32353443f, 0.10800000f, + -1.21159685f, 0.33785522f, 0.09450001f, + -1.22113919f, 0.34964880f, 0.07200000f, + -1.22761440f, 0.35765159f, 0.04049999f, + -1.23000002f, 0.36059999f, 0.00000000f, + -1.20625007f, 0.24843754f, 0.00000000f, + -1.20922494f, 0.25119376f, 0.04050000f, + -1.21730018f, 0.25867507f, 0.07200000f, + -1.22920001f, 0.26970002f, 0.09450001f, + -1.24365008f, 0.28308752f, 0.10800001f, + -1.25937510f, 0.29765627f, 0.11250000f, + -1.27509999f, 0.31222504f, 0.10800000f, + -1.28955007f, 0.32561252f, 0.09450001f, + -1.30145001f, 0.33663750f, 0.07200000f, + -1.30952501f, 0.34411874f, 0.04049999f, + -1.31250000f, 0.34687501f, 0.00000000f, + -1.25720000f, 0.23820004f, 0.00000000f, + -1.26063836f, 0.24066961f, 0.04050000f, + -1.26997125f, 0.24737285f, 0.07200000f, + -1.28372490f, 0.25725123f, 0.09450001f, + -1.30042577f, 0.26924643f, 0.10800001f, + -1.31860006f, 0.28230003f, 0.11250000f, + -1.33677435f, 0.29535359f, 0.10800000f, + -1.35347521f, 0.30734879f, 0.09450001f, + -1.36722875f, 0.31722721f, 0.07200000f, + -1.37656152f, 0.32393038f, 0.04049999f, + -1.38000000f, 0.32639998f, 0.00000000f, + -1.29735005f, 0.22391254f, 0.00000000f, + -1.30113411f, 0.22598207f, 0.04050000f, + -1.31140578f, 0.23159945f, 0.07200000f, + -1.32654238f, 0.23987763f, 0.09450001f, + -1.34492302f, 0.24992974f, 0.10800001f, + -1.36492503f, 0.26086879f, 0.11250000f, + -1.38492727f, 0.27180785f, 0.10800000f, + -1.40330780f, 0.28185993f, 0.09450001f, + -1.41844463f, 0.29013813f, 0.07200000f, + -1.42871594f, 0.29575545f, 0.04049999f, + -1.43250012f, 0.29782501f, 0.00000000f, + -1.32640004f, 0.20490001f, 0.00000000f, + -1.33042073f, 0.20643719f, 0.04050000f, + -1.34133446f, 0.21060961f, 0.07200000f, + -1.35741770f, 0.21675842f, 0.09450001f, + -1.37694728f, 0.22422481f, 0.10800001f, + -1.39820004f, 0.23234999f, 0.11250000f, + -1.41945279f, 0.24047519f, 0.10800000f, + -1.43898249f, 0.24794158f, 0.09450001f, + -1.45506573f, 0.25409040f, 0.07200000f, + -1.46597922f, 0.25826281f, 0.04049999f, + -1.47000003f, 0.25979999f, 0.00000000f, + -1.34404993f, 0.18048748f, 0.00000000f, + -1.34820640f, 0.18134111f, 0.04050000f, + -1.35948884f, 0.18365818f, 0.07200000f, + -1.37611520f, 0.18707278f, 0.09450001f, + -1.39630449f, 0.19121909f, 0.10800001f, + -1.41827500f, 0.19573122f, 0.11250000f, + -1.44024563f, 0.20024337f, 0.10800000f, + -1.46043491f, 0.20438966f, 0.09450001f, + -1.47706127f, 0.20780426f, 0.07200000f, + -1.48834348f, 0.21012132f, 0.04049999f, + -1.49250007f, 0.21097496f, 0.00000000f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.35419989f, 0.14999996f, 0.04050000f, + -1.36559999f, 0.14999999f, 0.07200000f, + -1.38239992f, 0.14999998f, 0.09450001f, + -1.40280008f, 0.14999999f, 0.10800001f, + -1.42499995f, 0.14999998f, 0.11250000f, + -1.44719994f, 0.14999998f, 0.10800000f, + -1.46760011f, 0.14999998f, 0.09450001f, + -1.48440003f, 0.14999998f, 0.07200000f, + -1.49580002f, 0.14999998f, 0.04049999f, + -1.50000000f, 0.14999998f, 0.00000000f, + -0.75000000f, 0.37500000f, 0.00000000f, + -0.75139999f, 0.37184998f, -0.04050000f, + -0.75520003f, 0.36330003f, -0.07200000f, + -0.76080000f, 0.35070002f, -0.09450001f, + -0.76760006f, 0.33540004f, -0.10800001f, + -0.77500004f, 0.31875002f, -0.11250000f, + -0.78240001f, 0.30210003f, -0.10800000f, + -0.78920001f, 0.28680006f, -0.09450001f, + -0.79480004f, 0.27420002f, -0.07200000f, + -0.79860002f, 0.26565003f, -0.04049999f, + -0.80000001f, 0.26250005f, 0.00000000f, + -0.89249992f, 0.37477499f, 0.00000000f, + -0.89272243f, 0.37162811f, -0.04049999f, + -0.89332676f, 0.36308670f, -0.07200000f, + -0.89421707f, 0.35049930f, -0.09449999f, + -0.89529842f, 0.33521461f, -0.10800000f, + -0.89647490f, 0.31858125f, -0.11250000f, + -0.89765155f, 0.30194789f, -0.10800000f, + -0.89873278f, 0.28666323f, -0.09449999f, + -0.89962316f, 0.27407581f, -0.07200000f, + -0.90022731f, 0.26553437f, -0.04049998f, + -0.90044993f, 0.26238751f, 0.00000000f, + -1.02000010f, 0.37320003f, 0.00000000f, + -1.01920474f, 0.37007520f, -0.04050000f, + -1.01704657f, 0.36159366f, -0.07200001f, + -1.01386571f, 0.34909445f, -0.09450001f, + -1.01000333f, 0.33391684f, -0.10800002f, + -1.00580001f, 0.31740004f, -0.11250001f, + -1.00159681f, 0.30088323f, -0.10800001f, + -0.99773443f, 0.28570566f, -0.09450001f, + -0.99455369f, 0.27320647f, -0.07200001f, + -0.99239522f, 0.26472485f, -0.04049999f, + -0.99160004f, 0.26160005f, 0.00000000f, + -1.13250005f, 0.36892501f, 0.00000000f, + -1.13083816f, 0.36586002f, -0.04050000f, + -1.12632775f, 0.35754091f, -0.07200001f, + -1.11968040f, 0.34528109f, -0.09450001f, + -1.11160886f, 0.33039424f, -0.10800002f, + -1.10282505f, 0.31419379f, -0.11250001f, + -1.09404123f, 0.29799333f, -0.10800001f, + -1.08596969f, 0.28310645f, -0.09450001f, + -1.07932246f, 0.27084661f, -0.07200001f, + -1.07481182f, 0.26252750f, -0.04049999f, + -1.07315004f, 0.25946254f, 0.00000000f, + -1.23000002f, 0.36059999f, 0.00000000f, + -1.22761440f, 0.35765156f, -0.04050000f, + -1.22113931f, 0.34964883f, -0.07200000f, + -1.21159685f, 0.33785522f, -0.09450001f, + -1.20000970f, 0.32353443f, -0.10800001f, + -1.18740010f, 0.30795002f, -0.11250000f, + -1.17479038f, 0.29236564f, -0.10800000f, + -1.16320324f, 0.27804482f, -0.09450001f, + -1.15366089f, 0.26625124f, -0.07200000f, + -1.14718568f, 0.25824845f, -0.04049999f, + -1.14480007f, 0.25530005f, 0.00000000f, + -1.31250000f, 0.34687501f, 0.00000000f, + -1.30952489f, 0.34411871f, -0.04050000f, + -1.30145013f, 0.33663750f, -0.07200000f, + -1.28955019f, 0.32561252f, -0.09450001f, + -1.27510011f, 0.31222504f, -0.10800001f, + -1.25937498f, 0.29765630f, -0.11250000f, + -1.24365008f, 0.28308752f, -0.10800000f, + -1.22920012f, 0.26970005f, -0.09450001f, + -1.21730018f, 0.25867504f, -0.07200000f, + -1.20922506f, 0.25119379f, -0.04049999f, + -1.20625007f, 0.24843754f, 0.00000000f, + -1.38000000f, 0.32639998f, 0.00000000f, + -1.37656140f, 0.32393035f, -0.04050000f, + -1.36722875f, 0.31722721f, -0.07200000f, + -1.35347521f, 0.30734879f, -0.09450001f, + -1.33677447f, 0.29535362f, -0.10800001f, + -1.31860006f, 0.28230000f, -0.11250000f, + -1.30042553f, 0.26924643f, -0.10800000f, + -1.28372478f, 0.25725123f, -0.09450001f, + -1.26997125f, 0.24737284f, -0.07200000f, + -1.26063836f, 0.24066964f, -0.04049999f, + -1.25720000f, 0.23820004f, 0.00000000f, + -1.43250012f, 0.29782501f, 0.00000000f, + -1.42871583f, 0.29575542f, -0.04050000f, + -1.41844463f, 0.29013813f, -0.07200000f, + -1.40330768f, 0.28185993f, -0.09450001f, + -1.38492751f, 0.27180785f, -0.10800001f, + -1.36492515f, 0.26086876f, -0.11250000f, + -1.34492302f, 0.24992973f, -0.10800000f, + -1.32654250f, 0.23987764f, -0.09450001f, + -1.31140566f, 0.23159944f, -0.07200000f, + -1.30113423f, 0.22598210f, -0.04049999f, + -1.29735005f, 0.22391254f, 0.00000000f, + -1.47000003f, 0.25979999f, 0.00000000f, + -1.46597922f, 0.25826275f, -0.04050000f, + -1.45506561f, 0.25409040f, -0.07200000f, + -1.43898249f, 0.24794158f, -0.09450001f, + -1.41945291f, 0.24047521f, -0.10800001f, + -1.39820004f, 0.23234999f, -0.11250000f, + -1.37694728f, 0.22422481f, -0.10800000f, + -1.35741770f, 0.21675842f, -0.09450001f, + -1.34133446f, 0.21060961f, -0.07200000f, + -1.33042085f, 0.20643722f, -0.04049999f, + -1.32640004f, 0.20490001f, 0.00000000f, + -1.49250007f, 0.21097496f, 0.00000000f, + -1.48834324f, 0.21012127f, -0.04050000f, + -1.47706139f, 0.20780428f, -0.07200000f, + -1.46043479f, 0.20438966f, -0.09450001f, + -1.44024563f, 0.20024338f, -0.10800001f, + -1.41827488f, 0.19573122f, -0.11250000f, + -1.39630437f, 0.19121908f, -0.10800000f, + -1.37611520f, 0.18707278f, -0.09450001f, + -1.35948884f, 0.18365818f, -0.07200000f, + -1.34820652f, 0.18134113f, -0.04049999f, + -1.34404993f, 0.18048748f, 0.00000000f, + -1.50000000f, 0.14999998f, 0.00000000f, + -1.49580002f, 0.14999996f, -0.04050000f, + -1.48440015f, 0.14999999f, -0.07200000f, + -1.46759999f, 0.14999998f, -0.09450001f, + -1.44720006f, 0.14999999f, -0.10800001f, + -1.42500007f, 0.14999998f, -0.11250000f, + -1.40280008f, 0.14999998f, -0.10800000f, + -1.38240004f, 0.14999998f, -0.09450001f, + -1.36559999f, 0.14999998f, -0.07200000f, + -1.35420001f, 0.14999998f, -0.04049999f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.35419989f, 0.14999996f, 0.04050000f, + -1.36559999f, 0.14999999f, 0.07200000f, + -1.38239992f, 0.14999998f, 0.09450001f, + -1.40280008f, 0.14999999f, 0.10800001f, + -1.42499995f, 0.14999998f, 0.11250000f, + -1.44719994f, 0.14999998f, 0.10800000f, + -1.46760011f, 0.14999998f, 0.09450001f, + -1.48440003f, 0.14999998f, 0.07200000f, + -1.49580002f, 0.14999998f, 0.04049999f, + -1.50000000f, 0.14999998f, 0.00000000f, + -1.34694993f, 0.11309998f, 0.00000000f, + -1.35108757f, 0.11225944f, 0.04049999f, + -1.36231863f, 0.10997803f, 0.07200000f, + -1.37886941f, 0.10661592f, 0.09449999f, + -1.39896679f, 0.10253337f, 0.10800000f, + -1.42083752f, 0.09809060f, 0.11250000f, + -1.44270813f, 0.09364782f, 0.10800000f, + -1.46280551f, 0.08956527f, 0.09449999f, + -1.47935629f, 0.08620317f, 0.07200000f, + -1.49058723f, 0.08392174f, 0.04049999f, + -1.49472487f, 0.08308122f, 0.00000000f, + -1.33760011f, 0.07080000f, 0.00000000f, + -1.34155357f, 0.06930479f, 0.04050000f, + -1.35228503f, 0.06524640f, 0.07200002f, + -1.36809933f, 0.05926559f, 0.09450001f, + -1.38730252f, 0.05200320f, 0.10800002f, + -1.40820003f, 0.04410000f, 0.11250001f, + -1.42909765f, 0.03619679f, 0.10800001f, + -1.44830084f, 0.02893440f, 0.09450001f, + -1.46411526f, 0.02295359f, 0.07200001f, + -1.47484648f, 0.01889519f, 0.04049999f, + -1.47880006f, 0.01739999f, 0.00000000f, + -1.32164991f, 0.02445000f, 0.00000000f, + -1.32530177f, 0.02245132f, 0.04050000f, + -1.33521414f, 0.01702635f, 0.07200002f, + -1.34982169f, 0.00903165f, 0.09450001f, + -1.36755967f, -0.00067620f, 0.10800002f, + -1.38686240f, -0.01124063f, 0.11250001f, + -1.40616536f, -0.02180506f, 0.10800001f, + -1.42390323f, -0.03151290f, 0.09450001f, + -1.43851089f, -0.03950761f, 0.07200001f, + -1.44842303f, -0.04493258f, 0.04049999f, + -1.45207500f, -0.04693126f, -0.00000000f, + -1.29880011f, -0.02460000f, -0.00000000f, + -1.30203676f, -0.02698559f, 0.04050000f, + -1.31082261f, -0.03346080f, 0.07200000f, + -1.32376969f, -0.04300320f, 0.09450001f, + -1.33949125f, -0.05459040f, 0.10800001f, + -1.35660005f, -0.06720001f, 0.11250000f, + -1.37370884f, -0.07980961f, 0.10800000f, + -1.38943052f, -0.09139681f, 0.09450001f, + -1.40237761f, -0.10093921f, 0.07200000f, + -1.41116321f, -0.10741441f, 0.04049999f, + -1.41439998f, -0.10980001f, -0.00000000f, + -1.26874995f, -0.07500000f, -0.00000000f, + -1.27146244f, -0.07769062f, 0.04050000f, + -1.27882504f, -0.08499375f, 0.07200000f, + -1.28967500f, -0.09575625f, 0.09450001f, + -1.30285001f, -0.10882500f, 0.10800001f, + -1.31718755f, -0.12304687f, 0.11250000f, + -1.33152497f, -0.13726875f, 0.10800000f, + -1.34470010f, -0.15033749f, 0.09450001f, + -1.35555005f, -0.16110000f, 0.07200000f, + -1.36291242f, -0.16840312f, 0.04049999f, + -1.36562502f, -0.17109375f, -0.00000000f, + -1.23119998f, -0.12540001f, -0.00000000f, + -1.23328304f, -0.12834840f, 0.04050000f, + -1.23893762f, -0.13635120f, 0.07200000f, + -1.24727035f, -0.14814480f, 0.09450001f, + -1.25738895f, -0.16246562f, 0.10800001f, + -1.26839995f, -0.17805001f, 0.11250000f, + -1.27941120f, -0.19363439f, 0.10800000f, + -1.28952956f, -0.20795521f, 0.09450001f, + -1.29786241f, -0.21974881f, 0.07200000f, + -1.30351675f, -0.22775160f, 0.04049999f, + -1.30559993f, -0.23070000f, -0.00000000f, + -1.18585014f, -0.17445001f, -0.00000000f, + -1.18720317f, -0.17764355f, 0.04050000f, + -1.19087601f, -0.18631189f, 0.07200000f, + -1.19628823f, -0.19908616f, 0.09450001f, + -1.20286059f, -0.21459784f, 0.10800001f, + -1.21001256f, -0.23147814f, 0.11250000f, + -1.21716475f, -0.24835847f, 0.10800000f, + -1.22373688f, -0.26387012f, 0.09450001f, + -1.22914934f, -0.27664441f, 0.07200000f, + -1.23282194f, -0.28531271f, 0.04049999f, + -1.23417509f, -0.28850627f, -0.00000000f, + -1.13240004f, -0.22080001f, -0.00000000f, + -1.13292634f, -0.22426079f, 0.04050000f, + -1.13435543f, -0.23365441f, 0.07200000f, + -1.13646090f, -0.24749762f, 0.09450001f, + -1.13901782f, -0.26430723f, 0.10800001f, + -1.14180005f, -0.28260002f, 0.11250000f, + -1.14458251f, -0.30089283f, 0.10800000f, + -1.14713919f, -0.31770241f, 0.09450001f, + -1.14924490f, -0.33154562f, 0.07200000f, + -1.15067363f, -0.34093922f, 0.04049999f, + -1.15120006f, -0.34440002f, -0.00000000f, + -1.07054996f, -0.26310003f, -0.00000000f, + -1.07015729f, -0.26688471f, 0.04050000f, + -1.06909144f, -0.27715757f, 0.07200000f, + -1.06752050f, -0.29229647f, 0.09450001f, + -1.06561327f, -0.31067947f, 0.10800001f, + -1.06353748f, -0.33068439f, 0.11250000f, + -1.06146181f, -0.35068941f, 0.10800000f, + -1.05955434f, -0.36907232f, 0.09450001f, + -1.05798364f, -0.38421124f, 0.07200000f, + -1.05691767f, -0.39448404f, 0.04049999f, + -1.05652499f, -0.39826876f, -0.00000000f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.99859989f, -0.30419996f, 0.04050000f, + -0.99480003f, -0.31560001f, 0.07200000f, + -0.98920000f, -0.33240002f, 0.09450001f, + -0.98240000f, -0.35280001f, 0.10800001f, + -0.97499996f, -0.37500000f, 0.11250000f, + -0.96759999f, -0.39720002f, 0.10800000f, + -0.96080005f, -0.41759998f, 0.09450001f, + -0.95520002f, -0.43440002f, 0.07200000f, + -0.95139992f, -0.44580001f, 0.04049999f, + -0.94999999f, -0.44999999f, -0.00000001f, + -1.50000000f, 0.14999998f, 0.00000000f, + -1.49580002f, 0.14999996f, -0.04050000f, + -1.48440015f, 0.14999999f, -0.07200000f, + -1.46759999f, 0.14999998f, -0.09450001f, + -1.44720006f, 0.14999999f, -0.10800001f, + -1.42500007f, 0.14999998f, -0.11250000f, + -1.40280008f, 0.14999998f, -0.10800000f, + -1.38240004f, 0.14999998f, -0.09450001f, + -1.36559999f, 0.14999998f, -0.07200000f, + -1.35420001f, 0.14999998f, -0.04049999f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.49472487f, 0.08308122f, 0.00000000f, + -1.49058712f, 0.08392174f, -0.04049999f, + -1.47935617f, 0.08620317f, -0.07200000f, + -1.46280551f, 0.08956527f, -0.09449999f, + -1.44270813f, 0.09364782f, -0.10800000f, + -1.42083728f, 0.09809060f, -0.11250000f, + -1.39896679f, 0.10253337f, -0.10800000f, + -1.37886930f, 0.10661593f, -0.09449999f, + -1.36231852f, 0.10997803f, -0.07200000f, + -1.35108757f, 0.11225945f, -0.04049999f, + -1.34694993f, 0.11309998f, 0.00000000f, + -1.47880006f, 0.01739999f, 0.00000000f, + -1.47484636f, 0.01889519f, -0.04050000f, + -1.46411538f, 0.02295360f, -0.07200002f, + -1.44830084f, 0.02893439f, -0.09450001f, + -1.42909777f, 0.03619680f, -0.10800002f, + -1.40820003f, 0.04409999f, -0.11250001f, + -1.38730240f, 0.05200320f, -0.10800001f, + -1.36809933f, 0.05926560f, -0.09450001f, + -1.35228491f, 0.06524640f, -0.07200001f, + -1.34155369f, 0.06930479f, -0.04049999f, + -1.33760011f, 0.07080000f, 0.00000000f, + -1.45207500f, -0.04693126f, -0.00000000f, + -1.44842303f, -0.04493257f, -0.04050000f, + -1.43851078f, -0.03950761f, -0.07200002f, + -1.42390323f, -0.03151290f, -0.09450001f, + -1.40616548f, -0.02180506f, -0.10800002f, + -1.38686240f, -0.01124063f, -0.11250001f, + -1.36755955f, -0.00067620f, -0.10800001f, + -1.34982181f, 0.00903165f, -0.09450001f, + -1.33521414f, 0.01702635f, -0.07200001f, + -1.32530189f, 0.02245133f, -0.04049999f, + -1.32164991f, 0.02445000f, 0.00000000f, + -1.41439998f, -0.10980001f, -0.00000000f, + -1.41116297f, -0.10741439f, -0.04050000f, + -1.40237772f, -0.10093922f, -0.07200000f, + -1.38943040f, -0.09139680f, -0.09450001f, + -1.37370884f, -0.07980961f, -0.10800001f, + -1.35660017f, -0.06720000f, -0.11250000f, + -1.33949125f, -0.05459040f, -0.10800000f, + -1.32376981f, -0.04300320f, -0.09450001f, + -1.31082249f, -0.03346080f, -0.07200000f, + -1.30203688f, -0.02698559f, -0.04049999f, + -1.29880011f, -0.02460000f, -0.00000000f, + -1.36562502f, -0.17109375f, -0.00000000f, + -1.36291230f, -0.16840309f, -0.04050000f, + -1.35555005f, -0.16110000f, -0.07200000f, + -1.34469998f, -0.15033749f, -0.09450001f, + -1.33152509f, -0.13726877f, -0.10800001f, + -1.31718755f, -0.12304687f, -0.11250000f, + -1.30285001f, -0.10882499f, -0.10800000f, + -1.28967500f, -0.09575625f, -0.09450001f, + -1.27882504f, -0.08499375f, -0.07200000f, + -1.27146244f, -0.07769062f, -0.04049999f, + -1.26874995f, -0.07500000f, -0.00000000f, + -1.30559993f, -0.23070000f, -0.00000000f, + -1.30351651f, -0.22775158f, -0.04050000f, + -1.29786241f, -0.21974881f, -0.07200000f, + -1.28952956f, -0.20795520f, -0.09450001f, + -1.27941132f, -0.19363441f, -0.10800001f, + -1.26839995f, -0.17805001f, -0.11250000f, + -1.25738883f, -0.16246560f, -0.10800000f, + -1.24727035f, -0.14814481f, -0.09450001f, + -1.23893762f, -0.13635120f, -0.07200000f, + -1.23328316f, -0.12834841f, -0.04049999f, + -1.23119998f, -0.12540001f, -0.00000000f, + -1.23417509f, -0.28850627f, -0.00000000f, + -1.23282194f, -0.28531265f, -0.04050000f, + -1.22914934f, -0.27664444f, -0.07200000f, + -1.22373676f, -0.26387009f, -0.09450001f, + -1.21716475f, -0.24835847f, -0.10800001f, + -1.21001267f, -0.23147814f, -0.11250000f, + -1.20286047f, -0.21459781f, -0.10800000f, + -1.19628835f, -0.19908617f, -0.09450001f, + -1.19087601f, -0.18631187f, -0.07200000f, + -1.18720317f, -0.17764360f, -0.04049999f, + -1.18585014f, -0.17445001f, -0.00000000f, + -1.15120006f, -0.34440002f, -0.00000000f, + -1.15067351f, -0.34093919f, -0.04050000f, + -1.14924490f, -0.33154565f, -0.07200001f, + -1.14713919f, -0.31770241f, -0.09450001f, + -1.14458263f, -0.30089283f, -0.10800001f, + -1.14180017f, -0.28259999f, -0.11250000f, + -1.13901758f, -0.26430720f, -0.10800000f, + -1.13646090f, -0.24749762f, -0.09450001f, + -1.13435531f, -0.23365441f, -0.07200000f, + -1.13292646f, -0.22426081f, -0.04049999f, + -1.13240004f, -0.22080001f, -0.00000000f, + -1.05652499f, -0.39826876f, -0.00000000f, + -1.05691755f, -0.39448401f, -0.04050000f, + -1.05798364f, -0.38421121f, -0.07200001f, + -1.05955434f, -0.36907232f, -0.09450001f, + -1.06146181f, -0.35068938f, -0.10800001f, + -1.06353748f, -0.33068442f, -0.11250000f, + -1.06561315f, -0.31067941f, -0.10800000f, + -1.06752062f, -0.29229647f, -0.09450001f, + -1.06909132f, -0.27715760f, -0.07200000f, + -1.07015729f, -0.26688474f, -0.04049999f, + -1.07054996f, -0.26310003f, -0.00000000f, + -0.94999999f, -0.44999999f, -0.00000001f, + -0.95139986f, -0.44579995f, -0.04050000f, + -0.95520008f, -0.43440005f, -0.07200001f, + -0.96079999f, -0.41760001f, -0.09450001f, + -0.96759999f, -0.39719999f, -0.10800001f, + -0.97500002f, -0.37500000f, -0.11250000f, + -0.98240000f, -0.35280001f, -0.10800000f, + -0.98920006f, -0.33240002f, -0.09450001f, + -0.99480003f, -0.31560001f, -0.07200000f, + -0.99860001f, -0.30419999f, -0.04049999f, + -1.00000000f, -0.30000001f, -0.00000000f, + 0.85000002f, -0.03750002f, -0.00000000f, + 0.84999996f, -0.04905002f, 0.08910000f, + 0.85000008f, -0.08040002f, 0.15840001f, + 0.85000002f, -0.12660003f, 0.20790000f, + 0.85000008f, -0.18270002f, 0.23760001f, + 0.85000002f, -0.24375001f, 0.24750000f, + 0.85000002f, -0.30480003f, 0.23760000f, + 0.85000002f, -0.36089998f, 0.20790002f, + 0.85000002f, -0.40710002f, 0.15840001f, + 0.85000002f, -0.43845001f, 0.08909997f, + 0.85000002f, -0.44999999f, -0.00000001f, + 0.96794993f, -0.02790002f, -0.00000000f, + 0.96969706f, -0.03838952f, 0.08755019f, + 0.97443962f, -0.06686102f, 0.15564480f, + 0.98142833f, -0.10881902f, 0.20428377f, + 0.98991477f, -0.15976802f, 0.23346718f, + 0.99914992f, -0.21521249f, 0.24319497f, + 1.00838506f, -0.27065700f, 0.23346716f, + 1.01687145f, -0.32160598f, 0.20428379f, + 1.02386022f, -0.36356401f, 0.15564479f, + 1.02860272f, -0.39203548f, 0.08755016f, + 1.03034985f, -0.40252498f, -0.00000000f, + 1.05560005f, -0.00120003f, -0.00000000f, + 1.05848956f, -0.01044003f, 0.08334360f, + 1.06633294f, -0.03552003f, 0.14816642f, + 1.07789123f, -0.07248003f, 0.19446841f, + 1.09192657f, -0.11736003f, 0.22224963f, + 1.10720015f, -0.16620001f, 0.23151001f, + 1.12247372f, -0.21504003f, 0.22224963f, + 1.13650894f, -0.25992000f, 0.19446844f, + 1.14806736f, -0.29688001f, 0.14816642f, + 1.15591049f, -0.32196000f, 0.08334358f, + 1.15880013f, -0.33120000f, -0.00000000f, + 1.11864996f, 0.03944998f, 0.00000000f, + 1.12222838f, 0.03158548f, 0.07714440f, + 1.13194120f, 0.01023899f, 0.13714561f, + 1.14625478f, -0.02121901f, 0.18000358f, + 1.16363573f, -0.05941800f, 0.20571840f, + 1.18255007f, -0.10098749f, 0.21428999f, + 1.20146441f, -0.14255700f, 0.20571840f, + 1.21884537f, -0.18075597f, 0.18000360f, + 1.23315883f, -0.21221398f, 0.13714559f, + 1.24287164f, -0.23356047f, 0.07714437f, + 1.24645007f, -0.24142496f, -0.00000000f, + 1.16280007f, 0.09089998f, 0.00000000f, + 1.16676486f, 0.08447397f, 0.06961680f, + 1.17752659f, 0.06703199f, 0.12376321f, + 1.19338572f, 0.04132799f, 0.16243920f, + 1.21264338f, 0.01011599f, 0.18564481f, + 1.23360014f, -0.02385001f, 0.19338000f, + 1.25455689f, -0.05781601f, 0.18564481f, + 1.27381444f, -0.08902800f, 0.16243921f, + 1.28967381f, -0.11473200f, 0.12376320f, + 1.30043530f, -0.13217400f, 0.06961678f, + 1.30440009f, -0.13859999f, -0.00000000f, + 1.19375002f, 0.14999998f, 0.00000000f, + 1.19794989f, 0.14501247f, 0.06142500f, + 1.20935011f, 0.13147499f, 0.10920001f, + 1.22614992f, 0.11152498f, 0.14332500f, + 1.24654996f, 0.08730000f, 0.16380000f, + 1.26874995f, 0.06093749f, 0.17062500f, + 1.29094982f, 0.03457499f, 0.16380000f, + 1.31134987f, 0.01035001f, 0.14332500f, + 1.32814991f, -0.00959999f, 0.10920000f, + 1.33954990f, -0.02313749f, 0.06142498f, + 1.34374988f, -0.02812499f, -0.00000000f, + 1.21719992f, 0.21360001f, 0.00000000f, + 1.22163498f, 0.20998798f, 0.05323320f, + 1.23367357f, 0.20018403f, 0.09463681f, + 1.25141430f, 0.18573602f, 0.12421080f, + 1.27295685f, 0.16819203f, 0.14195521f, + 1.29639995f, 0.14910004f, 0.14787000f, + 1.31984317f, 0.13000804f, 0.14195520f, + 1.34138560f, 0.11246406f, 0.12421081f, + 1.35912633f, 0.09801605f, 0.09463680f, + 1.37116480f, 0.08821206f, 0.05323318f, + 1.37559998f, 0.08460006f, 0.00000000f, + 1.23885000f, 0.27855000f, 0.00000000f, + 1.24367154f, 0.27618748f, 0.04570561f, + 1.25675893f, 0.26977503f, 0.08125442f, + 1.27604520f, 0.26032501f, 0.10664641f, + 1.29946446f, 0.24885003f, 0.12188162f, + 1.32495010f, 0.23636252f, 0.12696001f, + 1.35043573f, 0.22387503f, 0.12188161f, + 1.37385488f, 0.21240003f, 0.10664642f, + 1.39314127f, 0.20295003f, 0.08125441f, + 1.40622854f, 0.19653754f, 0.04570559f, + 1.41105008f, 0.19417503f, 0.00000000f, + 1.26440001f, 0.34170002f, 0.00000000f, + 1.26991034f, 0.34039798f, 0.03950640f, + 1.28486729f, 0.33686405f, 0.07023361f, + 1.30690885f, 0.33165601f, 0.09218160f, + 1.33367372f, 0.32533202f, 0.10535040f, + 1.36280000f, 0.31845003f, 0.10974000f, + 1.39192641f, 0.31156802f, 0.10535040f, + 1.41869128f, 0.30524403f, 0.09218161f, + 1.44073272f, 0.30003607f, 0.07023360f, + 1.45568967f, 0.29650205f, 0.03950639f, + 1.46120000f, 0.29520005f, 0.00000000f, + 1.29955006f, 0.39990005f, 0.00000000f, + 1.30620289f, 0.39940652f, 0.03529980f, + 1.32426059f, 0.39806706f, 0.06275520f, + 1.35087168f, 0.39609307f, 0.08236619f, + 1.38318527f, 0.39369607f, 0.09413280f, + 1.41835010f, 0.39108756f, 0.09805499f, + 1.45351493f, 0.38847905f, 0.09413280f, + 1.48582840f, 0.38608208f, 0.08236620f, + 1.51243973f, 0.38410807f, 0.06275519f, + 1.53049719f, 0.38276857f, 0.03529979f, + 1.53715003f, 0.38227507f, 0.00000000f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.35839975f, 0.44999996f, 0.03375000f, + 1.38120008f, 0.45000011f, 0.06000001f, + 1.41480005f, 0.45000002f, 0.07875000f, + 1.45560014f, 0.45000008f, 0.09000000f, + 1.50000000f, 0.45000005f, 0.09375000f, + 1.54439998f, 0.45000005f, 0.09000000f, + 1.58520007f, 0.45000008f, 0.07875000f, + 1.61880004f, 0.45000005f, 0.06000000f, + 1.64159989f, 0.45000005f, 0.03374999f, + 1.64999998f, 0.45000005f, 0.00000001f, + 0.85000002f, -0.44999999f, -0.00000001f, + 0.84999996f, -0.43844995f, -0.08910000f, + 0.85000008f, -0.40710002f, -0.15840001f, + 0.85000002f, -0.36089998f, -0.20790000f, + 0.85000008f, -0.30480000f, -0.23760001f, + 0.85000002f, -0.24375001f, -0.24750000f, + 0.85000002f, -0.18269999f, -0.23760000f, + 0.85000002f, -0.12660003f, -0.20790002f, + 0.85000002f, -0.08040002f, -0.15840001f, + 0.85000002f, -0.04905001f, -0.08909997f, + 0.85000002f, -0.03750002f, -0.00000000f, + 1.03034985f, -0.40252498f, -0.00000000f, + 1.02860260f, -0.39203545f, -0.08755019f, + 1.02386034f, -0.36356398f, -0.15564480f, + 1.01687145f, -0.32160601f, -0.20428377f, + 1.00838506f, -0.27065703f, -0.23346718f, + 0.99914992f, -0.21521249f, -0.24319497f, + 0.98991477f, -0.15976799f, -0.23346716f, + 0.98142833f, -0.10881902f, -0.20428379f, + 0.97443956f, -0.06686102f, -0.15564479f, + 0.96969712f, -0.03838951f, -0.08755016f, + 0.96794993f, -0.02790002f, -0.00000000f, + 1.15880013f, -0.33120000f, -0.00000000f, + 1.15591037f, -0.32195994f, -0.08334360f, + 1.14806736f, -0.29688004f, -0.14816642f, + 1.13650882f, -0.25992000f, -0.19446841f, + 1.12247384f, -0.21504001f, -0.22224963f, + 1.10720003f, -0.16620003f, -0.23151001f, + 1.09192646f, -0.11736001f, -0.22224963f, + 1.07789123f, -0.07248002f, -0.19446844f, + 1.06633282f, -0.03552002f, -0.14816642f, + 1.05848956f, -0.01044002f, -0.08334358f, + 1.05560005f, -0.00120003f, -0.00000000f, + 1.24645007f, -0.24142496f, -0.00000000f, + 1.24287152f, -0.23356044f, -0.07714440f, + 1.23315895f, -0.21221398f, -0.13714561f, + 1.21884525f, -0.18075597f, -0.18000358f, + 1.20146453f, -0.14255700f, -0.20571840f, + 1.18254995f, -0.10098749f, -0.21428999f, + 1.16363561f, -0.05941799f, -0.20571840f, + 1.14625478f, -0.02121901f, -0.18000360f, + 1.13194120f, 0.01023899f, -0.13714559f, + 1.12222838f, 0.03158549f, -0.07714437f, + 1.11864996f, 0.03944998f, 0.00000000f, + 1.30440009f, -0.13859999f, -0.00000000f, + 1.30043507f, -0.13217399f, -0.06961680f, + 1.28967369f, -0.11473200f, -0.12376321f, + 1.27381444f, -0.08902799f, -0.16243920f, + 1.25455701f, -0.05781601f, -0.18564481f, + 1.23360002f, -0.02385001f, -0.19338000f, + 1.21264338f, 0.01011600f, -0.18564481f, + 1.19338572f, 0.04132798f, -0.16243921f, + 1.17752647f, 0.06703199f, -0.12376320f, + 1.16676486f, 0.08447399f, -0.06961678f, + 1.16280007f, 0.09089998f, 0.00000000f, + 1.34374988f, -0.02812499f, -0.00000000f, + 1.33954978f, -0.02313748f, -0.06142500f, + 1.32814991f, -0.00959999f, -0.10920001f, + 1.31134987f, 0.01035001f, -0.14332500f, + 1.29095006f, 0.03457500f, -0.16380000f, + 1.26874995f, 0.06093749f, -0.17062500f, + 1.24654996f, 0.08730000f, -0.16380000f, + 1.22615004f, 0.11152498f, -0.14332500f, + 1.20935011f, 0.13147499f, -0.10920000f, + 1.19795001f, 0.14501248f, -0.06142498f, + 1.19375002f, 0.14999998f, 0.00000000f, + 1.37559998f, 0.08460006f, 0.00000000f, + 1.37116480f, 0.08821206f, -0.05323320f, + 1.35912645f, 0.09801605f, -0.09463680f, + 1.34138560f, 0.11246406f, -0.12421079f, + 1.31984317f, 0.13000806f, -0.14195520f, + 1.29639995f, 0.14910004f, -0.14786999f, + 1.27295685f, 0.16819203f, -0.14195520f, + 1.25141430f, 0.18573603f, -0.12421080f, + 1.23367357f, 0.20018402f, -0.09463680f, + 1.22163510f, 0.20998801f, -0.05323318f, + 1.21719992f, 0.21360001f, 0.00000000f, + 1.41105008f, 0.19417503f, 0.00000000f, + 1.40622830f, 0.19653751f, -0.04570560f, + 1.39314139f, 0.20295003f, -0.08125441f, + 1.37385488f, 0.21240002f, -0.10664640f, + 1.35043585f, 0.22387503f, -0.12188162f, + 1.32494998f, 0.23636252f, -0.12696001f, + 1.29946434f, 0.24885002f, -0.12188160f, + 1.27604532f, 0.26032501f, -0.10664640f, + 1.25675893f, 0.26977503f, -0.08125440f, + 1.24367166f, 0.27618751f, -0.04570558f, + 1.23885000f, 0.27855000f, 0.00000000f, + 1.46120000f, 0.29520005f, 0.00000000f, + 1.45568943f, 0.29650205f, -0.03950639f, + 1.44073284f, 0.30003604f, -0.07023360f, + 1.41869116f, 0.30524403f, -0.09218159f, + 1.39192653f, 0.31156805f, -0.10535039f, + 1.36280012f, 0.31845003f, -0.10973999f, + 1.33367360f, 0.32533202f, -0.10535039f, + 1.30690885f, 0.33165604f, -0.09218159f, + 1.28486729f, 0.33686402f, -0.07023359f, + 1.26991045f, 0.34039801f, -0.03950638f, + 1.26440001f, 0.34170002f, 0.00000000f, + 1.53715003f, 0.38227507f, 0.00000000f, + 1.53049695f, 0.38276854f, -0.03529979f, + 1.51243961f, 0.38410810f, -0.06275519f, + 1.48582840f, 0.38608205f, -0.08236619f, + 1.45351493f, 0.38847908f, -0.09413280f, + 1.41835010f, 0.39108753f, -0.09805499f, + 1.38318527f, 0.39369607f, -0.09413280f, + 1.35087168f, 0.39609307f, -0.08236620f, + 1.32426047f, 0.39806706f, -0.06275519f, + 1.30620289f, 0.39940655f, -0.03529978f, + 1.29955006f, 0.39990005f, 0.00000000f, + 1.64999998f, 0.45000005f, 0.00000001f, + 1.64159989f, 0.44999996f, -0.03374999f, + 1.61880016f, 0.45000011f, -0.05999999f, + 1.58519995f, 0.45000002f, -0.07874999f, + 1.54440010f, 0.45000008f, -0.09000000f, + 1.50000000f, 0.45000005f, -0.09374999f, + 1.45560002f, 0.45000005f, -0.08999999f, + 1.41480005f, 0.45000008f, -0.07875000f, + 1.38120008f, 0.45000005f, -0.05999999f, + 1.35839999f, 0.45000005f, -0.03374998f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.35839975f, 0.44999996f, 0.03375000f, + 1.38120008f, 0.45000011f, 0.06000001f, + 1.41480005f, 0.45000002f, 0.07875000f, + 1.45560014f, 0.45000008f, 0.09000000f, + 1.50000000f, 0.45000005f, 0.09375000f, + 1.54439998f, 0.45000005f, 0.09000000f, + 1.58520007f, 0.45000008f, 0.07875000f, + 1.61880004f, 0.45000005f, 0.06000000f, + 1.64159989f, 0.45000005f, 0.03374999f, + 1.64999998f, 0.45000005f, 0.00000001f, + 1.36489987f, 0.46012494f, 0.00000001f, + 1.37370336f, 0.46020287f, 0.03337200f, + 1.39759886f, 0.46041453f, 0.05932800f, + 1.43281305f, 0.46072635f, 0.07786799f, + 1.47557306f, 0.46110505f, 0.08899199f, + 1.52210617f, 0.46151716f, 0.09269999f, + 1.56863928f, 0.46192923f, 0.08899198f, + 1.61139929f, 0.46230793f, 0.07786799f, + 1.64661360f, 0.46261978f, 0.05932799f, + 1.67050874f, 0.46283138f, 0.03337199f, + 1.67931235f, 0.46290934f, 0.00000001f, + 1.37919998f, 0.46800002f, 0.00000001f, + 1.38818228f, 0.46815118f, 0.03234600f, + 1.41256332f, 0.46856162f, 0.05750401f, + 1.44849277f, 0.46916643f, 0.07547401f, + 1.49212170f, 0.46990088f, 0.08625601f, + 1.53960001f, 0.47070009f, 0.08985001f, + 1.58707845f, 0.47149926f, 0.08625601f, + 1.63070726f, 0.47223371f, 0.07547401f, + 1.66663694f, 0.47283852f, 0.05750401f, + 1.69101763f, 0.47324890f, 0.03234600f, + 1.70000005f, 0.47340012f, 0.00000001f, + 1.39230001f, 0.47362497f, 0.00000001f, + 1.40126371f, 0.47383994f, 0.03083400f, + 1.42559445f, 0.47442356f, 0.05481601f, + 1.46144974f, 0.47528344f, 0.07194600f, + 1.50498855f, 0.47632772f, 0.08222401f, + 1.55236876f, 0.47746408f, 0.08565000f, + 1.59974909f, 0.47860044f, 0.08222400f, + 1.64328790f, 0.47964469f, 0.07194600f, + 1.67914319f, 0.48050463f, 0.05481600f, + 1.70347357f, 0.48108816f, 0.03083399f, + 1.71243751f, 0.48130316f, 0.00000001f, + 1.40359998f, 0.47700000f, 0.00000001f, + 1.41237509f, 0.47726458f, 0.02899800f, + 1.43619370f, 0.47798279f, 0.05155201f, + 1.47129452f, 0.47904122f, 0.06766201f, + 1.51391697f, 0.48032647f, 0.07732801f, + 1.56030011f, 0.48172504f, 0.08055001f, + 1.60668325f, 0.48312366f, 0.07732800f, + 1.64930582f, 0.48440886f, 0.06766201f, + 1.68440676f, 0.48546728f, 0.05155201f, + 1.70822501f, 0.48618549f, 0.02899799f, + 1.71700025f, 0.48645008f, 0.00000001f, + 1.41249990f, 0.47812498f, 0.00000001f, + 1.42094362f, 0.47842023f, 0.02700000f, + 1.44386256f, 0.47922191f, 0.04800001f, + 1.47763753f, 0.48040313f, 0.06300000f, + 1.51865005f, 0.48183754f, 0.07200000f, + 1.56328130f, 0.48339847f, 0.07500000f, + 1.60791266f, 0.48495942f, 0.07200000f, + 1.64892507f, 0.48639381f, 0.06300000f, + 1.68270016f, 0.48757505f, 0.04800000f, + 1.70561898f, 0.48837662f, 0.02700000f, + 1.71406269f, 0.48867193f, 0.00000001f, + 1.41839993f, 0.47700000f, 0.00000001f, + 1.42639649f, 0.47730237f, 0.02500200f, + 1.44810247f, 0.47812322f, 0.04444801f, + 1.48008955f, 0.47933280f, 0.05833800f, + 1.51893127f, 0.48080164f, 0.06667200f, + 1.56120014f, 0.48240003f, 0.06945001f, + 1.60346889f, 0.48399845f, 0.06667200f, + 1.64231050f, 0.48546726f, 0.05833801f, + 1.67429769f, 0.48667687f, 0.04444801f, + 1.69600332f, 0.48749766f, 0.02500200f, + 1.70400012f, 0.48780006f, 0.00000001f, + 1.42070007f, 0.47362500f, 0.00000001f, + 1.42816150f, 0.47390610f, 0.02316600f, + 1.44841480f, 0.47466925f, 0.04118401f, + 1.47826135f, 0.47579375f, 0.05405401f, + 1.51450372f, 0.47715935f, 0.06177600f, + 1.55394375f, 0.47864535f, 0.06435001f, + 1.59338403f, 0.48013136f, 0.06177600f, + 1.62962627f, 0.48149687f, 0.05405401f, + 1.65947294f, 0.48262146f, 0.04118400f, + 1.67972589f, 0.48338455f, 0.02316600f, + 1.68718755f, 0.48366567f, 0.00000001f, + 1.41880012f, 0.46799999f, 0.00000001f, + 1.42566562f, 0.46822673f, 0.02165400f, + 1.44430101f, 0.46884245f, 0.03849601f, + 1.47176325f, 0.46974963f, 0.05052601f, + 1.50511050f, 0.47085124f, 0.05774401f, + 1.54139996f, 0.47205001f, 0.06015000f, + 1.57768965f, 0.47324884f, 0.05774400f, + 1.61103690f, 0.47435045f, 0.05052601f, + 1.63849926f, 0.47525764f, 0.03849601f, + 1.65713453f, 0.47587326f, 0.02165400f, + 1.66400003f, 0.47610006f, 0.00000001f, + 1.41209996f, 0.46012503f, 0.00000001f, + 1.41833580f, 0.46025965f, 0.02062801f, + 1.43526208f, 0.46062523f, 0.03667201f, + 1.46020591f, 0.46116382f, 0.04813201f, + 1.49049485f, 0.46181795f, 0.05500801f, + 1.52345622f, 0.46252972f, 0.05730001f, + 1.55641770f, 0.46324152f, 0.05500801f, + 1.58670676f, 0.46389559f, 0.04813201f, + 1.61165047f, 0.46443427f, 0.03667201f, + 1.62857664f, 0.46479973f, 0.02062800f, + 1.63481259f, 0.46493441f, 0.00000001f, + 1.39999998f, 0.45000005f, 0.00000001f, + 1.40559983f, 0.44999996f, 0.02025001f, + 1.42079997f, 0.45000011f, 0.03600001f, + 1.44319999f, 0.45000002f, 0.04725001f, + 1.47040009f, 0.45000008f, 0.05400001f, + 1.50000000f, 0.45000005f, 0.05625001f, + 1.52960002f, 0.45000005f, 0.05400001f, + 1.55680001f, 0.45000008f, 0.04725001f, + 1.57920003f, 0.45000005f, 0.03600001f, + 1.59440005f, 0.45000005f, 0.02025000f, + 1.60000002f, 0.45000005f, 0.00000001f, + 1.64999998f, 0.45000005f, 0.00000001f, + 1.64159989f, 0.44999996f, -0.03374999f, + 1.61880016f, 0.45000011f, -0.05999999f, + 1.58519995f, 0.45000002f, -0.07874999f, + 1.54440010f, 0.45000008f, -0.09000000f, + 1.50000000f, 0.45000005f, -0.09374999f, + 1.45560002f, 0.45000005f, -0.08999999f, + 1.41480005f, 0.45000008f, -0.07875000f, + 1.38120008f, 0.45000005f, -0.05999999f, + 1.35839999f, 0.45000005f, -0.03374998f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.67931235f, 0.46290934f, 0.00000001f, + 1.67050874f, 0.46283132f, -0.03337199f, + 1.64661360f, 0.46261978f, -0.05932799f, + 1.61139941f, 0.46230793f, -0.07786798f, + 1.56863928f, 0.46192926f, -0.08899198f, + 1.52210605f, 0.46151716f, -0.09269998f, + 1.47557306f, 0.46110505f, -0.08899198f, + 1.43281293f, 0.46072638f, -0.07786798f, + 1.39759874f, 0.46041453f, -0.05932799f, + 1.37370336f, 0.46020290f, -0.03337198f, + 1.36489987f, 0.46012494f, 0.00000001f, + 1.70000005f, 0.47340012f, 0.00000001f, + 1.69101751f, 0.47324887f, -0.03234600f, + 1.66663706f, 0.47283855f, -0.05750400f, + 1.63070726f, 0.47223368f, -0.07547399f, + 1.58707845f, 0.47149929f, -0.08625600f, + 1.53960001f, 0.47070006f, -0.08985000f, + 1.49212158f, 0.46990088f, -0.08625600f, + 1.44849288f, 0.46916646f, -0.07547400f, + 1.41256320f, 0.46856165f, -0.05750399f, + 1.38818240f, 0.46815124f, -0.03234598f, + 1.37919998f, 0.46800002f, 0.00000001f, + 1.71243751f, 0.48130316f, 0.00000001f, + 1.70347345f, 0.48108810f, -0.03083399f, + 1.67914343f, 0.48050466f, -0.05481599f, + 1.64328778f, 0.47964469f, -0.07194599f, + 1.59974921f, 0.47860047f, -0.08222400f, + 1.55236864f, 0.47746405f, -0.08564999f, + 1.50498843f, 0.47632769f, -0.08222400f, + 1.46144986f, 0.47528344f, -0.07194600f, + 1.42559433f, 0.47442353f, -0.05481599f, + 1.40126395f, 0.47383997f, -0.03083398f, + 1.39230001f, 0.47362497f, 0.00000001f, + 1.71700025f, 0.48645008f, 0.00000001f, + 1.70822489f, 0.48618543f, -0.02899799f, + 1.68440676f, 0.48546731f, -0.05155200f, + 1.64930582f, 0.48440889f, -0.06766199f, + 1.60668337f, 0.48312369f, -0.07732800f, + 1.56030011f, 0.48172504f, -0.08055000f, + 1.51391685f, 0.48032641f, -0.07732800f, + 1.47129452f, 0.47904122f, -0.06766200f, + 1.43619370f, 0.47798282f, -0.05155200f, + 1.41237521f, 0.47726461f, -0.02899799f, + 1.40359998f, 0.47700000f, 0.00000001f, + 1.71406269f, 0.48867193f, 0.00000001f, + 1.70561886f, 0.48837656f, -0.02699999f, + 1.68270016f, 0.48757505f, -0.04800000f, + 1.64892519f, 0.48639381f, -0.06299999f, + 1.60791278f, 0.48495948f, -0.07200000f, + 1.56328130f, 0.48339844f, -0.07500000f, + 1.51865005f, 0.48183751f, -0.07200000f, + 1.47763753f, 0.48040313f, -0.06299999f, + 1.44386244f, 0.47922188f, -0.04799999f, + 1.42094362f, 0.47842029f, -0.02699998f, + 1.41249990f, 0.47812498f, 0.00000001f, + 1.70400012f, 0.48780006f, 0.00000001f, + 1.69600320f, 0.48749760f, -0.02500199f, + 1.67429769f, 0.48667687f, -0.04444800f, + 1.64231050f, 0.48546728f, -0.05833799f, + 1.60346901f, 0.48399848f, -0.06667200f, + 1.56120002f, 0.48240003f, -0.06944999f, + 1.51893127f, 0.48080158f, -0.06667199f, + 1.48008966f, 0.47933280f, -0.05833799f, + 1.44810236f, 0.47812319f, -0.04444799f, + 1.42639661f, 0.47730240f, -0.02500199f, + 1.41839993f, 0.47700000f, 0.00000001f, + 1.68718755f, 0.48366567f, 0.00000001f, + 1.67972589f, 0.48338449f, -0.02316599f, + 1.65947306f, 0.48262149f, -0.04118400f, + 1.62962627f, 0.48149690f, -0.05405399f, + 1.59338415f, 0.48013139f, -0.06177600f, + 1.55394387f, 0.47864535f, -0.06434999f, + 1.51450372f, 0.47715932f, -0.06177600f, + 1.47826147f, 0.47579378f, -0.05405399f, + 1.44841480f, 0.47466925f, -0.04118399f, + 1.42816162f, 0.47390613f, -0.02316599f, + 1.42070007f, 0.47362500f, 0.00000001f, + 1.66400003f, 0.47610006f, 0.00000001f, + 1.65713418f, 0.47587320f, -0.02165399f, + 1.63849938f, 0.47525769f, -0.03849600f, + 1.61103702f, 0.47435045f, -0.05052599f, + 1.57768977f, 0.47324887f, -0.05774400f, + 1.54140007f, 0.47205001f, -0.06015000f, + 1.50511050f, 0.47085121f, -0.05774400f, + 1.47176337f, 0.46974963f, -0.05052600f, + 1.44430089f, 0.46884239f, -0.03849599f, + 1.42566574f, 0.46822679f, -0.02165399f, + 1.41880012f, 0.46799999f, 0.00000001f, + 1.63481259f, 0.46493441f, 0.00000001f, + 1.62857652f, 0.46479967f, -0.02062799f, + 1.61165059f, 0.46443427f, -0.03667200f, + 1.58670664f, 0.46389559f, -0.04813199f, + 1.55641782f, 0.46324155f, -0.05500800f, + 1.52345634f, 0.46252972f, -0.05730000f, + 1.49049485f, 0.46181795f, -0.05500799f, + 1.46020591f, 0.46116385f, -0.04813200f, + 1.43526220f, 0.46062523f, -0.03667200f, + 1.41833591f, 0.46025968f, -0.02062799f, + 1.41209996f, 0.46012503f, 0.00000001f, + 1.60000002f, 0.45000005f, 0.00000001f, + 1.59439981f, 0.44999996f, -0.02024999f, + 1.57920015f, 0.45000011f, -0.03600000f, + 1.55680001f, 0.45000002f, -0.04725000f, + 1.52960002f, 0.45000008f, -0.05400000f, + 1.50000000f, 0.45000005f, -0.05625000f, + 1.47039998f, 0.45000005f, -0.05400000f, + 1.44320011f, 0.45000008f, -0.04725000f, + 1.42079997f, 0.45000005f, -0.03599999f, + 1.40559995f, 0.45000005f, -0.02024999f, + 1.39999998f, 0.45000005f, 0.00000001f + }; + + const std::array<float, (vertexCount * 3)> teapotNormals = { + -0.90286040f, -0.42993385f, 0.00000000f, + -0.89153194f, -0.43015867f, -0.14189552f, + -0.85786587f, -0.43059322f, -0.28045613f, + -0.80313200f, -0.43100095f, -0.41136023f, + -0.72916991f, -0.43127316f, -0.53133309f, + -0.63793534f, -0.43136632f, -0.63793534f, + -0.53133315f, -0.43127266f, -0.72917002f, + -0.41136023f, -0.43100119f, -0.80313188f, + -0.28045616f, -0.43059263f, -0.85786611f, + -0.14189562f, -0.43015778f, -0.89153230f, + -0.00000000f, -0.42993385f, -0.90286040f, + -0.95656341f, -0.29152444f, 0.00000000f, + -0.94462180f, -0.29169482f, -0.15034524f, + -0.90906340f, -0.29202676f, -0.29719374f, + -0.85116220f, -0.29233694f, -0.43596110f, + -0.77283686f, -0.29254490f, -0.56315243f, + -0.67615676f, -0.29261592f, -0.67615676f, + -0.56315249f, -0.29254425f, -0.77283710f, + -0.43596104f, -0.29233718f, -0.85116208f, + -0.29719374f, -0.29202670f, -0.90906346f, + -0.15034510f, -0.29169548f, -0.94462162f, + -0.00000000f, -0.29152444f, -0.95656341f, + -0.99756998f, -0.06967134f, 0.00000000f, + -0.98516703f, -0.06971460f, -0.15679841f, + -0.94817692f, -0.06980211f, -0.30998084f, + -0.88786739f, -0.06988241f, -0.45476130f, + -0.80621493f, -0.06993677f, -0.58747447f, + -0.70537448f, -0.06995525f, -0.70537448f, + -0.58747447f, -0.06993637f, -0.80621505f, + -0.45476136f, -0.06988230f, -0.88786751f, + -0.30998087f, -0.06980199f, -0.94817698f, + -0.15679830f, -0.06971515f, -0.98516703f, + -0.00000000f, -0.06967134f, -0.99756998f, + -0.95136631f, 0.30806199f, 0.00000000f, + -0.93948323f, 0.30824155f, -0.14952739f, + -0.90410745f, 0.30858687f, -0.29557356f, + -0.84651184f, 0.30891240f, -0.43357921f, + -0.76860887f, 0.30912822f, -0.56007159f, + -0.67245591f, 0.30920231f, -0.67245591f, + -0.56007153f, 0.30912805f, -0.76860899f, + -0.43357912f, 0.30891296f, -0.84651166f, + -0.29557377f, 0.30858609f, -0.90410781f, + -0.14952743f, 0.30823970f, -0.93948382f, + 0.00000000f, 0.30806199f, -0.95136631f, + -0.59454966f, 0.80405891f, 0.00000000f, + -0.58691627f, 0.80424082f, -0.09341305f, + -0.56442964f, 0.80459291f, -0.18452507f, + -0.52813381f, 0.80492258f, -0.27050757f, + -0.47932491f, 0.80514228f, -0.34927556f, + -0.41930011f, 0.80521733f, -0.41930011f, + -0.34927583f, 0.80514193f, -0.47932535f, + -0.27050710f, 0.80492342f, -0.52813286f, + -0.18452539f, 0.80459219f, -0.56443053f, + -0.09341303f, 0.80424047f, -0.58691663f, + 0.00000000f, 0.80405891f, -0.59454966f, + 0.00000000f, 1.00000000f, 0.00000000f, + -0.00000000f, 1.00000000f, 0.00000002f, + 0.00000000f, 1.00000000f, -0.00000001f, + -0.00000037f, 1.00000000f, -0.00000014f, + 0.00000038f, 1.00000000f, 0.00000015f, + 0.00000000f, 1.00000000f, -0.00000000f, + -0.00000004f, 1.00000000f, 0.00000003f, + 0.00000018f, 1.00000000f, 0.00000035f, + 0.00000005f, 0.99999994f, -0.00000002f, + 0.00000000f, 1.00000000f, -0.00000000f, + 0.00000000f, 1.00000000f, -0.00000000f, + 0.39803210f, 0.91737145f, -0.00000000f, + 0.39287356f, 0.91746408f, 0.06252954f, + 0.37772986f, 0.91764414f, 0.12348856f, + 0.35335949f, 0.91781265f, 0.18098903f, + 0.32065529f, 0.91792440f, 0.23365574f, + 0.28048533f, 0.91796297f, 0.28048533f, + 0.23365568f, 0.91792440f, 0.32065514f, + 0.18098891f, 0.91781276f, 0.35335934f, + 0.12348875f, 0.91764396f, 0.37773019f, + 0.06252949f, 0.91746432f, 0.39287323f, + 0.00000000f, 0.91737145f, 0.39803210f, + 0.61678272f, 0.78713351f, -0.00000000f, + 0.60887474f, 0.78732491f, 0.09690809f, + 0.58556628f, 0.78769588f, 0.19143508f, + 0.54792851f, 0.78804308f, 0.28064632f, + 0.49730095f, 0.78827441f, 0.36237431f, + 0.43502763f, 0.78835386f, 0.43502763f, + 0.36237440f, 0.78827435f, 0.49730113f, + 0.28064600f, 0.78804362f, 0.54792786f, + 0.19143537f, 0.78769529f, 0.58556694f, + 0.09690812f, 0.78732455f, 0.60887510f, + 0.00000000f, 0.78713351f, 0.61678272f, + 0.74976826f, 0.66170049f, -0.00000000f, + 0.74024051f, 0.66193908f, 0.11781615f, + 0.71206385f, 0.66240013f, 0.23279002f, + 0.66643608f, 0.66283208f, 0.34134525f, + 0.60494393f, 0.66311979f, 0.44081178f, + 0.52921647f, 0.66321933f, 0.52921647f, + 0.44081184f, 0.66311955f, 0.60494399f, + 0.34134495f, 0.66283280f, 0.66643548f, + 0.23279032f, 0.66239935f, 0.71206450f, + 0.11781613f, 0.66193885f, 0.74024075f, + 0.00000000f, 0.66170049f, 0.74976826f, + 0.83935553f, 0.54358286f, -0.00000000f, + 0.82876492f, 0.54382867f, 0.13190563f, + 0.79735982f, 0.54430288f, 0.26067528f, + 0.74638999f, 0.54474837f, 0.38229731f, + 0.67759502f, 0.54504555f, 0.49375138f, + 0.59279579f, 0.54514796f, 0.59279579f, + 0.49375141f, 0.54504526f, 0.67759514f, + 0.38229698f, 0.54474950f, 0.74638933f, + 0.26067549f, 0.54430223f, 0.79736024f, + 0.13190556f, 0.54382777f, 0.82876539f, + 0.00000000f, 0.54358286f, 0.83935553f, + 0.90286040f, 0.42993385f, -0.00000000f, + 0.89153200f, 0.43015870f, 0.14189546f, + 0.85786629f, 0.43059230f, 0.28045624f, + 0.80313194f, 0.43100107f, 0.41136029f, + 0.72917026f, 0.43127215f, 0.53133333f, + 0.63793516f, 0.43136698f, 0.63793516f, + 0.53133321f, 0.43127269f, 0.72917008f, + 0.41136014f, 0.43100169f, 0.80313170f, + 0.28045627f, 0.43059269f, 0.85786611f, + 0.14189564f, 0.43015781f, 0.89153230f, + 0.00000000f, 0.42993385f, 0.90286040f, + 0.00000000f, -0.42993385f, 0.90286040f, + -0.14189552f, -0.43015867f, 0.89153194f, + -0.28045613f, -0.43059322f, 0.85786587f, + -0.41136023f, -0.43100095f, 0.80313200f, + -0.53133309f, -0.43127316f, 0.72916991f, + -0.63793534f, -0.43136632f, 0.63793534f, + -0.72917002f, -0.43127266f, 0.53133315f, + -0.80313188f, -0.43100119f, 0.41136023f, + -0.85786611f, -0.43059263f, 0.28045616f, + -0.89153230f, -0.43015778f, 0.14189562f, + -0.90286040f, -0.42993385f, 0.00000000f, + 0.00000000f, -0.29152444f, 0.95656341f, + -0.15034524f, -0.29169479f, 0.94462180f, + -0.29719374f, -0.29202676f, 0.90906340f, + -0.43596110f, -0.29233694f, 0.85116220f, + -0.56315243f, -0.29254490f, 0.77283686f, + -0.67615676f, -0.29261592f, 0.67615676f, + -0.77283710f, -0.29254425f, 0.56315249f, + -0.85116208f, -0.29233718f, 0.43596104f, + -0.90906346f, -0.29202670f, 0.29719374f, + -0.94462162f, -0.29169548f, 0.15034510f, + -0.95656341f, -0.29152444f, 0.00000000f, + 0.00000000f, -0.06967134f, 0.99756998f, + -0.15679841f, -0.06971460f, 0.98516703f, + -0.30998084f, -0.06980211f, 0.94817692f, + -0.45476130f, -0.06988241f, 0.88786739f, + -0.58747447f, -0.06993677f, 0.80621493f, + -0.70537448f, -0.06995525f, 0.70537448f, + -0.80621505f, -0.06993637f, 0.58747447f, + -0.88786751f, -0.06988230f, 0.45476136f, + -0.94817698f, -0.06980199f, 0.30998087f, + -0.98516703f, -0.06971514f, 0.15679830f, + -0.99756998f, -0.06967134f, 0.00000000f, + -0.00000000f, 0.30806199f, 0.95136631f, + -0.14952739f, 0.30824158f, 0.93948323f, + -0.29557356f, 0.30858687f, 0.90410745f, + -0.43357921f, 0.30891240f, 0.84651184f, + -0.56007159f, 0.30912822f, 0.76860887f, + -0.67245591f, 0.30920231f, 0.67245591f, + -0.76860899f, 0.30912805f, 0.56007153f, + -0.84651166f, 0.30891296f, 0.43357912f, + -0.90410781f, 0.30858609f, 0.29557377f, + -0.93948382f, 0.30823973f, 0.14952743f, + -0.95136631f, 0.30806199f, 0.00000000f, + -0.00000000f, 0.80405891f, 0.59454966f, + -0.09341305f, 0.80424082f, 0.58691627f, + -0.18452507f, 0.80459291f, 0.56442964f, + -0.27050757f, 0.80492258f, 0.52813381f, + -0.34927556f, 0.80514228f, 0.47932491f, + -0.41930011f, 0.80521733f, 0.41930011f, + -0.47932535f, 0.80514193f, 0.34927583f, + -0.52813286f, 0.80492342f, 0.27050710f, + -0.56443053f, 0.80459219f, 0.18452539f, + -0.58691663f, 0.80424052f, 0.09341303f, + -0.59454966f, 0.80405891f, 0.00000000f, + -0.00000000f, 1.00000000f, 0.00000000f, + 0.00000002f, 1.00000000f, 0.00000000f, + -0.00000001f, 1.00000000f, -0.00000000f, + -0.00000014f, 1.00000000f, 0.00000037f, + 0.00000015f, 1.00000000f, -0.00000038f, + -0.00000000f, 1.00000000f, 0.00000000f, + 0.00000003f, 1.00000000f, 0.00000004f, + 0.00000035f, 1.00000000f, -0.00000018f, + -0.00000002f, 0.99999994f, -0.00000005f, + -0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 0.91737145f, -0.39803210f, + 0.06252954f, 0.91746408f, -0.39287356f, + 0.12348856f, 0.91764414f, -0.37772986f, + 0.18098900f, 0.91781259f, -0.35335946f, + 0.23365574f, 0.91792440f, -0.32065529f, + 0.28048533f, 0.91796297f, -0.28048533f, + 0.32065514f, 0.91792440f, -0.23365568f, + 0.35335934f, 0.91781276f, -0.18098891f, + 0.37773019f, 0.91764396f, -0.12348875f, + 0.39287323f, 0.91746432f, -0.06252949f, + 0.39803210f, 0.91737145f, -0.00000000f, + 0.00000000f, 0.78713351f, -0.61678272f, + 0.09690809f, 0.78732491f, -0.60887474f, + 0.19143508f, 0.78769588f, -0.58556628f, + 0.28064632f, 0.78804308f, -0.54792851f, + 0.36237431f, 0.78827441f, -0.49730095f, + 0.43502763f, 0.78835386f, -0.43502763f, + 0.49730113f, 0.78827435f, -0.36237440f, + 0.54792786f, 0.78804362f, -0.28064600f, + 0.58556694f, 0.78769529f, -0.19143537f, + 0.60887510f, 0.78732455f, -0.09690812f, + 0.61678272f, 0.78713351f, -0.00000000f, + 0.00000000f, 0.66170049f, -0.74976826f, + 0.11781615f, 0.66193908f, -0.74024051f, + 0.23279002f, 0.66240013f, -0.71206385f, + 0.34134525f, 0.66283208f, -0.66643608f, + 0.44081178f, 0.66311979f, -0.60494393f, + 0.52921647f, 0.66321933f, -0.52921647f, + 0.60494399f, 0.66311955f, -0.44081184f, + 0.66643548f, 0.66283280f, -0.34134495f, + 0.71206450f, 0.66239935f, -0.23279032f, + 0.74024075f, 0.66193885f, -0.11781613f, + 0.74976826f, 0.66170049f, -0.00000000f, + 0.00000000f, 0.54358286f, -0.83935553f, + 0.13190563f, 0.54382867f, -0.82876492f, + 0.26067528f, 0.54430288f, -0.79735982f, + 0.38229731f, 0.54474837f, -0.74638999f, + 0.49375138f, 0.54504555f, -0.67759502f, + 0.59279579f, 0.54514796f, -0.59279579f, + 0.67759514f, 0.54504526f, -0.49375141f, + 0.74638933f, 0.54474950f, -0.38229698f, + 0.79736024f, 0.54430223f, -0.26067549f, + 0.82876539f, 0.54382777f, -0.13190556f, + 0.83935553f, 0.54358286f, -0.00000000f, + 0.00000000f, 0.42993385f, -0.90286040f, + 0.14189546f, 0.43015870f, -0.89153200f, + 0.28045624f, 0.43059230f, -0.85786629f, + 0.41136029f, 0.43100107f, -0.80313194f, + 0.53133333f, 0.43127215f, -0.72917026f, + 0.63793516f, 0.43136698f, -0.63793516f, + 0.72917008f, 0.43127269f, -0.53133321f, + 0.80313170f, 0.43100169f, -0.41136014f, + 0.85786611f, 0.43059269f, -0.28045627f, + 0.89153230f, 0.43015778f, -0.14189564f, + 0.90286040f, 0.42993385f, -0.00000000f, + -0.00000000f, -0.42993385f, -0.90286040f, + 0.14189552f, -0.43015867f, -0.89153194f, + 0.28045613f, -0.43059322f, -0.85786587f, + 0.41136023f, -0.43100095f, -0.80313200f, + 0.53133309f, -0.43127316f, -0.72916991f, + 0.63793534f, -0.43136632f, -0.63793534f, + 0.72917002f, -0.43127266f, -0.53133315f, + 0.80313188f, -0.43100119f, -0.41136023f, + 0.85786611f, -0.43059263f, -0.28045616f, + 0.89153230f, -0.43015775f, -0.14189562f, + 0.90286040f, -0.42993385f, 0.00000000f, + -0.00000000f, -0.29152444f, -0.95656341f, + 0.15034524f, -0.29169479f, -0.94462180f, + 0.29719374f, -0.29202676f, -0.90906340f, + 0.43596110f, -0.29233694f, -0.85116220f, + 0.56315243f, -0.29254490f, -0.77283686f, + 0.67615676f, -0.29261592f, -0.67615676f, + 0.77283710f, -0.29254425f, -0.56315249f, + 0.85116208f, -0.29233718f, -0.43596104f, + 0.90906346f, -0.29202670f, -0.29719374f, + 0.94462162f, -0.29169548f, -0.15034510f, + 0.95656341f, -0.29152444f, 0.00000000f, + -0.00000000f, -0.06967134f, -0.99756998f, + 0.15679841f, -0.06971460f, -0.98516703f, + 0.30998084f, -0.06980211f, -0.94817692f, + 0.45476130f, -0.06988241f, -0.88786739f, + 0.58747447f, -0.06993677f, -0.80621493f, + 0.70537448f, -0.06995525f, -0.70537448f, + 0.80621505f, -0.06993637f, -0.58747447f, + 0.88786751f, -0.06988230f, -0.45476136f, + 0.94817698f, -0.06980199f, -0.30998087f, + 0.98516703f, -0.06971515f, -0.15679830f, + 0.99756998f, -0.06967134f, 0.00000000f, + 0.00000000f, 0.30806199f, -0.95136631f, + 0.14952739f, 0.30824158f, -0.93948323f, + 0.29557356f, 0.30858687f, -0.90410745f, + 0.43357921f, 0.30891240f, -0.84651184f, + 0.56007159f, 0.30912822f, -0.76860887f, + 0.67245591f, 0.30920231f, -0.67245591f, + 0.76860899f, 0.30912805f, -0.56007153f, + 0.84651166f, 0.30891296f, -0.43357912f, + 0.90410781f, 0.30858609f, -0.29557377f, + 0.93948382f, 0.30823976f, -0.14952743f, + 0.95136631f, 0.30806199f, 0.00000000f, + 0.00000000f, 0.80405891f, -0.59454966f, + 0.09341305f, 0.80424082f, -0.58691627f, + 0.18452507f, 0.80459291f, -0.56442964f, + 0.27050757f, 0.80492258f, -0.52813381f, + 0.34927556f, 0.80514228f, -0.47932491f, + 0.41930011f, 0.80521733f, -0.41930011f, + 0.47932535f, 0.80514193f, -0.34927583f, + 0.52813286f, 0.80492342f, -0.27050710f, + 0.56443053f, 0.80459219f, -0.18452539f, + 0.58691663f, 0.80424052f, -0.09341303f, + 0.59454966f, 0.80405891f, 0.00000000f, + 0.00000000f, 1.00000000f, -0.00000000f, + -0.00000002f, 1.00000000f, -0.00000000f, + 0.00000001f, 1.00000000f, 0.00000000f, + 0.00000014f, 1.00000000f, -0.00000037f, + -0.00000015f, 1.00000000f, 0.00000038f, + 0.00000000f, 1.00000000f, 0.00000000f, + -0.00000003f, 1.00000000f, -0.00000004f, + -0.00000035f, 1.00000000f, 0.00000018f, + 0.00000002f, 0.99999994f, 0.00000005f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 0.91737145f, 0.39803210f, + -0.06252954f, 0.91746408f, 0.39287356f, + -0.12348856f, 0.91764414f, 0.37772986f, + -0.18098900f, 0.91781259f, 0.35335946f, + -0.23365574f, 0.91792440f, 0.32065529f, + -0.28048533f, 0.91796297f, 0.28048533f, + -0.32065514f, 0.91792440f, 0.23365568f, + -0.35335934f, 0.91781276f, 0.18098891f, + -0.37773019f, 0.91764396f, 0.12348875f, + -0.39287323f, 0.91746432f, 0.06252949f, + -0.39803210f, 0.91737145f, 0.00000000f, + 0.00000000f, 0.78713351f, 0.61678272f, + -0.09690809f, 0.78732491f, 0.60887474f, + -0.19143508f, 0.78769588f, 0.58556628f, + -0.28064632f, 0.78804308f, 0.54792851f, + -0.36237431f, 0.78827441f, 0.49730095f, + -0.43502763f, 0.78835386f, 0.43502763f, + -0.49730113f, 0.78827435f, 0.36237440f, + -0.54792786f, 0.78804362f, 0.28064600f, + -0.58556694f, 0.78769529f, 0.19143537f, + -0.60887510f, 0.78732455f, 0.09690812f, + -0.61678272f, 0.78713351f, 0.00000000f, + 0.00000000f, 0.66170049f, 0.74976826f, + -0.11781615f, 0.66193908f, 0.74024051f, + -0.23279002f, 0.66240013f, 0.71206385f, + -0.34134525f, 0.66283208f, 0.66643608f, + -0.44081178f, 0.66311979f, 0.60494393f, + -0.52921647f, 0.66321933f, 0.52921647f, + -0.60494399f, 0.66311955f, 0.44081184f, + -0.66643548f, 0.66283280f, 0.34134495f, + -0.71206450f, 0.66239935f, 0.23279032f, + -0.74024075f, 0.66193885f, 0.11781613f, + -0.74976826f, 0.66170049f, 0.00000000f, + 0.00000000f, 0.54358286f, 0.83935553f, + -0.13190563f, 0.54382867f, 0.82876492f, + -0.26067528f, 0.54430288f, 0.79735982f, + -0.38229731f, 0.54474837f, 0.74638999f, + -0.49375138f, 0.54504555f, 0.67759502f, + -0.59279579f, 0.54514796f, 0.59279579f, + -0.67759514f, 0.54504526f, 0.49375141f, + -0.74638933f, 0.54474950f, 0.38229698f, + -0.79736024f, 0.54430223f, 0.26067549f, + -0.82876539f, 0.54382777f, 0.13190556f, + -0.83935553f, 0.54358286f, 0.00000000f, + 0.00000000f, 0.42993385f, 0.90286040f, + -0.14189546f, 0.43015870f, 0.89153200f, + -0.28045624f, 0.43059230f, 0.85786629f, + -0.41136029f, 0.43100107f, 0.80313194f, + -0.53133333f, 0.43127215f, 0.72917026f, + -0.63793516f, 0.43136698f, 0.63793516f, + -0.72917008f, 0.43127269f, 0.53133321f, + -0.80313170f, 0.43100169f, 0.41136014f, + -0.85786611f, 0.43059269f, 0.28045627f, + -0.89153230f, 0.43015781f, 0.14189564f, + -0.90286040f, 0.42993385f, 0.00000000f, + 0.90286040f, -0.42993385f, 0.00000000f, + 0.89153194f, -0.43015867f, 0.14189552f, + 0.85786587f, -0.43059322f, 0.28045613f, + 0.80313200f, -0.43100095f, 0.41136023f, + 0.72916991f, -0.43127316f, 0.53133309f, + 0.63793534f, -0.43136632f, 0.63793534f, + 0.53133315f, -0.43127266f, 0.72917002f, + 0.41136023f, -0.43100119f, 0.80313188f, + 0.28045616f, -0.43059263f, 0.85786611f, + 0.14189562f, -0.43015778f, 0.89153230f, + 0.00000000f, -0.42993385f, 0.90286040f, + 0.95656341f, -0.29152444f, 0.00000000f, + 0.94462180f, -0.29169479f, 0.15034524f, + 0.90906340f, -0.29202676f, 0.29719374f, + 0.85116220f, -0.29233694f, 0.43596110f, + 0.77283686f, -0.29254490f, 0.56315243f, + 0.67615676f, -0.29261592f, 0.67615676f, + 0.56315249f, -0.29254425f, 0.77283710f, + 0.43596104f, -0.29233718f, 0.85116208f, + 0.29719374f, -0.29202670f, 0.90906346f, + 0.15034510f, -0.29169548f, 0.94462162f, + 0.00000000f, -0.29152444f, 0.95656341f, + 0.99756998f, -0.06967134f, 0.00000000f, + 0.98516703f, -0.06971459f, 0.15679841f, + 0.94817692f, -0.06980211f, 0.30998084f, + 0.88786739f, -0.06988241f, 0.45476130f, + 0.80621493f, -0.06993677f, 0.58747447f, + 0.70537448f, -0.06995525f, 0.70537448f, + 0.58747447f, -0.06993637f, 0.80621505f, + 0.45476136f, -0.06988230f, 0.88786751f, + 0.30998087f, -0.06980199f, 0.94817698f, + 0.15679830f, -0.06971515f, 0.98516703f, + 0.00000000f, -0.06967134f, 0.99756998f, + 0.95136631f, 0.30806199f, 0.00000000f, + 0.93948323f, 0.30824158f, 0.14952739f, + 0.90410745f, 0.30858687f, 0.29557356f, + 0.84651184f, 0.30891240f, 0.43357921f, + 0.76860887f, 0.30912822f, 0.56007159f, + 0.67245591f, 0.30920231f, 0.67245591f, + 0.56007153f, 0.30912805f, 0.76860899f, + 0.43357912f, 0.30891296f, 0.84651166f, + 0.29557377f, 0.30858609f, 0.90410781f, + 0.14952743f, 0.30823970f, 0.93948382f, + -0.00000000f, 0.30806199f, 0.95136631f, + 0.59454966f, 0.80405891f, 0.00000000f, + 0.58691627f, 0.80424082f, 0.09341305f, + 0.56442964f, 0.80459291f, 0.18452507f, + 0.52813381f, 0.80492258f, 0.27050757f, + 0.47932491f, 0.80514228f, 0.34927556f, + 0.41930011f, 0.80521733f, 0.41930011f, + 0.34927583f, 0.80514193f, 0.47932535f, + 0.27050710f, 0.80492342f, 0.52813286f, + 0.18452539f, 0.80459219f, 0.56443053f, + 0.09341303f, 0.80424047f, 0.58691663f, + -0.00000000f, 0.80405891f, 0.59454966f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, -0.00000002f, + -0.00000000f, 1.00000000f, 0.00000001f, + 0.00000037f, 1.00000000f, 0.00000014f, + -0.00000038f, 1.00000000f, -0.00000015f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000004f, 1.00000000f, -0.00000003f, + -0.00000018f, 1.00000000f, -0.00000035f, + -0.00000005f, 0.99999994f, 0.00000002f, + 0.00000000f, 1.00000000f, 0.00000000f, + -0.00000000f, 1.00000000f, 0.00000000f, + -0.39803210f, 0.91737145f, 0.00000000f, + -0.39287356f, 0.91746408f, -0.06252954f, + -0.37772986f, 0.91764414f, -0.12348856f, + -0.35335946f, 0.91781259f, -0.18098900f, + -0.32065529f, 0.91792440f, -0.23365574f, + -0.28048533f, 0.91796297f, -0.28048533f, + -0.23365568f, 0.91792440f, -0.32065514f, + -0.18098891f, 0.91781276f, -0.35335934f, + -0.12348875f, 0.91764396f, -0.37773019f, + -0.06252949f, 0.91746432f, -0.39287323f, + 0.00000000f, 0.91737145f, -0.39803210f, + -0.61678272f, 0.78713351f, 0.00000000f, + -0.60887474f, 0.78732485f, -0.09690809f, + -0.58556628f, 0.78769588f, -0.19143508f, + -0.54792851f, 0.78804308f, -0.28064632f, + -0.49730095f, 0.78827441f, -0.36237431f, + -0.43502763f, 0.78835386f, -0.43502763f, + -0.36237440f, 0.78827435f, -0.49730113f, + -0.28064600f, 0.78804362f, -0.54792786f, + -0.19143537f, 0.78769529f, -0.58556694f, + -0.09690812f, 0.78732455f, -0.60887510f, + 0.00000000f, 0.78713351f, -0.61678272f, + -0.74976826f, 0.66170049f, 0.00000000f, + -0.74024051f, 0.66193908f, -0.11781615f, + -0.71206385f, 0.66240013f, -0.23279002f, + -0.66643608f, 0.66283208f, -0.34134522f, + -0.60494393f, 0.66311979f, -0.44081178f, + -0.52921647f, 0.66321933f, -0.52921647f, + -0.44081184f, 0.66311955f, -0.60494399f, + -0.34134495f, 0.66283280f, -0.66643548f, + -0.23279032f, 0.66239935f, -0.71206450f, + -0.11781613f, 0.66193885f, -0.74024075f, + 0.00000000f, 0.66170049f, -0.74976826f, + -0.83935553f, 0.54358286f, 0.00000000f, + -0.82876492f, 0.54382867f, -0.13190563f, + -0.79735982f, 0.54430288f, -0.26067528f, + -0.74638999f, 0.54474837f, -0.38229731f, + -0.67759502f, 0.54504555f, -0.49375138f, + -0.59279579f, 0.54514796f, -0.59279579f, + -0.49375141f, 0.54504526f, -0.67759514f, + -0.38229698f, 0.54474950f, -0.74638933f, + -0.26067549f, 0.54430223f, -0.79736024f, + -0.13190556f, 0.54382777f, -0.82876539f, + 0.00000000f, 0.54358286f, -0.83935553f, + -0.90286040f, 0.42993385f, 0.00000000f, + -0.89153200f, 0.43015870f, -0.14189546f, + -0.85786629f, 0.43059230f, -0.28045624f, + -0.80313194f, 0.43100107f, -0.41136029f, + -0.72917026f, 0.43127215f, -0.53133333f, + -0.63793516f, 0.43136698f, -0.63793516f, + -0.53133321f, 0.43127269f, -0.72917008f, + -0.41136014f, 0.43100169f, -0.80313170f, + -0.28045627f, 0.43059269f, -0.85786611f, + -0.14189564f, 0.43015781f, -0.89153230f, + 0.00000000f, 0.42993385f, -0.90286040f, + 0.90286052f, 0.42993352f, -0.00000000f, + 0.89153242f, 0.43015760f, 0.14189552f, + 0.85786629f, 0.43059236f, 0.28045624f, + 0.80313188f, 0.43100101f, 0.41136026f, + 0.72917014f, 0.43127251f, 0.53133327f, + 0.63793558f, 0.43136591f, 0.63793558f, + 0.53133327f, 0.43127245f, 0.72917008f, + 0.41136032f, 0.43100089f, 0.80313200f, + 0.28045630f, 0.43059239f, 0.85786623f, + 0.14189564f, 0.43015793f, 0.89153224f, + 0.00000000f, 0.42993346f, 0.90286052f, + 0.90429026f, 0.42691806f, -0.00000000f, + 0.89294571f, 0.42714140f, 0.14212063f, + 0.85922909f, 0.42757416f, 0.28090176f, + 0.80441010f, 0.42798156f, 0.41201493f, + 0.73033202f, 0.42825174f, 0.53217989f, + 0.63895255f, 0.42834479f, 0.63895255f, + 0.53217989f, 0.42825180f, 0.73033208f, + 0.41201496f, 0.42798129f, 0.80441022f, + 0.28090179f, 0.42757428f, 0.85922897f, + 0.14212044f, 0.42714158f, 0.89294565f, + 0.00000000f, 0.42691806f, 0.90429026f, + 0.90856975f, 0.41773322f, -0.00000000f, + 0.89717585f, 0.41795388f, 0.14279392f, + 0.86330783f, 0.41838130f, 0.28223523f, + 0.80823600f, 0.41878363f, 0.41397455f, + 0.73381007f, 0.41905072f, 0.53471422f, + 0.64199662f, 0.41914272f, 0.64199662f, + 0.53471422f, 0.41905066f, 0.73381001f, + 0.41397452f, 0.41878343f, 0.80823600f, + 0.28223526f, 0.41838151f, 0.86330777f, + 0.14279366f, 0.41795388f, 0.89717585f, + 0.00000000f, 0.41773322f, 0.90856981f, + 0.91565990f, 0.40195388f, -0.00000000f, + 0.90418458f, 0.40216953f, 0.14390936f, + 0.87006593f, 0.40258732f, 0.28444463f, + 0.81457525f, 0.40298060f, 0.41722149f, + 0.73957306f, 0.40324163f, 0.53891361f, + 0.64704084f, 0.40333158f, 0.64704084f, + 0.53891361f, 0.40324160f, 0.73957300f, + 0.41722152f, 0.40298060f, 0.81457531f, + 0.28444469f, 0.40258750f, 0.87006587f, + 0.14390932f, 0.40216964f, 0.90418446f, + 0.00000000f, 0.40195388f, 0.91565990f, + 0.92546070f, 0.37884355f, -0.00000000f, + 0.91387308f, 0.37905136f, 0.14545137f, + 0.87940860f, 0.37945345f, 0.28749895f, + 0.82333946f, 0.37983215f, 0.42171046f, + 0.74754065f, 0.38008353f, 0.54471958f, + 0.65401483f, 0.38016999f, 0.65401483f, + 0.54471952f, 0.38008350f, 0.74754065f, + 0.42171052f, 0.37983203f, 0.82333946f, + 0.28749895f, 0.37945345f, 0.87940860f, + 0.14545140f, 0.37905133f, 0.91387314f, + 0.00000000f, 0.37884352f, 0.92546076f, + 0.93774879f, 0.34731436f, 0.00000000f, + 0.92602080f, 0.34750980f, 0.14738478f, + 0.89112353f, 0.34788847f, 0.29132879f, + 0.83432990f, 0.34824491f, 0.42733964f, + 0.75753278f, 0.34848174f, 0.55200058f, + 0.66276079f, 0.34856305f, 0.66276079f, + 0.55200052f, 0.34848171f, 0.75753278f, + 0.42733967f, 0.34824488f, 0.83432984f, + 0.29132882f, 0.34788850f, 0.89112341f, + 0.14738482f, 0.34750980f, 0.92602086f, + 0.00000000f, 0.34731436f, 0.93774879f, + 0.95206839f, 0.30588529f, -0.00000000f, + 0.94017762f, 0.30606282f, 0.14963792f, + 0.90477723f, 0.30640665f, 0.29579252f, + 0.84714013f, 0.30673033f, 0.43390101f, + 0.76918012f, 0.30694523f, 0.56048775f, + 0.67295587f, 0.30701905f, 0.67295587f, + 0.56048775f, 0.30694523f, 0.76918018f, + 0.43390101f, 0.30673033f, 0.84714013f, + 0.29579255f, 0.30640674f, 0.90477717f, + 0.14963797f, 0.30606288f, 0.94017768f, + 0.00000000f, 0.30588529f, 0.95206839f, + 0.96755409f, 0.25266385f, -0.00000000f, + 0.95548815f, 0.25281537f, 0.15207484f, + 0.91954517f, 0.25310859f, 0.30062059f, + 0.86099726f, 0.25338471f, 0.44099870f, + 0.78178006f, 0.25356814f, 0.56966931f, + 0.68398511f, 0.25363129f, 0.68398517f, + 0.56966925f, 0.25356814f, 0.78178018f, + 0.44099870f, 0.25338474f, 0.86099732f, + 0.30062056f, 0.25310862f, 0.91954517f, + 0.15207487f, 0.25281525f, 0.95548820f, + 0.00000000f, 0.25266385f, 0.96755409f, + 0.98266166f, 0.18540785f, -0.00000000f, + 0.97042567f, 0.18552248f, 0.15445223f, + 0.93395483f, 0.18574443f, 0.30533135f, + 0.87451965f, 0.18595363f, 0.44792461f, + 0.79407656f, 0.18609245f, 0.57862937f, + 0.69474876f, 0.18614022f, 0.69474876f, + 0.57862937f, 0.18609245f, 0.79407656f, + 0.44792467f, 0.18595368f, 0.87451971f, + 0.30533132f, 0.18574448f, 0.93395483f, + 0.15445219f, 0.18552248f, 0.97042572f, + 0.00000000f, 0.18540785f, 0.98266166f, + 0.99480653f, 0.10178412f, -0.00000000f, + 0.98243433f, 0.10184865f, 0.15636347f, + 0.94554055f, 0.10197352f, 0.30911899f, + 0.88539290f, 0.10209139f, 0.45349392f, + 0.80396467f, 0.10216936f, 0.58583462f, + 0.70340455f, 0.10219638f, 0.70340455f, + 0.58583462f, 0.10216936f, 0.80396467f, + 0.45349392f, 0.10209133f, 0.88539290f, + 0.30911896f, 0.10197353f, 0.94554049f, + 0.15636343f, 0.10184866f, 0.98243433f, + 0.00000000f, 0.10178412f, 0.99480653f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.98756981f, 0.00000000f, 0.15718083f, + 0.95049530f, 0.00000000f, 0.31073883f, + 0.89004338f, 0.00000000f, 0.45587584f, + 0.80819386f, 0.00000000f, 0.58891642f, + 0.70710677f, 0.00000000f, 0.70710677f, + 0.58891648f, 0.00000000f, 0.80819392f, + 0.45587584f, 0.00000000f, 0.89004332f, + 0.31073880f, 0.00000000f, 0.95049530f, + 0.15718080f, 0.00000000f, 0.98756981f, + 0.00000000f, 0.00000000f, 1.00000000f, + 0.00000000f, 0.42993355f, -0.90286052f, + 0.14189552f, 0.43015760f, -0.89153242f, + 0.28045624f, 0.43059236f, -0.85786629f, + 0.41136029f, 0.43100107f, -0.80313194f, + 0.53133321f, 0.43127251f, -0.72917008f, + 0.63793558f, 0.43136591f, -0.63793558f, + 0.72917008f, 0.43127251f, -0.53133327f, + 0.80313194f, 0.43100089f, -0.41136026f, + 0.85786623f, 0.43059239f, -0.28045630f, + 0.89153218f, 0.43015793f, -0.14189562f, + 0.90286052f, 0.42993352f, -0.00000000f, + 0.00000000f, 0.42691806f, -0.90429026f, + 0.14212063f, 0.42714140f, -0.89294571f, + 0.28090176f, 0.42757416f, -0.85922909f, + 0.41201493f, 0.42798156f, -0.80441010f, + 0.53217989f, 0.42825174f, -0.73033202f, + 0.63895255f, 0.42834479f, -0.63895255f, + 0.73033208f, 0.42825180f, -0.53217989f, + 0.80441022f, 0.42798129f, -0.41201496f, + 0.85922897f, 0.42757428f, -0.28090179f, + 0.89294565f, 0.42714158f, -0.14212044f, + 0.90429026f, 0.42691806f, -0.00000000f, + 0.00000000f, 0.41773325f, -0.90856969f, + 0.14279392f, 0.41795388f, -0.89717585f, + 0.28223523f, 0.41838136f, -0.86330783f, + 0.41397452f, 0.41878363f, -0.80823594f, + 0.53471416f, 0.41905075f, -0.73381007f, + 0.64199662f, 0.41914275f, -0.64199662f, + 0.73381001f, 0.41905072f, -0.53471422f, + 0.80823600f, 0.41878349f, -0.41397452f, + 0.86330777f, 0.41838151f, -0.28223526f, + 0.89717585f, 0.41795391f, -0.14279366f, + 0.90856975f, 0.41773322f, -0.00000000f, + 0.00000000f, 0.40195388f, -0.91565990f, + 0.14390936f, 0.40216953f, -0.90418458f, + 0.28444463f, 0.40258732f, -0.87006593f, + 0.41722149f, 0.40298060f, -0.81457525f, + 0.53891361f, 0.40324163f, -0.73957306f, + 0.64704084f, 0.40333158f, -0.64704084f, + 0.73957300f, 0.40324160f, -0.53891361f, + 0.81457531f, 0.40298060f, -0.41722152f, + 0.87006587f, 0.40258750f, -0.28444469f, + 0.90418446f, 0.40216964f, -0.14390932f, + 0.91565990f, 0.40195388f, -0.00000000f, + 0.00000000f, 0.37884355f, -0.92546070f, + 0.14545137f, 0.37905136f, -0.91387308f, + 0.28749895f, 0.37945345f, -0.87940860f, + 0.42171046f, 0.37983215f, -0.82333946f, + 0.54471958f, 0.38008353f, -0.74754065f, + 0.65401483f, 0.38017002f, -0.65401483f, + 0.74754071f, 0.38008356f, -0.54471958f, + 0.82333952f, 0.37983206f, -0.42171052f, + 0.87940860f, 0.37945351f, -0.28749895f, + 0.91387314f, 0.37905133f, -0.14545140f, + 0.92546070f, 0.37884355f, -0.00000000f, + 0.00000000f, 0.34731436f, -0.93774879f, + 0.14738478f, 0.34750980f, -0.92602080f, + 0.29132879f, 0.34788847f, -0.89112353f, + 0.42733964f, 0.34824491f, -0.83432990f, + 0.55200058f, 0.34848174f, -0.75753278f, + 0.66276079f, 0.34856305f, -0.66276079f, + 0.75753278f, 0.34848171f, -0.55200052f, + 0.83432984f, 0.34824488f, -0.42733967f, + 0.89112341f, 0.34788850f, -0.29132882f, + 0.92602086f, 0.34750980f, -0.14738482f, + 0.93774879f, 0.34731436f, 0.00000000f, + 0.00000000f, 0.30588529f, -0.95206845f, + 0.14963792f, 0.30606282f, -0.94017762f, + 0.29579252f, 0.30640665f, -0.90477723f, + 0.43390101f, 0.30673030f, -0.84714013f, + 0.56048775f, 0.30694523f, -0.76918012f, + 0.67295587f, 0.30701905f, -0.67295587f, + 0.76918018f, 0.30694523f, -0.56048775f, + 0.84714013f, 0.30673033f, -0.43390101f, + 0.90477717f, 0.30640674f, -0.29579255f, + 0.94017768f, 0.30606288f, -0.14963797f, + 0.95206839f, 0.30588529f, 0.00000000f, + 0.00000000f, 0.25266385f, -0.96755409f, + 0.15207484f, 0.25281537f, -0.95548815f, + 0.30062059f, 0.25310859f, -0.91954517f, + 0.44099876f, 0.25338471f, -0.86099732f, + 0.56966931f, 0.25356814f, -0.78178006f, + 0.68398511f, 0.25363129f, -0.68398511f, + 0.78178018f, 0.25356814f, -0.56966925f, + 0.86099732f, 0.25338474f, -0.44099864f, + 0.91954517f, 0.25310862f, -0.30062056f, + 0.95548820f, 0.25281525f, -0.15207487f, + 0.96755409f, 0.25266385f, 0.00000000f, + 0.00000000f, 0.18540785f, -0.98266166f, + 0.15445223f, 0.18552248f, -0.97042567f, + 0.30533135f, 0.18574443f, -0.93395483f, + 0.44792461f, 0.18595363f, -0.87451965f, + 0.57862937f, 0.18609245f, -0.79407662f, + 0.69474876f, 0.18614022f, -0.69474876f, + 0.79407656f, 0.18609245f, -0.57862937f, + 0.87451965f, 0.18595366f, -0.44792467f, + 0.93395483f, 0.18574448f, -0.30533132f, + 0.97042572f, 0.18552248f, -0.15445219f, + 0.98266166f, 0.18540785f, 0.00000000f, + 0.00000000f, 0.10178412f, -0.99480653f, + 0.15636347f, 0.10184865f, -0.98243433f, + 0.30911899f, 0.10197351f, -0.94554049f, + 0.45349398f, 0.10209139f, -0.88539296f, + 0.58583462f, 0.10216935f, -0.80396461f, + 0.70340455f, 0.10219637f, -0.70340455f, + 0.80396461f, 0.10216935f, -0.58583462f, + 0.88539290f, 0.10209133f, -0.45349395f, + 0.94554055f, 0.10197353f, -0.30911899f, + 0.98243439f, 0.10184865f, -0.15636343f, + 0.99480653f, 0.10178412f, -0.00000000f, + 0.00000000f, 0.00000000f, -1.00000000f, + 0.15718083f, 0.00000000f, -0.98756981f, + 0.31073883f, 0.00000000f, -0.95049530f, + 0.45587587f, 0.00000000f, -0.89004344f, + 0.58891642f, 0.00000000f, -0.80819386f, + 0.70710677f, 0.00000000f, -0.70710677f, + 0.80819386f, 0.00000000f, -0.58891642f, + 0.89004338f, 0.00000000f, -0.45587584f, + 0.95049530f, 0.00000000f, -0.31073883f, + 0.98756981f, 0.00000000f, -0.15718079f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.00000000f, 0.42993346f, 0.90286052f, + -0.14189553f, 0.43015754f, 0.89153242f, + -0.28045624f, 0.43059230f, 0.85786629f, + -0.41136029f, 0.43100101f, 0.80313194f, + -0.53133327f, 0.43127251f, 0.72917014f, + -0.63793558f, 0.43136591f, 0.63793558f, + -0.72917008f, 0.43127245f, 0.53133327f, + -0.80313200f, 0.43100089f, 0.41136032f, + -0.85786623f, 0.43059239f, 0.28045630f, + -0.89153230f, 0.43015793f, 0.14189564f, + -0.90286052f, 0.42993352f, 0.00000000f, + 0.00000000f, 0.42691806f, 0.90429026f, + -0.14212061f, 0.42714134f, 0.89294565f, + -0.28090173f, 0.42757410f, 0.85922903f, + -0.41201493f, 0.42798156f, 0.80441010f, + -0.53217989f, 0.42825174f, 0.73033202f, + -0.63895255f, 0.42834479f, 0.63895255f, + -0.73033208f, 0.42825180f, 0.53217989f, + -0.80441022f, 0.42798129f, 0.41201496f, + -0.85922897f, 0.42757428f, 0.28090179f, + -0.89294565f, 0.42714158f, 0.14212044f, + -0.90429026f, 0.42691806f, 0.00000000f, + 0.00000000f, 0.41773322f, 0.90856981f, + -0.14279392f, 0.41795379f, 0.89717585f, + -0.28223521f, 0.41838124f, 0.86330777f, + -0.41397455f, 0.41878363f, 0.80823600f, + -0.53471422f, 0.41905072f, 0.73381007f, + -0.64199662f, 0.41914272f, 0.64199662f, + -0.73381001f, 0.41905066f, 0.53471422f, + -0.80823600f, 0.41878343f, 0.41397452f, + -0.86330777f, 0.41838151f, 0.28223526f, + -0.89717585f, 0.41795388f, 0.14279366f, + -0.90856975f, 0.41773322f, 0.00000000f, + 0.00000000f, 0.40195388f, 0.91565990f, + -0.14390936f, 0.40216953f, 0.90418458f, + -0.28444460f, 0.40258726f, 0.87006587f, + -0.41722149f, 0.40298060f, 0.81457525f, + -0.53891361f, 0.40324163f, 0.73957306f, + -0.64704084f, 0.40333158f, 0.64704084f, + -0.73957300f, 0.40324160f, 0.53891361f, + -0.81457531f, 0.40298060f, 0.41722152f, + -0.87006587f, 0.40258750f, 0.28444469f, + -0.90418446f, 0.40216964f, 0.14390932f, + -0.91565990f, 0.40195388f, 0.00000000f, + 0.00000000f, 0.37884352f, 0.92546076f, + -0.14545135f, 0.37905130f, 0.91387308f, + -0.28749895f, 0.37945345f, 0.87940860f, + -0.42171046f, 0.37983215f, 0.82333946f, + -0.54471958f, 0.38008353f, 0.74754065f, + -0.65401483f, 0.38016999f, 0.65401483f, + -0.74754065f, 0.38008350f, 0.54471958f, + -0.82333946f, 0.37983203f, 0.42171055f, + -0.87940860f, 0.37945351f, 0.28749898f, + -0.91387314f, 0.37905133f, 0.14545140f, + -0.92546070f, 0.37884355f, 0.00000000f, + 0.00000000f, 0.34731436f, 0.93774879f, + -0.14738478f, 0.34750980f, 0.92602080f, + -0.29132879f, 0.34788847f, 0.89112353f, + -0.42733964f, 0.34824497f, 0.83432990f, + -0.55200058f, 0.34848174f, 0.75753278f, + -0.66276079f, 0.34856305f, 0.66276079f, + -0.75753278f, 0.34848171f, 0.55200052f, + -0.83432984f, 0.34824488f, 0.42733967f, + -0.89112341f, 0.34788850f, 0.29132882f, + -0.92602086f, 0.34750980f, 0.14738482f, + -0.93774879f, 0.34731436f, 0.00000000f, + 0.00000000f, 0.30588529f, 0.95206839f, + -0.14963792f, 0.30606285f, 0.94017768f, + -0.29579252f, 0.30640665f, 0.90477723f, + -0.43390101f, 0.30673033f, 0.84714013f, + -0.56048775f, 0.30694523f, 0.76918012f, + -0.67295587f, 0.30701905f, 0.67295587f, + -0.76918018f, 0.30694523f, 0.56048775f, + -0.84714013f, 0.30673033f, 0.43390101f, + -0.90477717f, 0.30640674f, 0.29579255f, + -0.94017768f, 0.30606288f, 0.14963797f, + -0.95206839f, 0.30588529f, 0.00000000f, + 0.00000000f, 0.25266385f, 0.96755409f, + -0.15207484f, 0.25281540f, 0.95548815f, + -0.30062059f, 0.25310859f, 0.91954517f, + -0.44099870f, 0.25338471f, 0.86099726f, + -0.56966931f, 0.25356814f, 0.78178006f, + -0.68398511f, 0.25363129f, 0.68398517f, + -0.78178018f, 0.25356814f, 0.56966925f, + -0.86099732f, 0.25338474f, 0.44099870f, + -0.91954517f, 0.25310862f, 0.30062056f, + -0.95548820f, 0.25281525f, 0.15207487f, + -0.96755409f, 0.25266385f, 0.00000000f, + 0.00000000f, 0.18540785f, 0.98266166f, + -0.15445222f, 0.18552248f, 0.97042567f, + -0.30533135f, 0.18574443f, 0.93395483f, + -0.44792467f, 0.18595368f, 0.87451977f, + -0.57862937f, 0.18609245f, 0.79407656f, + -0.69474876f, 0.18614022f, 0.69474876f, + -0.79407656f, 0.18609245f, 0.57862937f, + -0.87451971f, 0.18595368f, 0.44792467f, + -0.93395483f, 0.18574448f, 0.30533132f, + -0.97042572f, 0.18552248f, 0.15445219f, + -0.98266166f, 0.18540785f, 0.00000000f, + 0.00000000f, 0.10178412f, 0.99480653f, + -0.15636347f, 0.10184866f, 0.98243439f, + -0.30911899f, 0.10197352f, 0.94554055f, + -0.45349392f, 0.10209139f, 0.88539290f, + -0.58583462f, 0.10216936f, 0.80396467f, + -0.70340455f, 0.10219638f, 0.70340455f, + -0.80396467f, 0.10216936f, 0.58583462f, + -0.88539290f, 0.10209133f, 0.45349392f, + -0.94554055f, 0.10197353f, 0.30911899f, + -0.98243439f, 0.10184865f, 0.15636343f, + -0.99480653f, 0.10178412f, 0.00000000f, + 0.00000000f, 0.00000000f, 1.00000000f, + -0.15718085f, 0.00000000f, 0.98756987f, + -0.31073883f, 0.00000000f, 0.95049530f, + -0.45587584f, 0.00000000f, 0.89004338f, + -0.58891648f, 0.00000000f, 0.80819386f, + -0.70710677f, 0.00000000f, 0.70710677f, + -0.80819386f, 0.00000000f, 0.58891642f, + -0.89004338f, 0.00000000f, 0.45587584f, + -0.95049530f, 0.00000000f, 0.31073883f, + -0.98756981f, 0.00000000f, 0.15718079f, + -1.00000000f, 0.00000000f, -0.00000000f, + -0.90286052f, 0.42993352f, 0.00000000f, + -0.89153242f, 0.43015760f, -0.14189552f, + -0.85786629f, 0.43059236f, -0.28045624f, + -0.80313194f, 0.43100107f, -0.41136029f, + -0.72917014f, 0.43127251f, -0.53133327f, + -0.63793558f, 0.43136591f, -0.63793558f, + -0.53133327f, 0.43127257f, -0.72917008f, + -0.41136026f, 0.43100089f, -0.80313194f, + -0.28045627f, 0.43059239f, -0.85786617f, + -0.14189562f, 0.43015796f, -0.89153224f, + 0.00000000f, 0.42993355f, -0.90286052f, + -0.90429026f, 0.42691806f, 0.00000000f, + -0.89294571f, 0.42714143f, -0.14212063f, + -0.85922909f, 0.42757416f, -0.28090176f, + -0.80441010f, 0.42798156f, -0.41201493f, + -0.73033202f, 0.42825174f, -0.53217989f, + -0.63895255f, 0.42834479f, -0.63895255f, + -0.53217995f, 0.42825186f, -0.73033214f, + -0.41201496f, 0.42798129f, -0.80441022f, + -0.28090179f, 0.42757428f, -0.85922897f, + -0.14212044f, 0.42714158f, -0.89294565f, + 0.00000000f, 0.42691806f, -0.90429026f, + -0.90856975f, 0.41773322f, 0.00000000f, + -0.89717585f, 0.41795388f, -0.14279392f, + -0.86330783f, 0.41838130f, -0.28223523f, + -0.80823594f, 0.41878363f, -0.41397452f, + -0.73381007f, 0.41905075f, -0.53471416f, + -0.64199662f, 0.41914275f, -0.64199662f, + -0.53471422f, 0.41905072f, -0.73381001f, + -0.41397452f, 0.41878349f, -0.80823600f, + -0.28223523f, 0.41838151f, -0.86330771f, + -0.14279366f, 0.41795391f, -0.89717579f, + 0.00000000f, 0.41773325f, -0.90856969f, + -0.91565990f, 0.40195388f, 0.00000000f, + -0.90418458f, 0.40216953f, -0.14390936f, + -0.87006593f, 0.40258732f, -0.28444463f, + -0.81457525f, 0.40298060f, -0.41722149f, + -0.73957306f, 0.40324163f, -0.53891361f, + -0.64704084f, 0.40333158f, -0.64704084f, + -0.53891361f, 0.40324160f, -0.73957300f, + -0.41722152f, 0.40298060f, -0.81457531f, + -0.28444469f, 0.40258750f, -0.87006587f, + -0.14390932f, 0.40216964f, -0.90418446f, + 0.00000000f, 0.40195388f, -0.91565990f, + -0.92546070f, 0.37884355f, 0.00000000f, + -0.91387308f, 0.37905136f, -0.14545137f, + -0.87940860f, 0.37945345f, -0.28749895f, + -0.82333946f, 0.37983215f, -0.42171046f, + -0.74754065f, 0.38008353f, -0.54471952f, + -0.65401483f, 0.38017002f, -0.65401483f, + -0.54471958f, 0.38008356f, -0.74754071f, + -0.42171055f, 0.37983206f, -0.82333952f, + -0.28749898f, 0.37945351f, -0.87940860f, + -0.14545140f, 0.37905133f, -0.91387314f, + 0.00000000f, 0.37884355f, -0.92546070f, + -0.93774879f, 0.34731436f, 0.00000000f, + -0.92602080f, 0.34750980f, -0.14738476f, + -0.89112353f, 0.34788847f, -0.29132879f, + -0.83432990f, 0.34824491f, -0.42733961f, + -0.75753278f, 0.34848174f, -0.55200058f, + -0.66276079f, 0.34856305f, -0.66276079f, + -0.55200058f, 0.34848171f, -0.75753284f, + -0.42733967f, 0.34824488f, -0.83432984f, + -0.29132882f, 0.34788850f, -0.89112341f, + -0.14738482f, 0.34750980f, -0.92602086f, + 0.00000000f, 0.34731436f, -0.93774879f, + -0.95206839f, 0.30588529f, 0.00000000f, + -0.94017762f, 0.30606282f, -0.14963792f, + -0.90477723f, 0.30640665f, -0.29579252f, + -0.84714019f, 0.30673030f, -0.43390101f, + -0.76918012f, 0.30694523f, -0.56048775f, + -0.67295587f, 0.30701905f, -0.67295587f, + -0.56048775f, 0.30694520f, -0.76918018f, + -0.43390101f, 0.30673033f, -0.84714013f, + -0.29579252f, 0.30640671f, -0.90477717f, + -0.14963795f, 0.30606285f, -0.94017762f, + 0.00000000f, 0.30588529f, -0.95206845f, + -0.96755409f, 0.25266385f, 0.00000000f, + -0.95548815f, 0.25281537f, -0.15207484f, + -0.91954517f, 0.25310859f, -0.30062059f, + -0.86099732f, 0.25338471f, -0.44099876f, + -0.78178006f, 0.25356814f, -0.56966931f, + -0.68398511f, 0.25363129f, -0.68398511f, + -0.56966925f, 0.25356814f, -0.78178018f, + -0.44099870f, 0.25338474f, -0.86099732f, + -0.30062056f, 0.25310862f, -0.91954517f, + -0.15207487f, 0.25281525f, -0.95548820f, + 0.00000000f, 0.25266385f, -0.96755409f, + -0.98266166f, 0.18540785f, 0.00000000f, + -0.97042567f, 0.18552248f, -0.15445223f, + -0.93395483f, 0.18574443f, -0.30533135f, + -0.87451965f, 0.18595363f, -0.44792461f, + -0.79407662f, 0.18609245f, -0.57862937f, + -0.69474876f, 0.18614022f, -0.69474876f, + -0.57862937f, 0.18609245f, -0.79407662f, + -0.44792467f, 0.18595366f, -0.87451965f, + -0.30533135f, 0.18574448f, -0.93395483f, + -0.15445219f, 0.18552245f, -0.97042567f, + 0.00000000f, 0.18540785f, -0.98266166f, + -0.99480653f, 0.10178412f, 0.00000000f, + -0.98243433f, 0.10184865f, -0.15636347f, + -0.94554049f, 0.10197351f, -0.30911899f, + -0.88539296f, 0.10209139f, -0.45349398f, + -0.80396461f, 0.10216935f, -0.58583462f, + -0.70340455f, 0.10219637f, -0.70340455f, + -0.58583462f, 0.10216935f, -0.80396461f, + -0.45349395f, 0.10209133f, -0.88539290f, + -0.30911899f, 0.10197353f, -0.94554055f, + -0.15636343f, 0.10184865f, -0.98243439f, + 0.00000000f, 0.10178412f, -0.99480653f, + -1.00000000f, 0.00000000f, -0.00000000f, + -0.98756981f, -0.00000000f, -0.15718083f, + -0.95049530f, -0.00000000f, -0.31073886f, + -0.89004344f, -0.00000000f, -0.45587587f, + -0.80819386f, -0.00000000f, -0.58891642f, + -0.70710677f, -0.00000000f, -0.70710677f, + -0.58891648f, -0.00000000f, -0.80819392f, + -0.45587584f, -0.00000000f, -0.89004338f, + -0.31073880f, -0.00000000f, -0.95049530f, + -0.15718079f, -0.00000000f, -0.98756987f, + 0.00000000f, 0.00000000f, -1.00000000f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.98756981f, 0.00000000f, 0.15718083f, + 0.95049524f, 0.00000000f, 0.31073883f, + 0.89004338f, 0.00000000f, 0.45587587f, + 0.80819386f, 0.00000000f, 0.58891642f, + 0.70710677f, 0.00000000f, 0.70710677f, + 0.58891648f, 0.00000000f, 0.80819392f, + 0.45587584f, 0.00000000f, 0.89004332f, + 0.31073880f, 0.00000000f, 0.95049530f, + 0.15718080f, 0.00000000f, 0.98756981f, + 0.00000000f, 0.00000000f, 1.00000000f, + 0.97627187f, -0.21654844f, 0.00000000f, + 0.96410769f, -0.21668050f, 0.15344663f, + 0.92785990f, -0.21693638f, 0.30333877f, + 0.86879992f, -0.21717757f, 0.44499502f, + 0.78887528f, -0.21733762f, 0.57483923f, + 0.69019580f, -0.21739276f, 0.69019580f, + 0.57483923f, -0.21733767f, 0.78887522f, + 0.44499508f, -0.21717753f, 0.86879987f, + 0.30333874f, -0.21693641f, 0.92786002f, + 0.15344663f, -0.21668044f, 0.96410781f, + 0.00000000f, -0.21654841f, 0.97627187f, + 0.91505456f, -0.40332985f, -0.00000003f, + 0.90358627f, -0.40354598f, 0.14381410f, + 0.86948907f, -0.40396458f, 0.28425604f, + 0.81403410f, -0.40435863f, 0.41694435f, + 0.73908108f, -0.40462026f, 0.53855520f, + 0.64661026f, -0.40471029f, 0.64661026f, + 0.53855515f, -0.40462023f, 0.73908103f, + 0.41694435f, -0.40435848f, 0.81403404f, + 0.28425604f, -0.40396464f, 0.86948895f, + 0.14381401f, -0.40354609f, 0.90358627f, + 0.00000000f, -0.40332985f, 0.91505468f, + 0.83741641f, -0.54656547f, -0.00000004f, + 0.82684904f, -0.54681069f, 0.13160066f, + 0.79551321f, -0.54728562f, 0.26007158f, + 0.74465787f, -0.54773253f, 0.38141018f, + 0.67602134f, -0.54802895f, 0.49260470f, + 0.59141880f, -0.54813099f, 0.59141880f, + 0.49260470f, -0.54802889f, 0.67602134f, + 0.38141015f, -0.54773235f, 0.74465799f, + 0.26007164f, -0.54728562f, 0.79551315f, + 0.13160071f, -0.54681093f, 0.82684886f, + 0.00000000f, -0.54656547f, 0.83741641f, + 0.76153940f, -0.64811856f, -0.00000005f, + 0.75187135f, -0.64835888f, 0.11966719f, + 0.72326809f, -0.64882445f, 0.23645295f, + 0.67693549f, -0.64926237f, 0.34672299f, + 0.61448330f, -0.64955258f, 0.44776294f, + 0.53756469f, -0.64965260f, 0.53756469f, + 0.44776297f, -0.64955264f, 0.61448330f, + 0.34672314f, -0.64926219f, 0.67693549f, + 0.23645304f, -0.64882457f, 0.72326809f, + 0.11966727f, -0.64835912f, 0.75187117f, + 0.00000000f, -0.64811856f, 0.76153940f, + 0.69810015f, -0.71600008f, 0.00000000f, + 0.68919653f, -0.71622342f, 0.10969204f, + 0.66290152f, -0.71665543f, 0.21671773f, + 0.62036926f, -0.71706140f, 0.31775001f, + 0.56309497f, -0.71733099f, 0.41031727f, + 0.49259701f, -0.71742338f, 0.49259701f, + 0.41031730f, -0.71733087f, 0.56309515f, + 0.31775004f, -0.71706140f, 0.62036926f, + 0.21671782f, -0.71665543f, 0.66290152f, + 0.10969196f, -0.71622348f, 0.68919641f, + 0.00000000f, -0.71600014f, 0.69810015f, + 0.65312678f, -0.75724858f, 0.00000000f, + 0.64477170f, -0.75745511f, 0.10262139f, + 0.62012511f, -0.75785500f, 0.20273319f, + 0.58029598f, -0.75823092f, 0.29722479f, + 0.52669704f, -0.75848007f, 0.38379461f, + 0.46074849f, -0.75856555f, 0.46074849f, + 0.38379470f, -0.75847995f, 0.52669698f, + 0.29722482f, -0.75823075f, 0.58029616f, + 0.20273310f, -0.75785506f, 0.62012488f, + 0.10262127f, -0.75745541f, 0.64477140f, + 0.00000000f, -0.75724858f, 0.65312666f, + 0.63308728f, -0.77408046f, 0.00000000f, + 0.62497813f, -0.77427888f, 0.09947111f, + 0.60106885f, -0.77466297f, 0.19650327f, + 0.56244695f, -0.77502376f, 0.28808260f, + 0.51048630f, -0.77526313f, 0.37198231f, + 0.44656453f, -0.77534527f, 0.44656453f, + 0.37198231f, -0.77526301f, 0.51048636f, + 0.28808266f, -0.77502370f, 0.56244695f, + 0.19650330f, -0.77466315f, 0.60106856f, + 0.09947103f, -0.77427906f, 0.62497783f, + 0.00000000f, -0.77408046f, 0.63308716f, + 0.65312707f, -0.75724828f, 0.00000000f, + 0.64477211f, -0.75745481f, 0.10262145f, + 0.62012541f, -0.75785458f, 0.20273332f, + 0.58029628f, -0.75823045f, 0.29722497f, + 0.52669716f, -0.75847977f, 0.38379481f, + 0.46074894f, -0.75856501f, 0.46074894f, + 0.38379484f, -0.75847983f, 0.52669722f, + 0.29722500f, -0.75823039f, 0.58029646f, + 0.20273322f, -0.75785488f, 0.62012511f, + 0.10262135f, -0.75745499f, 0.64477187f, + 0.00000000f, -0.75724828f, 0.65312707f, + 0.76153988f, -0.64811796f, 0.00000000f, + 0.75187159f, -0.64835846f, 0.11966733f, + 0.72326809f, -0.64882451f, 0.23645300f, + 0.67693573f, -0.64926189f, 0.34672317f, + 0.61448365f, -0.64955217f, 0.44776317f, + 0.53756529f, -0.64965147f, 0.53756529f, + 0.44776306f, -0.64955246f, 0.61448342f, + 0.34672317f, -0.64926195f, 0.67693573f, + 0.23645295f, -0.64882445f, 0.72326815f, + 0.11966728f, -0.64835876f, 0.75187135f, + 0.00000000f, -0.64811832f, 0.76153970f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.98756987f, 0.00000000f, 0.15718076f, + 0.95049542f, 0.00000000f, 0.31073883f, + 0.89004332f, 0.00000000f, 0.45587590f, + 0.80819392f, 0.00000000f, 0.58891642f, + 0.70710683f, 0.00000000f, 0.70710683f, + 0.58891648f, 0.00000000f, 0.80819392f, + 0.45587584f, 0.00000000f, 0.89004332f, + 0.31073889f, 0.00000000f, 0.95049536f, + 0.15718086f, 0.00000000f, 0.98756987f, + 0.00000000f, 0.00000000f, 1.00000000f, + 0.00000000f, 0.00000000f, -1.00000000f, + 0.15718083f, 0.00000000f, -0.98756981f, + 0.31073886f, 0.00000000f, -0.95049542f, + 0.45587587f, 0.00000000f, -0.89004338f, + 0.58891648f, 0.00000000f, -0.80819392f, + 0.70710677f, 0.00000000f, -0.70710677f, + 0.80819392f, 0.00000000f, -0.58891642f, + 0.89004332f, 0.00000000f, -0.45587584f, + 0.95049536f, 0.00000000f, -0.31073883f, + 0.98756981f, 0.00000000f, -0.15718080f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.00000000f, -0.21654844f, -0.97627187f, + 0.15344663f, -0.21668050f, -0.96410769f, + 0.30333880f, -0.21693645f, -0.92786008f, + 0.44499505f, -0.21717761f, -0.86879992f, + 0.57483923f, -0.21733770f, -0.78887528f, + 0.69019580f, -0.21739276f, -0.69019580f, + 0.78887522f, -0.21733771f, -0.57483923f, + 0.86879992f, -0.21717757f, -0.44499508f, + 0.92785990f, -0.21693642f, -0.30333874f, + 0.96410769f, -0.21668047f, -0.15344661f, + 0.97627187f, -0.21654844f, 0.00000000f, + 0.00000000f, -0.40332985f, -0.91505456f, + 0.14381412f, -0.40354598f, -0.90358627f, + 0.28425601f, -0.40396458f, -0.86948901f, + 0.41694438f, -0.40435869f, -0.81403404f, + 0.53855515f, -0.40462032f, -0.73908097f, + 0.64661026f, -0.40471029f, -0.64661026f, + 0.73908108f, -0.40462029f, -0.53855515f, + 0.81403416f, -0.40435857f, -0.41694435f, + 0.86948901f, -0.40396467f, -0.28425604f, + 0.90358621f, -0.40354609f, -0.14381403f, + 0.91505456f, -0.40332985f, 0.00000000f, + 0.00000000f, -0.54656547f, -0.83741641f, + 0.13160068f, -0.54681069f, -0.82684904f, + 0.26007161f, -0.54728562f, -0.79551315f, + 0.38141018f, -0.54773253f, -0.74465787f, + 0.49260476f, -0.54802901f, -0.67602134f, + 0.59141880f, -0.54813099f, -0.59141880f, + 0.67602134f, -0.54802889f, -0.49260470f, + 0.74465799f, -0.54773235f, -0.38141015f, + 0.79551315f, -0.54728562f, -0.26007164f, + 0.82684886f, -0.54681093f, -0.13160068f, + 0.83741641f, -0.54656547f, 0.00000000f, + 0.00000000f, -0.64811856f, -0.76153940f, + 0.11966727f, -0.64835888f, -0.75187135f, + 0.23645303f, -0.64882445f, -0.72326821f, + 0.34672299f, -0.64926225f, -0.67693543f, + 0.44776300f, -0.64955264f, -0.61448330f, + 0.53756469f, -0.64965254f, -0.53756469f, + 0.61448336f, -0.64955264f, -0.44776294f, + 0.67693555f, -0.64926219f, -0.34672308f, + 0.72326809f, -0.64882457f, -0.23645304f, + 0.75187117f, -0.64835912f, -0.11966727f, + 0.76153940f, -0.64811856f, 0.00000000f, + 0.00000000f, -0.71600008f, -0.69810015f, + 0.10969204f, -0.71622342f, -0.68919653f, + 0.21671776f, -0.71665537f, -0.66290158f, + 0.31775004f, -0.71706134f, -0.62036926f, + 0.41031733f, -0.71733087f, -0.56309509f, + 0.49259701f, -0.71742338f, -0.49259701f, + 0.56309521f, -0.71733081f, -0.41031736f, + 0.62036926f, -0.71706134f, -0.31775004f, + 0.66290152f, -0.71665537f, -0.21671782f, + 0.68919653f, -0.71622342f, -0.10969198f, + 0.69810015f, -0.71600008f, 0.00000000f, + 0.00000000f, -0.75724858f, -0.65312678f, + 0.10262139f, -0.75745511f, -0.64477170f, + 0.20273322f, -0.75785494f, -0.62012511f, + 0.29722479f, -0.75823075f, -0.58029604f, + 0.38379470f, -0.75847995f, -0.52669716f, + 0.46074849f, -0.75856555f, -0.46074849f, + 0.52669710f, -0.75847995f, -0.38379475f, + 0.58029622f, -0.75823069f, -0.29722485f, + 0.62012500f, -0.75785506f, -0.20273314f, + 0.64477146f, -0.75745541f, -0.10262129f, + 0.65312678f, -0.75724858f, 0.00000000f, + 0.00000000f, -0.77408046f, -0.63308728f, + 0.09947111f, -0.77427888f, -0.62497813f, + 0.19650328f, -0.77466291f, -0.60106891f, + 0.28808263f, -0.77502370f, -0.56244701f, + 0.37198243f, -0.77526301f, -0.51048648f, + 0.44656453f, -0.77534527f, -0.44656453f, + 0.51048642f, -0.77526295f, -0.37198234f, + 0.56244701f, -0.77502364f, -0.28808269f, + 0.60106862f, -0.77466303f, -0.19650333f, + 0.62497795f, -0.77427900f, -0.09947104f, + 0.63308728f, -0.77408046f, 0.00000000f, + 0.00000000f, -0.75724828f, -0.65312707f, + 0.10262145f, -0.75745481f, -0.64477211f, + 0.20273340f, -0.75785440f, -0.62012565f, + 0.29722497f, -0.75823045f, -0.58029628f, + 0.38379493f, -0.75847960f, -0.52669734f, + 0.46074894f, -0.75856501f, -0.46074894f, + 0.52669722f, -0.75847983f, -0.38379484f, + 0.58029646f, -0.75823039f, -0.29722500f, + 0.62012535f, -0.75785476f, -0.20273331f, + 0.64477187f, -0.75745499f, -0.10262135f, + 0.65312707f, -0.75724828f, 0.00000000f, + 0.00000000f, -0.64811796f, -0.76153988f, + 0.11966733f, -0.64835846f, -0.75187159f, + 0.23645303f, -0.64882457f, -0.72326809f, + 0.34672326f, -0.64926159f, -0.67693597f, + 0.44776332f, -0.64955187f, -0.61448377f, + 0.53756529f, -0.64965147f, -0.53756529f, + 0.61448365f, -0.64955217f, -0.44776317f, + 0.67693591f, -0.64926165f, -0.34672329f, + 0.72326815f, -0.64882445f, -0.23645295f, + 0.75187165f, -0.64835852f, -0.11966733f, + 0.76153988f, -0.64811796f, 0.00000000f, + 0.00000000f, 0.00000000f, -1.00000000f, + 0.15718076f, 0.00000000f, -0.98756987f, + 0.31073883f, 0.00000000f, -0.95049542f, + 0.45587590f, 0.00000000f, -0.89004332f, + 0.58891642f, 0.00000000f, -0.80819392f, + 0.70710683f, 0.00000000f, -0.70710683f, + 0.80819392f, 0.00000000f, -0.58891648f, + 0.89004332f, 0.00000000f, -0.45587584f, + 0.95049536f, 0.00000000f, -0.31073889f, + 0.98756987f, 0.00000000f, -0.15718086f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.00000000f, 0.00000000f, 1.00000000f, + -0.15718085f, 0.00000000f, 0.98756987f, + -0.31073883f, 0.00000000f, 0.95049524f, + -0.45587587f, 0.00000000f, 0.89004338f, + -0.58891648f, 0.00000000f, 0.80819386f, + -0.70710677f, 0.00000000f, 0.70710677f, + -0.80819392f, 0.00000000f, 0.58891648f, + -0.89004332f, 0.00000000f, 0.45587584f, + -0.95049536f, 0.00000000f, 0.31073883f, + -0.98756981f, 0.00000000f, 0.15718080f, + -1.00000000f, 0.00000000f, -0.00000000f, + 0.00000000f, -0.21654841f, 0.97627187f, + -0.15344664f, -0.21668047f, 0.96410775f, + -0.30333877f, -0.21693638f, 0.92785990f, + -0.44499502f, -0.21717757f, 0.86879992f, + -0.57483923f, -0.21733765f, 0.78887528f, + -0.69019574f, -0.21739271f, 0.69019574f, + -0.78887522f, -0.21733767f, 0.57483923f, + -0.86879987f, -0.21717753f, 0.44499508f, + -0.92785990f, -0.21693642f, 0.30333874f, + -0.96410769f, -0.21668047f, 0.15344661f, + -0.97627187f, -0.21654844f, -0.00000000f, + 0.00000000f, -0.40332985f, 0.91505468f, + -0.14381412f, -0.40354595f, 0.90358633f, + -0.28425604f, -0.40396458f, 0.86948907f, + -0.41694441f, -0.40435863f, 0.81403410f, + -0.53855515f, -0.40462026f, 0.73908097f, + -0.64661026f, -0.40471026f, 0.64661026f, + -0.73908103f, -0.40462023f, 0.53855515f, + -0.81403416f, -0.40435854f, 0.41694435f, + -0.86948901f, -0.40396467f, 0.28425601f, + -0.90358621f, -0.40354609f, 0.14381400f, + -0.91505456f, -0.40332985f, -0.00000003f, + 0.00000000f, -0.54656547f, 0.83741641f, + -0.13160071f, -0.54681069f, 0.82684904f, + -0.26007161f, -0.54728562f, 0.79551321f, + -0.38141018f, -0.54773253f, 0.74465787f, + -0.49260470f, -0.54802895f, 0.67602134f, + -0.59141886f, -0.54813093f, 0.59141880f, + -0.67602134f, -0.54802889f, 0.49260467f, + -0.74465799f, -0.54773235f, 0.38141012f, + -0.79551315f, -0.54728562f, 0.26007164f, + -0.82684886f, -0.54681093f, 0.13160068f, + -0.83741641f, -0.54656547f, -0.00000004f, + 0.00000000f, -0.64811856f, 0.76153940f, + -0.11966725f, -0.64835888f, 0.75187135f, + -0.23645300f, -0.64882445f, 0.72326809f, + -0.34672302f, -0.64926237f, 0.67693537f, + -0.44776300f, -0.64955264f, 0.61448330f, + -0.53756469f, -0.64965260f, 0.53756469f, + -0.61448336f, -0.64955264f, 0.44776294f, + -0.67693555f, -0.64926219f, 0.34672308f, + -0.72326815f, -0.64882451f, 0.23645297f, + -0.75187117f, -0.64835912f, 0.11966722f, + -0.76153940f, -0.64811856f, -0.00000005f, + 0.00000000f, -0.71600014f, 0.69810015f, + -0.10969204f, -0.71622342f, 0.68919653f, + -0.21671773f, -0.71665543f, 0.66290152f, + -0.31775001f, -0.71706140f, 0.62036926f, + -0.41031730f, -0.71733093f, 0.56309503f, + -0.49259698f, -0.71742344f, 0.49259698f, + -0.56309515f, -0.71733087f, 0.41031730f, + -0.62036926f, -0.71706140f, 0.31775004f, + -0.66290152f, -0.71665537f, 0.21671782f, + -0.68919653f, -0.71622342f, 0.10969198f, + -0.69810015f, -0.71600008f, -0.00000000f, + 0.00000000f, -0.75724858f, 0.65312666f, + -0.10262137f, -0.75745529f, 0.64477158f, + -0.20273319f, -0.75785500f, 0.62012511f, + -0.29722479f, -0.75823092f, 0.58029598f, + -0.38379467f, -0.75848001f, 0.52669710f, + -0.46074843f, -0.75856560f, 0.46074843f, + -0.52669698f, -0.75847995f, 0.38379470f, + -0.58029616f, -0.75823075f, 0.29722482f, + -0.62012500f, -0.75785506f, 0.20273314f, + -0.64477146f, -0.75745541f, 0.10262129f, + -0.65312678f, -0.75724858f, -0.00000000f, + 0.00000000f, -0.77408046f, 0.63308716f, + -0.09947111f, -0.77427888f, 0.62497813f, + -0.19650327f, -0.77466297f, 0.60106885f, + -0.28808260f, -0.77502376f, 0.56244695f, + -0.37198234f, -0.77526301f, 0.51048636f, + -0.44656447f, -0.77534533f, 0.44656447f, + -0.51048636f, -0.77526301f, 0.37198231f, + -0.56244695f, -0.77502370f, 0.28808266f, + -0.60106862f, -0.77466303f, 0.19650333f, + -0.62497795f, -0.77427900f, 0.09947104f, + -0.63308728f, -0.77408046f, -0.00000000f, + 0.00000000f, -0.75724828f, 0.65312707f, + -0.10262142f, -0.75745505f, 0.64477187f, + -0.20273332f, -0.75785458f, 0.62012541f, + -0.29722497f, -0.75823045f, 0.58029628f, + -0.38379493f, -0.75847960f, 0.52669734f, + -0.46074894f, -0.75856501f, 0.46074894f, + -0.52669722f, -0.75847983f, 0.38379484f, + -0.58029646f, -0.75823039f, 0.29722500f, + -0.62012535f, -0.75785476f, 0.20273331f, + -0.64477187f, -0.75745499f, 0.10262135f, + -0.65312707f, -0.75724828f, -0.00000000f, + 0.00000000f, -0.64811832f, 0.76153970f, + -0.11966730f, -0.64835882f, 0.75187141f, + -0.23645303f, -0.64882457f, 0.72326809f, + -0.34672317f, -0.64926189f, 0.67693573f, + -0.44776317f, -0.64955217f, 0.61448365f, + -0.53756529f, -0.64965147f, 0.53756529f, + -0.61448342f, -0.64955246f, 0.44776306f, + -0.67693573f, -0.64926195f, 0.34672317f, + -0.72326815f, -0.64882445f, 0.23645295f, + -0.75187165f, -0.64835852f, 0.11966733f, + -0.76153988f, -0.64811796f, -0.00000000f, + 0.00000000f, 0.00000000f, 1.00000000f, + -0.15718076f, 0.00000000f, 0.98756987f, + -0.31073883f, 0.00000000f, 0.95049542f, + -0.45587590f, 0.00000000f, 0.89004332f, + -0.58891642f, 0.00000000f, 0.80819392f, + -0.70710683f, 0.00000000f, 0.70710683f, + -0.80819392f, 0.00000000f, 0.58891648f, + -0.89004332f, 0.00000000f, 0.45587584f, + -0.95049536f, 0.00000000f, 0.31073889f, + -0.98756987f, 0.00000000f, 0.15718086f, + -1.00000000f, 0.00000000f, -0.00000000f, + -1.00000000f, 0.00000000f, -0.00000000f, + -0.98756981f, -0.00000000f, -0.15718083f, + -0.95049542f, -0.00000000f, -0.31073886f, + -0.89004338f, -0.00000000f, -0.45587587f, + -0.80819392f, -0.00000000f, -0.58891648f, + -0.70710677f, -0.00000000f, -0.70710677f, + -0.58891642f, -0.00000000f, -0.80819392f, + -0.45587584f, -0.00000000f, -0.89004332f, + -0.31073883f, -0.00000000f, -0.95049536f, + -0.15718080f, -0.00000000f, -0.98756981f, + 0.00000000f, 0.00000000f, -1.00000000f, + -0.97627187f, -0.21654844f, -0.00000000f, + -0.96410769f, -0.21668050f, -0.15344663f, + -0.92786008f, -0.21693645f, -0.30333880f, + -0.86879992f, -0.21717761f, -0.44499505f, + -0.78887528f, -0.21733770f, -0.57483923f, + -0.69019580f, -0.21739276f, -0.69019580f, + -0.57483923f, -0.21733771f, -0.78887522f, + -0.44499508f, -0.21717757f, -0.86879992f, + -0.30333874f, -0.21693642f, -0.92785990f, + -0.15344661f, -0.21668047f, -0.96410769f, + 0.00000000f, -0.21654844f, -0.97627187f, + -0.91505456f, -0.40332985f, -0.00000000f, + -0.90358627f, -0.40354595f, -0.14381412f, + -0.86948901f, -0.40396458f, -0.28425601f, + -0.81403404f, -0.40435869f, -0.41694438f, + -0.73908097f, -0.40462032f, -0.53855515f, + -0.64661026f, -0.40471029f, -0.64661026f, + -0.53855515f, -0.40462029f, -0.73908108f, + -0.41694435f, -0.40435857f, -0.81403416f, + -0.28425604f, -0.40396467f, -0.86948901f, + -0.14381403f, -0.40354609f, -0.90358621f, + 0.00000000f, -0.40332985f, -0.91505456f, + -0.83741641f, -0.54656547f, -0.00000000f, + -0.82684904f, -0.54681069f, -0.13160068f, + -0.79551315f, -0.54728562f, -0.26007161f, + -0.74465787f, -0.54773253f, -0.38141018f, + -0.67602134f, -0.54802901f, -0.49260476f, + -0.59141880f, -0.54813099f, -0.59141880f, + -0.49260470f, -0.54802889f, -0.67602134f, + -0.38141015f, -0.54773235f, -0.74465799f, + -0.26007164f, -0.54728562f, -0.79551315f, + -0.13160068f, -0.54681093f, -0.82684886f, + 0.00000000f, -0.54656547f, -0.83741641f, + -0.76153940f, -0.64811856f, -0.00000000f, + -0.75187135f, -0.64835888f, -0.11966727f, + -0.72326821f, -0.64882445f, -0.23645303f, + -0.67693543f, -0.64926225f, -0.34672299f, + -0.61448330f, -0.64955264f, -0.44776300f, + -0.53756469f, -0.64965254f, -0.53756469f, + -0.44776294f, -0.64955264f, -0.61448336f, + -0.34672308f, -0.64926219f, -0.67693555f, + -0.23645304f, -0.64882457f, -0.72326809f, + -0.11966727f, -0.64835912f, -0.75187117f, + 0.00000000f, -0.64811856f, -0.76153940f, + -0.69810015f, -0.71600008f, -0.00000000f, + -0.68919653f, -0.71622342f, -0.10969204f, + -0.66290158f, -0.71665537f, -0.21671776f, + -0.62036926f, -0.71706134f, -0.31775004f, + -0.56309509f, -0.71733087f, -0.41031733f, + -0.49259701f, -0.71742338f, -0.49259701f, + -0.41031736f, -0.71733081f, -0.56309521f, + -0.31775004f, -0.71706134f, -0.62036926f, + -0.21671782f, -0.71665537f, -0.66290152f, + -0.10969198f, -0.71622342f, -0.68919653f, + 0.00000000f, -0.71600008f, -0.69810015f, + -0.65312678f, -0.75724858f, -0.00000000f, + -0.64477170f, -0.75745511f, -0.10262139f, + -0.62012511f, -0.75785494f, -0.20273322f, + -0.58029604f, -0.75823075f, -0.29722479f, + -0.52669716f, -0.75847995f, -0.38379470f, + -0.46074849f, -0.75856555f, -0.46074849f, + -0.38379475f, -0.75847995f, -0.52669710f, + -0.29722485f, -0.75823069f, -0.58029622f, + -0.20273314f, -0.75785506f, -0.62012500f, + -0.10262129f, -0.75745541f, -0.64477146f, + 0.00000000f, -0.75724858f, -0.65312678f, + -0.63308728f, -0.77408046f, -0.00000000f, + -0.62497813f, -0.77427888f, -0.09947111f, + -0.60106891f, -0.77466291f, -0.19650328f, + -0.56244701f, -0.77502370f, -0.28808263f, + -0.51048648f, -0.77526301f, -0.37198243f, + -0.44656453f, -0.77534527f, -0.44656453f, + -0.37198234f, -0.77526295f, -0.51048642f, + -0.28808269f, -0.77502364f, -0.56244701f, + -0.19650333f, -0.77466303f, -0.60106862f, + -0.09947104f, -0.77427900f, -0.62497795f, + 0.00000000f, -0.77408046f, -0.63308728f, + -0.65312707f, -0.75724828f, -0.00000000f, + -0.64477211f, -0.75745481f, -0.10262145f, + -0.62012565f, -0.75785440f, -0.20273340f, + -0.58029628f, -0.75823045f, -0.29722497f, + -0.52669734f, -0.75847960f, -0.38379493f, + -0.46074894f, -0.75856501f, -0.46074894f, + -0.38379484f, -0.75847983f, -0.52669722f, + -0.29722500f, -0.75823039f, -0.58029646f, + -0.20273331f, -0.75785476f, -0.62012535f, + -0.10262135f, -0.75745499f, -0.64477187f, + 0.00000000f, -0.75724828f, -0.65312707f, + -0.76153988f, -0.64811796f, -0.00000000f, + -0.75187159f, -0.64835846f, -0.11966733f, + -0.72326809f, -0.64882457f, -0.23645303f, + -0.67693597f, -0.64926159f, -0.34672326f, + -0.61448377f, -0.64955187f, -0.44776332f, + -0.53756529f, -0.64965147f, -0.53756529f, + -0.44776317f, -0.64955217f, -0.61448365f, + -0.34672329f, -0.64926165f, -0.67693591f, + -0.23645295f, -0.64882445f, -0.72326815f, + -0.11966733f, -0.64835852f, -0.75187165f, + 0.00000000f, -0.64811796f, -0.76153988f, + -1.00000000f, 0.00000000f, -0.00000000f, + -0.98756987f, -0.00000000f, -0.15718076f, + -0.95049536f, -0.00000000f, -0.31073886f, + -0.89004332f, -0.00000000f, -0.45587590f, + -0.80819392f, -0.00000000f, -0.58891642f, + -0.70710683f, -0.00000000f, -0.70710683f, + -0.58891648f, -0.00000000f, -0.80819392f, + -0.45587584f, -0.00000000f, -0.89004332f, + -0.31073889f, -0.00000000f, -0.95049536f, + -0.15718086f, -0.00000000f, -0.98756987f, + 0.00000000f, 0.00000000f, -1.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.10903012f, 0.99403834f, -0.00000000f, + 0.10761157f, 0.99404776f, 0.01699242f, + 0.10344726f, 0.99406654f, 0.03362026f, + 0.09674154f, 0.99408495f, 0.04935813f, + 0.08773998f, 0.99409753f, 0.06381103f, + 0.07668582f, 0.99410182f, 0.07668582f, + 0.06381113f, 0.99409747f, 0.08774014f, + 0.04935821f, 0.99408495f, 0.09674170f, + 0.03362055f, 0.99406654f, 0.10344723f, + 0.01699220f, 0.99404776f, 0.10761159f, + 0.00000000f, 0.99403834f, 0.10903012f, + 0.36039943f, 0.93279809f, -0.00000000f, + 0.35574505f, 0.93289334f, 0.05617307f, + 0.34204447f, 0.93308479f, 0.11116764f, + 0.31993434f, 0.93327194f, 0.16323482f, + 0.29020390f, 0.93340009f, 0.21105911f, + 0.25365368f, 0.93344504f, 0.25365371f, + 0.21105954f, 0.93340015f, 0.29020363f, + 0.16323458f, 0.93327194f, 0.31993434f, + 0.11116784f, 0.93308485f, 0.34204453f, + 0.05617263f, 0.93289340f, 0.35574511f, + 0.00000000f, 0.93279803f, 0.36039948f, + 0.88340259f, 0.46861488f, -0.00000000f, + 0.87244546f, 0.46889016f, 0.13777141f, + 0.83972138f, 0.46944135f, 0.27293366f, + 0.78624606f, 0.46997890f, 0.40116957f, + 0.71368778f, 0.47034633f, 0.51906073f, + 0.62396044f, 0.47047496f, 0.62396044f, + 0.51906067f, 0.47034639f, 0.71368772f, + 0.40116948f, 0.46997908f, 0.78624594f, + 0.27293396f, 0.46944159f, 0.83972114f, + 0.13777111f, 0.46889019f, 0.87244546f, + 0.00000000f, 0.46861485f, 0.88340265f, + 0.93448776f, -0.35599536f, 0.00000000f, + 0.92294377f, -0.35626677f, 0.14576957f, + 0.88841265f, -0.35681954f, 0.28879514f, + 0.83191514f, -0.35736835f, 0.42450577f, + 0.75519395f, -0.35774899f, 0.54927009f, + 0.66027242f, -0.35788339f, 0.66027254f, + 0.54926991f, -0.35774904f, 0.75519413f, + 0.42450562f, -0.35736838f, 0.83191508f, + 0.28879505f, -0.35681972f, 0.88841271f, + 0.14576939f, -0.35626683f, 0.92294371f, + 0.00000000f, -0.35599536f, 0.93448776f, + 0.78086889f, -0.62469494f, 0.00000000f, + 0.77104694f, -0.62501729f, 0.12181899f, + 0.74185717f, -0.62567103f, 0.24121319f, + 0.69436729f, -0.62631625f, 0.35437542f, + 0.63014323f, -0.62676215f, 0.45835435f, + 0.55089575f, -0.62691921f, 0.55089581f, + 0.45835426f, -0.62676227f, 0.63014323f, + 0.35437530f, -0.62631637f, 0.69436723f, + 0.24121325f, -0.62567103f, 0.74185717f, + 0.12181915f, -0.62501746f, 0.77104694f, + 0.00000000f, -0.62469494f, 0.78086889f, + 0.73252982f, -0.68073493f, 0.00000000f, + 0.72325647f, -0.68104768f, 0.11434216f, + 0.69576049f, -0.68168211f, 0.22633344f, + 0.65112102f, -0.68230885f, 0.33240935f, + 0.59084833f, -0.68274176f, 0.42983946f, + 0.51655358f, -0.68289435f, 0.51655358f, + 0.42983946f, -0.68274188f, 0.59084815f, + 0.33240965f, -0.68230897f, 0.65112090f, + 0.22633316f, -0.68168223f, 0.69576037f, + 0.11434201f, -0.68104780f, 0.72325629f, + 0.00000000f, -0.68073487f, 0.73252988f, + 0.76869494f, -0.63961560f, 0.00000000f, + 0.75896567f, -0.63995206f, 0.12013541f, + 0.73011720f, -0.64063656f, 0.23772609f, + 0.68329000f, -0.64131474f, 0.34904197f, + 0.62007785f, -0.64178431f, 0.45123872f, + 0.54217160f, -0.64195007f, 0.54217166f, + 0.45123863f, -0.64178431f, 0.62007785f, + 0.34904230f, -0.64131451f, 0.68328995f, + 0.23772585f, -0.64063650f, 0.73011738f, + 0.12013473f, -0.63995212f, 0.75896567f, + 0.00000000f, -0.63961560f, 0.76869500f, + 0.89442712f, -0.44721353f, 0.00000000f, + 0.88320464f, -0.44757605f, 0.14009038f, + 0.84982812f, -0.44832191f, 0.27712727f, + 0.79552603f, -0.44907007f, 0.40678552f, + 0.72211319f, -0.44959316f, 0.52575517f, + 0.63154542f, -0.44977871f, 0.63154531f, + 0.52575505f, -0.44959310f, 0.72211331f, + 0.40678558f, -0.44906983f, 0.79552603f, + 0.27712739f, -0.44832191f, 0.84982806f, + 0.14009029f, -0.44757628f, 0.88320440f, + 0.00000000f, -0.44721353f, 0.89442724f, + 0.98920345f, 0.14654903f, -0.00000000f, + 0.97693658f, 0.14651901f, 0.15532872f, + 0.94031984f, 0.14642376f, 0.30717853f, + 0.88055933f, 0.14629376f, 0.45079187f, + 0.79958105f, 0.14618376f, 0.58249503f, + 0.69951504f, 0.14614125f, 0.69951504f, + 0.58249503f, 0.14618391f, 0.79958105f, + 0.45079190f, 0.14629373f, 0.88055933f, + 0.30717853f, 0.14642377f, 0.94031984f, + 0.15532865f, 0.14651905f, 0.97693658f, + 0.00000000f, 0.14654903f, 0.98920339f, + 0.59999961f, 0.80000031f, -0.00000000f, + 0.59229910f, 0.80018437f, 0.09426992f, + 0.56961125f, 0.80054069f, 0.18621883f, + 0.53298515f, 0.80087584f, 0.27299231f, + 0.48373166f, 0.80109745f, 0.35248643f, + 0.42315489f, 0.80117404f, 0.42315492f, + 0.35248655f, 0.80109763f, 0.48373106f, + 0.27299243f, 0.80087572f, 0.53298527f, + 0.18621899f, 0.80054086f, 0.56961107f, + 0.09426976f, 0.80018455f, 0.59229887f, + 0.00000000f, 0.80000025f, 0.59999967f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 0.99403834f, -0.10903012f, + 0.01699242f, 0.99404776f, -0.10761157f, + 0.03362026f, 0.99406654f, -0.10344726f, + 0.04935814f, 0.99408495f, -0.09674154f, + 0.06381103f, 0.99409747f, -0.08773997f, + 0.07668582f, 0.99410182f, -0.07668582f, + 0.08774014f, 0.99409753f, -0.06381113f, + 0.09674169f, 0.99408489f, -0.04935820f, + 0.10344723f, 0.99406660f, -0.03362057f, + 0.10761159f, 0.99404776f, -0.01699222f, + 0.10903012f, 0.99403834f, -0.00000000f, + 0.00000000f, 0.93279809f, -0.36039945f, + 0.05617307f, 0.93289340f, -0.35574505f, + 0.11116765f, 0.93308485f, -0.34204447f, + 0.16323480f, 0.93327194f, -0.31993431f, + 0.21105908f, 0.93340009f, -0.29020390f, + 0.25365368f, 0.93344510f, -0.25365368f, + 0.29020360f, 0.93340009f, -0.21105953f, + 0.31993431f, 0.93327194f, -0.16323458f, + 0.34204447f, 0.93308485f, -0.11116787f, + 0.35574511f, 0.93289340f, -0.05617262f, + 0.36039943f, 0.93279809f, -0.00000000f, + 0.00000000f, 0.46861491f, -0.88340265f, + 0.13777143f, 0.46889022f, -0.87244546f, + 0.27293366f, 0.46944135f, -0.83972138f, + 0.40116957f, 0.46997890f, -0.78624600f, + 0.51906073f, 0.47034633f, -0.71368778f, + 0.62396044f, 0.47047499f, -0.62396038f, + 0.71368778f, 0.47034642f, -0.51906067f, + 0.78624600f, 0.46997911f, -0.40116948f, + 0.83972114f, 0.46944162f, -0.27293390f, + 0.87244540f, 0.46889022f, -0.13777119f, + 0.88340265f, 0.46861491f, -0.00000000f, + 0.00000000f, -0.35599536f, -0.93448770f, + 0.14576958f, -0.35626677f, -0.92294377f, + 0.28879517f, -0.35681960f, -0.88841271f, + 0.42450577f, -0.35736835f, -0.83191514f, + 0.54927015f, -0.35774902f, -0.75519395f, + 0.66027242f, -0.35788339f, -0.66027242f, + 0.75519413f, -0.35774907f, -0.54926997f, + 0.83191514f, -0.35736847f, -0.42450568f, + 0.88841271f, -0.35681972f, -0.28879502f, + 0.92294371f, -0.35626686f, -0.14576937f, + 0.93448776f, -0.35599536f, 0.00000000f, + 0.00000000f, -0.62469494f, -0.78086889f, + 0.12181899f, -0.62501729f, -0.77104694f, + 0.24121317f, -0.62567103f, -0.74185711f, + 0.35437539f, -0.62631625f, -0.69436729f, + 0.45835432f, -0.62676215f, -0.63014323f, + 0.55089581f, -0.62691921f, -0.55089581f, + 0.63014317f, -0.62676215f, -0.45835423f, + 0.69436723f, -0.62631643f, -0.35437530f, + 0.74185711f, -0.62567097f, -0.24121323f, + 0.77104694f, -0.62501740f, -0.12181912f, + 0.78086889f, -0.62469494f, 0.00000000f, + 0.00000000f, -0.68073487f, -0.73252988f, + 0.11434216f, -0.68104768f, -0.72325647f, + 0.22633334f, -0.68168211f, -0.69576049f, + 0.33240932f, -0.68230891f, -0.65112108f, + 0.42983937f, -0.68274176f, -0.59084839f, + 0.51655358f, -0.68289435f, -0.51655364f, + 0.59084809f, -0.68274182f, -0.42983949f, + 0.65112084f, -0.68230897f, -0.33240962f, + 0.69576043f, -0.68168229f, -0.22633317f, + 0.72325629f, -0.68104786f, -0.11434212f, + 0.73252994f, -0.68073493f, 0.00000000f, + 0.00000000f, -0.63961560f, -0.76869488f, + 0.12013536f, -0.63995206f, -0.75896567f, + 0.23772606f, -0.64063662f, -0.73011720f, + 0.34904191f, -0.64131474f, -0.68329000f, + 0.45123872f, -0.64178437f, -0.62007785f, + 0.54217160f, -0.64195001f, -0.54217160f, + 0.62007791f, -0.64178431f, -0.45123860f, + 0.68328995f, -0.64131457f, -0.34904230f, + 0.73011726f, -0.64063644f, -0.23772582f, + 0.75896567f, -0.63995212f, -0.12013467f, + 0.76869494f, -0.63961560f, 0.00000000f, + 0.00000000f, -0.44721362f, -0.89442724f, + 0.14009042f, -0.44757605f, -0.88320464f, + 0.27712736f, -0.44832203f, -0.84982812f, + 0.40678561f, -0.44907010f, -0.79552591f, + 0.52575535f, -0.44959316f, -0.72211301f, + 0.63154542f, -0.44977871f, -0.63154531f, + 0.72211337f, -0.44959310f, -0.52575493f, + 0.79552609f, -0.44906986f, -0.40678555f, + 0.84982818f, -0.44832194f, -0.27712733f, + 0.88320452f, -0.44757628f, -0.14009032f, + 0.89442724f, -0.44721356f, 0.00000000f, + 0.00000000f, 0.14654903f, -0.98920339f, + 0.15532865f, 0.14651905f, -0.97693658f, + 0.30717847f, 0.14642361f, -0.94031984f, + 0.45079190f, 0.14629373f, -0.88055927f, + 0.58249491f, 0.14618376f, -0.79958099f, + 0.69951510f, 0.14614128f, -0.69951516f, + 0.79958105f, 0.14618389f, -0.58249503f, + 0.88055938f, 0.14629366f, -0.45079195f, + 0.94031984f, 0.14642382f, -0.30717853f, + 0.97693658f, 0.14651911f, -0.15532877f, + 0.98920339f, 0.14654903f, -0.00000000f, + 0.00000000f, 0.80000031f, -0.59999961f, + 0.09426987f, 0.80018437f, -0.59229904f, + 0.18621887f, 0.80054075f, -0.56961137f, + 0.27299228f, 0.80087584f, -0.53298515f, + 0.35248643f, 0.80109745f, -0.48373166f, + 0.42315489f, 0.80117404f, -0.42315489f, + 0.48373106f, 0.80109769f, -0.35248649f, + 0.53298527f, 0.80087572f, -0.27299249f, + 0.56961101f, 0.80054092f, -0.18621904f, + 0.59229881f, 0.80018449f, -0.09426975f, + 0.59999961f, 0.80000025f, -0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 0.99403834f, 0.10903012f, + -0.01699241f, 0.99404776f, 0.10761157f, + -0.03362026f, 0.99406654f, 0.10344726f, + -0.04935814f, 0.99408495f, 0.09674154f, + -0.06381103f, 0.99409753f, 0.08773999f, + -0.07668582f, 0.99410182f, 0.07668582f, + -0.08774015f, 0.99409753f, 0.06381113f, + -0.09674170f, 0.99408495f, 0.04935821f, + -0.10344724f, 0.99406660f, 0.03362056f, + -0.10761159f, 0.99404776f, 0.01699222f, + -0.10903012f, 0.99403834f, 0.00000000f, + 0.00000000f, 0.93279803f, 0.36039948f, + -0.05617307f, 0.93289334f, 0.35574508f, + -0.11116763f, 0.93308485f, 0.34204450f, + -0.16323477f, 0.93327188f, 0.31993434f, + -0.21105908f, 0.93340015f, 0.29020393f, + -0.25365368f, 0.93344504f, 0.25365368f, + -0.29020357f, 0.93340009f, 0.21105954f, + -0.31993434f, 0.93327194f, 0.16323459f, + -0.34204450f, 0.93308485f, 0.11116788f, + -0.35574511f, 0.93289346f, 0.05617263f, + -0.36039943f, 0.93279809f, 0.00000000f, + 0.00000000f, 0.46861485f, 0.88340265f, + -0.13777140f, 0.46889010f, 0.87244546f, + -0.27293369f, 0.46944135f, 0.83972144f, + -0.40116957f, 0.46997890f, 0.78624612f, + -0.51906067f, 0.47034630f, 0.71368784f, + -0.62396044f, 0.47047496f, 0.62396044f, + -0.71368766f, 0.47034639f, 0.51906073f, + -0.78624600f, 0.46997911f, 0.40116954f, + -0.83972114f, 0.46944162f, 0.27293396f, + -0.87244540f, 0.46889028f, 0.13777120f, + -0.88340259f, 0.46861488f, 0.00000000f, + 0.00000000f, -0.35599536f, 0.93448776f, + -0.14576957f, -0.35626677f, 0.92294377f, + -0.28879514f, -0.35681957f, 0.88841271f, + -0.42450571f, -0.35736832f, 0.83191514f, + -0.54927009f, -0.35774896f, 0.75519395f, + -0.66027248f, -0.35788342f, 0.66027254f, + -0.75519407f, -0.35774904f, 0.54926997f, + -0.83191502f, -0.35736841f, 0.42450568f, + -0.88841271f, -0.35681972f, 0.28879508f, + -0.92294371f, -0.35626683f, 0.14576939f, + -0.93448776f, -0.35599536f, -0.00000000f, + 0.00000000f, -0.62469494f, 0.78086889f, + -0.12181900f, -0.62501729f, 0.77104706f, + -0.24121317f, -0.62567103f, 0.74185717f, + -0.35437536f, -0.62631625f, 0.69436735f, + -0.45835429f, -0.62676215f, 0.63014328f, + -0.55089581f, -0.62691921f, 0.55089581f, + -0.63014323f, -0.62676221f, 0.45835429f, + -0.69436723f, -0.62631643f, 0.35437533f, + -0.74185717f, -0.62567103f, 0.24121325f, + -0.77104694f, -0.62501740f, 0.12181915f, + -0.78086889f, -0.62469494f, -0.00000000f, + 0.00000000f, -0.68073487f, 0.73252988f, + -0.11434214f, -0.68104768f, 0.72325647f, + -0.22633335f, -0.68168211f, 0.69576049f, + -0.33240938f, -0.68230891f, 0.65112108f, + -0.42983943f, -0.68274170f, 0.59084833f, + -0.51655358f, -0.68289441f, 0.51655358f, + -0.59084809f, -0.68274182f, 0.42983949f, + -0.65112084f, -0.68230897f, 0.33240965f, + -0.69576037f, -0.68168223f, 0.22633319f, + -0.72325629f, -0.68104780f, 0.11434212f, + -0.73252982f, -0.68073493f, -0.00000000f, + 0.00000000f, -0.63961560f, 0.76869500f, + -0.12013534f, -0.63995206f, 0.75896573f, + -0.23772603f, -0.64063656f, 0.73011720f, + -0.34904197f, -0.64131474f, 0.68329000f, + -0.45123866f, -0.64178431f, 0.62007785f, + -0.54217160f, -0.64195001f, 0.54217160f, + -0.62007785f, -0.64178431f, 0.45123863f, + -0.68328989f, -0.64131457f, 0.34904236f, + -0.73011738f, -0.64063650f, 0.23772585f, + -0.75896567f, -0.63995212f, 0.12013467f, + -0.76869494f, -0.63961560f, -0.00000000f, + 0.00000000f, -0.44721353f, 0.89442724f, + -0.14009035f, -0.44757602f, 0.88320464f, + -0.27712739f, -0.44832197f, 0.84982818f, + -0.40678561f, -0.44907001f, 0.79552597f, + -0.52575523f, -0.44959310f, 0.72211307f, + -0.63154542f, -0.44977871f, 0.63154531f, + -0.72211337f, -0.44959313f, 0.52575499f, + -0.79552603f, -0.44906992f, 0.40678558f, + -0.84982818f, -0.44832194f, 0.27712733f, + -0.88320440f, -0.44757628f, 0.14009029f, + -0.89442712f, -0.44721353f, -0.00000000f, + 0.00000000f, 0.14654903f, 0.98920339f, + -0.15532869f, 0.14651905f, 0.97693658f, + -0.30717850f, 0.14642371f, 0.94031984f, + -0.45079190f, 0.14629376f, 0.88055927f, + -0.58249503f, 0.14618376f, 0.79958099f, + -0.69951504f, 0.14614125f, 0.69951504f, + -0.79958105f, 0.14618388f, 0.58249503f, + -0.88055938f, 0.14629366f, 0.45079190f, + -0.94031984f, 0.14642382f, 0.30717853f, + -0.97693658f, 0.14651911f, 0.15532877f, + -0.98920345f, 0.14654903f, 0.00000000f, + 0.00000000f, 0.80000025f, 0.59999967f, + -0.09426992f, 0.80018437f, 0.59229910f, + -0.18621887f, 0.80054075f, 0.56961131f, + -0.27299228f, 0.80087584f, 0.53298515f, + -0.35248643f, 0.80109739f, 0.48373166f, + -0.42315486f, 0.80117404f, 0.42315492f, + -0.48373103f, 0.80109763f, 0.35248649f, + -0.53298527f, 0.80087566f, 0.27299249f, + -0.56961095f, 0.80054086f, 0.18621902f, + -0.59229881f, 0.80018449f, 0.09426975f, + -0.59999961f, 0.80000031f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + -0.10903012f, 0.99403834f, 0.00000000f, + -0.10761159f, 0.99404788f, -0.01699242f, + -0.10344726f, 0.99406654f, -0.03362026f, + -0.09674155f, 0.99408489f, -0.04935813f, + -0.08773997f, 0.99409747f, -0.06381102f, + -0.07668582f, 0.99410182f, -0.07668582f, + -0.06381113f, 0.99409747f, -0.08774014f, + -0.04935822f, 0.99408489f, -0.09674168f, + -0.03362055f, 0.99406654f, -0.10344723f, + -0.01699222f, 0.99404776f, -0.10761159f, + 0.00000000f, 0.99403834f, -0.10903012f, + -0.36039943f, 0.93279809f, 0.00000000f, + -0.35574505f, 0.93289340f, -0.05617307f, + -0.34204450f, 0.93308485f, -0.11116764f, + -0.31993437f, 0.93327194f, -0.16323480f, + -0.29020390f, 0.93340009f, -0.21105908f, + -0.25365368f, 0.93344510f, -0.25365368f, + -0.21105953f, 0.93340009f, -0.29020354f, + -0.16323458f, 0.93327194f, -0.31993428f, + -0.11116786f, 0.93308479f, -0.34204444f, + -0.05617262f, 0.93289334f, -0.35574505f, + 0.00000000f, 0.93279809f, -0.36039945f, + -0.88340265f, 0.46861491f, 0.00000000f, + -0.87244540f, 0.46889016f, -0.13777140f, + -0.83972138f, 0.46944138f, -0.27293366f, + -0.78624606f, 0.46997890f, -0.40116951f, + -0.71368784f, 0.47034633f, -0.51906067f, + -0.62396044f, 0.47047499f, -0.62396038f, + -0.51906073f, 0.47034639f, -0.71368766f, + -0.40116948f, 0.46997911f, -0.78624594f, + -0.27293396f, 0.46944165f, -0.83972114f, + -0.13777120f, 0.46889028f, -0.87244540f, + 0.00000000f, 0.46861491f, -0.88340265f, + -0.93448776f, -0.35599536f, -0.00000000f, + -0.92294377f, -0.35626677f, -0.14576957f, + -0.88841271f, -0.35681954f, -0.28879514f, + -0.83191514f, -0.35736835f, -0.42450577f, + -0.75519401f, -0.35774902f, -0.54927009f, + -0.66027242f, -0.35788339f, -0.66027242f, + -0.54926997f, -0.35774904f, -0.75519407f, + -0.42450568f, -0.35736844f, -0.83191508f, + -0.28879514f, -0.35681972f, -0.88841265f, + -0.14576939f, -0.35626683f, -0.92294371f, + 0.00000000f, -0.35599536f, -0.93448770f, + -0.78086889f, -0.62469494f, -0.00000000f, + -0.77104706f, -0.62501734f, -0.12181900f, + -0.74185711f, -0.62567097f, -0.24121314f, + -0.69436729f, -0.62631625f, -0.35437539f, + -0.63014323f, -0.62676215f, -0.45835432f, + -0.55089581f, -0.62691921f, -0.55089581f, + -0.45835429f, -0.62676221f, -0.63014317f, + -0.35437530f, -0.62631643f, -0.69436723f, + -0.24121323f, -0.62567097f, -0.74185711f, + -0.12181915f, -0.62501746f, -0.77104694f, + 0.00000000f, -0.62469494f, -0.78086889f, + -0.73252994f, -0.68073493f, -0.00000000f, + -0.72325647f, -0.68104768f, -0.11434216f, + -0.69576049f, -0.68168211f, -0.22633339f, + -0.65112102f, -0.68230885f, -0.33240935f, + -0.59084833f, -0.68274176f, -0.42983946f, + -0.51655358f, -0.68289435f, -0.51655364f, + -0.42983946f, -0.68274188f, -0.59084809f, + -0.33240956f, -0.68230897f, -0.65112090f, + -0.22633308f, -0.68168223f, -0.69576037f, + -0.11434201f, -0.68104780f, -0.72325629f, + 0.00000000f, -0.68073487f, -0.73252988f, + -0.76869494f, -0.63961560f, -0.00000000f, + -0.75896567f, -0.63995206f, -0.12013541f, + -0.73011720f, -0.64063656f, -0.23772609f, + -0.68329000f, -0.64131469f, -0.34904197f, + -0.62007785f, -0.64178431f, -0.45123869f, + -0.54217160f, -0.64195007f, -0.54217166f, + -0.45123863f, -0.64178437f, -0.62007785f, + -0.34904227f, -0.64131457f, -0.68328995f, + -0.23772582f, -0.64063644f, -0.73011726f, + -0.12013479f, -0.63995218f, -0.75896567f, + 0.00000000f, -0.63961560f, -0.76869488f, + -0.89442724f, -0.44721356f, -0.00000000f, + -0.88320464f, -0.44757605f, -0.14009038f, + -0.84982818f, -0.44832191f, -0.27712730f, + -0.79552603f, -0.44907001f, -0.40678552f, + -0.72211319f, -0.44959310f, -0.52575517f, + -0.63154542f, -0.44977871f, -0.63154531f, + -0.52575505f, -0.44959310f, -0.72211331f, + -0.40678564f, -0.44906986f, -0.79552597f, + -0.27712736f, -0.44832200f, -0.84982800f, + -0.14009029f, -0.44757628f, -0.88320440f, + 0.00000000f, -0.44721362f, -0.89442724f, + -0.98920339f, 0.14654903f, 0.00000000f, + -0.97693658f, 0.14651901f, -0.15532872f, + -0.94031984f, 0.14642377f, -0.30717847f, + -0.88055927f, 0.14629376f, -0.45079187f, + -0.79958099f, 0.14618376f, -0.58249503f, + -0.69951510f, 0.14614128f, -0.69951516f, + -0.58249503f, 0.14618391f, -0.79958105f, + -0.45079190f, 0.14629370f, -0.88055933f, + -0.30717865f, 0.14642373f, -0.94031984f, + -0.15532877f, 0.14651911f, -0.97693658f, + 0.00000000f, 0.14654903f, -0.98920339f, + -0.59999961f, 0.80000025f, 0.00000000f, + -0.59229910f, 0.80018437f, -0.09426993f, + -0.56961125f, 0.80054069f, -0.18621883f, + -0.53298515f, 0.80087584f, -0.27299231f, + -0.48373166f, 0.80109745f, -0.35248643f, + -0.42315486f, 0.80117404f, -0.42315489f, + -0.35248655f, 0.80109769f, -0.48373106f, + -0.27299246f, 0.80087566f, -0.53298521f, + -0.18621899f, 0.80054086f, -0.56961107f, + -0.09426975f, 0.80018449f, -0.59229881f, + 0.00000000f, 0.80000031f, -0.59999961f, + 0.60000014f, 0.79999983f, -0.00000000f, + 0.59229946f, 0.80018395f, 0.09426998f, + 0.56961179f, 0.80054033f, 0.18621901f, + 0.53298604f, 0.80087507f, 0.27299276f, + 0.48373187f, 0.80109727f, 0.35248655f, + 0.42315534f, 0.80117357f, 0.42315537f, + 0.35248691f, 0.80109721f, 0.48373157f, + 0.27299273f, 0.80087519f, 0.53298587f, + 0.18621919f, 0.80054045f, 0.56961161f, + 0.09426985f, 0.80018401f, 0.59229940f, + 0.00000000f, 0.79999983f, 0.60000014f, + 0.35491538f, 0.93489850f, -0.00000000f, + 0.35030773f, 0.93497366f, 0.05575488f, + 0.33679214f, 0.93511921f, 0.11010502f, + 0.31505084f, 0.93525589f, 0.16136751f, + 0.28588474f, 0.93534654f, 0.20831876f, + 0.25006858f, 0.93537772f, 0.25006849f, + 0.20831899f, 0.93534654f, 0.28588459f, + 0.16136734f, 0.93525594f, 0.31505048f, + 0.11010502f, 0.93511939f, 0.33679175f, + 0.05575469f, 0.93497372f, 0.35030770f, + 0.00000000f, 0.93489844f, 0.35491538f, + 0.23813862f, 0.97123122f, -0.00000000f, + 0.23503657f, 0.97126639f, 0.03740831f, + 0.22594899f, 0.97133446f, 0.07386778f, + 0.21134609f, 0.97139829f, 0.10825043f, + 0.19177027f, 0.97144073f, 0.13973957f, + 0.16774194f, 0.97145522f, 0.16774194f, + 0.13973957f, 0.97144073f, 0.19177030f, + 0.10825033f, 0.97139841f, 0.21134590f, + 0.07386787f, 0.97133446f, 0.22594878f, + 0.03740822f, 0.97126633f, 0.23503660f, + 0.00000000f, 0.97123122f, 0.23813862f, + 0.17979470f, 0.98370415f, -0.00000000f, + 0.17744993f, 0.98372447f, 0.02824289f, + 0.17058375f, 0.98376375f, 0.05576761f, + 0.15955459f, 0.98380059f, 0.08172295f, + 0.14477314f, 0.98382503f, 0.10549361f, + 0.12663263f, 0.98383349f, 0.12663263f, + 0.10549360f, 0.98382503f, 0.14477316f, + 0.08172285f, 0.98380071f, 0.15955447f, + 0.05576771f, 0.98376375f, 0.17058358f, + 0.02824305f, 0.98372453f, 0.17744984f, + 0.00000000f, 0.98370415f, 0.17979471f, + 0.15294123f, 0.98823535f, -0.00000000f, + 0.15094571f, 0.98825008f, 0.02402467f, + 0.14510347f, 0.98827863f, 0.04743762f, + 0.13572034f, 0.98830545f, 0.06951533f, + 0.12314621f, 0.98832321f, 0.08973438f, + 0.10771532f, 0.98832929f, 0.10771532f, + 0.08973443f, 0.98832321f, 0.12314621f, + 0.06951523f, 0.98830539f, 0.13572021f, + 0.04743744f, 0.98827863f, 0.14510347f, + 0.02402461f, 0.98825002f, 0.15094574f, + 0.00000000f, 0.98823535f, 0.15294123f, + 0.14834042f, 0.98893636f, -0.00000000f, + 0.14640494f, 0.98895019f, 0.02330164f, + 0.14073798f, 0.98897713f, 0.04601044f, + 0.13163701f, 0.98900241f, 0.06742381f, + 0.11944108f, 0.98901922f, 0.08703452f, + 0.10447443f, 0.98902482f, 0.10447443f, + 0.08703458f, 0.98901910f, 0.11944108f, + 0.06742378f, 0.98900241f, 0.13163690f, + 0.04601069f, 0.98897713f, 0.14073797f, + 0.02330185f, 0.98895031f, 0.14640486f, + 0.00000000f, 0.98893636f, 0.14834042f, + 0.16577734f, 0.98616314f, -0.00000000f, + 0.16361488f, 0.98618054f, 0.02604098f, + 0.15728295f, 0.98621398f, 0.05141935f, + 0.14711300f, 0.98624545f, 0.07535055f, + 0.13348369f, 0.98626626f, 0.09726719f, + 0.11675765f, 0.98627347f, 0.11675765f, + 0.09726722f, 0.98626631f, 0.13348375f, + 0.07535047f, 0.98624545f, 0.14711283f, + 0.05141941f, 0.98621410f, 0.15728293f, + 0.02604095f, 0.98618054f, 0.16361482f, + 0.00000000f, 0.98616314f, 0.16577734f, + 0.21457930f, 0.97670650f, -0.00000000f, + 0.21178281f, 0.97673523f, 0.03370722f, + 0.20359138f, 0.97679090f, 0.06655866f, + 0.19043125f, 0.97684300f, 0.09753795f, + 0.17279114f, 0.97687763f, 0.12590984f, + 0.15114051f, 0.97688949f, 0.15114051f, + 0.12590988f, 0.97687763f, 0.17279126f, + 0.09753782f, 0.97684312f, 0.19043098f, + 0.06655872f, 0.97679090f, 0.20359136f, + 0.03370715f, 0.97673535f, 0.21178272f, + 0.00000000f, 0.97670650f, 0.21457930f, + 0.32579350f, 0.94544101f, -0.00000000f, + 0.32155997f, 0.94550508f, 0.05117927f, + 0.30914524f, 0.94562930f, 0.10106669f, + 0.28918239f, 0.94574553f, 0.14811778f, + 0.26240703f, 0.94582283f, 0.19121127f, + 0.22953111f, 0.94584930f, 0.22953111f, + 0.19121137f, 0.94582278f, 0.26240712f, + 0.14811763f, 0.94574559f, 0.28918216f, + 0.10106679f, 0.94562918f, 0.30914542f, + 0.05117931f, 0.94550508f, 0.32155982f, + 0.00000000f, 0.94544101f, 0.32579350f, + 0.59999937f, 0.80000049f, -0.00000000f, + 0.59229898f, 0.80018443f, 0.09426988f, + 0.56961066f, 0.80054110f, 0.18621883f, + 0.53298545f, 0.80087554f, 0.27299255f, + 0.48373094f, 0.80109799f, 0.35248604f, + 0.42315483f, 0.80117410f, 0.42315483f, + 0.35248625f, 0.80109781f, 0.48373121f, + 0.27299234f, 0.80087590f, 0.53298503f, + 0.18621901f, 0.80054098f, 0.56961095f, + 0.09426989f, 0.80018461f, 0.59229875f, + 0.00000000f, 0.80000049f, 0.59999937f, + 0.99999994f, 0.00000000f, -0.00000000f, + 0.98756981f, 0.00000000f, 0.15718086f, + 0.95049530f, 0.00000000f, 0.31073883f, + 0.89004338f, 0.00000000f, 0.45587584f, + 0.80819392f, 0.00000000f, 0.58891648f, + 0.70710683f, 0.00000000f, 0.70710683f, + 0.58891642f, 0.00000000f, 0.80819392f, + 0.45587584f, 0.00000000f, 0.89004338f, + 0.31073889f, 0.00000000f, 0.95049530f, + 0.15718089f, 0.00000000f, 0.98756981f, + 0.00000000f, 0.00000000f, 0.99999994f, + 0.00000000f, 0.79999989f, -0.60000014f, + 0.09426994f, 0.80018401f, -0.59229946f, + 0.18621901f, 0.80054039f, -0.56961179f, + 0.27299276f, 0.80087507f, -0.53298604f, + 0.35248658f, 0.80109727f, -0.48373187f, + 0.42315534f, 0.80117357f, -0.42315534f, + 0.48373160f, 0.80109727f, -0.35248691f, + 0.53298575f, 0.80087519f, -0.27299273f, + 0.56961155f, 0.80054045f, -0.18621923f, + 0.59229940f, 0.80018401f, -0.09426985f, + 0.60000026f, 0.79999977f, -0.00000000f, + 0.00000000f, 0.93489850f, -0.35491538f, + 0.05575490f, 0.93497372f, -0.35030776f, + 0.11010505f, 0.93511921f, -0.33679211f, + 0.16136754f, 0.93525589f, -0.31505081f, + 0.20831878f, 0.93534660f, -0.28588474f, + 0.25006852f, 0.93537766f, -0.25006849f, + 0.28588459f, 0.93534654f, -0.20831895f, + 0.31505051f, 0.93525606f, -0.16136728f, + 0.33679172f, 0.93511939f, -0.11010498f, + 0.35030773f, 0.93497372f, -0.05575464f, + 0.35491535f, 0.93489844f, -0.00000000f, + 0.00000000f, 0.97123122f, -0.23813862f, + 0.03740832f, 0.97126639f, -0.23503657f, + 0.07386777f, 0.97133446f, -0.22594899f, + 0.10825042f, 0.97139835f, -0.21134609f, + 0.13973957f, 0.97144073f, -0.19177027f, + 0.16774191f, 0.97145522f, -0.16774194f, + 0.19177030f, 0.97144073f, -0.13973957f, + 0.21134590f, 0.97139835f, -0.10825033f, + 0.22594878f, 0.97133446f, -0.07386787f, + 0.23503660f, 0.97126633f, -0.03740822f, + 0.23813862f, 0.97123122f, -0.00000000f, + 0.00000000f, 0.98370415f, -0.17979471f, + 0.02824289f, 0.98372447f, -0.17744993f, + 0.05576760f, 0.98376375f, -0.17058372f, + 0.08172296f, 0.98380059f, -0.15955459f, + 0.10549361f, 0.98382509f, -0.14477314f, + 0.12663263f, 0.98383349f, -0.12663262f, + 0.14477316f, 0.98382503f, -0.10549360f, + 0.15955447f, 0.98380071f, -0.08172285f, + 0.17058358f, 0.98376375f, -0.05576771f, + 0.17744984f, 0.98372453f, -0.02824305f, + 0.17979470f, 0.98370415f, -0.00000000f, + 0.00000000f, 0.98823535f, -0.15294123f, + 0.02402467f, 0.98825008f, -0.15094571f, + 0.04743762f, 0.98827863f, -0.14510345f, + 0.06951532f, 0.98830533f, -0.13572033f, + 0.08973438f, 0.98832327f, -0.12314621f, + 0.10771532f, 0.98832929f, -0.10771532f, + 0.12314620f, 0.98832321f, -0.08973442f, + 0.13572021f, 0.98830539f, -0.06951523f, + 0.14510347f, 0.98827863f, -0.04743744f, + 0.15094574f, 0.98825008f, -0.02402462f, + 0.15294123f, 0.98823529f, -0.00000000f, + 0.00000000f, 0.98893636f, -0.14834042f, + 0.02330164f, 0.98895019f, -0.14640494f, + 0.04601044f, 0.98897713f, -0.14073797f, + 0.06742381f, 0.98900241f, -0.13163701f, + 0.08703452f, 0.98901922f, -0.11944108f, + 0.10447443f, 0.98902482f, -0.10447443f, + 0.11944108f, 0.98901910f, -0.08703458f, + 0.13163690f, 0.98900241f, -0.06742378f, + 0.14073797f, 0.98897713f, -0.04601069f, + 0.14640486f, 0.98895031f, -0.02330185f, + 0.14834042f, 0.98893636f, -0.00000000f, + 0.00000000f, 0.98616314f, -0.16577734f, + 0.02604098f, 0.98618054f, -0.16361488f, + 0.05141935f, 0.98621410f, -0.15728295f, + 0.07535054f, 0.98624545f, -0.14711298f, + 0.09726719f, 0.98626626f, -0.13348369f, + 0.11675765f, 0.98627347f, -0.11675765f, + 0.13348375f, 0.98626631f, -0.09726722f, + 0.14711283f, 0.98624545f, -0.07535047f, + 0.15728293f, 0.98621410f, -0.05141941f, + 0.16361482f, 0.98618054f, -0.02604095f, + 0.16577734f, 0.98616314f, -0.00000000f, + 0.00000000f, 0.97670650f, -0.21457930f, + 0.03370722f, 0.97673523f, -0.21178281f, + 0.06655866f, 0.97679090f, -0.20359138f, + 0.09753795f, 0.97684300f, -0.19043125f, + 0.12590984f, 0.97687763f, -0.17279114f, + 0.15114051f, 0.97688949f, -0.15114051f, + 0.17279126f, 0.97687763f, -0.12590988f, + 0.19043098f, 0.97684312f, -0.09753782f, + 0.20359136f, 0.97679090f, -0.06655872f, + 0.21178272f, 0.97673535f, -0.03370715f, + 0.21457930f, 0.97670650f, -0.00000000f, + 0.00000000f, 0.94544101f, -0.32579350f, + 0.05117927f, 0.94550508f, -0.32155997f, + 0.10106669f, 0.94562936f, -0.30914521f, + 0.14811778f, 0.94574553f, -0.28918239f, + 0.19121125f, 0.94582278f, -0.26240700f, + 0.22953111f, 0.94584930f, -0.22953111f, + 0.26240712f, 0.94582278f, -0.19121137f, + 0.28918216f, 0.94574559f, -0.14811763f, + 0.30914542f, 0.94562918f, -0.10106679f, + 0.32155982f, 0.94550508f, -0.05117931f, + 0.32579350f, 0.94544101f, -0.00000000f, + 0.00000000f, 0.80000049f, -0.59999937f, + 0.09426988f, 0.80018443f, -0.59229898f, + 0.18621883f, 0.80054110f, -0.56961066f, + 0.27299255f, 0.80087554f, -0.53298545f, + 0.35248604f, 0.80109799f, -0.48373094f, + 0.42315483f, 0.80117410f, -0.42315483f, + 0.48373121f, 0.80109781f, -0.35248625f, + 0.53298503f, 0.80087590f, -0.27299234f, + 0.56961095f, 0.80054098f, -0.18621901f, + 0.59229875f, 0.80018461f, -0.09426989f, + 0.59999937f, 0.80000049f, -0.00000000f, + 0.00000000f, 0.00000000f, -0.99999994f, + 0.15718086f, 0.00000000f, -0.98756981f, + 0.31073883f, 0.00000000f, -0.95049530f, + 0.45587584f, 0.00000000f, -0.89004338f, + 0.58891648f, 0.00000000f, -0.80819392f, + 0.70710683f, 0.00000000f, -0.70710683f, + 0.80819392f, 0.00000000f, -0.58891642f, + 0.89004338f, 0.00000000f, -0.45587584f, + 0.95049530f, 0.00000000f, -0.31073889f, + 0.98756981f, 0.00000000f, -0.15718089f, + 0.99999994f, 0.00000000f, -0.00000000f, + 0.00000000f, 0.79999983f, 0.60000014f, + -0.09426998f, 0.80018401f, 0.59229952f, + -0.18621902f, 0.80054033f, 0.56961179f, + -0.27299276f, 0.80087507f, 0.53298604f, + -0.35248658f, 0.80109727f, 0.48373187f, + -0.42315531f, 0.80117357f, 0.42315537f, + -0.48373151f, 0.80109727f, 0.35248685f, + -0.53298575f, 0.80087519f, 0.27299273f, + -0.56961155f, 0.80054045f, 0.18621922f, + -0.59229940f, 0.80018401f, 0.09426985f, + -0.60000014f, 0.79999983f, 0.00000000f, + 0.00000000f, 0.93489844f, 0.35491538f, + -0.05575493f, 0.93497372f, 0.35030776f, + -0.11010505f, 0.93511921f, 0.33679211f, + -0.16136758f, 0.93525589f, 0.31505081f, + -0.20831881f, 0.93534654f, 0.28588471f, + -0.25006858f, 0.93537772f, 0.25006849f, + -0.28588459f, 0.93534654f, 0.20831895f, + -0.31505051f, 0.93525594f, 0.16136727f, + -0.33679172f, 0.93511927f, 0.11010496f, + -0.35030773f, 0.93497372f, 0.05575464f, + -0.35491538f, 0.93489850f, 0.00000000f, + 0.00000000f, 0.97123122f, 0.23813862f, + -0.03740831f, 0.97126639f, 0.23503657f, + -0.07386778f, 0.97133446f, 0.22594899f, + -0.10825043f, 0.97139829f, 0.21134609f, + -0.13973956f, 0.97144073f, 0.19177027f, + -0.16774194f, 0.97145522f, 0.16774194f, + -0.19177030f, 0.97144073f, 0.13973957f, + -0.21134590f, 0.97139841f, 0.10825033f, + -0.22594878f, 0.97133440f, 0.07386787f, + -0.23503658f, 0.97126639f, 0.03740823f, + -0.23813862f, 0.97123122f, 0.00000000f, + 0.00000000f, 0.98370415f, 0.17979471f, + -0.02824289f, 0.98372447f, 0.17744993f, + -0.05576761f, 0.98376375f, 0.17058375f, + -0.08172295f, 0.98380059f, 0.15955459f, + -0.10549361f, 0.98382503f, 0.14477316f, + -0.12663263f, 0.98383349f, 0.12663263f, + -0.14477316f, 0.98382503f, 0.10549360f, + -0.15955447f, 0.98380071f, 0.08172285f, + -0.17058358f, 0.98376375f, 0.05576771f, + -0.17744984f, 0.98372447f, 0.02824305f, + -0.17979470f, 0.98370415f, 0.00000000f, + 0.00000000f, 0.98823535f, 0.15294123f, + -0.02402467f, 0.98825002f, 0.15094571f, + -0.04743762f, 0.98827863f, 0.14510347f, + -0.06951531f, 0.98830533f, 0.13572033f, + -0.08973438f, 0.98832321f, 0.12314621f, + -0.10771532f, 0.98832929f, 0.10771532f, + -0.12314621f, 0.98832321f, 0.08973443f, + -0.13572021f, 0.98830539f, 0.06951523f, + -0.14510347f, 0.98827863f, 0.04743744f, + -0.15094574f, 0.98825002f, 0.02402461f, + -0.15294123f, 0.98823535f, 0.00000000f, + 0.00000000f, 0.98893636f, 0.14834042f, + -0.02330164f, 0.98895019f, 0.14640494f, + -0.04601044f, 0.98897719f, 0.14073798f, + -0.06742381f, 0.98900241f, 0.13163702f, + -0.08703452f, 0.98901922f, 0.11944108f, + -0.10447443f, 0.98902482f, 0.10447443f, + -0.11944108f, 0.98901910f, 0.08703458f, + -0.13163692f, 0.98900247f, 0.06742378f, + -0.14073797f, 0.98897713f, 0.04601069f, + -0.14640486f, 0.98895031f, 0.02330185f, + -0.14834042f, 0.98893636f, 0.00000000f, + 0.00000000f, 0.98616314f, 0.16577734f, + -0.02604098f, 0.98618054f, 0.16361488f, + -0.05141935f, 0.98621398f, 0.15728295f, + -0.07535055f, 0.98624545f, 0.14711300f, + -0.09726719f, 0.98626626f, 0.13348369f, + -0.11675765f, 0.98627347f, 0.11675765f, + -0.13348375f, 0.98626631f, 0.09726722f, + -0.14711285f, 0.98624545f, 0.07535048f, + -0.15728293f, 0.98621410f, 0.05141941f, + -0.16361482f, 0.98618054f, 0.02604095f, + -0.16577734f, 0.98616314f, 0.00000000f, + 0.00000000f, 0.97670650f, 0.21457930f, + -0.03370723f, 0.97673535f, 0.21178287f, + -0.06655866f, 0.97679090f, 0.20359138f, + -0.09753795f, 0.97684300f, 0.19043125f, + -0.12590984f, 0.97687763f, 0.17279114f, + -0.15114051f, 0.97688949f, 0.15114051f, + -0.17279126f, 0.97687763f, 0.12590988f, + -0.19043098f, 0.97684312f, 0.09753782f, + -0.20359136f, 0.97679090f, 0.06655872f, + -0.21178272f, 0.97673535f, 0.03370715f, + -0.21457930f, 0.97670650f, 0.00000000f, + 0.00000000f, 0.94544101f, 0.32579350f, + -0.05117927f, 0.94550508f, 0.32155997f, + -0.10106669f, 0.94562930f, 0.30914524f, + -0.14811778f, 0.94574553f, 0.28918239f, + -0.19121127f, 0.94582283f, 0.26240703f, + -0.22953111f, 0.94584930f, 0.22953111f, + -0.26240712f, 0.94582278f, 0.19121137f, + -0.28918216f, 0.94574559f, 0.14811763f, + -0.30914542f, 0.94562918f, 0.10106679f, + -0.32155982f, 0.94550508f, 0.05117931f, + -0.32579350f, 0.94544101f, 0.00000000f, + 0.00000000f, 0.80000049f, 0.59999937f, + -0.09426988f, 0.80018443f, 0.59229898f, + -0.18621883f, 0.80054110f, 0.56961066f, + -0.27299255f, 0.80087554f, 0.53298545f, + -0.35248604f, 0.80109799f, 0.48373094f, + -0.42315483f, 0.80117410f, 0.42315483f, + -0.48373121f, 0.80109781f, 0.35248625f, + -0.53298503f, 0.80087590f, 0.27299234f, + -0.56961095f, 0.80054098f, 0.18621901f, + -0.59229875f, 0.80018461f, 0.09426989f, + -0.59999937f, 0.80000049f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.99999994f, + -0.15718086f, 0.00000000f, 0.98756981f, + -0.31073883f, 0.00000000f, 0.95049530f, + -0.45587584f, 0.00000000f, 0.89004338f, + -0.58891648f, 0.00000000f, 0.80819392f, + -0.70710683f, 0.00000000f, 0.70710683f, + -0.80819392f, 0.00000000f, 0.58891642f, + -0.89004338f, 0.00000000f, 0.45587584f, + -0.95049530f, 0.00000000f, 0.31073889f, + -0.98756981f, 0.00000000f, 0.15718089f, + -0.99999994f, 0.00000000f, -0.00000000f, + -0.60000026f, 0.79999977f, 0.00000000f, + -0.59229946f, 0.80018401f, -0.09426999f, + -0.56961179f, 0.80054033f, -0.18621901f, + -0.53298604f, 0.80087507f, -0.27299276f, + -0.48373181f, 0.80109727f, -0.35248649f, + -0.42315531f, 0.80117357f, -0.42315534f, + -0.35248691f, 0.80109721f, -0.48373157f, + -0.27299273f, 0.80087519f, -0.53298575f, + -0.18621917f, 0.80054057f, -0.56961155f, + -0.09426985f, 0.80018407f, -0.59229940f, + 0.00000000f, 0.79999989f, -0.60000014f, + -0.35491535f, 0.93489844f, 0.00000000f, + -0.35030776f, 0.93497372f, -0.05575490f, + -0.33679211f, 0.93511921f, -0.11010504f, + -0.31505084f, 0.93525589f, -0.16136752f, + -0.28588474f, 0.93534660f, -0.20831876f, + -0.25006852f, 0.93537766f, -0.25006849f, + -0.20831898f, 0.93534660f, -0.28588462f, + -0.16136733f, 0.93525600f, -0.31505051f, + -0.11010498f, 0.93511939f, -0.33679172f, + -0.05575464f, 0.93497372f, -0.35030770f, + 0.00000000f, 0.93489850f, -0.35491538f, + -0.23813862f, 0.97123122f, 0.00000000f, + -0.23503657f, 0.97126639f, -0.03740831f, + -0.22594899f, 0.97133446f, -0.07386778f, + -0.21134609f, 0.97139835f, -0.10825043f, + -0.19177027f, 0.97144073f, -0.13973957f, + -0.16774194f, 0.97145522f, -0.16774194f, + -0.13973959f, 0.97144073f, -0.19177033f, + -0.10825032f, 0.97139841f, -0.21134591f, + -0.07386787f, 0.97133446f, -0.22594878f, + -0.03740822f, 0.97126633f, -0.23503660f, + 0.00000000f, 0.97123122f, -0.23813862f, + -0.17979470f, 0.98370415f, 0.00000000f, + -0.17744993f, 0.98372447f, -0.02824289f, + -0.17058375f, 0.98376375f, -0.05576761f, + -0.15955459f, 0.98380065f, -0.08172295f, + -0.14477316f, 0.98382503f, -0.10549361f, + -0.12663263f, 0.98383349f, -0.12663262f, + -0.10549361f, 0.98382503f, -0.14477316f, + -0.08172286f, 0.98380071f, -0.15955447f, + -0.05576771f, 0.98376375f, -0.17058356f, + -0.02824305f, 0.98372453f, -0.17744984f, + 0.00000000f, 0.98370415f, -0.17979471f, + -0.15294123f, 0.98823529f, 0.00000000f, + -0.15094571f, 0.98825008f, -0.02402467f, + -0.14510347f, 0.98827863f, -0.04743762f, + -0.13572033f, 0.98830533f, -0.06951531f, + -0.12314621f, 0.98832321f, -0.08973438f, + -0.10771532f, 0.98832929f, -0.10771532f, + -0.08973443f, 0.98832321f, -0.12314620f, + -0.06951524f, 0.98830539f, -0.13572021f, + -0.04743744f, 0.98827863f, -0.14510347f, + -0.02402461f, 0.98825002f, -0.15094574f, + 0.00000000f, 0.98823535f, -0.15294123f, + -0.14834042f, 0.98893636f, 0.00000000f, + -0.14640494f, 0.98895019f, -0.02330164f, + -0.14073798f, 0.98897713f, -0.04601044f, + -0.13163701f, 0.98900241f, -0.06742381f, + -0.11944108f, 0.98901922f, -0.08703452f, + -0.10447443f, 0.98902482f, -0.10447443f, + -0.08703458f, 0.98901910f, -0.11944108f, + -0.06742378f, 0.98900241f, -0.13163690f, + -0.04601069f, 0.98897713f, -0.14073797f, + -0.02330185f, 0.98895031f, -0.14640486f, + 0.00000000f, 0.98893636f, -0.14834042f, + -0.16577734f, 0.98616314f, 0.00000000f, + -0.16361488f, 0.98618054f, -0.02604098f, + -0.15728295f, 0.98621398f, -0.05141935f, + -0.14711300f, 0.98624545f, -0.07535055f, + -0.13348369f, 0.98626626f, -0.09726719f, + -0.11675765f, 0.98627347f, -0.11675765f, + -0.09726722f, 0.98626631f, -0.13348375f, + -0.07535047f, 0.98624545f, -0.14711283f, + -0.05141941f, 0.98621410f, -0.15728293f, + -0.02604095f, 0.98618054f, -0.16361482f, + 0.00000000f, 0.98616314f, -0.16577734f, + -0.21457930f, 0.97670650f, 0.00000000f, + -0.21178281f, 0.97673523f, -0.03370722f, + -0.20359138f, 0.97679090f, -0.06655866f, + -0.19043125f, 0.97684300f, -0.09753795f, + -0.17279114f, 0.97687763f, -0.12590984f, + -0.15114051f, 0.97688949f, -0.15114051f, + -0.12590988f, 0.97687763f, -0.17279126f, + -0.09753782f, 0.97684312f, -0.19043098f, + -0.06655872f, 0.97679090f, -0.20359136f, + -0.03370715f, 0.97673535f, -0.21178272f, + 0.00000000f, 0.97670650f, -0.21457930f, + -0.32579350f, 0.94544101f, 0.00000000f, + -0.32155997f, 0.94550508f, -0.05117927f, + -0.30914524f, 0.94562930f, -0.10106669f, + -0.28918239f, 0.94574553f, -0.14811778f, + -0.26240703f, 0.94582283f, -0.19121127f, + -0.22953111f, 0.94584930f, -0.22953111f, + -0.19121137f, 0.94582283f, -0.26240712f, + -0.14811763f, 0.94574559f, -0.28918216f, + -0.10106679f, 0.94562918f, -0.30914542f, + -0.05117931f, 0.94550508f, -0.32155982f, + 0.00000000f, 0.94544101f, -0.32579350f, + -0.59999937f, 0.80000049f, 0.00000000f, + -0.59229898f, 0.80018443f, -0.09426988f, + -0.56961066f, 0.80054110f, -0.18621883f, + -0.53298545f, 0.80087554f, -0.27299255f, + -0.48373094f, 0.80109799f, -0.35248604f, + -0.42315483f, 0.80117410f, -0.42315483f, + -0.35248625f, 0.80109781f, -0.48373121f, + -0.27299234f, 0.80087590f, -0.53298503f, + -0.18621901f, 0.80054098f, -0.56961095f, + -0.09426989f, 0.80018461f, -0.59229875f, + 0.00000000f, 0.80000049f, -0.59999937f, + -0.99999994f, 0.00000000f, -0.00000000f, + -0.98756981f, -0.00000000f, -0.15718086f, + -0.95049530f, -0.00000000f, -0.31073883f, + -0.89004338f, -0.00000000f, -0.45587584f, + -0.80819392f, -0.00000000f, -0.58891648f, + -0.70710683f, -0.00000000f, -0.70710683f, + -0.58891642f, -0.00000000f, -0.80819392f, + -0.45587584f, -0.00000000f, -0.89004338f, + -0.31073889f, -0.00000000f, -0.95049530f, + -0.15718089f, -0.00000000f, -0.98756981f, + 0.00000000f, 0.00000000f, -0.99999994f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -0.99992561f, 0.01220202f, + 0.00191707f, -0.99992567f, 0.01204260f, + 0.00378433f, -0.99992579f, 0.01157632f, + 0.00554587f, -0.99992591f, 0.01082732f, + 0.00715852f, -0.99992603f, 0.00982409f, + 0.00859298f, -0.99992615f, 0.00859298f, + 0.00982405f, -0.99992603f, 0.00715861f, + 0.01082739f, -0.99992591f, 0.00554574f, + 0.01157622f, -0.99992579f, 0.00378453f, + 0.01204260f, -0.99992561f, 0.00191707f, + 0.01220204f, -0.99992549f, 0.00000000f, + 0.00000000f, -0.99958426f, 0.02883408f, + 0.00452943f, -0.99958473f, 0.02845749f, + 0.00894296f, -0.99958575f, 0.02735529f, + 0.01310474f, -0.99958670f, 0.02558575f, + 0.01691613f, -0.99958730f, 0.02321488f, + 0.02030576f, -0.99958766f, 0.02030576f, + 0.02321492f, -0.99958742f, 0.01691616f, + 0.02558579f, -0.99958670f, 0.01310467f, + 0.02735530f, -0.99958575f, 0.00894308f, + 0.02845747f, -0.99958473f, 0.00452948f, + 0.02883412f, -0.99958426f, 0.00000000f, + 0.00000000f, -0.99862915f, 0.05234338f, + 0.00822229f, -0.99863088f, 0.05165981f, + 0.01623459f, -0.99863428f, 0.04965891f, + 0.02378965f, -0.99863744f, 0.04644668f, + 0.03070865f, -0.99863952f, 0.04214293f, + 0.03686193f, -0.99864024f, 0.03686193f, + 0.04214287f, -0.99863952f, 0.03070885f, + 0.04644670f, -0.99863750f, 0.02378960f, + 0.04965906f, -0.99863428f, 0.01623469f, + 0.05165980f, -0.99863088f, 0.00822228f, + 0.05234344f, -0.99862915f, 0.00000000f, + 0.00000000f, -0.99619961f, 0.08709927f, + 0.01368168f, -0.99620444f, 0.08596209f, + 0.02701467f, -0.99621379f, 0.08263328f, + 0.03958682f, -0.99622256f, 0.07728840f, + 0.05110020f, -0.99622840f, 0.07012693f, + 0.06133937f, -0.99623030f, 0.06133937f, + 0.07012701f, -0.99622846f, 0.05110029f, + 0.07728842f, -0.99622256f, 0.03958675f, + 0.08263330f, -0.99621379f, 0.02701474f, + 0.08596208f, -0.99620444f, 0.01368166f, + 0.08709935f, -0.99619961f, 0.00000000f, + 0.00000000f, -0.98994946f, 0.14142129f, + 0.02221482f, -0.98996216f, 0.13957602f, + 0.04386425f, -0.98998660f, 0.13417313f, + 0.06427860f, -0.99000955f, 0.12549633f, + 0.08297431f, -0.99002475f, 0.11386906f, + 0.09960055f, -0.99002999f, 0.09960055f, + 0.11386913f, -0.99002481f, 0.08297440f, + 0.12549631f, -0.99000955f, 0.06427859f, + 0.13417305f, -0.98998660f, 0.04386439f, + 0.13957596f, -0.98996210f, 0.02221479f, + 0.14142138f, -0.98994946f, 0.00000000f, + 0.00000000f, -0.97268748f, 0.23211849f, + 0.03646255f, -0.97272098f, 0.22909468f, + 0.07200014f, -0.97278571f, 0.22023587f, + 0.10551301f, -0.97284645f, 0.20600158f, + 0.13620538f, -0.97288692f, 0.18692032f, + 0.16349944f, -0.97290081f, 0.16349944f, + 0.18692048f, -0.97288686f, 0.13620566f, + 0.20600158f, -0.97284657f, 0.10551302f, + 0.22023584f, -0.97278577f, 0.07200018f, + 0.22909461f, -0.97272098f, 0.03646262f, + 0.23211864f, -0.97268748f, 0.00000000f, + 0.00000000f, -0.91959101f, 0.39287689f, + 0.06171954f, -0.91968167f, 0.38778374f, + 0.12188835f, -0.91985726f, 0.37283489f, + 0.17864260f, -0.92002207f, 0.34877822f, + 0.23062557f, -0.92013133f, 0.31649676f, + 0.27684769f, -0.92016888f, 0.27684754f, + 0.31649706f, -0.92013121f, 0.23062554f, + 0.34877831f, -0.92002207f, 0.17864247f, + 0.37283483f, -0.91985726f, 0.12188824f, + 0.38778353f, -0.91968179f, 0.06171941f, + 0.39287689f, -0.91959101f, 0.00000000f, + 0.00000000f, -0.74740958f, 0.66436350f, + 0.10438798f, -0.74762046f, 0.65587103f, + 0.20622680f, -0.74802911f, 0.63081133f, + 0.30235204f, -0.74841273f, 0.59030640f, + 0.39041957f, -0.74866760f, 0.53578854f, + 0.46870315f, -0.74875546f, 0.46870315f, + 0.53578901f, -0.74866718f, 0.39041984f, + 0.59030616f, -0.74841291f, 0.30235201f, + 0.63081133f, -0.74802911f, 0.20622671f, + 0.65587062f, -0.74762082f, 0.10438793f, + 0.66436350f, -0.74740958f, 0.00000000f, + 0.00000000f, -0.35008657f, 0.93671733f, + 0.14722249f, -0.35028356f, 0.92500114f, + 0.29100731f, -0.35066363f, 0.89014024f, + 0.42686713f, -0.35102227f, 0.83340722f, + 0.55138916f, -0.35126165f, 0.75669360f, + 0.66202652f, -0.35134289f, 0.66202652f, + 0.75669384f, -0.35126114f, 0.55138922f, + 0.83340722f, -0.35102227f, 0.42686713f, + 0.89013988f, -0.35066500f, 0.29100722f, + 0.92500126f, -0.35028300f, 0.14722241f, + 0.93671727f, -0.35008690f, 0.00000000f, + 0.00000000f, 0.00000000f, 1.00000000f, + 0.15718076f, -0.00000000f, 0.98756981f, + 0.31073880f, -0.00000000f, 0.95049536f, + 0.45587587f, -0.00000000f, 0.89004326f, + 0.58891648f, -0.00000000f, 0.80819386f, + 0.70710683f, -0.00000000f, 0.70710683f, + 0.80819392f, -0.00000000f, 0.58891648f, + 0.89004338f, -0.00000000f, 0.45587590f, + 0.95049524f, -0.00000000f, 0.31073886f, + 0.98756975f, -0.00000000f, 0.15718083f, + 1.00000000f, -0.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.01220204f, -0.99992561f, 0.00000000f, + 0.01204262f, -0.99992567f, -0.00191707f, + 0.01157635f, -0.99992579f, -0.00378435f, + 0.01082734f, -0.99992597f, -0.00554588f, + 0.00982409f, -0.99992603f, -0.00715852f, + 0.00859299f, -0.99992621f, -0.00859299f, + 0.00715861f, -0.99992603f, -0.00982405f, + 0.00554574f, -0.99992591f, -0.01082739f, + 0.00378453f, -0.99992585f, -0.01157622f, + 0.00191707f, -0.99992567f, -0.01204260f, + -0.00000000f, -0.99992561f, -0.01220204f, + 0.02883412f, -0.99958426f, 0.00000000f, + 0.02845753f, -0.99958473f, -0.00452944f, + 0.02735537f, -0.99958575f, -0.00894299f, + 0.02558579f, -0.99958676f, -0.01310476f, + 0.02321488f, -0.99958736f, -0.01691613f, + 0.02030579f, -0.99958766f, -0.02030579f, + 0.01691616f, -0.99958742f, -0.02321492f, + 0.01310467f, -0.99958670f, -0.02558579f, + 0.00894308f, -0.99958575f, -0.02735530f, + 0.00452948f, -0.99958473f, -0.02845747f, + -0.00000000f, -0.99958426f, -0.02883412f, + 0.05234344f, -0.99862915f, 0.00000000f, + 0.05165986f, -0.99863088f, -0.00822230f, + 0.04965902f, -0.99863428f, -0.01623463f, + 0.04644673f, -0.99863744f, -0.02378968f, + 0.04214293f, -0.99863952f, -0.03070865f, + 0.03686197f, -0.99864024f, -0.03686197f, + 0.03070885f, -0.99863952f, -0.04214287f, + 0.02378960f, -0.99863750f, -0.04644670f, + 0.01623469f, -0.99863428f, -0.04965906f, + 0.00822228f, -0.99863094f, -0.05165981f, + -0.00000000f, -0.99862915f, -0.05234344f, + 0.08709935f, -0.99619961f, 0.00000000f, + 0.08596216f, -0.99620444f, -0.01368170f, + 0.08263341f, -0.99621379f, -0.02701472f, + 0.07728846f, -0.99622256f, -0.03958685f, + 0.07012693f, -0.99622840f, -0.05110020f, + 0.06133943f, -0.99623030f, -0.06133943f, + 0.05110029f, -0.99622846f, -0.07012701f, + 0.03958676f, -0.99622262f, -0.07728843f, + 0.02701474f, -0.99621379f, -0.08263331f, + 0.01368166f, -0.99620444f, -0.08596208f, + -0.00000000f, -0.99619961f, -0.08709935f, + 0.14142138f, -0.98994946f, 0.00000000f, + 0.13957602f, -0.98996216f, -0.02221482f, + 0.13417330f, -0.98998660f, -0.04386431f, + 0.12549642f, -0.99000955f, -0.06427864f, + 0.11386906f, -0.99002475f, -0.08297431f, + 0.09960062f, -0.99002999f, -0.09960062f, + 0.08297440f, -0.99002481f, -0.11386913f, + 0.06427859f, -0.99000955f, -0.12549631f, + 0.04386439f, -0.98998660f, -0.13417305f, + 0.02221479f, -0.98996210f, -0.13957596f, + -0.00000000f, -0.98994946f, -0.14142138f, + 0.23211864f, -0.97268748f, 0.00000000f, + 0.22909468f, -0.97272098f, -0.03646255f, + 0.22023600f, -0.97278571f, -0.07200018f, + 0.20600171f, -0.97284645f, -0.10551308f, + 0.18692029f, -0.97288686f, -0.13620538f, + 0.16349953f, -0.97290069f, -0.16349953f, + 0.13620566f, -0.97288686f, -0.18692048f, + 0.10551302f, -0.97284657f, -0.20600158f, + 0.07200018f, -0.97278577f, -0.22023584f, + 0.03646262f, -0.97272098f, -0.22909461f, + -0.00000000f, -0.97268748f, -0.23211864f, + 0.39287689f, -0.91959101f, 0.00000000f, + 0.38778380f, -0.91968179f, -0.06171946f, + 0.37283456f, -0.91985744f, -0.12188818f, + 0.34877828f, -0.92002207f, -0.17864251f, + 0.31649682f, -0.92013133f, -0.23062550f, + 0.27684763f, -0.92016888f, -0.27684763f, + 0.23062561f, -0.92013121f, -0.31649697f, + 0.17864254f, -0.92002207f, -0.34877825f, + 0.12188834f, -0.91985726f, -0.37283480f, + 0.06171951f, -0.91968179f, -0.38778350f, + -0.00000000f, -0.91959101f, -0.39287689f, + 0.66436350f, -0.74740958f, 0.00000000f, + 0.65587103f, -0.74762046f, -0.10438798f, + 0.63081098f, -0.74802947f, -0.20622666f, + 0.59030640f, -0.74841273f, -0.30235204f, + 0.53578854f, -0.74866760f, -0.39041957f, + 0.46870315f, -0.74875546f, -0.46870315f, + 0.39041984f, -0.74866718f, -0.53578901f, + 0.30235201f, -0.74841291f, -0.59030616f, + 0.20622671f, -0.74802911f, -0.63081133f, + 0.10438793f, -0.74762082f, -0.65587062f, + -0.00000000f, -0.74740958f, -0.66436350f, + 0.93671727f, -0.35008690f, 0.00000000f, + 0.92500097f, -0.35028383f, -0.14722244f, + 0.89014006f, -0.35066432f, -0.29100725f, + 0.83340710f, -0.35102299f, -0.42686704f, + 0.75669360f, -0.35126165f, -0.55138916f, + 0.66202641f, -0.35134318f, -0.66202641f, + 0.55138922f, -0.35126114f, -0.75669384f, + 0.42686713f, -0.35102227f, -0.83340722f, + 0.29100722f, -0.35066500f, -0.89013988f, + 0.14722241f, -0.35028300f, -0.92500132f, + -0.00000000f, -0.35008690f, -0.93671727f, + 1.00000000f, -0.00000000f, 0.00000000f, + 0.98756981f, 0.00000000f, -0.15718076f, + 0.95049536f, 0.00000000f, -0.31073886f, + 0.89004332f, 0.00000000f, -0.45587593f, + 0.80819386f, 0.00000000f, -0.58891648f, + 0.70710677f, 0.00000000f, -0.70710677f, + 0.58891648f, 0.00000000f, -0.80819392f, + 0.45587590f, 0.00000000f, -0.89004338f, + 0.31073886f, 0.00000000f, -0.95049524f, + 0.15718083f, 0.00000000f, -0.98756975f, + 0.00000000f, 0.00000000f, -1.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + -0.01220204f, -0.99992549f, 0.00000000f, + -0.01204262f, -0.99992567f, 0.00191707f, + -0.01157633f, -0.99992579f, 0.00378434f, + -0.01082734f, -0.99992591f, 0.00554588f, + -0.00982409f, -0.99992603f, 0.00715852f, + -0.00859298f, -0.99992615f, 0.00859298f, + -0.00715860f, -0.99992603f, 0.00982403f, + -0.00554574f, -0.99992591f, 0.01082739f, + -0.00378453f, -0.99992585f, 0.01157620f, + -0.00191707f, -0.99992567f, 0.01204258f, + 0.00000000f, -0.99992561f, 0.01220202f, + -0.02883412f, -0.99958426f, 0.00000000f, + -0.02845753f, -0.99958473f, 0.00452944f, + -0.02735532f, -0.99958575f, 0.00894297f, + -0.02558579f, -0.99958670f, 0.01310476f, + -0.02321488f, -0.99958730f, 0.01691613f, + -0.02030576f, -0.99958766f, 0.02030576f, + -0.01691614f, -0.99958742f, 0.02321489f, + -0.01310467f, -0.99958670f, 0.02558579f, + -0.00894307f, -0.99958575f, 0.02735526f, + -0.00452947f, -0.99958473f, 0.02845743f, + 0.00000000f, -0.99958426f, 0.02883408f, + -0.05234344f, -0.99862915f, 0.00000000f, + -0.05165986f, -0.99863088f, 0.00822230f, + -0.04965896f, -0.99863428f, 0.01623461f, + -0.04644673f, -0.99863744f, 0.02378967f, + -0.04214293f, -0.99863946f, 0.03070865f, + -0.03686193f, -0.99864024f, 0.03686193f, + -0.03070882f, -0.99863952f, 0.04214283f, + -0.02378960f, -0.99863750f, 0.04644670f, + -0.01623467f, -0.99863428f, 0.04965901f, + -0.00822227f, -0.99863088f, 0.05165974f, + 0.00000000f, -0.99862915f, 0.05234338f, + -0.08709935f, -0.99619961f, 0.00000000f, + -0.08596216f, -0.99620444f, 0.01368170f, + -0.08263335f, -0.99621379f, 0.02701470f, + -0.07728847f, -0.99622256f, 0.03958685f, + -0.07012694f, -0.99622846f, 0.05110020f, + -0.06133937f, -0.99623030f, 0.06133937f, + -0.05110025f, -0.99622846f, 0.07012695f, + -0.03958676f, -0.99622262f, 0.07728843f, + -0.02701471f, -0.99621379f, 0.08263323f, + -0.01368164f, -0.99620444f, 0.08596201f, + 0.00000000f, -0.99619961f, 0.08709927f, + -0.14142138f, -0.98994946f, 0.00000000f, + -0.13957602f, -0.98996216f, 0.02221482f, + -0.13417321f, -0.98998666f, 0.04386427f, + -0.12549642f, -0.99000955f, 0.06427864f, + -0.11386906f, -0.99002475f, 0.08297431f, + -0.09960055f, -0.99002999f, 0.09960055f, + -0.08297434f, -0.99002481f, 0.11386906f, + -0.06427859f, -0.99000955f, 0.12549631f, + -0.04386435f, -0.98998660f, 0.13417295f, + -0.02221478f, -0.98996210f, 0.13957585f, + 0.00000000f, -0.98994946f, 0.14142129f, + -0.23211864f, -0.97268748f, 0.00000000f, + -0.22909468f, -0.97272098f, 0.03646255f, + -0.22023587f, -0.97278571f, 0.07200014f, + -0.20600173f, -0.97284651f, 0.10551308f, + -0.18692032f, -0.97288692f, 0.13620538f, + -0.16349944f, -0.97290081f, 0.16349944f, + -0.13620557f, -0.97288686f, 0.18692036f, + -0.10551302f, -0.97284657f, 0.20600158f, + -0.07200012f, -0.97278577f, 0.22023571f, + -0.03646260f, -0.97272110f, 0.22909448f, + 0.00000000f, -0.97268748f, 0.23211849f, + -0.39287689f, -0.91959101f, 0.00000000f, + -0.38778380f, -0.91968179f, 0.06171943f, + -0.37283459f, -0.91985744f, 0.12188814f, + -0.34877831f, -0.92002207f, 0.17864247f, + -0.31649688f, -0.92013133f, 0.23062544f, + -0.27684769f, -0.92016888f, 0.27684754f, + -0.23062566f, -0.92013121f, 0.31649694f, + -0.17864263f, -0.92002207f, 0.34877819f, + -0.12188834f, -0.91985726f, 0.37283480f, + -0.06171941f, -0.91968179f, 0.38778353f, + 0.00000000f, -0.91959101f, 0.39287689f, + -0.66436350f, -0.74740958f, 0.00000000f, + -0.65587103f, -0.74762046f, 0.10438798f, + -0.63081098f, -0.74802947f, 0.20622666f, + -0.59030640f, -0.74841273f, 0.30235204f, + -0.53578854f, -0.74866760f, 0.39041957f, + -0.46870315f, -0.74875546f, 0.46870315f, + -0.39041984f, -0.74866718f, 0.53578901f, + -0.30235201f, -0.74841291f, 0.59030616f, + -0.20622671f, -0.74802911f, 0.63081133f, + -0.10438793f, -0.74762082f, 0.65587062f, + 0.00000000f, -0.74740958f, 0.66436350f, + -0.93671727f, -0.35008690f, 0.00000000f, + -0.92500097f, -0.35028383f, 0.14722244f, + -0.89014024f, -0.35066363f, 0.29100731f, + -0.83340710f, -0.35102299f, 0.42686704f, + -0.75669360f, -0.35126165f, 0.55138916f, + -0.66202652f, -0.35134289f, 0.66202652f, + -0.55138934f, -0.35126081f, 0.75669390f, + -0.42686713f, -0.35102227f, 0.83340722f, + -0.29100725f, -0.35066465f, 0.89013994f, + -0.14722243f, -0.35028264f, 0.92500138f, + 0.00000000f, -0.35008657f, 0.93671733f, + -1.00000000f, 0.00000000f, 0.00000000f, + -0.98756981f, 0.00000000f, 0.15718076f, + -0.95049542f, 0.00000000f, 0.31073883f, + -0.89004332f, 0.00000000f, 0.45587593f, + -0.80819386f, 0.00000000f, 0.58891648f, + -0.70710683f, 0.00000000f, 0.70710683f, + -0.58891654f, 0.00000000f, 0.80819386f, + -0.45587590f, 0.00000000f, 0.89004338f, + -0.31073889f, 0.00000000f, 0.95049536f, + -0.15718086f, 0.00000000f, 0.98756987f, + 0.00000000f, 0.00000000f, 1.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + -0.00000000f, -0.99992561f, -0.01220204f, + -0.00191707f, -0.99992567f, -0.01204262f, + -0.00378434f, -0.99992579f, -0.01157635f, + -0.00554588f, -0.99992591f, -0.01082734f, + -0.00715852f, -0.99992603f, -0.00982409f, + -0.00859299f, -0.99992621f, -0.00859299f, + -0.00982405f, -0.99992603f, -0.00715861f, + -0.01082739f, -0.99992591f, -0.00554574f, + -0.01157622f, -0.99992585f, -0.00378453f, + -0.01204260f, -0.99992567f, -0.00191707f, + -0.01220204f, -0.99992561f, 0.00000000f, + -0.00000000f, -0.99958426f, -0.02883412f, + -0.00452944f, -0.99958473f, -0.02845753f, + -0.00894299f, -0.99958575f, -0.02735537f, + -0.01310476f, -0.99958670f, -0.02558579f, + -0.01691613f, -0.99958730f, -0.02321488f, + -0.02030579f, -0.99958766f, -0.02030579f, + -0.02321492f, -0.99958742f, -0.01691616f, + -0.02558579f, -0.99958670f, -0.01310467f, + -0.02735530f, -0.99958575f, -0.00894308f, + -0.02845747f, -0.99958473f, -0.00452948f, + -0.02883412f, -0.99958426f, 0.00000000f, + -0.00000000f, -0.99862915f, -0.05234344f, + -0.00822230f, -0.99863088f, -0.05165986f, + -0.01623463f, -0.99863428f, -0.04965902f, + -0.02378967f, -0.99863744f, -0.04644673f, + -0.03070865f, -0.99863952f, -0.04214294f, + -0.03686197f, -0.99864024f, -0.03686197f, + -0.04214287f, -0.99863952f, -0.03070885f, + -0.04644670f, -0.99863750f, -0.02378960f, + -0.04965907f, -0.99863434f, -0.01623469f, + -0.05165980f, -0.99863088f, -0.00822228f, + -0.05234344f, -0.99862915f, 0.00000000f, + -0.00000000f, -0.99619961f, -0.08709935f, + -0.01368170f, -0.99620444f, -0.08596216f, + -0.02701472f, -0.99621379f, -0.08263342f, + -0.03958685f, -0.99622256f, -0.07728846f, + -0.05110020f, -0.99622840f, -0.07012693f, + -0.06133943f, -0.99623030f, -0.06133943f, + -0.07012701f, -0.99622840f, -0.05110029f, + -0.07728843f, -0.99622262f, -0.03958676f, + -0.08263330f, -0.99621379f, -0.02701474f, + -0.08596208f, -0.99620444f, -0.01368166f, + -0.08709935f, -0.99619961f, 0.00000000f, + -0.00000000f, -0.98994946f, -0.14142138f, + -0.02221482f, -0.98996216f, -0.13957602f, + -0.04386431f, -0.98998660f, -0.13417332f, + -0.06427864f, -0.99000955f, -0.12549642f, + -0.08297431f, -0.99002475f, -0.11386906f, + -0.09960062f, -0.99002999f, -0.09960062f, + -0.11386913f, -0.99002481f, -0.08297440f, + -0.12549631f, -0.99000955f, -0.06427859f, + -0.13417305f, -0.98998660f, -0.04386439f, + -0.13957594f, -0.98996210f, -0.02221479f, + -0.14142138f, -0.98994946f, 0.00000000f, + -0.00000000f, -0.97268748f, -0.23211864f, + -0.03646255f, -0.97272098f, -0.22909468f, + -0.07200018f, -0.97278571f, -0.22023600f, + -0.10551308f, -0.97284645f, -0.20600171f, + -0.13620538f, -0.97288692f, -0.18692032f, + -0.16349953f, -0.97290069f, -0.16349953f, + -0.18692048f, -0.97288686f, -0.13620566f, + -0.20600158f, -0.97284657f, -0.10551302f, + -0.22023584f, -0.97278577f, -0.07200018f, + -0.22909461f, -0.97272098f, -0.03646262f, + -0.23211864f, -0.97268748f, 0.00000000f, + -0.00000000f, -0.91959101f, -0.39287689f, + -0.06171946f, -0.91968179f, -0.38778380f, + -0.12188818f, -0.91985744f, -0.37283456f, + -0.17864251f, -0.92002207f, -0.34877828f, + -0.23062550f, -0.92013133f, -0.31649682f, + -0.27684763f, -0.92016888f, -0.27684763f, + -0.31649697f, -0.92013121f, -0.23062561f, + -0.34877825f, -0.92002207f, -0.17864254f, + -0.37283480f, -0.91985726f, -0.12188834f, + -0.38778350f, -0.91968179f, -0.06171951f, + -0.39287689f, -0.91959101f, 0.00000000f, + -0.00000000f, -0.74740958f, -0.66436350f, + -0.10438798f, -0.74762046f, -0.65587103f, + -0.20622666f, -0.74802947f, -0.63081098f, + -0.30235204f, -0.74841273f, -0.59030640f, + -0.39041957f, -0.74866760f, -0.53578854f, + -0.46870315f, -0.74875546f, -0.46870315f, + -0.53578901f, -0.74866718f, -0.39041984f, + -0.59030616f, -0.74841291f, -0.30235201f, + -0.63081133f, -0.74802911f, -0.20622671f, + -0.65587062f, -0.74762082f, -0.10438793f, + -0.66436350f, -0.74740958f, 0.00000000f, + -0.00000000f, -0.35008690f, -0.93671727f, + -0.14722244f, -0.35028386f, -0.92500097f, + -0.29100725f, -0.35066432f, -0.89014006f, + -0.42686704f, -0.35102299f, -0.83340710f, + -0.55138916f, -0.35126165f, -0.75669360f, + -0.66202641f, -0.35134318f, -0.66202641f, + -0.75669384f, -0.35126114f, -0.55138922f, + -0.83340722f, -0.35102227f, -0.42686713f, + -0.89013988f, -0.35066500f, -0.29100722f, + -0.92500126f, -0.35028300f, -0.14722241f, + -0.93671727f, -0.35008690f, 0.00000000f, + 0.00000000f, 0.00000000f, -1.00000000f, + -0.15718076f, 0.00000000f, -0.98756981f, + -0.31073880f, 0.00000000f, -0.95049536f, + -0.45587593f, 0.00000000f, -0.89004332f, + -0.58891648f, 0.00000000f, -0.80819386f, + -0.70710677f, 0.00000000f, -0.70710677f, + -0.80819392f, 0.00000000f, -0.58891648f, + -0.89004338f, 0.00000000f, -0.45587590f, + -0.95049524f, 0.00000000f, -0.31073886f, + -0.98756975f, 0.00000000f, -0.15718083f, + -1.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, -1.00000000f, 0.00000000f, + 0.00000000f, -0.98605883f, 0.16639723f, + 0.00000000f, -0.92847669f, 0.37139052f, + 0.00000000f, -0.78563637f, 0.61868852f, + 0.00000000f, -0.48564312f, 0.87415707f, + 0.00000000f, -0.00000002f, 0.99999994f, + 0.00000000f, 0.48564315f, 0.87415707f, + 0.00000000f, 0.78563654f, 0.61868834f, + 0.00000000f, 0.92847681f, 0.37139040f, + 0.00000000f, 0.98605877f, 0.16639741f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00352115f, -0.99999374f, 0.00000000f, + 0.00352907f, -0.98608685f, 0.16619359f, + 0.00346285f, -0.92863286f, 0.37098381f, + 0.00309265f, -0.78601688f, 0.61819726f, + 0.00202332f, -0.48612255f, 0.87388837f, + 0.00000001f, -0.00000002f, 1.00000000f, + -0.00223059f, 0.48613369f, 0.87388152f, + -0.00373999f, 0.78603256f, 0.61817366f, + -0.00453865f, 0.92863941f, 0.37095594f, + -0.00490123f, 0.98608381f, 0.16617684f, + -0.00499993f, 0.99998748f, 0.00000000f, + 0.01562307f, -0.99987787f, 0.00000000f, + 0.01566775f, -0.98604649f, 0.16573150f, + 0.01539990f, -0.92885453f, 0.37012452f, + 0.01378791f, -0.78663695f, 0.61726177f, + 0.00904643f, -0.48685381f, 0.87343663f, + 0.00000000f, -0.00000002f, 0.99999994f, + -0.01000922f, 0.48666272f, 0.87353265f, + -0.01679711f, 0.78630960f, 0.61760414f, + -0.02040106f, 0.92859751f, 0.37052733f, + -0.02204463f, 0.98588336f, 0.16597590f, + -0.02249430f, 0.99974698f, 0.00000000f, + 0.03959709f, -0.99921572f, 0.00000000f, + 0.03973298f, -0.98543602f, 0.16533947f, + 0.03911157f, -0.92837644f, 0.36957741f, + 0.03508222f, -0.78619260f, 0.61698496f, + 0.02305110f, -0.48625365f, 0.87351370f, + -0.00000000f, -0.00000002f, 1.00000000f, + -0.02556038f, 0.48519081f, 0.87403470f, + -0.04298534f, 0.78433579f, 0.61884552f, + -0.05230516f, 0.92685103f, 0.37176794f, + -0.05658430f, 0.98438811f, 0.16666794f, + -0.05776057f, 0.99833041f, 0.00000000f, + 0.08081594f, -0.99672896f, 0.00000000f, + 0.08112378f, -0.98284930f, 0.16560850f, + 0.07990950f, -0.92532861f, 0.37065011f, + 0.07166215f, -0.78199679f, 0.61914897f, + 0.04697014f, -0.48146683f, 0.87520474f, + 0.00000001f, -0.00000001f, 0.99999994f, + -0.05205173f, 0.47823566f, 0.87668771f, + -0.08801019f, 0.77604234f, 0.62450969f, + -0.10756110f, 0.91994244f, 0.37701002f, + -0.11663799f, 0.97860771f, 0.16947733f, + -0.11914521f, 0.99287677f, 0.00000000f, + 0.14834052f, -0.98893636f, 0.00000000f, + 0.14888510f, -0.97454345f, 0.16762571f, + 0.14646719f, -0.91512442f, 0.37562558f, + 0.13072294f, -0.76828307f, 0.62662005f, + 0.08480435f, -0.46723226f, 0.88005811f, + 0.00000000f, -0.00000002f, 1.00000000f, + -0.09347267f, 0.45980066f, 0.88308901f, + -0.15982038f, 0.75331390f, 0.63794643f, + -0.19704285f, 0.89976454f, 0.38935578f, + -0.21463276f, 0.96069551f, 0.17605987f, + -0.21951221f, 0.97560972f, 0.00000000f, + 0.25746500f, -0.96628761f, 0.00000000f, + 0.25805786f, -0.95046312f, 0.17328028f, + 0.25243360f, -0.88633966f, 0.38817427f, + 0.22221342f, -0.73262465f, 0.64333671f, + 0.14082101f, -0.43436509f, 0.88966089f, + -0.00000000f, -0.00000003f, 1.00000000f, + -0.15323463f, 0.42038015f, 0.89431524f, + -0.26688996f, 0.70047694f, 0.66189259f, + -0.33389512f, 0.84794778f, 0.41170225f, + -0.36636090f, 0.91127318f, 0.18804511f, + -0.37538239f, 0.92687011f, 0.00000000f, + 0.43072972f, -0.90248102f, 0.00000000f, + 0.42996117f, -0.88366210f, 0.18513416f, + 0.41455880f, -0.81121510f, 0.41239676f, + 0.35453796f, -0.65030318f, 0.67186946f, + 0.21581109f, -0.36961418f, 0.90377587f, + 0.00000001f, -0.00000001f, 1.00000000f, + -0.22965637f, 0.34849611f, 0.90874004f, + -0.40901679f, 0.59286922f, 0.69369388f, + -0.52110124f, 0.72998118f, 0.44224542f, + -0.57683110f, 0.79082137f, 0.20461525f, + -0.59223664f, 0.80576408f, 0.00000000f, + 0.67827994f, -0.73480362f, 0.00000000f, + 0.67117316f, -0.71288520f, 0.20327650f, + 0.62980002f, -0.63613772f, 0.44573602f, + 0.51530230f, -0.48705083f, 0.70515603f, + 0.29868835f, -0.26309496f, 0.91736919f, + 0.00000001f, -0.00000001f, 1.00000000f, + -0.30877924f, 0.24007824f, 0.92033571f, + -0.55776328f, 0.41359809f, 0.71960878f, + -0.71884412f, 0.51453108f, 0.46746215f, + -0.79948735f, 0.55961394f, 0.21829374f, + -0.82137012f, 0.57039577f, 0.00000000f, + 0.91750979f, -0.39771309f, 0.00000000f, + 0.89836329f, -0.38162711f, 0.21749499f, + 0.81937253f, -0.33061025f, 0.46832207f, + 0.64609301f, -0.24353826f, 0.72336227f, + 0.36258951f, -0.12712903f, 0.92323726f, + 0.00000001f, -0.00000000f, 1.00000000f, + -0.36578882f, 0.11278567f, 0.92383873f, + -0.65935183f, 0.19359557f, 0.72648203f, + -0.84755963f, 0.23992470f, 0.47336963f, + -0.93993521f, 0.25999326f, 0.22119047f, + -0.96436578f, 0.26457235f, 0.00000000f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.97560978f, -0.00000001f, 0.21951191f, + 0.88235295f, 0.00000000f, 0.47058815f, + 0.68965501f, 0.00000000f, 0.72413802f, + 0.38461545f, 0.00000000f, 0.92307693f, + 0.00000001f, 0.00000000f, 1.00000000f, + -0.38461548f, 0.00000000f, 0.92307681f, + -0.68965536f, 0.00000000f, 0.72413772f, + -0.88235283f, 0.00000000f, 0.47058827f, + -0.97560972f, -0.00000000f, 0.21951239f, + -0.99999994f, 0.00000000f, -0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + 0.00000000f, 0.98605877f, -0.16639729f, + 0.00000000f, 0.92847687f, -0.37139040f, + 0.00000000f, 0.78563643f, -0.61868840f, + 0.00000000f, 0.48564321f, -0.87415713f, + -0.00000000f, -0.00000001f, -1.00000000f, + -0.00000000f, -0.48564330f, -0.87415713f, + -0.00000000f, -0.78563637f, -0.61868852f, + -0.00000000f, -0.92847681f, -0.37139040f, + -0.00000000f, -0.98605883f, -0.16639724f, + 0.00000000f, -1.00000000f, 0.00000000f, + -0.00499993f, 0.99998748f, 0.00000000f, + -0.00490124f, 0.98608375f, -0.16617680f, + -0.00453864f, 0.92863935f, -0.37095582f, + -0.00373999f, 0.78603268f, -0.61817366f, + -0.00223057f, 0.48613375f, -0.87388164f, + 0.00000001f, -0.00000001f, -1.00000000f, + 0.00202331f, -0.48612273f, -0.87388825f, + 0.00309265f, -0.78601700f, -0.61819714f, + 0.00346281f, -0.92863292f, -0.37098360f, + 0.00352907f, -0.98608685f, -0.16619354f, + 0.00352115f, -0.99999374f, 0.00000000f, + -0.02249430f, 0.99974698f, 0.00000000f, + -0.02204463f, 0.98588336f, -0.16597593f, + -0.02040106f, 0.92859745f, -0.37052733f, + -0.01679711f, 0.78630960f, -0.61760414f, + -0.01000922f, 0.48666272f, -0.87353259f, + 0.00000000f, -0.00000001f, -1.00000000f, + 0.00904643f, -0.48685390f, -0.87343657f, + 0.01378791f, -0.78663707f, -0.61726171f, + 0.01539990f, -0.92885453f, -0.37012431f, + 0.01566775f, -0.98604643f, -0.16573155f, + 0.01562307f, -0.99987787f, 0.00000000f, + -0.05776057f, 0.99833041f, 0.00000000f, + -0.05658428f, 0.98438817f, -0.16666785f, + -0.05230515f, 0.92685103f, -0.37176776f, + -0.04298534f, 0.78433579f, -0.61884540f, + -0.02556037f, 0.48519066f, -0.87403476f, + -0.00000000f, -0.00000001f, -1.00000000f, + 0.02305111f, -0.48625365f, -0.87351352f, + 0.03508221f, -0.78619260f, -0.61698502f, + 0.03911154f, -0.92837650f, -0.36957735f, + 0.03973297f, -0.98543602f, -0.16533944f, + 0.03959709f, -0.99921572f, 0.00000000f, + -0.11914521f, 0.99287677f, 0.00000000f, + -0.11663795f, 0.97860771f, -0.16947742f, + -0.10756104f, 0.91994238f, -0.37700999f, + -0.08801019f, 0.77604234f, -0.62450969f, + -0.05205172f, 0.47823566f, -0.87668771f, + 0.00000001f, -0.00000001f, -1.00000000f, + 0.04697014f, -0.48146695f, -0.87520474f, + 0.07166213f, -0.78199679f, -0.61914903f, + 0.07990947f, -0.92532867f, -0.37064984f, + 0.08112375f, -0.98284930f, -0.16560827f, + 0.08081594f, -0.99672896f, 0.00000000f, + -0.21951221f, 0.97560972f, 0.00000000f, + -0.21463269f, 0.96069551f, -0.17605998f, + -0.19704288f, 0.89976460f, -0.38935572f, + -0.15982041f, 0.75331384f, -0.63794643f, + -0.09347264f, 0.45980066f, -0.88308901f, + 0.00000000f, -0.00000001f, -1.00000000f, + 0.08480434f, -0.46723235f, -0.88005805f, + 0.13072288f, -0.76828313f, -0.62661994f, + 0.14646715f, -0.91512454f, -0.37562546f, + 0.14888506f, -0.97454339f, -0.16762568f, + 0.14834052f, -0.98893636f, 0.00000000f, + -0.37538239f, 0.92687011f, 0.00000000f, + -0.36636072f, 0.91127330f, -0.18804498f, + -0.33389512f, 0.84794778f, -0.41170213f, + -0.26688999f, 0.70047688f, -0.66189259f, + -0.15323462f, 0.42038018f, -0.89431512f, + -0.00000001f, -0.00000002f, -1.00000000f, + 0.14082104f, -0.43436518f, -0.88966078f, + 0.22221328f, -0.73262471f, -0.64333671f, + 0.25243348f, -0.88633984f, -0.38817394f, + 0.25805777f, -0.95046312f, -0.17328016f, + 0.25746500f, -0.96628761f, 0.00000000f, + -0.59223664f, 0.80576408f, 0.00000000f, + -0.57683092f, 0.79082137f, -0.20461576f, + -0.52110112f, 0.72998136f, -0.44224519f, + -0.40901700f, 0.59286916f, -0.69369400f, + -0.22965620f, 0.34849602f, -0.90874004f, + 0.00000001f, -0.00000001f, -0.99999994f, + 0.21581109f, -0.36961427f, -0.90377587f, + 0.35453799f, -0.65030324f, -0.67186940f, + 0.41455874f, -0.81121504f, -0.41239697f, + 0.42996103f, -0.88366216f, -0.18513407f, + 0.43072972f, -0.90248102f, 0.00000000f, + -0.82137012f, 0.57039577f, 0.00000000f, + -0.79948711f, 0.55961424f, -0.21829398f, + -0.71884418f, 0.51453096f, -0.46746221f, + -0.55776322f, 0.41359806f, -0.71960890f, + -0.30877942f, 0.24007839f, -0.92033559f, + 0.00000001f, -0.00000001f, -1.00000000f, + 0.29868859f, -0.26309517f, -0.91736907f, + 0.51530242f, -0.48705092f, -0.70515585f, + 0.62979978f, -0.63613826f, -0.44573584f, + 0.67117304f, -0.71288520f, -0.20327665f, + 0.67827994f, -0.73480362f, 0.00000000f, + -0.96436578f, 0.26457235f, 0.00000000f, + -0.93993515f, 0.25999367f, -0.22119042f, + -0.84756005f, 0.23992476f, -0.47336900f, + -0.65935189f, 0.19359526f, -0.72648191f, + -0.36578867f, 0.11278601f, -0.92383868f, + 0.00000001f, -0.00000000f, -1.00000000f, + 0.36258978f, -0.12712936f, -0.92323720f, + 0.64609277f, -0.24353866f, -0.72336233f, + 0.81937248f, -0.33061025f, -0.46832204f, + 0.89836329f, -0.38162714f, -0.21749526f, + 0.91750979f, -0.39771309f, 0.00000000f, + -0.99999994f, 0.00000000f, -0.00000000f, + -0.97560984f, 0.00000000f, -0.21951208f, + -0.88235319f, -0.00000000f, -0.47058785f, + -0.68965513f, -0.00000000f, -0.72413802f, + -0.38461575f, -0.00000000f, -0.92307681f, + 0.00000001f, 0.00000000f, -1.00000000f, + 0.38461554f, 0.00000000f, -0.92307687f, + 0.68965495f, 0.00000000f, -0.72413814f, + 0.88235325f, 0.00000000f, -0.47058773f, + 0.97560984f, 0.00000001f, -0.21951178f, + 1.00000000f, 0.00000000f, -0.00000000f, + 1.00000000f, 0.00000000f, -0.00000000f, + 0.97560984f, 0.00000000f, 0.21951193f, + 0.88235295f, 0.00000000f, 0.47058818f, + 0.68965501f, 0.00000000f, 0.72413808f, + 0.38461548f, 0.00000000f, 0.92307693f, + 0.00000001f, 0.00000000f, 1.00000000f, + -0.38461551f, 0.00000000f, 0.92307681f, + -0.68965536f, 0.00000000f, 0.72413766f, + -0.88235295f, 0.00000000f, 0.47058836f, + -0.97560972f, 0.00000000f, 0.21951239f, + -1.00000000f, 0.00000000f, -0.00000000f, + 0.98828548f, 0.15261687f, -0.00000000f, + 0.96395302f, 0.14908172f, 0.22038466f, + 0.87109631f, 0.13522971f, 0.47212726f, + 0.67989933f, 0.10607001f, 0.72559357f, + 0.37859201f, 0.05936911f, 0.92365760f, + -0.00000000f, 0.00000002f, 1.00000000f, + -0.37849104f, -0.05988481f, 0.92366570f, + -0.67957181f, -0.10787752f, 0.72563398f, + -0.87054414f, -0.13852927f, 0.47218913f, + -0.96324706f, -0.15351437f, 0.22042809f, + -0.98752415f, -0.15746772f, -0.00000000f, + 0.96225405f, 0.27215251f, -0.00000000f, + 0.93821144f, 0.26704645f, 0.22010332f, + 0.84699523f, 0.24504340f, 0.47175488f, + 0.66009367f, 0.19516250f, 0.72538817f, + 0.36684823f, 0.11101300f, 0.92363340f, + -0.00000000f, 0.00000001f, 1.00000000f, + -0.36529851f, -0.11522681f, 0.92373145f, + -0.65501243f, -0.20990686f, 0.72587734f, + -0.83835191f, -0.27185294f, 0.47250620f, + -0.92713392f, -0.30289504f, 0.22062922f, + -0.95031464f, -0.31129095f, -0.00000000f, + 0.92727089f, 0.37439111f, -0.00000000f, + 0.90412927f, 0.36844388f, 0.21633159f, + 0.81684649f, 0.34092909f, 0.46532696f, + 0.63770211f, 0.27489299f, 0.71956229f, + 0.35483336f, 0.15842862f, 0.92140847f, + -0.00000000f, -0.00000000f, 1.00000000f, + -0.35035580f, -0.16681494f, 0.92164183f, + -0.62295628f, -0.30413818f, 0.72071177f, + -0.79164261f, -0.39387864f, 0.46707779f, + -0.87175226f, -0.43899769f, 0.21755241f, + -0.89236629f, -0.45131177f, -0.00000000f, + 0.88411051f, 0.46727774f, -0.00000000f, + 0.86261570f, 0.46090138f, 0.20848046f, + 0.78192133f, 0.42974684f, 0.45157146f, + 0.61448407f, 0.35097304f, 0.70656025f, + 0.34419492f, 0.20510472f, 0.91622162f, + -0.00000001f, 0.00000001f, 1.00000000f, + -0.33593270f, -0.21662095f, 0.91663766f, + -0.58751225f, -0.39089262f, 0.70854241f, + -0.73610902f, -0.50157106f, 0.45449966f, + -0.80393779f, -0.55621833f, 0.21048787f, + -0.82090527f, -0.57106435f, -0.00000000f, + 0.83205020f, 0.55470026f, -0.00000000f, + 0.81281066f, 0.54816157f, 0.19712368f, + 0.74092257f, 0.51492828f, 0.43114099f, + 0.58900535f, 0.42658585f, 0.68636519f, + 0.33426523f, 0.25343350f, 0.90776551f, + -0.00000001f, -0.00000000f, 1.00000000f, + -0.32207918f, -0.26620632f, 0.90851486f, + -0.55016369f, -0.47074062f, 0.68972695f, + -0.67611545f, -0.59405363f, 0.43585345f, + -0.73050374f, -0.65288639f, 0.20025899f, + -0.74358070f, -0.66864622f, -0.00000000f, + 0.76992410f, 0.63813549f, -0.00000000f, + 0.75327897f, 0.63149405f, 0.18381010f, + 0.69146866f, 0.59718293f, 0.40650177f, + 0.55801696f, 0.50208545f, 0.66070211f, + 0.32275081f, 0.30397698f, 0.89634252f, + 0.00000000f, -0.00000001f, 1.00000000f, + -0.30733797f, -0.31568038f, 0.89771330f, + -0.51069558f, -0.54324114f, 0.66639268f, + -0.61454564f, -0.67152673f, 0.41398731f, + -0.65671909f, -0.73016632f, 0.18861942f, + -0.66642159f, -0.74557513f, -0.00000000f, + 0.69667339f, 0.71738845f, -0.00000000f, + 0.68270814f, 0.71039432f, 0.17102490f, + 0.63117641f, 0.67495537f, 0.38216704f, + 0.51756680f, 0.57469726f, 0.63391453f, + 0.30609715f, 0.35447037f, 0.88354695f, + -0.00000001f, -0.00000001f, 1.00000000f, + -0.28927997f, -0.36283797f, 0.88581359f, + -0.46819973f, -0.60650212f, 0.64260727f, + -0.55317837f, -0.73456883f, 0.39294046f, + -0.58594245f, -0.79062068f, 0.17773652f, + -0.59321886f, -0.80504113f, -0.00000000f, + 0.61179918f, 0.79101306f, -0.00000000f, + 0.60046834f, 0.78304410f, 0.16210969f, + 0.55861497f, 0.74493736f, 0.36471584f, + 0.46437371f, 0.63860297f, 0.61363119f, + 0.28028923f, 0.39887190f, 0.87312043f, + -0.00000001f, -0.00000002f, 1.00000000f, + -0.26522434f, -0.40266120f, 0.87608224f, + -0.42191574f, -0.65742224f, 0.62432611f, + -0.49311385f, -0.78382802f, 0.37742877f, + -0.51994032f, -0.83714205f, 0.16986848f, + -0.52585983f, -0.85057127f, -0.00000000f, + 0.51578081f, 0.85672063f, -0.00000000f, + 0.50704503f, 0.84674674f, 0.16101368f, + 0.47396615f, 0.80263937f, 0.36211339f, + 0.39728773f, 0.68575180f, 0.60984170f, + 0.24266504f, 0.42777768f, 0.87070084f, + -0.00000001f, -0.00000002f, 0.99999994f, + -0.23340400f, -0.42794749f, 0.87314582f, + -0.37172547f, -0.69227660f, 0.61852515f, + -0.43502232f, -0.81983733f, 0.37232029f, + -0.45938149f, -0.87235868f, 0.16720937f, + -0.46495038f, -0.88533664f, -0.00000000f, + 0.41036469f, 0.91192144f, -0.00000000f, + 0.40426248f, 0.89836109f, 0.17181143f, + 0.37923580f, 0.84274662f, 0.38204512f, + 0.31827313f, 0.70727378f, 0.63124174f, + 0.19429605f, 0.43176913f, 0.88080895f, + -0.00000001f, -0.00000002f, 1.00000000f, + -0.19429621f, -0.43176931f, 0.88080889f, + -0.31827319f, -0.70727384f, 0.63124150f, + -0.37923595f, -0.84274650f, 0.38204503f, + -0.40426233f, -0.89836109f, 0.17181182f, + -0.41036457f, -0.91192156f, -0.00000000f, + -1.00000000f, 0.00000000f, -0.00000000f, + -0.97560984f, -0.00000000f, -0.21951209f, + -0.88235319f, -0.00000000f, -0.47058782f, + -0.68965513f, -0.00000000f, -0.72413796f, + -0.38461569f, -0.00000000f, -0.92307675f, + 0.00000001f, 0.00000000f, -1.00000000f, + 0.38461548f, 0.00000000f, -0.92307687f, + 0.68965495f, 0.00000000f, -0.72413814f, + 0.88235319f, 0.00000000f, -0.47058773f, + 0.97560990f, 0.00000000f, -0.21951178f, + 1.00000000f, 0.00000000f, -0.00000000f, + -0.98752415f, -0.15746772f, -0.00000000f, + -0.96324700f, -0.15351437f, -0.22042856f, + -0.87054449f, -0.13852932f, -0.47218847f, + -0.67957199f, -0.10787728f, -0.72563392f, + -0.37849066f, -0.05988473f, -0.92366582f, + 0.00000000f, 0.00000002f, -1.00000000f, + 0.37859198f, 0.05936907f, -0.92365766f, + 0.67989922f, 0.10607000f, -0.72559375f, + 0.87109667f, 0.13522977f, -0.47212654f, + 0.96395284f, 0.14908154f, -0.22038496f, + 0.98828548f, 0.15261687f, -0.00000000f, + -0.95031464f, -0.31129095f, -0.00000000f, + -0.92713398f, -0.30289510f, -0.22062880f, + -0.83835226f, -0.27185315f, -0.47250551f, + -0.65501243f, -0.20990673f, -0.72587729f, + -0.36529854f, -0.11522687f, -0.92373139f, + 0.00000000f, 0.00000001f, -1.00000000f, + 0.36684859f, 0.11101311f, -0.92363316f, + 0.66009378f, 0.19516268f, -0.72538793f, + 0.84699517f, 0.24504340f, -0.47175524f, + 0.93821150f, 0.26704639f, -0.22010335f, + 0.96225405f, 0.27215251f, -0.00000000f, + -0.89236629f, -0.45131177f, -0.00000000f, + -0.87175226f, -0.43899778f, -0.21755199f, + -0.79164267f, -0.39387897f, -0.46707743f, + -0.62295640f, -0.30413797f, -0.72071171f, + -0.35035577f, -0.16681507f, -0.92164183f, + 0.00000000f, -0.00000000f, -1.00000000f, + 0.35483366f, 0.15842879f, -0.92140841f, + 0.63770241f, 0.27489290f, -0.71956205f, + 0.81684643f, 0.34092909f, -0.46532708f, + 0.90412921f, 0.36844400f, -0.21633165f, + 0.92727089f, 0.37439111f, -0.00000000f, + -0.82090527f, -0.57106435f, -0.00000000f, + -0.80393773f, -0.55621839f, -0.21048813f, + -0.73610926f, -0.50157106f, -0.45449942f, + -0.58751225f, -0.39089242f, -0.70854253f, + -0.33593246f, -0.21662092f, -0.91663772f, + -0.00000001f, 0.00000001f, -1.00000000f, + 0.34419492f, 0.20510472f, -0.91622150f, + 0.61448371f, 0.35097295f, -0.70656049f, + 0.78192151f, 0.42974693f, -0.45157096f, + 0.86261582f, 0.46090129f, -0.20848002f, + 0.88411051f, 0.46727774f, -0.00000000f, + -0.74358070f, -0.66864622f, -0.00000000f, + -0.73050368f, -0.65288651f, -0.20025879f, + -0.67611581f, -0.59405386f, -0.43585256f, + -0.55016387f, -0.47074053f, -0.68972677f, + -0.32207900f, -0.26620629f, -0.90851492f, + -0.00000000f, -0.00000000f, -1.00000000f, + 0.33426523f, 0.25343353f, -0.90776551f, + 0.58900535f, 0.42658606f, -0.68636519f, + 0.74092275f, 0.51492846f, -0.43114042f, + 0.81281078f, 0.54816157f, -0.19712304f, + 0.83205020f, 0.55470026f, -0.00000000f, + -0.66642159f, -0.74557513f, -0.00000000f, + -0.65671885f, -0.73016638f, -0.18861963f, + -0.61454582f, -0.67152685f, -0.41398701f, + -0.51069564f, -0.54324096f, -0.66639268f, + -0.30733815f, -0.31568071f, -0.89771318f, + 0.00000000f, -0.00000001f, -1.00000000f, + 0.32275072f, 0.30397689f, -0.89634252f, + 0.55801678f, 0.50208527f, -0.66070235f, + 0.69146866f, 0.59718293f, -0.40650174f, + 0.75327885f, 0.63149405f, -0.18381011f, + 0.76992410f, 0.63813549f, -0.00000000f, + -0.59321886f, -0.80504113f, -0.00000000f, + -0.58594233f, -0.79062068f, -0.17773685f, + -0.55317843f, -0.73456901f, -0.39294016f, + -0.46819982f, -0.60650206f, -0.64260739f, + -0.28927994f, -0.36283809f, -0.88581365f, + -0.00000001f, -0.00000001f, -1.00000000f, + 0.30609706f, 0.35447022f, -0.88354713f, + 0.51756680f, 0.57469732f, -0.63391447f, + 0.63117641f, 0.67495537f, -0.38216686f, + 0.68270808f, 0.71039438f, -0.17102489f, + 0.69667339f, 0.71738845f, -0.00000000f, + -0.52585983f, -0.85057127f, -0.00000000f, + -0.51994026f, -0.83714211f, -0.16986860f, + -0.49311391f, -0.78382814f, -0.37742817f, + -0.42191574f, -0.65742230f, -0.62432611f, + -0.26522428f, -0.40266114f, -0.87608230f, + -0.00000001f, -0.00000001f, -1.00000000f, + 0.28028926f, 0.39887193f, -0.87312031f, + 0.46437368f, 0.63860303f, -0.61363125f, + 0.55861503f, 0.74493748f, -0.36471558f, + 0.60046846f, 0.78304422f, -0.16210929f, + 0.61179918f, 0.79101306f, -0.00000000f, + -0.46495038f, -0.88533664f, -0.00000000f, + -0.45938149f, -0.87235868f, -0.16720954f, + -0.43502232f, -0.81983721f, -0.37232047f, + -0.37172547f, -0.69227666f, -0.61852509f, + -0.23340388f, -0.42794740f, -0.87314582f, + -0.00000001f, -0.00000001f, -0.99999994f, + 0.24266508f, 0.42777771f, -0.87070072f, + 0.39728773f, 0.68575174f, -0.60984188f, + 0.47396612f, 0.80263925f, -0.36211371f, + 0.50704491f, 0.84674692f, -0.16101298f, + 0.51578081f, 0.85672063f, -0.00000000f, + -0.41036457f, -0.91192156f, -0.00000000f, + -0.40426230f, -0.89836115f, -0.17181180f, + -0.37923595f, -0.84274662f, -0.38204485f, + -0.31827325f, -0.70727384f, -0.63124156f, + -0.19429615f, -0.43176928f, -0.88080883f, + 0.00000001f, 0.00000002f, -0.99999994f, + 0.19429608f, 0.43176919f, -0.88080895f, + 0.31827322f, 0.70727378f, -0.63124168f, + 0.37923586f, 0.84274662f, -0.38204494f, + 0.40426245f, 0.89836115f, -0.17181125f, + 0.41036469f, 0.91192144f, -0.00000000f, + -0.00000000f, 1.00000000f, 0.00000002f, + -0.00663520f, 0.96262980f, 0.27073959f, + -0.02044736f, 0.83187640f, 0.55458421f, + -0.02920749f, 0.60578465f, 0.79509228f, + -0.02326994f, 0.31614211f, 0.94842637f, + 0.00000000f, -0.00000001f, 1.00000000f, + 0.03764164f, -0.31600380f, 0.94801098f, + 0.08246279f, -0.60397911f, 0.79272252f, + 0.12348490f, -0.82568222f, 0.55045468f, + 0.15017621f, -0.95173377f, 0.26767510f, + 0.15867807f, -0.98733038f, 0.00000000f, + -0.18035127f, 0.98360229f, 0.00000002f, + -0.17532882f, 0.95096803f, 0.25479335f, + -0.15287721f, 0.83426702f, 0.52974254f, + -0.10462277f, 0.62215620f, 0.77587104f, + -0.03044831f, 0.33461118f, 0.94186425f, + 0.06193968f, 0.01031707f, 0.99802655f, + 0.16082527f, -0.31103027f, 0.93669385f, + 0.25167316f, -0.59030336f, 0.76694363f, + 0.31965575f, -0.79168862f, 0.52062392f, + 0.35713619f, -0.90012705f, 0.24944942f, + 0.36760372f, -0.92998248f, 0.00000000f, + -0.41766369f, 0.90860170f, 0.00000001f, + -0.39988622f, 0.88385296f, 0.24268301f, + -0.33610013f, 0.79113972f, 0.51101321f, + -0.21738812f, 0.61049283f, 0.76160419f, + -0.05202692f, 0.34848279f, 0.93587017f, + 0.13481762f, 0.04216119f, 0.98997307f, + 0.31265706f, -0.25846288f, 0.91402549f, + 0.45580176f, -0.50834626f, 0.73063594f, + 0.55002815f, -0.67951775f, 0.48551479f, + 0.59663087f, -0.76890105f, 0.22983235f, + 0.60857272f, -0.79349810f, 0.00000000f, + -0.66257387f, 0.74899656f, 0.00000002f, + -0.63599384f, 0.73751098f, 0.22713293f, + -0.54030949f, 0.68535352f, 0.48821744f, + -0.35592762f, 0.56165427f, 0.74690026f, + -0.09161030f, 0.35298181f, 0.93113446f, + 0.19997165f, 0.09098855f, 0.97556776f, + 0.45451909f, -0.16425833f, 0.87546074f, + 0.63705850f, -0.36688966f, 0.67790008f, + 0.74545228f, -0.50049365f, 0.44023511f, + 0.79541844f, -0.56993616f, 0.20611253f, + 0.80771065f, -0.58957899f, 0.00000000f, + -0.83617663f, 0.54846013f, 0.00000000f, + -0.80830199f, 0.55003119f, 0.21003231f, + -0.70330739f, 0.54000026f, 0.46234021f, + -0.48353508f, 0.48285684f, 0.73009795f, + -0.14467996f, 0.34488079f, 0.92742926f, + 0.23135546f, 0.14274487f, 0.96234012f, + 0.53892589f, -0.06078010f, 0.84015751f, + 0.74133462f, -0.22075030f, 0.63379204f, + 0.85413134f, -0.32620990f, 0.40502673f, + 0.90458620f, -0.38241822f, 0.18836212f, + 0.91694802f, -0.39900666f, 0.00000000f, + -0.92058182f, 0.39054993f, 0.00000000f, + -0.89363056f, 0.40022916f, 0.20307857f, + -0.78890914f, 0.41686431f, 0.45149362f, + -0.56006563f, 0.40645269f, 0.72188824f, + -0.19519633f, 0.32757819f, 0.92444086f, + 0.21408427f, 0.18028127f, 0.96003473f, + 0.54745990f, 0.01576568f, 0.83668333f, + 0.76624936f, -0.12207591f, 0.63084018f, + 0.88872969f, -0.21771559f, 0.40343457f, + 0.94407701f, -0.27101600f, 0.18780012f, + 0.95782626f, -0.28734791f, 0.00000000f, + -0.94868332f, 0.31622770f, 0.00000000f, + -0.91918433f, 0.33012095f, 0.21475680f, + -0.80660689f, 0.35781714f, 0.47048092f, + -0.57353723f, 0.36397618f, 0.73387766f, + -0.22571620f, 0.31080776f, 0.92328256f, + 0.15842876f, 0.19453593f, 0.96801656f, + 0.49079683f, 0.04867331f, 0.86991346f, + 0.73050225f, -0.08864024f, 0.67713326f, + 0.87579465f, -0.19275309f, 0.44252673f, + 0.94458473f, -0.25386029f, 0.20812173f, + 0.96200842f, -0.27301973f, 0.00000000f, + -0.94363600f, 0.33098498f, 0.00000000f, + -0.90570927f, 0.34520733f, 0.24601370f, + -0.77261388f, 0.36835617f, 0.51708955f, + -0.53611439f, 0.36250114f, 0.76234782f, + -0.23092552f, 0.30191389f, 0.92494398f, + 0.09193577f, 0.18763106f, 0.97792768f, + 0.39456257f, 0.03777903f, 0.91809207f, + 0.64655590f, -0.11995956f, 0.75337595f, + 0.82085574f, -0.25226805f, 0.51240283f, + 0.90997124f, -0.33401650f, 0.24573438f, + 0.93303967f, -0.35977367f, 0.00000000f, + -0.90138495f, 0.43301868f, 0.00000000f, + -0.85063618f, 0.43930891f, 0.28883508f, + -0.69515461f, 0.43491554f, 0.57237101f, + -0.46838579f, 0.39142096f, 0.79208869f, + -0.21674377f, 0.30048704f, 0.92883235f, + 0.03872212f, 0.16388227f, 0.98571962f, + 0.29301688f, -0.01331866f, 0.95601457f, + 0.53064740f, -0.21285287f, 0.82043099f, + 0.71452874f, -0.39256069f, 0.57908958f, + 0.81452185f, -0.50658178f, 0.28271735f, + 0.84053087f, -0.54176366f, 0.00000000f, + -0.79407930f, 0.60781413f, 0.00000000f, + -0.73794198f, 0.59429532f, 0.31977278f, + -0.58513945f, 0.53897822f, 0.60589951f, + -0.39107439f, 0.43975663f, 0.80850166f, + -0.19196139f, 0.30015025f, 0.93437707f, + 0.00855905f, 0.11538364f, 0.99328417f, + 0.21362855f, -0.11758450f, 0.96981269f, + 0.40814662f, -0.37572223f, 0.83201510f, + 0.55414897f, -0.59705591f, 0.58003724f, + 0.62806821f, -0.72647786f, 0.27885503f, + 0.64588672f, -0.76343334f, 0.00000000f, + -0.60000002f, 0.80000001f, 0.00000000f, + -0.56169033f, 0.76974368f, 0.30331263f, + -0.45668069f, 0.67062843f, 0.58455133f, + -0.31731656f, 0.50979352f, 0.79963785f, + -0.16375455f, 0.28897864f, 0.94322628f, + 0.00000001f, -0.00000001f, 1.00000000f, + 0.16118340f, -0.33475828f, 0.92841625f, + 0.28603175f, -0.63137418f, 0.72079998f, + 0.35418314f, -0.81793916f, 0.45335400f, + 0.37968126f, -0.90211189f, 0.20502774f, + 0.38461548f, -0.92307693f, 0.00000000f, + 0.15867807f, -0.98733038f, 0.00000000f, + 0.15017621f, -0.95173377f, -0.26767510f, + 0.12348484f, -0.82568216f, -0.55045474f, + 0.08246280f, -0.60397911f, -0.79272252f, + 0.03764160f, -0.31600368f, -0.94801098f, + 0.00000000f, -0.00000000f, -1.00000000f, + -0.02326995f, 0.31614220f, -0.94842637f, + -0.02920748f, 0.60578465f, -0.79509240f, + -0.02044738f, 0.83187640f, -0.55458409f, + -0.00663519f, 0.96262980f, -0.27073947f, + -0.00000000f, 1.00000000f, 0.00000002f, + 0.36760372f, -0.92998248f, 0.00000000f, + 0.35713634f, -0.90012699f, -0.24944946f, + 0.31965572f, -0.79168868f, -0.52062398f, + 0.25167322f, -0.59030336f, -0.76694363f, + 0.16082519f, -0.31103012f, -0.93669391f, + 0.06193968f, 0.01031707f, -0.99802655f, + -0.03044830f, 0.33461127f, -0.94186413f, + -0.10462277f, 0.62215620f, -0.77587098f, + -0.15287724f, 0.83426702f, -0.52974242f, + -0.17532876f, 0.95096809f, -0.25479320f, + -0.18035127f, 0.98360229f, 0.00000001f, + 0.60857272f, -0.79349810f, 0.00000000f, + 0.59663087f, -0.76890081f, -0.22983252f, + 0.55002815f, -0.67951781f, -0.48551467f, + 0.45580178f, -0.50834626f, -0.73063594f, + 0.31265700f, -0.25846279f, -0.91402549f, + 0.13481762f, 0.04216120f, -0.98997307f, + -0.05202700f, 0.34848288f, -0.93587005f, + -0.21738812f, 0.61049283f, -0.76160419f, + -0.33610022f, 0.79113984f, -0.51101309f, + -0.39988619f, 0.88385296f, -0.24268289f, + -0.41766369f, 0.90860170f, 0.00000001f, + 0.80771065f, -0.58957899f, 0.00000000f, + 0.79541844f, -0.56993616f, -0.20611253f, + 0.74545234f, -0.50049359f, -0.44023511f, + 0.63705850f, -0.36688969f, -0.67790002f, + 0.45451906f, -0.16425829f, -0.87546074f, + 0.19997162f, 0.09098855f, -0.97556776f, + -0.09161033f, 0.35298187f, -0.93113440f, + -0.35592768f, 0.56165421f, -0.74690026f, + -0.54030943f, 0.68535358f, -0.48821732f, + -0.63599378f, 0.73751110f, -0.22713269f, + -0.66257387f, 0.74899656f, 0.00000000f, + 0.91694802f, -0.39900666f, 0.00000000f, + 0.90458608f, -0.38241825f, -0.18836221f, + 0.85413140f, -0.32620987f, -0.40502664f, + 0.74133456f, -0.22075026f, -0.63379204f, + 0.53892601f, -0.06078019f, -0.84015751f, + 0.23135549f, 0.14274485f, -0.96234012f, + -0.14468013f, 0.34488085f, -0.92742920f, + -0.48353511f, 0.48285678f, -0.73009801f, + -0.70330739f, 0.54000038f, -0.46234021f, + -0.80830193f, 0.55003130f, -0.21003252f, + -0.83617663f, 0.54846013f, 0.00000000f, + 0.95782626f, -0.28734791f, 0.00000000f, + 0.94407701f, -0.27101621f, -0.18779969f, + 0.88872993f, -0.21771561f, -0.40343419f, + 0.76624936f, -0.12207589f, -0.63084036f, + 0.54745996f, 0.01576565f, -0.83668339f, + 0.21408427f, 0.18028127f, -0.96003473f, + -0.19519642f, 0.32757822f, -0.92444074f, + -0.56006563f, 0.40645254f, -0.72188836f, + -0.78890932f, 0.41686437f, -0.45149341f, + -0.89363045f, 0.40022945f, -0.20307872f, + -0.92058182f, 0.39054993f, 0.00000000f, + 0.96200842f, -0.27301973f, 0.00000000f, + 0.94458461f, -0.25386047f, -0.20812206f, + 0.87579477f, -0.19275305f, -0.44252670f, + 0.73050219f, -0.08864018f, -0.67713314f, + 0.49079677f, 0.04867329f, -0.86991346f, + 0.15842876f, 0.19453594f, -0.96801656f, + -0.22571620f, 0.31080776f, -0.92328250f, + -0.57353723f, 0.36397609f, -0.73387772f, + -0.80660677f, 0.35781729f, -0.47048086f, + -0.91918421f, 0.33012089f, -0.21475697f, + -0.94868326f, 0.31622767f, 0.00000000f, + 0.93303967f, -0.35977367f, 0.00000000f, + 0.90997100f, -0.33401665f, -0.24573487f, + 0.82085598f, -0.25226820f, -0.51240242f, + 0.64655590f, -0.11995946f, -0.75337583f, + 0.39456260f, 0.03777899f, -0.91809213f, + 0.09193578f, 0.18763106f, -0.97792768f, + -0.23092572f, 0.30191398f, -0.92494386f, + -0.53611463f, 0.36250117f, -0.76234764f, + -0.77261376f, 0.36835647f, -0.51708949f, + -0.90570915f, 0.34520757f, -0.24601352f, + -0.94363600f, 0.33098498f, 0.00000000f, + 0.84053087f, -0.54176372f, 0.00000000f, + 0.81452167f, -0.50658214f, -0.28271732f, + 0.71452862f, -0.39256078f, -0.57908970f, + 0.53064746f, -0.21285276f, -0.82043099f, + 0.29301694f, -0.01331866f, -0.95601451f, + 0.03872212f, 0.16388229f, -0.98571962f, + -0.21674389f, 0.30048716f, -0.92883229f, + -0.46838561f, 0.39142081f, -0.79208875f, + -0.69515425f, 0.43491599f, -0.57237113f, + -0.85063595f, 0.43930909f, -0.28883564f, + -0.90138495f, 0.43301874f, 0.00000000f, + 0.64588672f, -0.76343334f, 0.00000000f, + 0.62806827f, -0.72647810f, -0.27885461f, + 0.55414909f, -0.59705591f, -0.58003718f, + 0.40814668f, -0.37572223f, -0.83201504f, + 0.21362852f, -0.11758441f, -0.96981281f, + 0.00855905f, 0.11538365f, -0.99328417f, + -0.19196151f, 0.30015031f, -0.93437707f, + -0.39107460f, 0.43975663f, -0.80850154f, + -0.58513945f, 0.53897864f, -0.60589927f, + -0.73794204f, 0.59429544f, -0.31977260f, + -0.79407930f, 0.60781413f, 0.00000000f, + 0.38461545f, -0.92307687f, 0.00000000f, + 0.37968108f, -0.90211183f, -0.20502785f, + 0.35418308f, -0.81793898f, -0.45335433f, + 0.28603172f, -0.63137424f, -0.72079992f, + 0.16118342f, -0.33475828f, -0.92841625f, + 0.00000001f, 0.00000004f, -1.00000000f, + -0.16375461f, 0.28897879f, -0.94322622f, + -0.31731653f, 0.50979352f, -0.79963791f, + -0.45668048f, 0.67062896f, -0.58455086f, + -0.56169003f, 0.76974392f, -0.30331281f, + -0.59999996f, 0.79999995f, 0.00000000f, + -0.60000002f, 0.80000001f, 0.00000000f, + -0.56168801f, 0.76974595f, 0.30331135f, + -0.45668051f, 0.67062873f, 0.58455110f, + -0.31731650f, 0.50979382f, 0.79963768f, + -0.16375455f, 0.28897852f, 0.94322622f, + 0.00000001f, -0.00000001f, 1.00000000f, + 0.16118334f, -0.33475927f, 0.92841595f, + 0.28603160f, -0.63137466f, 0.72079957f, + 0.35418302f, -0.81793934f, 0.45335382f, + 0.37968105f, -0.90211189f, 0.20502764f, + 0.38461483f, -0.92307717f, 0.00000000f, + -0.52215356f, 0.85285151f, 0.00000000f, + -0.48819607f, 0.82820129f, 0.27522215f, + -0.39745197f, 0.74893141f, 0.53022045f, + -0.27929682f, 0.62227607f, 0.73127675f, + -0.14856830f, 0.44534677f, 0.88294607f, + -0.00172636f, 0.19493943f, 0.98081374f, + 0.16121377f, -0.14311403f, 0.97648787f, + 0.30696428f, -0.50290024f, 0.80800015f, + 0.39156401f, -0.75766802f, 0.52212727f, + 0.42092448f, -0.87572849f, 0.23647870f, + 0.42558387f, -0.90491897f, 0.00000000f, + -0.43938431f, 0.89829928f, 0.00000000f, + -0.41034442f, 0.88024288f, 0.23830649f, + -0.33475637f, 0.82335114f, 0.45829135f, + -0.23836468f, 0.73443186f, 0.63544637f, + -0.13188016f, 0.60968041f, 0.78159934f, + -0.00710458f, 0.42205760f, 0.90654117f, + 0.15046835f, 0.12560046f, 0.98060381f, + 0.32566464f, -0.28249556f, 0.90229636f, + 0.44518387f, -0.64581746f, 0.62026703f, + 0.48392394f, -0.82802969f, 0.28316852f, + 0.48799869f, -0.87284440f, 0.00000000f, + -0.34358078f, 0.93912309f, 0.00000000f, + -0.32073438f, 0.92827159f, 0.18825857f, + -0.26279277f, 0.89484864f, 0.36081272f, + -0.19049235f, 0.84415460f, 0.50111455f, + -0.11119042f, 0.77414727f, 0.62316346f, + -0.01599536f, 0.66692048f, 0.74495709f, + 0.11782130f, 0.47883904f, 0.86996061f, + 0.31610468f, 0.13072994f, 0.93967414f, + 0.52400970f, -0.36312634f, 0.77042389f, + 0.60744083f, -0.70159787f, 0.37252650f, + 0.61339337f, -0.78977746f, 0.00000000f, + -0.21540894f, 0.97652394f, 0.00000000f, + -0.20210963f, 0.97277087f, 0.11343981f, + -0.16888496f, 0.96128762f, 0.21772476f, + -0.12775169f, 0.94399923f, 0.30421191f, + -0.08228281f, 0.92055047f, 0.38185909f, + -0.02672647f, 0.88635093f, 0.46224198f, + 0.05417515f, 0.83025962f, 0.55473769f, + 0.19413483f, 0.72417361f, 0.66172826f, + 0.45968795f, 0.49317458f, 0.73855662f, + 0.83387059f, 0.06007503f, 0.54868102f, + 0.98019689f, -0.19802566f, 0.00000000f, + 0.00000000f, 1.00000000f, 0.00000000f, + -0.00798650f, 0.99980050f, -0.01830650f, + -0.02329356f, 0.99955261f, -0.01876155f, + -0.03473718f, 0.99939632f, -0.00068433f, + -0.03873326f, 0.99887133f, 0.02749065f, + -0.03489117f, 0.99762177f, 0.05944324f, + -0.02229466f, 0.99563199f, 0.09066366f, + 0.00150407f, 0.99339056f, 0.11477303f, + 0.03894940f, 0.99219519f, 0.11845525f, + 0.08366866f, 0.99324965f, 0.08034155f, + 0.10653293f, 0.99430919f, 0.00000000f, + 0.47222057f, 0.88148040f, -0.00000000f, + 0.38292861f, 0.87748671f, -0.28876084f, + 0.22452858f, 0.87743115f, -0.42391223f, + 0.10304770f, 0.89068371f, -0.44279075f, + 0.02195636f, 0.90869540f, -0.41688216f, + -0.03502908f, 0.92632145f, -0.37510204f, + -0.07944816f, 0.94263619f, -0.32422990f, + -0.11721789f, 0.95783943f, -0.26230437f, + -0.14933059f, 0.97128469f, -0.18522017f, + -0.17214298f, 0.98060298f, -0.09372614f, + -0.18032812f, 0.98360652f, 0.00000000f, + 0.99778539f, 0.06651436f, -0.00000000f, + 0.80641705f, 0.16489866f, -0.56789082f, + 0.47624663f, 0.34165922f, -0.81022102f, + 0.23846878f, 0.49079743f, -0.83800387f, + 0.08315616f, 0.60699552f, -0.79034263f, + -0.02645415f, 0.70211625f, -0.71157062f, + -0.11138277f, 0.78460753f, -0.60990572f, + -0.18041402f, 0.85678113f, -0.48309112f, + -0.23395304f, 0.91435987f, -0.33047256f, + -0.26772311f, 0.94976443f, -0.16208562f, + -0.27868998f, 0.96038115f, 0.00000000f, + 0.84905565f, -0.52830338f, 0.00000000f, + 0.74757648f, -0.43204904f, -0.50444335f, + 0.51660007f, -0.20501730f, -0.83131957f, + 0.29630959f, 0.03158545f, -0.95456952f, + 0.12317742f, 0.24186306f, -0.96246016f, + -0.01423063f, 0.43078220f, -0.90234375f, + -0.12728645f, 0.60243106f, -0.78795618f, + -0.21896675f, 0.75068080f, -0.62332320f, + -0.28577635f, 0.86156183f, -0.41957495f, + -0.32389721f, 0.92431885f, -0.20180508f, + -0.33514237f, 0.94216758f, 0.00000000f, + 0.69542080f, -0.71860266f, 0.00000000f, + 0.63358843f, -0.65843248f, -0.40624171f, + 0.47831276f, -0.49247903f, -0.72710478f, + 0.30356473f, -0.28035074f, -0.91063273f, + 0.14240842f, -0.05222204f, -0.98842943f, + -0.00406526f, 0.18825392f, -0.98211193f, + -0.13672033f, 0.43257806f, -0.89116985f, + -0.24777175f, 0.65353578f, -0.71519232f, + -0.32531679f, 0.81566668f, -0.47838998f, + -0.36555430f, 0.90269053f, -0.22697979f, + -0.37628973f, 0.92650205f, 0.00000000f, + 0.59999752f, -0.80000180f, 0.00000000f, + 0.55578601f, -0.76151079f, -0.33347145f, + 0.43982491f, -0.64441258f, -0.62552899f, + 0.29645419f, -0.47233042f, -0.83007169f, + 0.14913522f, -0.25836608f, -0.95446616f, + 0.00000000f, -0.00000000f, -1.00000000f, + -0.14759630f, 0.29311213f, -0.94461662f, + -0.27540490f, 0.57402539f, -0.77113360f, + -0.36081311f, 0.77877104f, -0.51315647f, + -0.40083086f, 0.88402218f, -0.24049798f, + -0.41036457f, 0.91192162f, 0.00000000f, + 0.38461483f, -0.92307711f, 0.00000000f, + 0.37968156f, -0.90211177f, -0.20502809f, + 0.35418290f, -0.81793922f, -0.45335409f, + 0.28603169f, -0.63137424f, -0.72079986f, + 0.16118337f, -0.33475906f, -0.92841601f, + 0.00000001f, -0.00000001f, -1.00000000f, + -0.16375463f, 0.28897840f, -0.94322634f, + -0.31731617f, 0.50979501f, -0.79963702f, + -0.45668083f, 0.67062843f, -0.58455133f, + -0.56169003f, 0.76974392f, -0.30331278f, + -0.60000002f, 0.80000001f, 0.00000000f, + 0.42558384f, -0.90491897f, 0.00000000f, + 0.42092523f, -0.87572807f, -0.23647898f, + 0.39156422f, -0.75766778f, -0.52212739f, + 0.30696440f, -0.50289935f, -0.80800068f, + 0.16121379f, -0.14311399f, -0.97648787f, + -0.00172636f, 0.19493946f, -0.98081374f, + -0.14856836f, 0.44534636f, -0.88294625f, + -0.27929646f, 0.62227708f, -0.73127621f, + -0.39745176f, 0.74893188f, -0.53022003f, + -0.48819649f, 0.82820088f, -0.27522251f, + -0.52215350f, 0.85285145f, 0.00000000f, + 0.48799869f, -0.87284434f, 0.00000000f, + 0.48392478f, -0.82802933f, -0.28316849f, + 0.44518381f, -0.64581764f, -0.62026691f, + 0.32566467f, -0.28249523f, -0.90229648f, + 0.15046827f, 0.12560080f, -0.98060375f, + -0.00710459f, 0.42205763f, -0.90654117f, + -0.13188021f, 0.60968047f, -0.78159928f, + -0.23836447f, 0.73443234f, -0.63544589f, + -0.33475649f, 0.82335120f, -0.45829135f, + -0.41034439f, 0.88024282f, -0.23830633f, + -0.43938428f, 0.89829916f, 0.00000000f, + 0.61339337f, -0.78977746f, 0.00000000f, + 0.60744220f, -0.70159632f, -0.37252715f, + 0.52400988f, -0.36312595f, -0.77042401f, + 0.31610486f, 0.13072906f, -0.93967414f, + 0.11782125f, 0.47883931f, -0.86996049f, + -0.01599536f, 0.66692042f, -0.74495715f, + -0.11119034f, 0.77414769f, -0.62316298f, + -0.19049208f, 0.84415501f, -0.50111377f, + -0.26279294f, 0.89484841f, -0.36081317f, + -0.32073432f, 0.92827165f, -0.18825847f, + -0.34358078f, 0.93912309f, 0.00000000f, + 0.98019677f, -0.19802561f, 0.00000000f, + 0.83387059f, 0.06008269f, -0.54868007f, + 0.45968765f, 0.49317545f, -0.73855609f, + 0.19413501f, 0.72417301f, -0.66172880f, + 0.05417512f, 0.83025974f, -0.55473757f, + -0.02672647f, 0.88635093f, -0.46224201f, + -0.08228284f, 0.92055041f, -0.38185903f, + -0.12775156f, 0.94399947f, -0.30421141f, + -0.16888507f, 0.96128756f, -0.21772504f, + -0.20210969f, 0.97277093f, -0.11343937f, + -0.21540894f, 0.97652400f, 0.00000000f, + 0.10653293f, 0.99430919f, 0.00000000f, + 0.08366887f, 0.99324965f, -0.08034138f, + 0.03894951f, 0.99219519f, -0.11845496f, + 0.00150435f, 0.99339044f, -0.11477400f, + -0.02229443f, 0.99563181f, -0.09066474f, + -0.03489117f, 0.99762177f, -0.05944332f, + -0.03873324f, 0.99887133f, -0.02749083f, + -0.03473703f, 0.99939620f, 0.00068491f, + -0.02329374f, 0.99955261f, 0.01876094f, + -0.00798648f, 0.99980056f, 0.01830641f, + 0.00000000f, 1.00000000f, 0.00000000f, + -0.18032812f, 0.98360652f, 0.00000000f, + -0.17214245f, 0.98060316f, 0.09372532f, + -0.14933097f, 0.97128439f, 0.18522131f, + -0.11721768f, 0.95783967f, 0.26230362f, + -0.07944822f, 0.94263613f, 0.32423037f, + -0.03502908f, 0.92632139f, 0.37510198f, + 0.02195640f, 0.90869552f, 0.41688174f, + 0.10304778f, 0.89068371f, 0.44279081f, + 0.22452924f, 0.87743056f, 0.42391288f, + 0.38292828f, 0.87748682f, 0.28876066f, + 0.47222057f, 0.88148040f, -0.00000000f, + -0.27868998f, 0.96038115f, 0.00000000f, + -0.26772285f, 0.94976461f, 0.16208540f, + -0.23395303f, 0.91435981f, 0.33047244f, + -0.18041429f, 0.85678071f, 0.48309162f, + -0.11138276f, 0.78460753f, 0.60990566f, + -0.02645415f, 0.70211625f, 0.71157062f, + 0.08315627f, 0.60699481f, 0.79034311f, + 0.23846854f, 0.49079826f, 0.83800346f, + 0.47624737f, 0.34165865f, 0.81022084f, + 0.80641758f, 0.16489473f, 0.56789118f, + 0.99778539f, 0.06651436f, -0.00000000f, + -0.33514237f, 0.94216758f, 0.00000000f, + -0.32389730f, 0.92431867f, 0.20180558f, + -0.28577620f, 0.86156183f, 0.41957507f, + -0.21896696f, 0.75067997f, 0.62332416f, + -0.12728633f, 0.60243118f, 0.78795624f, + -0.01423062f, 0.43078214f, 0.90234381f, + 0.12317742f, 0.24186318f, 0.96246016f, + 0.29630962f, 0.03158538f, 0.95456952f, + 0.51659995f, -0.20501706f, 0.83131969f, + 0.74757755f, -0.43204758f, 0.50444305f, + 0.84905565f, -0.52830338f, 0.00000000f, + -0.37628970f, 0.92650205f, 0.00000000f, + -0.36555433f, 0.90269059f, 0.22697939f, + -0.32531694f, 0.81566656f, 0.47838989f, + -0.24777193f, 0.65353495f, 0.71519297f, + -0.13672033f, 0.43257833f, 0.89116967f, + -0.00406526f, 0.18825392f, 0.98211193f, + 0.14240852f, -0.05222183f, 0.98842937f, + 0.30356464f, -0.28035024f, 0.91063285f, + 0.47831330f, -0.49247789f, 0.72710514f, + 0.63358814f, -0.65843308f, 0.40624121f, + 0.69542086f, -0.71860272f, 0.00000000f, + -0.41036457f, 0.91192162f, 0.00000000f, + -0.40083098f, 0.88402182f, 0.24049932f, + -0.36081249f, 0.77877188f, 0.51315570f, + -0.27540508f, 0.57402408f, 0.77113444f, + -0.14759634f, 0.29311273f, 0.94461650f, + 0.00000000f, -0.00000000f, 1.00000000f, + 0.14913538f, -0.25836638f, 0.95446599f, + 0.29645407f, -0.47232965f, 0.83007205f, + 0.43982542f, -0.64441097f, 0.62553024f, + 0.55578405f, -0.76151282f, 0.33347026f, + 0.59999752f, -0.80000180f, 0.00000000f + }; + + const std::array<float, (vertexCount * 2)> teapotUVCoords = { + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f, + 1.00000000f, 0.00000000f, + 0.89999998f, 0.00000000f, + 0.80000001f, 0.00000000f, + 0.69999999f, 0.00000000f, + 0.60000002f, 0.00000000f, + 0.50000000f, 0.00000000f, + 0.39999998f, 0.00000000f, + 0.30000001f, 0.00000000f, + 0.19999999f, 0.00000000f, + 0.09999996f, 0.00000000f, + 0.00000000f, 0.00000000f, + 1.00000000f, 0.10000000f, + 0.89999992f, 0.10000000f, + 0.79999995f, 0.10000000f, + 0.69999999f, 0.10000000f, + 0.60000002f, 0.10000000f, + 0.50000000f, 0.10000000f, + 0.39999995f, 0.10000000f, + 0.30000001f, 0.10000000f, + 0.19999997f, 0.10000000f, + 0.09999996f, 0.10000000f, + 0.00000000f, 0.10000000f, + 1.00000000f, 0.20000000f, + 0.89999998f, 0.20000000f, + 0.80000007f, 0.20000000f, + 0.69999999f, 0.20000000f, + 0.60000002f, 0.20000000f, + 0.50000000f, 0.20000000f, + 0.39999998f, 0.20000000f, + 0.30000001f, 0.20000000f, + 0.19999999f, 0.20000000f, + 0.09999997f, 0.20000000f, + 0.00000000f, 0.20000000f, + 1.00000000f, 0.30000001f, + 0.89999998f, 0.30000001f, + 0.80000001f, 0.30000001f, + 0.69999999f, 0.30000001f, + 0.60000002f, 0.30000001f, + 0.50000000f, 0.30000001f, + 0.39999998f, 0.30000001f, + 0.30000001f, 0.30000001f, + 0.19999999f, 0.30000001f, + 0.09999996f, 0.30000001f, + 0.00000000f, 0.30000001f, + 1.00000000f, 0.40000001f, + 0.89999998f, 0.40000001f, + 0.80000007f, 0.40000001f, + 0.70000005f, 0.40000001f, + 0.60000002f, 0.40000001f, + 0.50000000f, 0.40000001f, + 0.39999998f, 0.40000001f, + 0.30000001f, 0.40000001f, + 0.19999999f, 0.40000001f, + 0.09999996f, 0.40000001f, + 0.00000000f, 0.40000001f, + 1.00000000f, 0.50000000f, + 0.89999998f, 0.50000000f, + 0.80000001f, 0.50000000f, + 0.69999999f, 0.50000000f, + 0.60000002f, 0.50000000f, + 0.50000000f, 0.50000000f, + 0.39999998f, 0.50000000f, + 0.30000001f, 0.50000000f, + 0.19999999f, 0.50000000f, + 0.09999996f, 0.50000000f, + 0.00000000f, 0.50000000f, + 1.00000000f, 0.60000002f, + 0.89999998f, 0.60000002f, + 0.80000001f, 0.60000002f, + 0.69999999f, 0.60000002f, + 0.60000002f, 0.60000002f, + 0.50000000f, 0.60000002f, + 0.39999998f, 0.60000002f, + 0.30000001f, 0.60000002f, + 0.19999999f, 0.60000002f, + 0.09999996f, 0.60000002f, + 0.00000000f, 0.60000002f, + 1.00000000f, 0.69999999f, + 0.89999998f, 0.69999999f, + 0.80000001f, 0.69999999f, + 0.69999999f, 0.69999999f, + 0.60000002f, 0.69999999f, + 0.50000000f, 0.69999999f, + 0.39999998f, 0.69999999f, + 0.30000001f, 0.69999999f, + 0.19999999f, 0.69999999f, + 0.09999996f, 0.69999999f, + 0.00000000f, 0.69999999f, + 1.00000000f, 0.80000001f, + 0.89999998f, 0.80000001f, + 0.80000007f, 0.80000001f, + 0.69999999f, 0.80000001f, + 0.60000002f, 0.80000001f, + 0.50000000f, 0.80000001f, + 0.39999998f, 0.80000001f, + 0.30000001f, 0.80000001f, + 0.19999999f, 0.80000001f, + 0.09999996f, 0.80000001f, + 0.00000000f, 0.80000001f, + 1.00000000f, 0.90000004f, + 0.89999998f, 0.90000004f, + 0.80000001f, 0.90000004f, + 0.69999999f, 0.90000004f, + 0.60000002f, 0.90000004f, + 0.50000000f, 0.90000004f, + 0.39999998f, 0.90000004f, + 0.30000001f, 0.90000004f, + 0.19999999f, 0.90000004f, + 0.09999996f, 0.90000004f, + 0.00000000f, 0.90000004f, + 1.00000000f, 1.00000000f, + 0.89999998f, 1.00000000f, + 0.80000001f, 1.00000000f, + 0.69999999f, 1.00000000f, + 0.60000002f, 1.00000000f, + 0.50000000f, 1.00000000f, + 0.39999998f, 1.00000000f, + 0.30000001f, 1.00000000f, + 0.19999999f, 1.00000000f, + 0.09999996f, 1.00000000f, + 0.00000000f, 1.00000000f + }; + + const std::array<uint16_t, indexCount> teapotIndices = { + 0, 1, 11, 11, 1, 12, + 1, 2, 12, 12, 2, 13, + 2, 3, 13, 13, 3, 14, + 3, 4, 14, 14, 4, 15, + 4, 5, 15, 15, 5, 16, + 5, 6, 16, 16, 6, 17, + 6, 7, 17, 17, 7, 18, + 7, 8, 18, 18, 8, 19, + 8, 9, 19, 19, 9, 20, + 9, 10, 20, 20, 10, 21, + 11, 12, 22, 22, 12, 23, + 12, 13, 23, 23, 13, 24, + 13, 14, 24, 24, 14, 25, + 14, 15, 25, 25, 15, 26, + 15, 16, 26, 26, 16, 27, + 16, 17, 27, 27, 17, 28, + 17, 18, 28, 28, 18, 29, + 18, 19, 29, 29, 19, 30, + 19, 20, 30, 30, 20, 31, + 20, 21, 31, 31, 21, 32, + 22, 23, 33, 33, 23, 34, + 23, 24, 34, 34, 24, 35, + 24, 25, 35, 35, 25, 36, + 25, 26, 36, 36, 26, 37, + 26, 27, 37, 37, 27, 38, + 27, 28, 38, 38, 28, 39, + 28, 29, 39, 39, 29, 40, + 29, 30, 40, 40, 30, 41, + 30, 31, 41, 41, 31, 42, + 31, 32, 42, 42, 32, 43, + 33, 34, 44, 44, 34, 45, + 34, 35, 45, 45, 35, 46, + 35, 36, 46, 46, 36, 47, + 36, 37, 47, 47, 37, 48, + 37, 38, 48, 48, 38, 49, + 38, 39, 49, 49, 39, 50, + 39, 40, 50, 50, 40, 51, + 40, 41, 51, 51, 41, 52, + 41, 42, 52, 52, 42, 53, + 42, 43, 53, 53, 43, 54, + 44, 45, 55, 55, 45, 56, + 45, 46, 56, 56, 46, 57, + 46, 47, 57, 57, 47, 58, + 47, 48, 58, 58, 48, 59, + 48, 49, 59, 59, 49, 60, + 49, 50, 60, 60, 50, 61, + 50, 51, 61, 61, 51, 62, + 51, 52, 62, 62, 52, 63, + 52, 53, 63, 63, 53, 64, + 53, 54, 64, 64, 54, 65, + 55, 56, 66, 66, 56, 67, + 56, 57, 67, 67, 57, 68, + 57, 58, 68, 68, 58, 69, + 58, 59, 69, 69, 59, 70, + 59, 60, 70, 70, 60, 71, + 60, 61, 71, 71, 61, 72, + 61, 62, 72, 72, 62, 73, + 62, 63, 73, 73, 63, 74, + 63, 64, 74, 74, 64, 75, + 64, 65, 75, 75, 65, 76, + 66, 67, 77, 77, 67, 78, + 67, 68, 78, 78, 68, 79, + 68, 69, 79, 79, 69, 80, + 69, 70, 80, 80, 70, 81, + 70, 71, 81, 81, 71, 82, + 71, 72, 82, 82, 72, 83, + 72, 73, 83, 83, 73, 84, + 73, 74, 84, 84, 74, 85, + 74, 75, 85, 85, 75, 86, + 75, 76, 86, 86, 76, 87, + 77, 78, 88, 88, 78, 89, + 78, 79, 89, 89, 79, 90, + 79, 80, 90, 90, 80, 91, + 80, 81, 91, 91, 81, 92, + 81, 82, 92, 92, 82, 93, + 82, 83, 93, 93, 83, 94, + 83, 84, 94, 94, 84, 95, + 84, 85, 95, 95, 85, 96, + 85, 86, 96, 96, 86, 97, + 86, 87, 97, 97, 87, 98, + 88, 89, 99, 99, 89, 100, + 89, 90, 100, 100, 90, 101, + 90, 91, 101, 101, 91, 102, + 91, 92, 102, 102, 92, 103, + 92, 93, 103, 103, 93, 104, + 93, 94, 104, 104, 94, 105, + 94, 95, 105, 105, 95, 106, + 95, 96, 106, 106, 96, 107, + 96, 97, 107, 107, 97, 108, + 97, 98, 108, 108, 98, 109, + 99, 100, 110, 110, 100, 111, + 100, 101, 111, 111, 101, 112, + 101, 102, 112, 112, 102, 113, + 102, 103, 113, 113, 103, 114, + 103, 104, 114, 114, 104, 115, + 104, 105, 115, 115, 105, 116, + 105, 106, 116, 116, 106, 117, + 106, 107, 117, 117, 107, 118, + 107, 108, 118, 118, 108, 119, + 108, 109, 119, 119, 109, 120, + 121, 122, 132, 132, 122, 133, + 122, 123, 133, 133, 123, 134, + 123, 124, 134, 134, 124, 135, + 124, 125, 135, 135, 125, 136, + 125, 126, 136, 136, 126, 137, + 126, 127, 137, 137, 127, 138, + 127, 128, 138, 138, 128, 139, + 128, 129, 139, 139, 129, 140, + 129, 130, 140, 140, 130, 141, + 130, 131, 141, 141, 131, 142, + 132, 133, 143, 143, 133, 144, + 133, 134, 144, 144, 134, 145, + 134, 135, 145, 145, 135, 146, + 135, 136, 146, 146, 136, 147, + 136, 137, 147, 147, 137, 148, + 137, 138, 148, 148, 138, 149, + 138, 139, 149, 149, 139, 150, + 139, 140, 150, 150, 140, 151, + 140, 141, 151, 151, 141, 152, + 141, 142, 152, 152, 142, 153, + 143, 144, 154, 154, 144, 155, + 144, 145, 155, 155, 145, 156, + 145, 146, 156, 156, 146, 157, + 146, 147, 157, 157, 147, 158, + 147, 148, 158, 158, 148, 159, + 148, 149, 159, 159, 149, 160, + 149, 150, 160, 160, 150, 161, + 150, 151, 161, 161, 151, 162, + 151, 152, 162, 162, 152, 163, + 152, 153, 163, 163, 153, 164, + 154, 155, 165, 165, 155, 166, + 155, 156, 166, 166, 156, 167, + 156, 157, 167, 167, 157, 168, + 157, 158, 168, 168, 158, 169, + 158, 159, 169, 169, 159, 170, + 159, 160, 170, 170, 160, 171, + 160, 161, 171, 171, 161, 172, + 161, 162, 172, 172, 162, 173, + 162, 163, 173, 173, 163, 174, + 163, 164, 174, 174, 164, 175, + 165, 166, 176, 176, 166, 177, + 166, 167, 177, 177, 167, 178, + 167, 168, 178, 178, 168, 179, + 168, 169, 179, 179, 169, 180, + 169, 170, 180, 180, 170, 181, + 170, 171, 181, 181, 171, 182, + 171, 172, 182, 182, 172, 183, + 172, 173, 183, 183, 173, 184, + 173, 174, 184, 184, 174, 185, + 174, 175, 185, 185, 175, 186, + 176, 177, 187, 187, 177, 188, + 177, 178, 188, 188, 178, 189, + 178, 179, 189, 189, 179, 190, + 179, 180, 190, 190, 180, 191, + 180, 181, 191, 191, 181, 192, + 181, 182, 192, 192, 182, 193, + 182, 183, 193, 193, 183, 194, + 183, 184, 194, 194, 184, 195, + 184, 185, 195, 195, 185, 196, + 185, 186, 196, 196, 186, 197, + 187, 188, 198, 198, 188, 199, + 188, 189, 199, 199, 189, 200, + 189, 190, 200, 200, 190, 201, + 190, 191, 201, 201, 191, 202, + 191, 192, 202, 202, 192, 203, + 192, 193, 203, 203, 193, 204, + 193, 194, 204, 204, 194, 205, + 194, 195, 205, 205, 195, 206, + 195, 196, 206, 206, 196, 207, + 196, 197, 207, 207, 197, 208, + 198, 199, 209, 209, 199, 210, + 199, 200, 210, 210, 200, 211, + 200, 201, 211, 211, 201, 212, + 201, 202, 212, 212, 202, 213, + 202, 203, 213, 213, 203, 214, + 203, 204, 214, 214, 204, 215, + 204, 205, 215, 215, 205, 216, + 205, 206, 216, 216, 206, 217, + 206, 207, 217, 217, 207, 218, + 207, 208, 218, 218, 208, 219, + 209, 210, 220, 220, 210, 221, + 210, 211, 221, 221, 211, 222, + 211, 212, 222, 222, 212, 223, + 212, 213, 223, 223, 213, 224, + 213, 214, 224, 224, 214, 225, + 214, 215, 225, 225, 215, 226, + 215, 216, 226, 226, 216, 227, + 216, 217, 227, 227, 217, 228, + 217, 218, 228, 228, 218, 229, + 218, 219, 229, 229, 219, 230, + 220, 221, 231, 231, 221, 232, + 221, 222, 232, 232, 222, 233, + 222, 223, 233, 233, 223, 234, + 223, 224, 234, 234, 224, 235, + 224, 225, 235, 235, 225, 236, + 225, 226, 236, 236, 226, 237, + 226, 227, 237, 237, 227, 238, + 227, 228, 238, 238, 228, 239, + 228, 229, 239, 239, 229, 240, + 229, 230, 240, 240, 230, 241, + 242, 243, 253, 253, 243, 254, + 243, 244, 254, 254, 244, 255, + 244, 245, 255, 255, 245, 256, + 245, 246, 256, 256, 246, 257, + 246, 247, 257, 257, 247, 258, + 247, 248, 258, 258, 248, 259, + 248, 249, 259, 259, 249, 260, + 249, 250, 260, 260, 250, 261, + 250, 251, 261, 261, 251, 262, + 251, 252, 262, 262, 252, 263, + 253, 254, 264, 264, 254, 265, + 254, 255, 265, 265, 255, 266, + 255, 256, 266, 266, 256, 267, + 256, 257, 267, 267, 257, 268, + 257, 258, 268, 268, 258, 269, + 258, 259, 269, 269, 259, 270, + 259, 260, 270, 270, 260, 271, + 260, 261, 271, 271, 261, 272, + 261, 262, 272, 272, 262, 273, + 262, 263, 273, 273, 263, 274, + 264, 265, 275, 275, 265, 276, + 265, 266, 276, 276, 266, 277, + 266, 267, 277, 277, 267, 278, + 267, 268, 278, 278, 268, 279, + 268, 269, 279, 279, 269, 280, + 269, 270, 280, 280, 270, 281, + 270, 271, 281, 281, 271, 282, + 271, 272, 282, 282, 272, 283, + 272, 273, 283, 283, 273, 284, + 273, 274, 284, 284, 274, 285, + 275, 276, 286, 286, 276, 287, + 276, 277, 287, 287, 277, 288, + 277, 278, 288, 288, 278, 289, + 278, 279, 289, 289, 279, 290, + 279, 280, 290, 290, 280, 291, + 280, 281, 291, 291, 281, 292, + 281, 282, 292, 292, 282, 293, + 282, 283, 293, 293, 283, 294, + 283, 284, 294, 294, 284, 295, + 284, 285, 295, 295, 285, 296, + 286, 287, 297, 297, 287, 298, + 287, 288, 298, 298, 288, 299, + 288, 289, 299, 299, 289, 300, + 289, 290, 300, 300, 290, 301, + 290, 291, 301, 301, 291, 302, + 291, 292, 302, 302, 292, 303, + 292, 293, 303, 303, 293, 304, + 293, 294, 304, 304, 294, 305, + 294, 295, 305, 305, 295, 306, + 295, 296, 306, 306, 296, 307, + 297, 298, 308, 308, 298, 309, + 298, 299, 309, 309, 299, 310, + 299, 300, 310, 310, 300, 311, + 300, 301, 311, 311, 301, 312, + 301, 302, 312, 312, 302, 313, + 302, 303, 313, 313, 303, 314, + 303, 304, 314, 314, 304, 315, + 304, 305, 315, 315, 305, 316, + 305, 306, 316, 316, 306, 317, + 306, 307, 317, 317, 307, 318, + 308, 309, 319, 319, 309, 320, + 309, 310, 320, 320, 310, 321, + 310, 311, 321, 321, 311, 322, + 311, 312, 322, 322, 312, 323, + 312, 313, 323, 323, 313, 324, + 313, 314, 324, 324, 314, 325, + 314, 315, 325, 325, 315, 326, + 315, 316, 326, 326, 316, 327, + 316, 317, 327, 327, 317, 328, + 317, 318, 328, 328, 318, 329, + 319, 320, 330, 330, 320, 331, + 320, 321, 331, 331, 321, 332, + 321, 322, 332, 332, 322, 333, + 322, 323, 333, 333, 323, 334, + 323, 324, 334, 334, 324, 335, + 324, 325, 335, 335, 325, 336, + 325, 326, 336, 336, 326, 337, + 326, 327, 337, 337, 327, 338, + 327, 328, 338, 338, 328, 339, + 328, 329, 339, 339, 329, 340, + 330, 331, 341, 341, 331, 342, + 331, 332, 342, 342, 332, 343, + 332, 333, 343, 343, 333, 344, + 333, 334, 344, 344, 334, 345, + 334, 335, 345, 345, 335, 346, + 335, 336, 346, 346, 336, 347, + 336, 337, 347, 347, 337, 348, + 337, 338, 348, 348, 338, 349, + 338, 339, 349, 349, 339, 350, + 339, 340, 350, 350, 340, 351, + 341, 342, 352, 352, 342, 353, + 342, 343, 353, 353, 343, 354, + 343, 344, 354, 354, 344, 355, + 344, 345, 355, 355, 345, 356, + 345, 346, 356, 356, 346, 357, + 346, 347, 357, 357, 347, 358, + 347, 348, 358, 358, 348, 359, + 348, 349, 359, 359, 349, 360, + 349, 350, 360, 360, 350, 361, + 350, 351, 361, 361, 351, 362, + 363, 364, 374, 374, 364, 375, + 364, 365, 375, 375, 365, 376, + 365, 366, 376, 376, 366, 377, + 366, 367, 377, 377, 367, 378, + 367, 368, 378, 378, 368, 379, + 368, 369, 379, 379, 369, 380, + 369, 370, 380, 380, 370, 381, + 370, 371, 381, 381, 371, 382, + 371, 372, 382, 382, 372, 383, + 372, 373, 383, 383, 373, 384, + 374, 375, 385, 385, 375, 386, + 375, 376, 386, 386, 376, 387, + 376, 377, 387, 387, 377, 388, + 377, 378, 388, 388, 378, 389, + 378, 379, 389, 389, 379, 390, + 379, 380, 390, 390, 380, 391, + 380, 381, 391, 391, 381, 392, + 381, 382, 392, 392, 382, 393, + 382, 383, 393, 393, 383, 394, + 383, 384, 394, 394, 384, 395, + 385, 386, 396, 396, 386, 397, + 386, 387, 397, 397, 387, 398, + 387, 388, 398, 398, 388, 399, + 388, 389, 399, 399, 389, 400, + 389, 390, 400, 400, 390, 401, + 390, 391, 401, 401, 391, 402, + 391, 392, 402, 402, 392, 403, + 392, 393, 403, 403, 393, 404, + 393, 394, 404, 404, 394, 405, + 394, 395, 405, 405, 395, 406, + 396, 397, 407, 407, 397, 408, + 397, 398, 408, 408, 398, 409, + 398, 399, 409, 409, 399, 410, + 399, 400, 410, 410, 400, 411, + 400, 401, 411, 411, 401, 412, + 401, 402, 412, 412, 402, 413, + 402, 403, 413, 413, 403, 414, + 403, 404, 414, 414, 404, 415, + 404, 405, 415, 415, 405, 416, + 405, 406, 416, 416, 406, 417, + 407, 408, 418, 418, 408, 419, + 408, 409, 419, 419, 409, 420, + 409, 410, 420, 420, 410, 421, + 410, 411, 421, 421, 411, 422, + 411, 412, 422, 422, 412, 423, + 412, 413, 423, 423, 413, 424, + 413, 414, 424, 424, 414, 425, + 414, 415, 425, 425, 415, 426, + 415, 416, 426, 426, 416, 427, + 416, 417, 427, 427, 417, 428, + 418, 419, 429, 429, 419, 430, + 419, 420, 430, 430, 420, 431, + 420, 421, 431, 431, 421, 432, + 421, 422, 432, 432, 422, 433, + 422, 423, 433, 433, 423, 434, + 423, 424, 434, 434, 424, 435, + 424, 425, 435, 435, 425, 436, + 425, 426, 436, 436, 426, 437, + 426, 427, 437, 437, 427, 438, + 427, 428, 438, 438, 428, 439, + 429, 430, 440, 440, 430, 441, + 430, 431, 441, 441, 431, 442, + 431, 432, 442, 442, 432, 443, + 432, 433, 443, 443, 433, 444, + 433, 434, 444, 444, 434, 445, + 434, 435, 445, 445, 435, 446, + 435, 436, 446, 446, 436, 447, + 436, 437, 447, 447, 437, 448, + 437, 438, 448, 448, 438, 449, + 438, 439, 449, 449, 439, 450, + 440, 441, 451, 451, 441, 452, + 441, 442, 452, 452, 442, 453, + 442, 443, 453, 453, 443, 454, + 443, 444, 454, 454, 444, 455, + 444, 445, 455, 455, 445, 456, + 445, 446, 456, 456, 446, 457, + 446, 447, 457, 457, 447, 458, + 447, 448, 458, 458, 448, 459, + 448, 449, 459, 459, 449, 460, + 449, 450, 460, 460, 450, 461, + 451, 452, 462, 462, 452, 463, + 452, 453, 463, 463, 453, 464, + 453, 454, 464, 464, 454, 465, + 454, 455, 465, 465, 455, 466, + 455, 456, 466, 466, 456, 467, + 456, 457, 467, 467, 457, 468, + 457, 458, 468, 468, 458, 469, + 458, 459, 469, 469, 459, 470, + 459, 460, 470, 470, 460, 471, + 460, 461, 471, 471, 461, 472, + 462, 463, 473, 473, 463, 474, + 463, 464, 474, 474, 464, 475, + 464, 465, 475, 475, 465, 476, + 465, 466, 476, 476, 466, 477, + 466, 467, 477, 477, 467, 478, + 467, 468, 478, 478, 468, 479, + 468, 469, 479, 479, 469, 480, + 469, 470, 480, 480, 470, 481, + 470, 471, 481, 481, 471, 482, + 471, 472, 482, 482, 472, 483, + 484, 485, 495, 495, 485, 496, + 485, 486, 496, 496, 486, 497, + 486, 487, 497, 497, 487, 498, + 487, 488, 498, 498, 488, 499, + 488, 489, 499, 499, 489, 500, + 489, 490, 500, 500, 490, 501, + 490, 491, 501, 501, 491, 502, + 491, 492, 502, 502, 492, 503, + 492, 493, 503, 503, 493, 504, + 493, 494, 504, 504, 494, 505, + 495, 496, 506, 506, 496, 507, + 496, 497, 507, 507, 497, 508, + 497, 498, 508, 508, 498, 509, + 498, 499, 509, 509, 499, 510, + 499, 500, 510, 510, 500, 511, + 500, 501, 511, 511, 501, 512, + 501, 502, 512, 512, 502, 513, + 502, 503, 513, 513, 503, 514, + 503, 504, 514, 514, 504, 515, + 504, 505, 515, 515, 505, 516, + 506, 507, 517, 517, 507, 518, + 507, 508, 518, 518, 508, 519, + 508, 509, 519, 519, 509, 520, + 509, 510, 520, 520, 510, 521, + 510, 511, 521, 521, 511, 522, + 511, 512, 522, 522, 512, 523, + 512, 513, 523, 523, 513, 524, + 513, 514, 524, 524, 514, 525, + 514, 515, 525, 525, 515, 526, + 515, 516, 526, 526, 516, 527, + 517, 518, 528, 528, 518, 529, + 518, 519, 529, 529, 519, 530, + 519, 520, 530, 530, 520, 531, + 520, 521, 531, 531, 521, 532, + 521, 522, 532, 532, 522, 533, + 522, 523, 533, 533, 523, 534, + 523, 524, 534, 534, 524, 535, + 524, 525, 535, 535, 525, 536, + 525, 526, 536, 536, 526, 537, + 526, 527, 537, 537, 527, 538, + 528, 529, 539, 539, 529, 540, + 529, 530, 540, 540, 530, 541, + 530, 531, 541, 541, 531, 542, + 531, 532, 542, 542, 532, 543, + 532, 533, 543, 543, 533, 544, + 533, 534, 544, 544, 534, 545, + 534, 535, 545, 545, 535, 546, + 535, 536, 546, 546, 536, 547, + 536, 537, 547, 547, 537, 548, + 537, 538, 548, 548, 538, 549, + 539, 540, 550, 550, 540, 551, + 540, 541, 551, 551, 541, 552, + 541, 542, 552, 552, 542, 553, + 542, 543, 553, 553, 543, 554, + 543, 544, 554, 554, 544, 555, + 544, 545, 555, 555, 545, 556, + 545, 546, 556, 556, 546, 557, + 546, 547, 557, 557, 547, 558, + 547, 548, 558, 558, 548, 559, + 548, 549, 559, 559, 549, 560, + 550, 551, 561, 561, 551, 562, + 551, 552, 562, 562, 552, 563, + 552, 553, 563, 563, 553, 564, + 553, 554, 564, 564, 554, 565, + 554, 555, 565, 565, 555, 566, + 555, 556, 566, 566, 556, 567, + 556, 557, 567, 567, 557, 568, + 557, 558, 568, 568, 558, 569, + 558, 559, 569, 569, 559, 570, + 559, 560, 570, 570, 560, 571, + 561, 562, 572, 572, 562, 573, + 562, 563, 573, 573, 563, 574, + 563, 564, 574, 574, 564, 575, + 564, 565, 575, 575, 565, 576, + 565, 566, 576, 576, 566, 577, + 566, 567, 577, 577, 567, 578, + 567, 568, 578, 578, 568, 579, + 568, 569, 579, 579, 569, 580, + 569, 570, 580, 580, 570, 581, + 570, 571, 581, 581, 571, 582, + 572, 573, 583, 583, 573, 584, + 573, 574, 584, 584, 574, 585, + 574, 575, 585, 585, 575, 586, + 575, 576, 586, 586, 576, 587, + 576, 577, 587, 587, 577, 588, + 577, 578, 588, 588, 578, 589, + 578, 579, 589, 589, 579, 590, + 579, 580, 590, 590, 580, 591, + 580, 581, 591, 591, 581, 592, + 581, 582, 592, 592, 582, 593, + 583, 584, 594, 594, 584, 595, + 584, 585, 595, 595, 585, 596, + 585, 586, 596, 596, 586, 597, + 586, 587, 597, 597, 587, 598, + 587, 588, 598, 598, 588, 599, + 588, 589, 599, 599, 589, 600, + 589, 590, 600, 600, 590, 601, + 590, 591, 601, 601, 591, 602, + 591, 592, 602, 602, 592, 603, + 592, 593, 603, 603, 593, 604, + 605, 606, 616, 616, 606, 617, + 606, 607, 617, 617, 607, 618, + 607, 608, 618, 618, 608, 619, + 608, 609, 619, 619, 609, 620, + 609, 610, 620, 620, 610, 621, + 610, 611, 621, 621, 611, 622, + 611, 612, 622, 622, 612, 623, + 612, 613, 623, 623, 613, 624, + 613, 614, 624, 624, 614, 625, + 614, 615, 625, 625, 615, 626, + 616, 617, 627, 627, 617, 628, + 617, 618, 628, 628, 618, 629, + 618, 619, 629, 629, 619, 630, + 619, 620, 630, 630, 620, 631, + 620, 621, 631, 631, 621, 632, + 621, 622, 632, 632, 622, 633, + 622, 623, 633, 633, 623, 634, + 623, 624, 634, 634, 624, 635, + 624, 625, 635, 635, 625, 636, + 625, 626, 636, 636, 626, 637, + 627, 628, 638, 638, 628, 639, + 628, 629, 639, 639, 629, 640, + 629, 630, 640, 640, 630, 641, + 630, 631, 641, 641, 631, 642, + 631, 632, 642, 642, 632, 643, + 632, 633, 643, 643, 633, 644, + 633, 634, 644, 644, 634, 645, + 634, 635, 645, 645, 635, 646, + 635, 636, 646, 646, 636, 647, + 636, 637, 647, 647, 637, 648, + 638, 639, 649, 649, 639, 650, + 639, 640, 650, 650, 640, 651, + 640, 641, 651, 651, 641, 652, + 641, 642, 652, 652, 642, 653, + 642, 643, 653, 653, 643, 654, + 643, 644, 654, 654, 644, 655, + 644, 645, 655, 655, 645, 656, + 645, 646, 656, 656, 646, 657, + 646, 647, 657, 657, 647, 658, + 647, 648, 658, 658, 648, 659, + 649, 650, 660, 660, 650, 661, + 650, 651, 661, 661, 651, 662, + 651, 652, 662, 662, 652, 663, + 652, 653, 663, 663, 653, 664, + 653, 654, 664, 664, 654, 665, + 654, 655, 665, 665, 655, 666, + 655, 656, 666, 666, 656, 667, + 656, 657, 667, 667, 657, 668, + 657, 658, 668, 668, 658, 669, + 658, 659, 669, 669, 659, 670, + 660, 661, 671, 671, 661, 672, + 661, 662, 672, 672, 662, 673, + 662, 663, 673, 673, 663, 674, + 663, 664, 674, 674, 664, 675, + 664, 665, 675, 675, 665, 676, + 665, 666, 676, 676, 666, 677, + 666, 667, 677, 677, 667, 678, + 667, 668, 678, 678, 668, 679, + 668, 669, 679, 679, 669, 680, + 669, 670, 680, 680, 670, 681, + 671, 672, 682, 682, 672, 683, + 672, 673, 683, 683, 673, 684, + 673, 674, 684, 684, 674, 685, + 674, 675, 685, 685, 675, 686, + 675, 676, 686, 686, 676, 687, + 676, 677, 687, 687, 677, 688, + 677, 678, 688, 688, 678, 689, + 678, 679, 689, 689, 679, 690, + 679, 680, 690, 690, 680, 691, + 680, 681, 691, 691, 681, 692, + 682, 683, 693, 693, 683, 694, + 683, 684, 694, 694, 684, 695, + 684, 685, 695, 695, 685, 696, + 685, 686, 696, 696, 686, 697, + 686, 687, 697, 697, 687, 698, + 687, 688, 698, 698, 688, 699, + 688, 689, 699, 699, 689, 700, + 689, 690, 700, 700, 690, 701, + 690, 691, 701, 701, 691, 702, + 691, 692, 702, 702, 692, 703, + 693, 694, 704, 704, 694, 705, + 694, 695, 705, 705, 695, 706, + 695, 696, 706, 706, 696, 707, + 696, 697, 707, 707, 697, 708, + 697, 698, 708, 708, 698, 709, + 698, 699, 709, 709, 699, 710, + 699, 700, 710, 710, 700, 711, + 700, 701, 711, 711, 701, 712, + 701, 702, 712, 712, 702, 713, + 702, 703, 713, 713, 703, 714, + 704, 705, 715, 715, 705, 716, + 705, 706, 716, 716, 706, 717, + 706, 707, 717, 717, 707, 718, + 707, 708, 718, 718, 708, 719, + 708, 709, 719, 719, 709, 720, + 709, 710, 720, 720, 710, 721, + 710, 711, 721, 721, 711, 722, + 711, 712, 722, 722, 712, 723, + 712, 713, 723, 723, 713, 724, + 713, 714, 724, 724, 714, 725, + 726, 727, 737, 737, 727, 738, + 727, 728, 738, 738, 728, 739, + 728, 729, 739, 739, 729, 740, + 729, 730, 740, 740, 730, 741, + 730, 731, 741, 741, 731, 742, + 731, 732, 742, 742, 732, 743, + 732, 733, 743, 743, 733, 744, + 733, 734, 744, 744, 734, 745, + 734, 735, 745, 745, 735, 746, + 735, 736, 746, 746, 736, 747, + 737, 738, 748, 748, 738, 749, + 738, 739, 749, 749, 739, 750, + 739, 740, 750, 750, 740, 751, + 740, 741, 751, 751, 741, 752, + 741, 742, 752, 752, 742, 753, + 742, 743, 753, 753, 743, 754, + 743, 744, 754, 754, 744, 755, + 744, 745, 755, 755, 745, 756, + 745, 746, 756, 756, 746, 757, + 746, 747, 757, 757, 747, 758, + 748, 749, 759, 759, 749, 760, + 749, 750, 760, 760, 750, 761, + 750, 751, 761, 761, 751, 762, + 751, 752, 762, 762, 752, 763, + 752, 753, 763, 763, 753, 764, + 753, 754, 764, 764, 754, 765, + 754, 755, 765, 765, 755, 766, + 755, 756, 766, 766, 756, 767, + 756, 757, 767, 767, 757, 768, + 757, 758, 768, 768, 758, 769, + 759, 760, 770, 770, 760, 771, + 760, 761, 771, 771, 761, 772, + 761, 762, 772, 772, 762, 773, + 762, 763, 773, 773, 763, 774, + 763, 764, 774, 774, 764, 775, + 764, 765, 775, 775, 765, 776, + 765, 766, 776, 776, 766, 777, + 766, 767, 777, 777, 767, 778, + 767, 768, 778, 778, 768, 779, + 768, 769, 779, 779, 769, 780, + 770, 771, 781, 781, 771, 782, + 771, 772, 782, 782, 772, 783, + 772, 773, 783, 783, 773, 784, + 773, 774, 784, 784, 774, 785, + 774, 775, 785, 785, 775, 786, + 775, 776, 786, 786, 776, 787, + 776, 777, 787, 787, 777, 788, + 777, 778, 788, 788, 778, 789, + 778, 779, 789, 789, 779, 790, + 779, 780, 790, 790, 780, 791, + 781, 782, 792, 792, 782, 793, + 782, 783, 793, 793, 783, 794, + 783, 784, 794, 794, 784, 795, + 784, 785, 795, 795, 785, 796, + 785, 786, 796, 796, 786, 797, + 786, 787, 797, 797, 787, 798, + 787, 788, 798, 798, 788, 799, + 788, 789, 799, 799, 789, 800, + 789, 790, 800, 800, 790, 801, + 790, 791, 801, 801, 791, 802, + 792, 793, 803, 803, 793, 804, + 793, 794, 804, 804, 794, 805, + 794, 795, 805, 805, 795, 806, + 795, 796, 806, 806, 796, 807, + 796, 797, 807, 807, 797, 808, + 797, 798, 808, 808, 798, 809, + 798, 799, 809, 809, 799, 810, + 799, 800, 810, 810, 800, 811, + 800, 801, 811, 811, 801, 812, + 801, 802, 812, 812, 802, 813, + 803, 804, 814, 814, 804, 815, + 804, 805, 815, 815, 805, 816, + 805, 806, 816, 816, 806, 817, + 806, 807, 817, 817, 807, 818, + 807, 808, 818, 818, 808, 819, + 808, 809, 819, 819, 809, 820, + 809, 810, 820, 820, 810, 821, + 810, 811, 821, 821, 811, 822, + 811, 812, 822, 822, 812, 823, + 812, 813, 823, 823, 813, 824, + 814, 815, 825, 825, 815, 826, + 815, 816, 826, 826, 816, 827, + 816, 817, 827, 827, 817, 828, + 817, 818, 828, 828, 818, 829, + 818, 819, 829, 829, 819, 830, + 819, 820, 830, 830, 820, 831, + 820, 821, 831, 831, 821, 832, + 821, 822, 832, 832, 822, 833, + 822, 823, 833, 833, 823, 834, + 823, 824, 834, 834, 824, 835, + 825, 826, 836, 836, 826, 837, + 826, 827, 837, 837, 827, 838, + 827, 828, 838, 838, 828, 839, + 828, 829, 839, 839, 829, 840, + 829, 830, 840, 840, 830, 841, + 830, 831, 841, 841, 831, 842, + 831, 832, 842, 842, 832, 843, + 832, 833, 843, 843, 833, 844, + 833, 834, 844, 844, 834, 845, + 834, 835, 845, 845, 835, 846, + 847, 848, 858, 858, 848, 859, + 848, 849, 859, 859, 849, 860, + 849, 850, 860, 860, 850, 861, + 850, 851, 861, 861, 851, 862, + 851, 852, 862, 862, 852, 863, + 852, 853, 863, 863, 853, 864, + 853, 854, 864, 864, 854, 865, + 854, 855, 865, 865, 855, 866, + 855, 856, 866, 866, 856, 867, + 856, 857, 867, 867, 857, 868, + 858, 859, 869, 869, 859, 870, + 859, 860, 870, 870, 860, 871, + 860, 861, 871, 871, 861, 872, + 861, 862, 872, 872, 862, 873, + 862, 863, 873, 873, 863, 874, + 863, 864, 874, 874, 864, 875, + 864, 865, 875, 875, 865, 876, + 865, 866, 876, 876, 866, 877, + 866, 867, 877, 877, 867, 878, + 867, 868, 878, 878, 868, 879, + 869, 870, 880, 880, 870, 881, + 870, 871, 881, 881, 871, 882, + 871, 872, 882, 882, 872, 883, + 872, 873, 883, 883, 873, 884, + 873, 874, 884, 884, 874, 885, + 874, 875, 885, 885, 875, 886, + 875, 876, 886, 886, 876, 887, + 876, 877, 887, 887, 877, 888, + 877, 878, 888, 888, 878, 889, + 878, 879, 889, 889, 879, 890, + 880, 881, 891, 891, 881, 892, + 881, 882, 892, 892, 882, 893, + 882, 883, 893, 893, 883, 894, + 883, 884, 894, 894, 884, 895, + 884, 885, 895, 895, 885, 896, + 885, 886, 896, 896, 886, 897, + 886, 887, 897, 897, 887, 898, + 887, 888, 898, 898, 888, 899, + 888, 889, 899, 899, 889, 900, + 889, 890, 900, 900, 890, 901, + 891, 892, 902, 902, 892, 903, + 892, 893, 903, 903, 893, 904, + 893, 894, 904, 904, 894, 905, + 894, 895, 905, 905, 895, 906, + 895, 896, 906, 906, 896, 907, + 896, 897, 907, 907, 897, 908, + 897, 898, 908, 908, 898, 909, + 898, 899, 909, 909, 899, 910, + 899, 900, 910, 910, 900, 911, + 900, 901, 911, 911, 901, 912, + 902, 903, 913, 913, 903, 914, + 903, 904, 914, 914, 904, 915, + 904, 905, 915, 915, 905, 916, + 905, 906, 916, 916, 906, 917, + 906, 907, 917, 917, 907, 918, + 907, 908, 918, 918, 908, 919, + 908, 909, 919, 919, 909, 920, + 909, 910, 920, 920, 910, 921, + 910, 911, 921, 921, 911, 922, + 911, 912, 922, 922, 912, 923, + 913, 914, 924, 924, 914, 925, + 914, 915, 925, 925, 915, 926, + 915, 916, 926, 926, 916, 927, + 916, 917, 927, 927, 917, 928, + 917, 918, 928, 928, 918, 929, + 918, 919, 929, 929, 919, 930, + 919, 920, 930, 930, 920, 931, + 920, 921, 931, 931, 921, 932, + 921, 922, 932, 932, 922, 933, + 922, 923, 933, 933, 923, 934, + 924, 925, 935, 935, 925, 936, + 925, 926, 936, 936, 926, 937, + 926, 927, 937, 937, 927, 938, + 927, 928, 938, 938, 928, 939, + 928, 929, 939, 939, 929, 940, + 929, 930, 940, 940, 930, 941, + 930, 931, 941, 941, 931, 942, + 931, 932, 942, 942, 932, 943, + 932, 933, 943, 943, 933, 944, + 933, 934, 944, 944, 934, 945, + 935, 936, 946, 946, 936, 947, + 936, 937, 947, 947, 937, 948, + 937, 938, 948, 948, 938, 949, + 938, 939, 949, 949, 939, 950, + 939, 940, 950, 950, 940, 951, + 940, 941, 951, 951, 941, 952, + 941, 942, 952, 952, 942, 953, + 942, 943, 953, 953, 943, 954, + 943, 944, 954, 954, 944, 955, + 944, 945, 955, 955, 945, 956, + 946, 947, 957, 957, 947, 958, + 947, 948, 958, 958, 948, 959, + 948, 949, 959, 959, 949, 960, + 949, 950, 960, 960, 950, 961, + 950, 951, 961, 961, 951, 962, + 951, 952, 962, 962, 952, 963, + 952, 953, 963, 963, 953, 964, + 953, 954, 964, 964, 954, 965, + 954, 955, 965, 965, 955, 966, + 955, 956, 966, 966, 956, 967, + 968, 969, 979, 979, 969, 980, + 969, 970, 980, 980, 970, 981, + 970, 971, 981, 981, 971, 982, + 971, 972, 982, 982, 972, 983, + 972, 973, 983, 983, 973, 984, + 973, 974, 984, 984, 974, 985, + 974, 975, 985, 985, 975, 986, + 975, 976, 986, 986, 976, 987, + 976, 977, 987, 987, 977, 988, + 977, 978, 988, 988, 978, 989, + 979, 980, 990, 990, 980, 991, + 980, 981, 991, 991, 981, 992, + 981, 982, 992, 992, 982, 993, + 982, 983, 993, 993, 983, 994, + 983, 984, 994, 994, 984, 995, + 984, 985, 995, 995, 985, 996, + 985, 986, 996, 996, 986, 997, + 986, 987, 997, 997, 987, 998, + 987, 988, 998, 998, 988, 999, + 988, 989, 999, 999, 989, 1000, + 990, 991, 1001, 1001, 991, 1002, + 991, 992, 1002, 1002, 992, 1003, + 992, 993, 1003, 1003, 993, 1004, + 993, 994, 1004, 1004, 994, 1005, + 994, 995, 1005, 1005, 995, 1006, + 995, 996, 1006, 1006, 996, 1007, + 996, 997, 1007, 1007, 997, 1008, + 997, 998, 1008, 1008, 998, 1009, + 998, 999, 1009, 1009, 999, 1010, + 999, 1000, 1010, 1010, 1000, 1011, + 1001, 1002, 1012, 1012, 1002, 1013, + 1002, 1003, 1013, 1013, 1003, 1014, + 1003, 1004, 1014, 1014, 1004, 1015, + 1004, 1005, 1015, 1015, 1005, 1016, + 1005, 1006, 1016, 1016, 1006, 1017, + 1006, 1007, 1017, 1017, 1007, 1018, + 1007, 1008, 1018, 1018, 1008, 1019, + 1008, 1009, 1019, 1019, 1009, 1020, + 1009, 1010, 1020, 1020, 1010, 1021, + 1010, 1011, 1021, 1021, 1011, 1022, + 1012, 1013, 1023, 1023, 1013, 1024, + 1013, 1014, 1024, 1024, 1014, 1025, + 1014, 1015, 1025, 1025, 1015, 1026, + 1015, 1016, 1026, 1026, 1016, 1027, + 1016, 1017, 1027, 1027, 1017, 1028, + 1017, 1018, 1028, 1028, 1018, 1029, + 1018, 1019, 1029, 1029, 1019, 1030, + 1019, 1020, 1030, 1030, 1020, 1031, + 1020, 1021, 1031, 1031, 1021, 1032, + 1021, 1022, 1032, 1032, 1022, 1033, + 1023, 1024, 1034, 1034, 1024, 1035, + 1024, 1025, 1035, 1035, 1025, 1036, + 1025, 1026, 1036, 1036, 1026, 1037, + 1026, 1027, 1037, 1037, 1027, 1038, + 1027, 1028, 1038, 1038, 1028, 1039, + 1028, 1029, 1039, 1039, 1029, 1040, + 1029, 1030, 1040, 1040, 1030, 1041, + 1030, 1031, 1041, 1041, 1031, 1042, + 1031, 1032, 1042, 1042, 1032, 1043, + 1032, 1033, 1043, 1043, 1033, 1044, + 1034, 1035, 1045, 1045, 1035, 1046, + 1035, 1036, 1046, 1046, 1036, 1047, + 1036, 1037, 1047, 1047, 1037, 1048, + 1037, 1038, 1048, 1048, 1038, 1049, + 1038, 1039, 1049, 1049, 1039, 1050, + 1039, 1040, 1050, 1050, 1040, 1051, + 1040, 1041, 1051, 1051, 1041, 1052, + 1041, 1042, 1052, 1052, 1042, 1053, + 1042, 1043, 1053, 1053, 1043, 1054, + 1043, 1044, 1054, 1054, 1044, 1055, + 1045, 1046, 1056, 1056, 1046, 1057, + 1046, 1047, 1057, 1057, 1047, 1058, + 1047, 1048, 1058, 1058, 1048, 1059, + 1048, 1049, 1059, 1059, 1049, 1060, + 1049, 1050, 1060, 1060, 1050, 1061, + 1050, 1051, 1061, 1061, 1051, 1062, + 1051, 1052, 1062, 1062, 1052, 1063, + 1052, 1053, 1063, 1063, 1053, 1064, + 1053, 1054, 1064, 1064, 1054, 1065, + 1054, 1055, 1065, 1065, 1055, 1066, + 1056, 1057, 1067, 1067, 1057, 1068, + 1057, 1058, 1068, 1068, 1058, 1069, + 1058, 1059, 1069, 1069, 1059, 1070, + 1059, 1060, 1070, 1070, 1060, 1071, + 1060, 1061, 1071, 1071, 1061, 1072, + 1061, 1062, 1072, 1072, 1062, 1073, + 1062, 1063, 1073, 1073, 1063, 1074, + 1063, 1064, 1074, 1074, 1064, 1075, + 1064, 1065, 1075, 1075, 1065, 1076, + 1065, 1066, 1076, 1076, 1066, 1077, + 1067, 1068, 1078, 1078, 1068, 1079, + 1068, 1069, 1079, 1079, 1069, 1080, + 1069, 1070, 1080, 1080, 1070, 1081, + 1070, 1071, 1081, 1081, 1071, 1082, + 1071, 1072, 1082, 1082, 1072, 1083, + 1072, 1073, 1083, 1083, 1073, 1084, + 1073, 1074, 1084, 1084, 1074, 1085, + 1074, 1075, 1085, 1085, 1075, 1086, + 1075, 1076, 1086, 1086, 1076, 1087, + 1076, 1077, 1087, 1087, 1077, 1088, + 1089, 1090, 1100, 1100, 1090, 1101, + 1090, 1091, 1101, 1101, 1091, 1102, + 1091, 1092, 1102, 1102, 1092, 1103, + 1092, 1093, 1103, 1103, 1093, 1104, + 1093, 1094, 1104, 1104, 1094, 1105, + 1094, 1095, 1105, 1105, 1095, 1106, + 1095, 1096, 1106, 1106, 1096, 1107, + 1096, 1097, 1107, 1107, 1097, 1108, + 1097, 1098, 1108, 1108, 1098, 1109, + 1098, 1099, 1109, 1109, 1099, 1110, + 1100, 1101, 1111, 1111, 1101, 1112, + 1101, 1102, 1112, 1112, 1102, 1113, + 1102, 1103, 1113, 1113, 1103, 1114, + 1103, 1104, 1114, 1114, 1104, 1115, + 1104, 1105, 1115, 1115, 1105, 1116, + 1105, 1106, 1116, 1116, 1106, 1117, + 1106, 1107, 1117, 1117, 1107, 1118, + 1107, 1108, 1118, 1118, 1108, 1119, + 1108, 1109, 1119, 1119, 1109, 1120, + 1109, 1110, 1120, 1120, 1110, 1121, + 1111, 1112, 1122, 1122, 1112, 1123, + 1112, 1113, 1123, 1123, 1113, 1124, + 1113, 1114, 1124, 1124, 1114, 1125, + 1114, 1115, 1125, 1125, 1115, 1126, + 1115, 1116, 1126, 1126, 1116, 1127, + 1116, 1117, 1127, 1127, 1117, 1128, + 1117, 1118, 1128, 1128, 1118, 1129, + 1118, 1119, 1129, 1129, 1119, 1130, + 1119, 1120, 1130, 1130, 1120, 1131, + 1120, 1121, 1131, 1131, 1121, 1132, + 1122, 1123, 1133, 1133, 1123, 1134, + 1123, 1124, 1134, 1134, 1124, 1135, + 1124, 1125, 1135, 1135, 1125, 1136, + 1125, 1126, 1136, 1136, 1126, 1137, + 1126, 1127, 1137, 1137, 1127, 1138, + 1127, 1128, 1138, 1138, 1128, 1139, + 1128, 1129, 1139, 1139, 1129, 1140, + 1129, 1130, 1140, 1140, 1130, 1141, + 1130, 1131, 1141, 1141, 1131, 1142, + 1131, 1132, 1142, 1142, 1132, 1143, + 1133, 1134, 1144, 1144, 1134, 1145, + 1134, 1135, 1145, 1145, 1135, 1146, + 1135, 1136, 1146, 1146, 1136, 1147, + 1136, 1137, 1147, 1147, 1137, 1148, + 1137, 1138, 1148, 1148, 1138, 1149, + 1138, 1139, 1149, 1149, 1139, 1150, + 1139, 1140, 1150, 1150, 1140, 1151, + 1140, 1141, 1151, 1151, 1141, 1152, + 1141, 1142, 1152, 1152, 1142, 1153, + 1142, 1143, 1153, 1153, 1143, 1154, + 1144, 1145, 1155, 1155, 1145, 1156, + 1145, 1146, 1156, 1156, 1146, 1157, + 1146, 1147, 1157, 1157, 1147, 1158, + 1147, 1148, 1158, 1158, 1148, 1159, + 1148, 1149, 1159, 1159, 1149, 1160, + 1149, 1150, 1160, 1160, 1150, 1161, + 1150, 1151, 1161, 1161, 1151, 1162, + 1151, 1152, 1162, 1162, 1152, 1163, + 1152, 1153, 1163, 1163, 1153, 1164, + 1153, 1154, 1164, 1164, 1154, 1165, + 1155, 1156, 1166, 1166, 1156, 1167, + 1156, 1157, 1167, 1167, 1157, 1168, + 1157, 1158, 1168, 1168, 1158, 1169, + 1158, 1159, 1169, 1169, 1159, 1170, + 1159, 1160, 1170, 1170, 1160, 1171, + 1160, 1161, 1171, 1171, 1161, 1172, + 1161, 1162, 1172, 1172, 1162, 1173, + 1162, 1163, 1173, 1173, 1163, 1174, + 1163, 1164, 1174, 1174, 1164, 1175, + 1164, 1165, 1175, 1175, 1165, 1176, + 1166, 1167, 1177, 1177, 1167, 1178, + 1167, 1168, 1178, 1178, 1168, 1179, + 1168, 1169, 1179, 1179, 1169, 1180, + 1169, 1170, 1180, 1180, 1170, 1181, + 1170, 1171, 1181, 1181, 1171, 1182, + 1171, 1172, 1182, 1182, 1172, 1183, + 1172, 1173, 1183, 1183, 1173, 1184, + 1173, 1174, 1184, 1184, 1174, 1185, + 1174, 1175, 1185, 1185, 1175, 1186, + 1175, 1176, 1186, 1186, 1176, 1187, + 1177, 1178, 1188, 1188, 1178, 1189, + 1178, 1179, 1189, 1189, 1179, 1190, + 1179, 1180, 1190, 1190, 1180, 1191, + 1180, 1181, 1191, 1191, 1181, 1192, + 1181, 1182, 1192, 1192, 1182, 1193, + 1182, 1183, 1193, 1193, 1183, 1194, + 1183, 1184, 1194, 1194, 1184, 1195, + 1184, 1185, 1195, 1195, 1185, 1196, + 1185, 1186, 1196, 1196, 1186, 1197, + 1186, 1187, 1197, 1197, 1187, 1198, + 1188, 1189, 1199, 1199, 1189, 1200, + 1189, 1190, 1200, 1200, 1190, 1201, + 1190, 1191, 1201, 1201, 1191, 1202, + 1191, 1192, 1202, 1202, 1192, 1203, + 1192, 1193, 1203, 1203, 1193, 1204, + 1193, 1194, 1204, 1204, 1194, 1205, + 1194, 1195, 1205, 1205, 1195, 1206, + 1195, 1196, 1206, 1206, 1196, 1207, + 1196, 1197, 1207, 1207, 1197, 1208, + 1197, 1198, 1208, 1208, 1198, 1209, + 1210, 1211, 1221, 1221, 1211, 1222, + 1211, 1212, 1222, 1222, 1212, 1223, + 1212, 1213, 1223, 1223, 1213, 1224, + 1213, 1214, 1224, 1224, 1214, 1225, + 1214, 1215, 1225, 1225, 1215, 1226, + 1215, 1216, 1226, 1226, 1216, 1227, + 1216, 1217, 1227, 1227, 1217, 1228, + 1217, 1218, 1228, 1228, 1218, 1229, + 1218, 1219, 1229, 1229, 1219, 1230, + 1219, 1220, 1230, 1230, 1220, 1231, + 1221, 1222, 1232, 1232, 1222, 1233, + 1222, 1223, 1233, 1233, 1223, 1234, + 1223, 1224, 1234, 1234, 1224, 1235, + 1224, 1225, 1235, 1235, 1225, 1236, + 1225, 1226, 1236, 1236, 1226, 1237, + 1226, 1227, 1237, 1237, 1227, 1238, + 1227, 1228, 1238, 1238, 1228, 1239, + 1228, 1229, 1239, 1239, 1229, 1240, + 1229, 1230, 1240, 1240, 1230, 1241, + 1230, 1231, 1241, 1241, 1231, 1242, + 1232, 1233, 1243, 1243, 1233, 1244, + 1233, 1234, 1244, 1244, 1234, 1245, + 1234, 1235, 1245, 1245, 1235, 1246, + 1235, 1236, 1246, 1246, 1236, 1247, + 1236, 1237, 1247, 1247, 1237, 1248, + 1237, 1238, 1248, 1248, 1238, 1249, + 1238, 1239, 1249, 1249, 1239, 1250, + 1239, 1240, 1250, 1250, 1240, 1251, + 1240, 1241, 1251, 1251, 1241, 1252, + 1241, 1242, 1252, 1252, 1242, 1253, + 1243, 1244, 1254, 1254, 1244, 1255, + 1244, 1245, 1255, 1255, 1245, 1256, + 1245, 1246, 1256, 1256, 1246, 1257, + 1246, 1247, 1257, 1257, 1247, 1258, + 1247, 1248, 1258, 1258, 1248, 1259, + 1248, 1249, 1259, 1259, 1249, 1260, + 1249, 1250, 1260, 1260, 1250, 1261, + 1250, 1251, 1261, 1261, 1251, 1262, + 1251, 1252, 1262, 1262, 1252, 1263, + 1252, 1253, 1263, 1263, 1253, 1264, + 1254, 1255, 1265, 1265, 1255, 1266, + 1255, 1256, 1266, 1266, 1256, 1267, + 1256, 1257, 1267, 1267, 1257, 1268, + 1257, 1258, 1268, 1268, 1258, 1269, + 1258, 1259, 1269, 1269, 1259, 1270, + 1259, 1260, 1270, 1270, 1260, 1271, + 1260, 1261, 1271, 1271, 1261, 1272, + 1261, 1262, 1272, 1272, 1262, 1273, + 1262, 1263, 1273, 1273, 1263, 1274, + 1263, 1264, 1274, 1274, 1264, 1275, + 1265, 1266, 1276, 1276, 1266, 1277, + 1266, 1267, 1277, 1277, 1267, 1278, + 1267, 1268, 1278, 1278, 1268, 1279, + 1268, 1269, 1279, 1279, 1269, 1280, + 1269, 1270, 1280, 1280, 1270, 1281, + 1270, 1271, 1281, 1281, 1271, 1282, + 1271, 1272, 1282, 1282, 1272, 1283, + 1272, 1273, 1283, 1283, 1273, 1284, + 1273, 1274, 1284, 1284, 1274, 1285, + 1274, 1275, 1285, 1285, 1275, 1286, + 1276, 1277, 1287, 1287, 1277, 1288, + 1277, 1278, 1288, 1288, 1278, 1289, + 1278, 1279, 1289, 1289, 1279, 1290, + 1279, 1280, 1290, 1290, 1280, 1291, + 1280, 1281, 1291, 1291, 1281, 1292, + 1281, 1282, 1292, 1292, 1282, 1293, + 1282, 1283, 1293, 1293, 1283, 1294, + 1283, 1284, 1294, 1294, 1284, 1295, + 1284, 1285, 1295, 1295, 1285, 1296, + 1285, 1286, 1296, 1296, 1286, 1297, + 1287, 1288, 1298, 1298, 1288, 1299, + 1288, 1289, 1299, 1299, 1289, 1300, + 1289, 1290, 1300, 1300, 1290, 1301, + 1290, 1291, 1301, 1301, 1291, 1302, + 1291, 1292, 1302, 1302, 1292, 1303, + 1292, 1293, 1303, 1303, 1293, 1304, + 1293, 1294, 1304, 1304, 1294, 1305, + 1294, 1295, 1305, 1305, 1295, 1306, + 1295, 1296, 1306, 1306, 1296, 1307, + 1296, 1297, 1307, 1307, 1297, 1308, + 1298, 1299, 1309, 1309, 1299, 1310, + 1299, 1300, 1310, 1310, 1300, 1311, + 1300, 1301, 1311, 1311, 1301, 1312, + 1301, 1302, 1312, 1312, 1302, 1313, + 1302, 1303, 1313, 1313, 1303, 1314, + 1303, 1304, 1314, 1314, 1304, 1315, + 1304, 1305, 1315, 1315, 1305, 1316, + 1305, 1306, 1316, 1316, 1306, 1317, + 1306, 1307, 1317, 1317, 1307, 1318, + 1307, 1308, 1318, 1318, 1308, 1319, + 1309, 1310, 1320, 1320, 1310, 1321, + 1310, 1311, 1321, 1321, 1311, 1322, + 1311, 1312, 1322, 1322, 1312, 1323, + 1312, 1313, 1323, 1323, 1313, 1324, + 1313, 1314, 1324, 1324, 1314, 1325, + 1314, 1315, 1325, 1325, 1315, 1326, + 1315, 1316, 1326, 1326, 1316, 1327, + 1316, 1317, 1327, 1327, 1317, 1328, + 1317, 1318, 1328, 1328, 1318, 1329, + 1318, 1319, 1329, 1329, 1319, 1330, + 1331, 1332, 1342, 1342, 1332, 1343, + 1332, 1333, 1343, 1343, 1333, 1344, + 1333, 1334, 1344, 1344, 1334, 1345, + 1334, 1335, 1345, 1345, 1335, 1346, + 1335, 1336, 1346, 1346, 1336, 1347, + 1336, 1337, 1347, 1347, 1337, 1348, + 1337, 1338, 1348, 1348, 1338, 1349, + 1338, 1339, 1349, 1349, 1339, 1350, + 1339, 1340, 1350, 1350, 1340, 1351, + 1340, 1341, 1351, 1351, 1341, 1352, + 1342, 1343, 1353, 1353, 1343, 1354, + 1343, 1344, 1354, 1354, 1344, 1355, + 1344, 1345, 1355, 1355, 1345, 1356, + 1345, 1346, 1356, 1356, 1346, 1357, + 1346, 1347, 1357, 1357, 1347, 1358, + 1347, 1348, 1358, 1358, 1348, 1359, + 1348, 1349, 1359, 1359, 1349, 1360, + 1349, 1350, 1360, 1360, 1350, 1361, + 1350, 1351, 1361, 1361, 1351, 1362, + 1351, 1352, 1362, 1362, 1352, 1363, + 1353, 1354, 1364, 1364, 1354, 1365, + 1354, 1355, 1365, 1365, 1355, 1366, + 1355, 1356, 1366, 1366, 1356, 1367, + 1356, 1357, 1367, 1367, 1357, 1368, + 1357, 1358, 1368, 1368, 1358, 1369, + 1358, 1359, 1369, 1369, 1359, 1370, + 1359, 1360, 1370, 1370, 1360, 1371, + 1360, 1361, 1371, 1371, 1361, 1372, + 1361, 1362, 1372, 1372, 1362, 1373, + 1362, 1363, 1373, 1373, 1363, 1374, + 1364, 1365, 1375, 1375, 1365, 1376, + 1365, 1366, 1376, 1376, 1366, 1377, + 1366, 1367, 1377, 1377, 1367, 1378, + 1367, 1368, 1378, 1378, 1368, 1379, + 1368, 1369, 1379, 1379, 1369, 1380, + 1369, 1370, 1380, 1380, 1370, 1381, + 1370, 1371, 1381, 1381, 1371, 1382, + 1371, 1372, 1382, 1382, 1372, 1383, + 1372, 1373, 1383, 1383, 1373, 1384, + 1373, 1374, 1384, 1384, 1374, 1385, + 1375, 1376, 1386, 1386, 1376, 1387, + 1376, 1377, 1387, 1387, 1377, 1388, + 1377, 1378, 1388, 1388, 1378, 1389, + 1378, 1379, 1389, 1389, 1379, 1390, + 1379, 1380, 1390, 1390, 1380, 1391, + 1380, 1381, 1391, 1391, 1381, 1392, + 1381, 1382, 1392, 1392, 1382, 1393, + 1382, 1383, 1393, 1393, 1383, 1394, + 1383, 1384, 1394, 1394, 1384, 1395, + 1384, 1385, 1395, 1395, 1385, 1396, + 1386, 1387, 1397, 1397, 1387, 1398, + 1387, 1388, 1398, 1398, 1388, 1399, + 1388, 1389, 1399, 1399, 1389, 1400, + 1389, 1390, 1400, 1400, 1390, 1401, + 1390, 1391, 1401, 1401, 1391, 1402, + 1391, 1392, 1402, 1402, 1392, 1403, + 1392, 1393, 1403, 1403, 1393, 1404, + 1393, 1394, 1404, 1404, 1394, 1405, + 1394, 1395, 1405, 1405, 1395, 1406, + 1395, 1396, 1406, 1406, 1396, 1407, + 1397, 1398, 1408, 1408, 1398, 1409, + 1398, 1399, 1409, 1409, 1399, 1410, + 1399, 1400, 1410, 1410, 1400, 1411, + 1400, 1401, 1411, 1411, 1401, 1412, + 1401, 1402, 1412, 1412, 1402, 1413, + 1402, 1403, 1413, 1413, 1403, 1414, + 1403, 1404, 1414, 1414, 1404, 1415, + 1404, 1405, 1415, 1415, 1405, 1416, + 1405, 1406, 1416, 1416, 1406, 1417, + 1406, 1407, 1417, 1417, 1407, 1418, + 1408, 1409, 1419, 1419, 1409, 1420, + 1409, 1410, 1420, 1420, 1410, 1421, + 1410, 1411, 1421, 1421, 1411, 1422, + 1411, 1412, 1422, 1422, 1412, 1423, + 1412, 1413, 1423, 1423, 1413, 1424, + 1413, 1414, 1424, 1424, 1414, 1425, + 1414, 1415, 1425, 1425, 1415, 1426, + 1415, 1416, 1426, 1426, 1416, 1427, + 1416, 1417, 1427, 1427, 1417, 1428, + 1417, 1418, 1428, 1428, 1418, 1429, + 1419, 1420, 1430, 1430, 1420, 1431, + 1420, 1421, 1431, 1431, 1421, 1432, + 1421, 1422, 1432, 1432, 1422, 1433, + 1422, 1423, 1433, 1433, 1423, 1434, + 1423, 1424, 1434, 1434, 1424, 1435, + 1424, 1425, 1435, 1435, 1425, 1436, + 1425, 1426, 1436, 1436, 1426, 1437, + 1426, 1427, 1437, 1437, 1427, 1438, + 1427, 1428, 1438, 1438, 1428, 1439, + 1428, 1429, 1439, 1439, 1429, 1440, + 1430, 1431, 1441, 1441, 1431, 1442, + 1431, 1432, 1442, 1442, 1432, 1443, + 1432, 1433, 1443, 1443, 1433, 1444, + 1433, 1434, 1444, 1444, 1434, 1445, + 1434, 1435, 1445, 1445, 1435, 1446, + 1435, 1436, 1446, 1446, 1436, 1447, + 1436, 1437, 1447, 1447, 1437, 1448, + 1437, 1438, 1448, 1448, 1438, 1449, + 1438, 1439, 1449, 1449, 1439, 1450, + 1439, 1440, 1450, 1450, 1440, 1451, + 1452, 1453, 1463, 1463, 1453, 1464, + 1453, 1454, 1464, 1464, 1454, 1465, + 1454, 1455, 1465, 1465, 1455, 1466, + 1455, 1456, 1466, 1466, 1456, 1467, + 1456, 1457, 1467, 1467, 1457, 1468, + 1457, 1458, 1468, 1468, 1458, 1469, + 1458, 1459, 1469, 1469, 1459, 1470, + 1459, 1460, 1470, 1470, 1460, 1471, + 1460, 1461, 1471, 1471, 1461, 1472, + 1461, 1462, 1472, 1472, 1462, 1473, + 1463, 1464, 1474, 1474, 1464, 1475, + 1464, 1465, 1475, 1475, 1465, 1476, + 1465, 1466, 1476, 1476, 1466, 1477, + 1466, 1467, 1477, 1477, 1467, 1478, + 1467, 1468, 1478, 1478, 1468, 1479, + 1468, 1469, 1479, 1479, 1469, 1480, + 1469, 1470, 1480, 1480, 1470, 1481, + 1470, 1471, 1481, 1481, 1471, 1482, + 1471, 1472, 1482, 1482, 1472, 1483, + 1472, 1473, 1483, 1483, 1473, 1484, + 1474, 1475, 1485, 1485, 1475, 1486, + 1475, 1476, 1486, 1486, 1476, 1487, + 1476, 1477, 1487, 1487, 1477, 1488, + 1477, 1478, 1488, 1488, 1478, 1489, + 1478, 1479, 1489, 1489, 1479, 1490, + 1479, 1480, 1490, 1490, 1480, 1491, + 1480, 1481, 1491, 1491, 1481, 1492, + 1481, 1482, 1492, 1492, 1482, 1493, + 1482, 1483, 1493, 1493, 1483, 1494, + 1483, 1484, 1494, 1494, 1484, 1495, + 1485, 1486, 1496, 1496, 1486, 1497, + 1486, 1487, 1497, 1497, 1487, 1498, + 1487, 1488, 1498, 1498, 1488, 1499, + 1488, 1489, 1499, 1499, 1489, 1500, + 1489, 1490, 1500, 1500, 1490, 1501, + 1490, 1491, 1501, 1501, 1491, 1502, + 1491, 1492, 1502, 1502, 1492, 1503, + 1492, 1493, 1503, 1503, 1493, 1504, + 1493, 1494, 1504, 1504, 1494, 1505, + 1494, 1495, 1505, 1505, 1495, 1506, + 1496, 1497, 1507, 1507, 1497, 1508, + 1497, 1498, 1508, 1508, 1498, 1509, + 1498, 1499, 1509, 1509, 1499, 1510, + 1499, 1500, 1510, 1510, 1500, 1511, + 1500, 1501, 1511, 1511, 1501, 1512, + 1501, 1502, 1512, 1512, 1502, 1513, + 1502, 1503, 1513, 1513, 1503, 1514, + 1503, 1504, 1514, 1514, 1504, 1515, + 1504, 1505, 1515, 1515, 1505, 1516, + 1505, 1506, 1516, 1516, 1506, 1517, + 1507, 1508, 1518, 1518, 1508, 1519, + 1508, 1509, 1519, 1519, 1509, 1520, + 1509, 1510, 1520, 1520, 1510, 1521, + 1510, 1511, 1521, 1521, 1511, 1522, + 1511, 1512, 1522, 1522, 1512, 1523, + 1512, 1513, 1523, 1523, 1513, 1524, + 1513, 1514, 1524, 1524, 1514, 1525, + 1514, 1515, 1525, 1525, 1515, 1526, + 1515, 1516, 1526, 1526, 1516, 1527, + 1516, 1517, 1527, 1527, 1517, 1528, + 1518, 1519, 1529, 1529, 1519, 1530, + 1519, 1520, 1530, 1530, 1520, 1531, + 1520, 1521, 1531, 1531, 1521, 1532, + 1521, 1522, 1532, 1532, 1522, 1533, + 1522, 1523, 1533, 1533, 1523, 1534, + 1523, 1524, 1534, 1534, 1524, 1535, + 1524, 1525, 1535, 1535, 1525, 1536, + 1525, 1526, 1536, 1536, 1526, 1537, + 1526, 1527, 1537, 1537, 1527, 1538, + 1527, 1528, 1538, 1538, 1528, 1539, + 1529, 1530, 1540, 1540, 1530, 1541, + 1530, 1531, 1541, 1541, 1531, 1542, + 1531, 1532, 1542, 1542, 1532, 1543, + 1532, 1533, 1543, 1543, 1533, 1544, + 1533, 1534, 1544, 1544, 1534, 1545, + 1534, 1535, 1545, 1545, 1535, 1546, + 1535, 1536, 1546, 1546, 1536, 1547, + 1536, 1537, 1547, 1547, 1537, 1548, + 1537, 1538, 1548, 1548, 1538, 1549, + 1538, 1539, 1549, 1549, 1539, 1550, + 1540, 1541, 1551, 1551, 1541, 1552, + 1541, 1542, 1552, 1552, 1542, 1553, + 1542, 1543, 1553, 1553, 1543, 1554, + 1543, 1544, 1554, 1554, 1544, 1555, + 1544, 1545, 1555, 1555, 1545, 1556, + 1545, 1546, 1556, 1556, 1546, 1557, + 1546, 1547, 1557, 1557, 1547, 1558, + 1547, 1548, 1558, 1558, 1548, 1559, + 1548, 1549, 1559, 1559, 1549, 1560, + 1549, 1550, 1560, 1560, 1550, 1561, + 1551, 1552, 1562, 1562, 1552, 1563, + 1552, 1553, 1563, 1563, 1553, 1564, + 1553, 1554, 1564, 1564, 1554, 1565, + 1554, 1555, 1565, 1565, 1555, 1566, + 1555, 1556, 1566, 1566, 1556, 1567, + 1556, 1557, 1567, 1567, 1557, 1568, + 1557, 1558, 1568, 1568, 1558, 1569, + 1558, 1559, 1569, 1569, 1559, 1570, + 1559, 1560, 1570, 1570, 1560, 1571, + 1560, 1561, 1571, 1571, 1561, 1572, + 1573, 1574, 1584, 1584, 1574, 1585, + 1574, 1575, 1585, 1585, 1575, 1586, + 1575, 1576, 1586, 1586, 1576, 1587, + 1576, 1577, 1587, 1587, 1577, 1588, + 1577, 1578, 1588, 1588, 1578, 1589, + 1578, 1579, 1589, 1589, 1579, 1590, + 1579, 1580, 1590, 1590, 1580, 1591, + 1580, 1581, 1591, 1591, 1581, 1592, + 1581, 1582, 1592, 1592, 1582, 1593, + 1582, 1583, 1593, 1593, 1583, 1594, + 1584, 1585, 1595, 1595, 1585, 1596, + 1585, 1586, 1596, 1596, 1586, 1597, + 1586, 1587, 1597, 1597, 1587, 1598, + 1587, 1588, 1598, 1598, 1588, 1599, + 1588, 1589, 1599, 1599, 1589, 1600, + 1589, 1590, 1600, 1600, 1590, 1601, + 1590, 1591, 1601, 1601, 1591, 1602, + 1591, 1592, 1602, 1602, 1592, 1603, + 1592, 1593, 1603, 1603, 1593, 1604, + 1593, 1594, 1604, 1604, 1594, 1605, + 1595, 1596, 1606, 1606, 1596, 1607, + 1596, 1597, 1607, 1607, 1597, 1608, + 1597, 1598, 1608, 1608, 1598, 1609, + 1598, 1599, 1609, 1609, 1599, 1610, + 1599, 1600, 1610, 1610, 1600, 1611, + 1600, 1601, 1611, 1611, 1601, 1612, + 1601, 1602, 1612, 1612, 1602, 1613, + 1602, 1603, 1613, 1613, 1603, 1614, + 1603, 1604, 1614, 1614, 1604, 1615, + 1604, 1605, 1615, 1615, 1605, 1616, + 1606, 1607, 1617, 1617, 1607, 1618, + 1607, 1608, 1618, 1618, 1608, 1619, + 1608, 1609, 1619, 1619, 1609, 1620, + 1609, 1610, 1620, 1620, 1610, 1621, + 1610, 1611, 1621, 1621, 1611, 1622, + 1611, 1612, 1622, 1622, 1612, 1623, + 1612, 1613, 1623, 1623, 1613, 1624, + 1613, 1614, 1624, 1624, 1614, 1625, + 1614, 1615, 1625, 1625, 1615, 1626, + 1615, 1616, 1626, 1626, 1616, 1627, + 1617, 1618, 1628, 1628, 1618, 1629, + 1618, 1619, 1629, 1629, 1619, 1630, + 1619, 1620, 1630, 1630, 1620, 1631, + 1620, 1621, 1631, 1631, 1621, 1632, + 1621, 1622, 1632, 1632, 1622, 1633, + 1622, 1623, 1633, 1633, 1623, 1634, + 1623, 1624, 1634, 1634, 1624, 1635, + 1624, 1625, 1635, 1635, 1625, 1636, + 1625, 1626, 1636, 1636, 1626, 1637, + 1626, 1627, 1637, 1637, 1627, 1638, + 1628, 1629, 1639, 1639, 1629, 1640, + 1629, 1630, 1640, 1640, 1630, 1641, + 1630, 1631, 1641, 1641, 1631, 1642, + 1631, 1632, 1642, 1642, 1632, 1643, + 1632, 1633, 1643, 1643, 1633, 1644, + 1633, 1634, 1644, 1644, 1634, 1645, + 1634, 1635, 1645, 1645, 1635, 1646, + 1635, 1636, 1646, 1646, 1636, 1647, + 1636, 1637, 1647, 1647, 1637, 1648, + 1637, 1638, 1648, 1648, 1638, 1649, + 1639, 1640, 1650, 1650, 1640, 1651, + 1640, 1641, 1651, 1651, 1641, 1652, + 1641, 1642, 1652, 1652, 1642, 1653, + 1642, 1643, 1653, 1653, 1643, 1654, + 1643, 1644, 1654, 1654, 1644, 1655, + 1644, 1645, 1655, 1655, 1645, 1656, + 1645, 1646, 1656, 1656, 1646, 1657, + 1646, 1647, 1657, 1657, 1647, 1658, + 1647, 1648, 1658, 1658, 1648, 1659, + 1648, 1649, 1659, 1659, 1649, 1660, + 1650, 1651, 1661, 1661, 1651, 1662, + 1651, 1652, 1662, 1662, 1652, 1663, + 1652, 1653, 1663, 1663, 1653, 1664, + 1653, 1654, 1664, 1664, 1654, 1665, + 1654, 1655, 1665, 1665, 1655, 1666, + 1655, 1656, 1666, 1666, 1656, 1667, + 1656, 1657, 1667, 1667, 1657, 1668, + 1657, 1658, 1668, 1668, 1658, 1669, + 1658, 1659, 1669, 1669, 1659, 1670, + 1659, 1660, 1670, 1670, 1660, 1671, + 1661, 1662, 1672, 1672, 1662, 1673, + 1662, 1663, 1673, 1673, 1663, 1674, + 1663, 1664, 1674, 1674, 1664, 1675, + 1664, 1665, 1675, 1675, 1665, 1676, + 1665, 1666, 1676, 1676, 1666, 1677, + 1666, 1667, 1677, 1677, 1667, 1678, + 1667, 1668, 1678, 1678, 1668, 1679, + 1668, 1669, 1679, 1679, 1669, 1680, + 1669, 1670, 1680, 1680, 1670, 1681, + 1670, 1671, 1681, 1681, 1671, 1682, + 1672, 1673, 1683, 1683, 1673, 1684, + 1673, 1674, 1684, 1684, 1674, 1685, + 1674, 1675, 1685, 1685, 1675, 1686, + 1675, 1676, 1686, 1686, 1676, 1687, + 1676, 1677, 1687, 1687, 1677, 1688, + 1677, 1678, 1688, 1688, 1678, 1689, + 1678, 1679, 1689, 1689, 1679, 1690, + 1679, 1680, 1690, 1690, 1680, 1691, + 1680, 1681, 1691, 1691, 1681, 1692, + 1681, 1682, 1692, 1692, 1682, 1693, + 1694, 1695, 1705, 1705, 1695, 1706, + 1695, 1696, 1706, 1706, 1696, 1707, + 1696, 1697, 1707, 1707, 1697, 1708, + 1697, 1698, 1708, 1708, 1698, 1709, + 1698, 1699, 1709, 1709, 1699, 1710, + 1699, 1700, 1710, 1710, 1700, 1711, + 1700, 1701, 1711, 1711, 1701, 1712, + 1701, 1702, 1712, 1712, 1702, 1713, + 1702, 1703, 1713, 1713, 1703, 1714, + 1703, 1704, 1714, 1714, 1704, 1715, + 1705, 1706, 1716, 1716, 1706, 1717, + 1706, 1707, 1717, 1717, 1707, 1718, + 1707, 1708, 1718, 1718, 1708, 1719, + 1708, 1709, 1719, 1719, 1709, 1720, + 1709, 1710, 1720, 1720, 1710, 1721, + 1710, 1711, 1721, 1721, 1711, 1722, + 1711, 1712, 1722, 1722, 1712, 1723, + 1712, 1713, 1723, 1723, 1713, 1724, + 1713, 1714, 1724, 1724, 1714, 1725, + 1714, 1715, 1725, 1725, 1715, 1726, + 1716, 1717, 1727, 1727, 1717, 1728, + 1717, 1718, 1728, 1728, 1718, 1729, + 1718, 1719, 1729, 1729, 1719, 1730, + 1719, 1720, 1730, 1730, 1720, 1731, + 1720, 1721, 1731, 1731, 1721, 1732, + 1721, 1722, 1732, 1732, 1722, 1733, + 1722, 1723, 1733, 1733, 1723, 1734, + 1723, 1724, 1734, 1734, 1724, 1735, + 1724, 1725, 1735, 1735, 1725, 1736, + 1725, 1726, 1736, 1736, 1726, 1737, + 1727, 1728, 1738, 1738, 1728, 1739, + 1728, 1729, 1739, 1739, 1729, 1740, + 1729, 1730, 1740, 1740, 1730, 1741, + 1730, 1731, 1741, 1741, 1731, 1742, + 1731, 1732, 1742, 1742, 1732, 1743, + 1732, 1733, 1743, 1743, 1733, 1744, + 1733, 1734, 1744, 1744, 1734, 1745, + 1734, 1735, 1745, 1745, 1735, 1746, + 1735, 1736, 1746, 1746, 1736, 1747, + 1736, 1737, 1747, 1747, 1737, 1748, + 1738, 1739, 1749, 1749, 1739, 1750, + 1739, 1740, 1750, 1750, 1740, 1751, + 1740, 1741, 1751, 1751, 1741, 1752, + 1741, 1742, 1752, 1752, 1742, 1753, + 1742, 1743, 1753, 1753, 1743, 1754, + 1743, 1744, 1754, 1754, 1744, 1755, + 1744, 1745, 1755, 1755, 1745, 1756, + 1745, 1746, 1756, 1756, 1746, 1757, + 1746, 1747, 1757, 1757, 1747, 1758, + 1747, 1748, 1758, 1758, 1748, 1759, + 1749, 1750, 1760, 1760, 1750, 1761, + 1750, 1751, 1761, 1761, 1751, 1762, + 1751, 1752, 1762, 1762, 1752, 1763, + 1752, 1753, 1763, 1763, 1753, 1764, + 1753, 1754, 1764, 1764, 1754, 1765, + 1754, 1755, 1765, 1765, 1755, 1766, + 1755, 1756, 1766, 1766, 1756, 1767, + 1756, 1757, 1767, 1767, 1757, 1768, + 1757, 1758, 1768, 1768, 1758, 1769, + 1758, 1759, 1769, 1769, 1759, 1770, + 1760, 1761, 1771, 1771, 1761, 1772, + 1761, 1762, 1772, 1772, 1762, 1773, + 1762, 1763, 1773, 1773, 1763, 1774, + 1763, 1764, 1774, 1774, 1764, 1775, + 1764, 1765, 1775, 1775, 1765, 1776, + 1765, 1766, 1776, 1776, 1766, 1777, + 1766, 1767, 1777, 1777, 1767, 1778, + 1767, 1768, 1778, 1778, 1768, 1779, + 1768, 1769, 1779, 1779, 1769, 1780, + 1769, 1770, 1780, 1780, 1770, 1781, + 1771, 1772, 1782, 1782, 1772, 1783, + 1772, 1773, 1783, 1783, 1773, 1784, + 1773, 1774, 1784, 1784, 1774, 1785, + 1774, 1775, 1785, 1785, 1775, 1786, + 1775, 1776, 1786, 1786, 1776, 1787, + 1776, 1777, 1787, 1787, 1777, 1788, + 1777, 1778, 1788, 1788, 1778, 1789, + 1778, 1779, 1789, 1789, 1779, 1790, + 1779, 1780, 1790, 1790, 1780, 1791, + 1780, 1781, 1791, 1791, 1781, 1792, + 1782, 1783, 1793, 1793, 1783, 1794, + 1783, 1784, 1794, 1794, 1784, 1795, + 1784, 1785, 1795, 1795, 1785, 1796, + 1785, 1786, 1796, 1796, 1786, 1797, + 1786, 1787, 1797, 1797, 1787, 1798, + 1787, 1788, 1798, 1798, 1788, 1799, + 1788, 1789, 1799, 1799, 1789, 1800, + 1789, 1790, 1800, 1800, 1790, 1801, + 1790, 1791, 1801, 1801, 1791, 1802, + 1791, 1792, 1802, 1802, 1792, 1803, + 1793, 1794, 1804, 1804, 1794, 1805, + 1794, 1795, 1805, 1805, 1795, 1806, + 1795, 1796, 1806, 1806, 1796, 1807, + 1796, 1797, 1807, 1807, 1797, 1808, + 1797, 1798, 1808, 1808, 1798, 1809, + 1798, 1799, 1809, 1809, 1799, 1810, + 1799, 1800, 1810, 1810, 1800, 1811, + 1800, 1801, 1811, 1811, 1801, 1812, + 1801, 1802, 1812, 1812, 1802, 1813, + 1802, 1803, 1813, 1813, 1803, 1814, + 1815, 1816, 1826, 1826, 1816, 1827, + 1816, 1817, 1827, 1827, 1817, 1828, + 1817, 1818, 1828, 1828, 1818, 1829, + 1818, 1819, 1829, 1829, 1819, 1830, + 1819, 1820, 1830, 1830, 1820, 1831, + 1820, 1821, 1831, 1831, 1821, 1832, + 1821, 1822, 1832, 1832, 1822, 1833, + 1822, 1823, 1833, 1833, 1823, 1834, + 1823, 1824, 1834, 1834, 1824, 1835, + 1824, 1825, 1835, 1835, 1825, 1836, + 1826, 1827, 1837, 1837, 1827, 1838, + 1827, 1828, 1838, 1838, 1828, 1839, + 1828, 1829, 1839, 1839, 1829, 1840, + 1829, 1830, 1840, 1840, 1830, 1841, + 1830, 1831, 1841, 1841, 1831, 1842, + 1831, 1832, 1842, 1842, 1832, 1843, + 1832, 1833, 1843, 1843, 1833, 1844, + 1833, 1834, 1844, 1844, 1834, 1845, + 1834, 1835, 1845, 1845, 1835, 1846, + 1835, 1836, 1846, 1846, 1836, 1847, + 1837, 1838, 1848, 1848, 1838, 1849, + 1838, 1839, 1849, 1849, 1839, 1850, + 1839, 1840, 1850, 1850, 1840, 1851, + 1840, 1841, 1851, 1851, 1841, 1852, + 1841, 1842, 1852, 1852, 1842, 1853, + 1842, 1843, 1853, 1853, 1843, 1854, + 1843, 1844, 1854, 1854, 1844, 1855, + 1844, 1845, 1855, 1855, 1845, 1856, + 1845, 1846, 1856, 1856, 1846, 1857, + 1846, 1847, 1857, 1857, 1847, 1858, + 1848, 1849, 1859, 1859, 1849, 1860, + 1849, 1850, 1860, 1860, 1850, 1861, + 1850, 1851, 1861, 1861, 1851, 1862, + 1851, 1852, 1862, 1862, 1852, 1863, + 1852, 1853, 1863, 1863, 1853, 1864, + 1853, 1854, 1864, 1864, 1854, 1865, + 1854, 1855, 1865, 1865, 1855, 1866, + 1855, 1856, 1866, 1866, 1856, 1867, + 1856, 1857, 1867, 1867, 1857, 1868, + 1857, 1858, 1868, 1868, 1858, 1869, + 1859, 1860, 1870, 1870, 1860, 1871, + 1860, 1861, 1871, 1871, 1861, 1872, + 1861, 1862, 1872, 1872, 1862, 1873, + 1862, 1863, 1873, 1873, 1863, 1874, + 1863, 1864, 1874, 1874, 1864, 1875, + 1864, 1865, 1875, 1875, 1865, 1876, + 1865, 1866, 1876, 1876, 1866, 1877, + 1866, 1867, 1877, 1877, 1867, 1878, + 1867, 1868, 1878, 1878, 1868, 1879, + 1868, 1869, 1879, 1879, 1869, 1880, + 1870, 1871, 1881, 1881, 1871, 1882, + 1871, 1872, 1882, 1882, 1872, 1883, + 1872, 1873, 1883, 1883, 1873, 1884, + 1873, 1874, 1884, 1884, 1874, 1885, + 1874, 1875, 1885, 1885, 1875, 1886, + 1875, 1876, 1886, 1886, 1876, 1887, + 1876, 1877, 1887, 1887, 1877, 1888, + 1877, 1878, 1888, 1888, 1878, 1889, + 1878, 1879, 1889, 1889, 1879, 1890, + 1879, 1880, 1890, 1890, 1880, 1891, + 1881, 1882, 1892, 1892, 1882, 1893, + 1882, 1883, 1893, 1893, 1883, 1894, + 1883, 1884, 1894, 1894, 1884, 1895, + 1884, 1885, 1895, 1895, 1885, 1896, + 1885, 1886, 1896, 1896, 1886, 1897, + 1886, 1887, 1897, 1897, 1887, 1898, + 1887, 1888, 1898, 1898, 1888, 1899, + 1888, 1889, 1899, 1899, 1889, 1900, + 1889, 1890, 1900, 1900, 1890, 1901, + 1890, 1891, 1901, 1901, 1891, 1902, + 1892, 1893, 1903, 1903, 1893, 1904, + 1893, 1894, 1904, 1904, 1894, 1905, + 1894, 1895, 1905, 1905, 1895, 1906, + 1895, 1896, 1906, 1906, 1896, 1907, + 1896, 1897, 1907, 1907, 1897, 1908, + 1897, 1898, 1908, 1908, 1898, 1909, + 1898, 1899, 1909, 1909, 1899, 1910, + 1899, 1900, 1910, 1910, 1900, 1911, + 1900, 1901, 1911, 1911, 1901, 1912, + 1901, 1902, 1912, 1912, 1902, 1913, + 1903, 1904, 1914, 1914, 1904, 1915, + 1904, 1905, 1915, 1915, 1905, 1916, + 1905, 1906, 1916, 1916, 1906, 1917, + 1906, 1907, 1917, 1917, 1907, 1918, + 1907, 1908, 1918, 1918, 1908, 1919, + 1908, 1909, 1919, 1919, 1909, 1920, + 1909, 1910, 1920, 1920, 1910, 1921, + 1910, 1911, 1921, 1921, 1911, 1922, + 1911, 1912, 1922, 1922, 1912, 1923, + 1912, 1913, 1923, 1923, 1913, 1924, + 1914, 1915, 1925, 1925, 1915, 1926, + 1915, 1916, 1926, 1926, 1916, 1927, + 1916, 1917, 1927, 1927, 1917, 1928, + 1917, 1918, 1928, 1928, 1918, 1929, + 1918, 1919, 1929, 1929, 1919, 1930, + 1919, 1920, 1930, 1930, 1920, 1931, + 1920, 1921, 1931, 1931, 1921, 1932, + 1921, 1922, 1932, 1932, 1922, 1933, + 1922, 1923, 1933, 1933, 1923, 1934, + 1923, 1924, 1934, 1934, 1924, 1935, + 1936, 1937, 1947, 1947, 1937, 1948, + 1937, 1938, 1948, 1948, 1938, 1949, + 1938, 1939, 1949, 1949, 1939, 1950, + 1939, 1940, 1950, 1950, 1940, 1951, + 1940, 1941, 1951, 1951, 1941, 1952, + 1941, 1942, 1952, 1952, 1942, 1953, + 1942, 1943, 1953, 1953, 1943, 1954, + 1943, 1944, 1954, 1954, 1944, 1955, + 1944, 1945, 1955, 1955, 1945, 1956, + 1945, 1946, 1956, 1956, 1946, 1957, + 1947, 1948, 1958, 1958, 1948, 1959, + 1948, 1949, 1959, 1959, 1949, 1960, + 1949, 1950, 1960, 1960, 1950, 1961, + 1950, 1951, 1961, 1961, 1951, 1962, + 1951, 1952, 1962, 1962, 1952, 1963, + 1952, 1953, 1963, 1963, 1953, 1964, + 1953, 1954, 1964, 1964, 1954, 1965, + 1954, 1955, 1965, 1965, 1955, 1966, + 1955, 1956, 1966, 1966, 1956, 1967, + 1956, 1957, 1967, 1967, 1957, 1968, + 1958, 1959, 1969, 1969, 1959, 1970, + 1959, 1960, 1970, 1970, 1960, 1971, + 1960, 1961, 1971, 1971, 1961, 1972, + 1961, 1962, 1972, 1972, 1962, 1973, + 1962, 1963, 1973, 1973, 1963, 1974, + 1963, 1964, 1974, 1974, 1964, 1975, + 1964, 1965, 1975, 1975, 1965, 1976, + 1965, 1966, 1976, 1976, 1966, 1977, + 1966, 1967, 1977, 1977, 1967, 1978, + 1967, 1968, 1978, 1978, 1968, 1979, + 1969, 1970, 1980, 1980, 1970, 1981, + 1970, 1971, 1981, 1981, 1971, 1982, + 1971, 1972, 1982, 1982, 1972, 1983, + 1972, 1973, 1983, 1983, 1973, 1984, + 1973, 1974, 1984, 1984, 1974, 1985, + 1974, 1975, 1985, 1985, 1975, 1986, + 1975, 1976, 1986, 1986, 1976, 1987, + 1976, 1977, 1987, 1987, 1977, 1988, + 1977, 1978, 1988, 1988, 1978, 1989, + 1978, 1979, 1989, 1989, 1979, 1990, + 1980, 1981, 1991, 1991, 1981, 1992, + 1981, 1982, 1992, 1992, 1982, 1993, + 1982, 1983, 1993, 1993, 1983, 1994, + 1983, 1984, 1994, 1994, 1984, 1995, + 1984, 1985, 1995, 1995, 1985, 1996, + 1985, 1986, 1996, 1996, 1986, 1997, + 1986, 1987, 1997, 1997, 1987, 1998, + 1987, 1988, 1998, 1998, 1988, 1999, + 1988, 1989, 1999, 1999, 1989, 2000, + 1989, 1990, 2000, 2000, 1990, 2001, + 1991, 1992, 2002, 2002, 1992, 2003, + 1992, 1993, 2003, 2003, 1993, 2004, + 1993, 1994, 2004, 2004, 1994, 2005, + 1994, 1995, 2005, 2005, 1995, 2006, + 1995, 1996, 2006, 2006, 1996, 2007, + 1996, 1997, 2007, 2007, 1997, 2008, + 1997, 1998, 2008, 2008, 1998, 2009, + 1998, 1999, 2009, 2009, 1999, 2010, + 1999, 2000, 2010, 2010, 2000, 2011, + 2000, 2001, 2011, 2011, 2001, 2012, + 2002, 2003, 2013, 2013, 2003, 2014, + 2003, 2004, 2014, 2014, 2004, 2015, + 2004, 2005, 2015, 2015, 2005, 2016, + 2005, 2006, 2016, 2016, 2006, 2017, + 2006, 2007, 2017, 2017, 2007, 2018, + 2007, 2008, 2018, 2018, 2008, 2019, + 2008, 2009, 2019, 2019, 2009, 2020, + 2009, 2010, 2020, 2020, 2010, 2021, + 2010, 2011, 2021, 2021, 2011, 2022, + 2011, 2012, 2022, 2022, 2012, 2023, + 2013, 2014, 2024, 2024, 2014, 2025, + 2014, 2015, 2025, 2025, 2015, 2026, + 2015, 2016, 2026, 2026, 2016, 2027, + 2016, 2017, 2027, 2027, 2017, 2028, + 2017, 2018, 2028, 2028, 2018, 2029, + 2018, 2019, 2029, 2029, 2019, 2030, + 2019, 2020, 2030, 2030, 2020, 2031, + 2020, 2021, 2031, 2031, 2021, 2032, + 2021, 2022, 2032, 2032, 2022, 2033, + 2022, 2023, 2033, 2033, 2023, 2034, + 2024, 2025, 2035, 2035, 2025, 2036, + 2025, 2026, 2036, 2036, 2026, 2037, + 2026, 2027, 2037, 2037, 2027, 2038, + 2027, 2028, 2038, 2038, 2028, 2039, + 2028, 2029, 2039, 2039, 2029, 2040, + 2029, 2030, 2040, 2040, 2030, 2041, + 2030, 2031, 2041, 2041, 2031, 2042, + 2031, 2032, 2042, 2042, 2032, 2043, + 2032, 2033, 2043, 2043, 2033, 2044, + 2033, 2034, 2044, 2044, 2034, 2045, + 2035, 2036, 2046, 2046, 2036, 2047, + 2036, 2037, 2047, 2047, 2037, 2048, + 2037, 2038, 2048, 2048, 2038, 2049, + 2038, 2039, 2049, 2049, 2039, 2050, + 2039, 2040, 2050, 2050, 2040, 2051, + 2040, 2041, 2051, 2051, 2041, 2052, + 2041, 2042, 2052, 2052, 2042, 2053, + 2042, 2043, 2053, 2053, 2043, 2054, + 2043, 2044, 2054, 2054, 2044, 2055, + 2044, 2045, 2055, 2055, 2045, 2056, + 2057, 2058, 2068, 2068, 2058, 2069, + 2058, 2059, 2069, 2069, 2059, 2070, + 2059, 2060, 2070, 2070, 2060, 2071, + 2060, 2061, 2071, 2071, 2061, 2072, + 2061, 2062, 2072, 2072, 2062, 2073, + 2062, 2063, 2073, 2073, 2063, 2074, + 2063, 2064, 2074, 2074, 2064, 2075, + 2064, 2065, 2075, 2075, 2065, 2076, + 2065, 2066, 2076, 2076, 2066, 2077, + 2066, 2067, 2077, 2077, 2067, 2078, + 2068, 2069, 2079, 2079, 2069, 2080, + 2069, 2070, 2080, 2080, 2070, 2081, + 2070, 2071, 2081, 2081, 2071, 2082, + 2071, 2072, 2082, 2082, 2072, 2083, + 2072, 2073, 2083, 2083, 2073, 2084, + 2073, 2074, 2084, 2084, 2074, 2085, + 2074, 2075, 2085, 2085, 2075, 2086, + 2075, 2076, 2086, 2086, 2076, 2087, + 2076, 2077, 2087, 2087, 2077, 2088, + 2077, 2078, 2088, 2088, 2078, 2089, + 2079, 2080, 2090, 2090, 2080, 2091, + 2080, 2081, 2091, 2091, 2081, 2092, + 2081, 2082, 2092, 2092, 2082, 2093, + 2082, 2083, 2093, 2093, 2083, 2094, + 2083, 2084, 2094, 2094, 2084, 2095, + 2084, 2085, 2095, 2095, 2085, 2096, + 2085, 2086, 2096, 2096, 2086, 2097, + 2086, 2087, 2097, 2097, 2087, 2098, + 2087, 2088, 2098, 2098, 2088, 2099, + 2088, 2089, 2099, 2099, 2089, 2100, + 2090, 2091, 2101, 2101, 2091, 2102, + 2091, 2092, 2102, 2102, 2092, 2103, + 2092, 2093, 2103, 2103, 2093, 2104, + 2093, 2094, 2104, 2104, 2094, 2105, + 2094, 2095, 2105, 2105, 2095, 2106, + 2095, 2096, 2106, 2106, 2096, 2107, + 2096, 2097, 2107, 2107, 2097, 2108, + 2097, 2098, 2108, 2108, 2098, 2109, + 2098, 2099, 2109, 2109, 2099, 2110, + 2099, 2100, 2110, 2110, 2100, 2111, + 2101, 2102, 2112, 2112, 2102, 2113, + 2102, 2103, 2113, 2113, 2103, 2114, + 2103, 2104, 2114, 2114, 2104, 2115, + 2104, 2105, 2115, 2115, 2105, 2116, + 2105, 2106, 2116, 2116, 2106, 2117, + 2106, 2107, 2117, 2117, 2107, 2118, + 2107, 2108, 2118, 2118, 2108, 2119, + 2108, 2109, 2119, 2119, 2109, 2120, + 2109, 2110, 2120, 2120, 2110, 2121, + 2110, 2111, 2121, 2121, 2111, 2122, + 2112, 2113, 2123, 2123, 2113, 2124, + 2113, 2114, 2124, 2124, 2114, 2125, + 2114, 2115, 2125, 2125, 2115, 2126, + 2115, 2116, 2126, 2126, 2116, 2127, + 2116, 2117, 2127, 2127, 2117, 2128, + 2117, 2118, 2128, 2128, 2118, 2129, + 2118, 2119, 2129, 2129, 2119, 2130, + 2119, 2120, 2130, 2130, 2120, 2131, + 2120, 2121, 2131, 2131, 2121, 2132, + 2121, 2122, 2132, 2132, 2122, 2133, + 2123, 2124, 2134, 2134, 2124, 2135, + 2124, 2125, 2135, 2135, 2125, 2136, + 2125, 2126, 2136, 2136, 2126, 2137, + 2126, 2127, 2137, 2137, 2127, 2138, + 2127, 2128, 2138, 2138, 2128, 2139, + 2128, 2129, 2139, 2139, 2129, 2140, + 2129, 2130, 2140, 2140, 2130, 2141, + 2130, 2131, 2141, 2141, 2131, 2142, + 2131, 2132, 2142, 2142, 2132, 2143, + 2132, 2133, 2143, 2143, 2133, 2144, + 2134, 2135, 2145, 2145, 2135, 2146, + 2135, 2136, 2146, 2146, 2136, 2147, + 2136, 2137, 2147, 2147, 2137, 2148, + 2137, 2138, 2148, 2148, 2138, 2149, + 2138, 2139, 2149, 2149, 2139, 2150, + 2139, 2140, 2150, 2150, 2140, 2151, + 2140, 2141, 2151, 2151, 2141, 2152, + 2141, 2142, 2152, 2152, 2142, 2153, + 2142, 2143, 2153, 2153, 2143, 2154, + 2143, 2144, 2154, 2154, 2144, 2155, + 2145, 2146, 2156, 2156, 2146, 2157, + 2146, 2147, 2157, 2157, 2147, 2158, + 2147, 2148, 2158, 2158, 2148, 2159, + 2148, 2149, 2159, 2159, 2149, 2160, + 2149, 2150, 2160, 2160, 2150, 2161, + 2150, 2151, 2161, 2161, 2151, 2162, + 2151, 2152, 2162, 2162, 2152, 2163, + 2152, 2153, 2163, 2163, 2153, 2164, + 2153, 2154, 2164, 2164, 2154, 2165, + 2154, 2155, 2165, 2165, 2155, 2166, + 2156, 2157, 2167, 2167, 2157, 2168, + 2157, 2158, 2168, 2168, 2158, 2169, + 2158, 2159, 2169, 2169, 2159, 2170, + 2159, 2160, 2170, 2170, 2160, 2171, + 2160, 2161, 2171, 2171, 2161, 2172, + 2161, 2162, 2172, 2172, 2162, 2173, + 2162, 2163, 2173, 2173, 2163, 2174, + 2163, 2164, 2174, 2174, 2164, 2175, + 2164, 2165, 2175, 2175, 2165, 2176, + 2165, 2166, 2176, 2176, 2166, 2177, + 2178, 2179, 2189, 2189, 2179, 2190, + 2179, 2180, 2190, 2190, 2180, 2191, + 2180, 2181, 2191, 2191, 2181, 2192, + 2181, 2182, 2192, 2192, 2182, 2193, + 2182, 2183, 2193, 2193, 2183, 2194, + 2183, 2184, 2194, 2194, 2184, 2195, + 2184, 2185, 2195, 2195, 2185, 2196, + 2185, 2186, 2196, 2196, 2186, 2197, + 2186, 2187, 2197, 2197, 2187, 2198, + 2187, 2188, 2198, 2198, 2188, 2199, + 2189, 2190, 2200, 2200, 2190, 2201, + 2190, 2191, 2201, 2201, 2191, 2202, + 2191, 2192, 2202, 2202, 2192, 2203, + 2192, 2193, 2203, 2203, 2193, 2204, + 2193, 2194, 2204, 2204, 2194, 2205, + 2194, 2195, 2205, 2205, 2195, 2206, + 2195, 2196, 2206, 2206, 2196, 2207, + 2196, 2197, 2207, 2207, 2197, 2208, + 2197, 2198, 2208, 2208, 2198, 2209, + 2198, 2199, 2209, 2209, 2199, 2210, + 2200, 2201, 2211, 2211, 2201, 2212, + 2201, 2202, 2212, 2212, 2202, 2213, + 2202, 2203, 2213, 2213, 2203, 2214, + 2203, 2204, 2214, 2214, 2204, 2215, + 2204, 2205, 2215, 2215, 2205, 2216, + 2205, 2206, 2216, 2216, 2206, 2217, + 2206, 2207, 2217, 2217, 2207, 2218, + 2207, 2208, 2218, 2218, 2208, 2219, + 2208, 2209, 2219, 2219, 2209, 2220, + 2209, 2210, 2220, 2220, 2210, 2221, + 2211, 2212, 2222, 2222, 2212, 2223, + 2212, 2213, 2223, 2223, 2213, 2224, + 2213, 2214, 2224, 2224, 2214, 2225, + 2214, 2215, 2225, 2225, 2215, 2226, + 2215, 2216, 2226, 2226, 2216, 2227, + 2216, 2217, 2227, 2227, 2217, 2228, + 2217, 2218, 2228, 2228, 2218, 2229, + 2218, 2219, 2229, 2229, 2219, 2230, + 2219, 2220, 2230, 2230, 2220, 2231, + 2220, 2221, 2231, 2231, 2221, 2232, + 2222, 2223, 2233, 2233, 2223, 2234, + 2223, 2224, 2234, 2234, 2224, 2235, + 2224, 2225, 2235, 2235, 2225, 2236, + 2225, 2226, 2236, 2236, 2226, 2237, + 2226, 2227, 2237, 2237, 2227, 2238, + 2227, 2228, 2238, 2238, 2228, 2239, + 2228, 2229, 2239, 2239, 2229, 2240, + 2229, 2230, 2240, 2240, 2230, 2241, + 2230, 2231, 2241, 2241, 2231, 2242, + 2231, 2232, 2242, 2242, 2232, 2243, + 2233, 2234, 2244, 2244, 2234, 2245, + 2234, 2235, 2245, 2245, 2235, 2246, + 2235, 2236, 2246, 2246, 2236, 2247, + 2236, 2237, 2247, 2247, 2237, 2248, + 2237, 2238, 2248, 2248, 2238, 2249, + 2238, 2239, 2249, 2249, 2239, 2250, + 2239, 2240, 2250, 2250, 2240, 2251, + 2240, 2241, 2251, 2251, 2241, 2252, + 2241, 2242, 2252, 2252, 2242, 2253, + 2242, 2243, 2253, 2253, 2243, 2254, + 2244, 2245, 2255, 2255, 2245, 2256, + 2245, 2246, 2256, 2256, 2246, 2257, + 2246, 2247, 2257, 2257, 2247, 2258, + 2247, 2248, 2258, 2258, 2248, 2259, + 2248, 2249, 2259, 2259, 2249, 2260, + 2249, 2250, 2260, 2260, 2250, 2261, + 2250, 2251, 2261, 2261, 2251, 2262, + 2251, 2252, 2262, 2262, 2252, 2263, + 2252, 2253, 2263, 2263, 2253, 2264, + 2253, 2254, 2264, 2264, 2254, 2265, + 2255, 2256, 2266, 2266, 2256, 2267, + 2256, 2257, 2267, 2267, 2257, 2268, + 2257, 2258, 2268, 2268, 2258, 2269, + 2258, 2259, 2269, 2269, 2259, 2270, + 2259, 2260, 2270, 2270, 2260, 2271, + 2260, 2261, 2271, 2271, 2261, 2272, + 2261, 2262, 2272, 2272, 2262, 2273, + 2262, 2263, 2273, 2273, 2263, 2274, + 2263, 2264, 2274, 2274, 2264, 2275, + 2264, 2265, 2275, 2275, 2265, 2276, + 2266, 2267, 2277, 2277, 2267, 2278, + 2267, 2268, 2278, 2278, 2268, 2279, + 2268, 2269, 2279, 2279, 2269, 2280, + 2269, 2270, 2280, 2280, 2270, 2281, + 2270, 2271, 2281, 2281, 2271, 2282, + 2271, 2272, 2282, 2282, 2272, 2283, + 2272, 2273, 2283, 2283, 2273, 2284, + 2273, 2274, 2284, 2284, 2274, 2285, + 2274, 2275, 2285, 2285, 2275, 2286, + 2275, 2276, 2286, 2286, 2276, 2287, + 2277, 2278, 2288, 2288, 2278, 2289, + 2278, 2279, 2289, 2289, 2279, 2290, + 2279, 2280, 2290, 2290, 2280, 2291, + 2280, 2281, 2291, 2291, 2281, 2292, + 2281, 2282, 2292, 2292, 2282, 2293, + 2282, 2283, 2293, 2293, 2283, 2294, + 2283, 2284, 2294, 2294, 2284, 2295, + 2284, 2285, 2295, 2295, 2285, 2296, + 2285, 2286, 2296, 2296, 2286, 2297, + 2286, 2287, 2297, 2297, 2287, 2298, + 2299, 2300, 2310, 2310, 2300, 2311, + 2300, 2301, 2311, 2311, 2301, 2312, + 2301, 2302, 2312, 2312, 2302, 2313, + 2302, 2303, 2313, 2313, 2303, 2314, + 2303, 2304, 2314, 2314, 2304, 2315, + 2304, 2305, 2315, 2315, 2305, 2316, + 2305, 2306, 2316, 2316, 2306, 2317, + 2306, 2307, 2317, 2317, 2307, 2318, + 2307, 2308, 2318, 2318, 2308, 2319, + 2308, 2309, 2319, 2319, 2309, 2320, + 2310, 2311, 2321, 2321, 2311, 2322, + 2311, 2312, 2322, 2322, 2312, 2323, + 2312, 2313, 2323, 2323, 2313, 2324, + 2313, 2314, 2324, 2324, 2314, 2325, + 2314, 2315, 2325, 2325, 2315, 2326, + 2315, 2316, 2326, 2326, 2316, 2327, + 2316, 2317, 2327, 2327, 2317, 2328, + 2317, 2318, 2328, 2328, 2318, 2329, + 2318, 2319, 2329, 2329, 2319, 2330, + 2319, 2320, 2330, 2330, 2320, 2331, + 2321, 2322, 2332, 2332, 2322, 2333, + 2322, 2323, 2333, 2333, 2323, 2334, + 2323, 2324, 2334, 2334, 2324, 2335, + 2324, 2325, 2335, 2335, 2325, 2336, + 2325, 2326, 2336, 2336, 2326, 2337, + 2326, 2327, 2337, 2337, 2327, 2338, + 2327, 2328, 2338, 2338, 2328, 2339, + 2328, 2329, 2339, 2339, 2329, 2340, + 2329, 2330, 2340, 2340, 2330, 2341, + 2330, 2331, 2341, 2341, 2331, 2342, + 2332, 2333, 2343, 2343, 2333, 2344, + 2333, 2334, 2344, 2344, 2334, 2345, + 2334, 2335, 2345, 2345, 2335, 2346, + 2335, 2336, 2346, 2346, 2336, 2347, + 2336, 2337, 2347, 2347, 2337, 2348, + 2337, 2338, 2348, 2348, 2338, 2349, + 2338, 2339, 2349, 2349, 2339, 2350, + 2339, 2340, 2350, 2350, 2340, 2351, + 2340, 2341, 2351, 2351, 2341, 2352, + 2341, 2342, 2352, 2352, 2342, 2353, + 2343, 2344, 2354, 2354, 2344, 2355, + 2344, 2345, 2355, 2355, 2345, 2356, + 2345, 2346, 2356, 2356, 2346, 2357, + 2346, 2347, 2357, 2357, 2347, 2358, + 2347, 2348, 2358, 2358, 2348, 2359, + 2348, 2349, 2359, 2359, 2349, 2360, + 2349, 2350, 2360, 2360, 2350, 2361, + 2350, 2351, 2361, 2361, 2351, 2362, + 2351, 2352, 2362, 2362, 2352, 2363, + 2352, 2353, 2363, 2363, 2353, 2364, + 2354, 2355, 2365, 2365, 2355, 2366, + 2355, 2356, 2366, 2366, 2356, 2367, + 2356, 2357, 2367, 2367, 2357, 2368, + 2357, 2358, 2368, 2368, 2358, 2369, + 2358, 2359, 2369, 2369, 2359, 2370, + 2359, 2360, 2370, 2370, 2360, 2371, + 2360, 2361, 2371, 2371, 2361, 2372, + 2361, 2362, 2372, 2372, 2362, 2373, + 2362, 2363, 2373, 2373, 2363, 2374, + 2363, 2364, 2374, 2374, 2364, 2375, + 2365, 2366, 2376, 2376, 2366, 2377, + 2366, 2367, 2377, 2377, 2367, 2378, + 2367, 2368, 2378, 2378, 2368, 2379, + 2368, 2369, 2379, 2379, 2369, 2380, + 2369, 2370, 2380, 2380, 2370, 2381, + 2370, 2371, 2381, 2381, 2371, 2382, + 2371, 2372, 2382, 2382, 2372, 2383, + 2372, 2373, 2383, 2383, 2373, 2384, + 2373, 2374, 2384, 2384, 2374, 2385, + 2374, 2375, 2385, 2385, 2375, 2386, + 2376, 2377, 2387, 2387, 2377, 2388, + 2377, 2378, 2388, 2388, 2378, 2389, + 2378, 2379, 2389, 2389, 2379, 2390, + 2379, 2380, 2390, 2390, 2380, 2391, + 2380, 2381, 2391, 2391, 2381, 2392, + 2381, 2382, 2392, 2392, 2382, 2393, + 2382, 2383, 2393, 2393, 2383, 2394, + 2383, 2384, 2394, 2394, 2384, 2395, + 2384, 2385, 2395, 2395, 2385, 2396, + 2385, 2386, 2396, 2396, 2386, 2397, + 2387, 2388, 2398, 2398, 2388, 2399, + 2388, 2389, 2399, 2399, 2389, 2400, + 2389, 2390, 2400, 2400, 2390, 2401, + 2390, 2391, 2401, 2401, 2391, 2402, + 2391, 2392, 2402, 2402, 2392, 2403, + 2392, 2393, 2403, 2403, 2393, 2404, + 2393, 2394, 2404, 2404, 2394, 2405, + 2394, 2395, 2405, 2405, 2395, 2406, + 2395, 2396, 2406, 2406, 2396, 2407, + 2396, 2397, 2407, 2407, 2397, 2408, + 2398, 2399, 2409, 2409, 2399, 2410, + 2399, 2400, 2410, 2410, 2400, 2411, + 2400, 2401, 2411, 2411, 2401, 2412, + 2401, 2402, 2412, 2412, 2402, 2413, + 2402, 2403, 2413, 2413, 2403, 2414, + 2403, 2404, 2414, 2414, 2404, 2415, + 2404, 2405, 2415, 2415, 2405, 2416, + 2405, 2406, 2416, 2416, 2406, 2417, + 2406, 2407, 2417, 2417, 2407, 2418, + 2407, 2408, 2418, 2418, 2408, 2419, + 2420, 2421, 2431, 2431, 2421, 2432, + 2421, 2422, 2432, 2432, 2422, 2433, + 2422, 2423, 2433, 2433, 2423, 2434, + 2423, 2424, 2434, 2434, 2424, 2435, + 2424, 2425, 2435, 2435, 2425, 2436, + 2425, 2426, 2436, 2436, 2426, 2437, + 2426, 2427, 2437, 2437, 2427, 2438, + 2427, 2428, 2438, 2438, 2428, 2439, + 2428, 2429, 2439, 2439, 2429, 2440, + 2429, 2430, 2440, 2440, 2430, 2441, + 2431, 2432, 2442, 2442, 2432, 2443, + 2432, 2433, 2443, 2443, 2433, 2444, + 2433, 2434, 2444, 2444, 2434, 2445, + 2434, 2435, 2445, 2445, 2435, 2446, + 2435, 2436, 2446, 2446, 2436, 2447, + 2436, 2437, 2447, 2447, 2437, 2448, + 2437, 2438, 2448, 2448, 2438, 2449, + 2438, 2439, 2449, 2449, 2439, 2450, + 2439, 2440, 2450, 2450, 2440, 2451, + 2440, 2441, 2451, 2451, 2441, 2452, + 2442, 2443, 2453, 2453, 2443, 2454, + 2443, 2444, 2454, 2454, 2444, 2455, + 2444, 2445, 2455, 2455, 2445, 2456, + 2445, 2446, 2456, 2456, 2446, 2457, + 2446, 2447, 2457, 2457, 2447, 2458, + 2447, 2448, 2458, 2458, 2448, 2459, + 2448, 2449, 2459, 2459, 2449, 2460, + 2449, 2450, 2460, 2460, 2450, 2461, + 2450, 2451, 2461, 2461, 2451, 2462, + 2451, 2452, 2462, 2462, 2452, 2463, + 2453, 2454, 2464, 2464, 2454, 2465, + 2454, 2455, 2465, 2465, 2455, 2466, + 2455, 2456, 2466, 2466, 2456, 2467, + 2456, 2457, 2467, 2467, 2457, 2468, + 2457, 2458, 2468, 2468, 2458, 2469, + 2458, 2459, 2469, 2469, 2459, 2470, + 2459, 2460, 2470, 2470, 2460, 2471, + 2460, 2461, 2471, 2471, 2461, 2472, + 2461, 2462, 2472, 2472, 2462, 2473, + 2462, 2463, 2473, 2473, 2463, 2474, + 2464, 2465, 2475, 2475, 2465, 2476, + 2465, 2466, 2476, 2476, 2466, 2477, + 2466, 2467, 2477, 2477, 2467, 2478, + 2467, 2468, 2478, 2478, 2468, 2479, + 2468, 2469, 2479, 2479, 2469, 2480, + 2469, 2470, 2480, 2480, 2470, 2481, + 2470, 2471, 2481, 2481, 2471, 2482, + 2471, 2472, 2482, 2482, 2472, 2483, + 2472, 2473, 2483, 2483, 2473, 2484, + 2473, 2474, 2484, 2484, 2474, 2485, + 2475, 2476, 2486, 2486, 2476, 2487, + 2476, 2477, 2487, 2487, 2477, 2488, + 2477, 2478, 2488, 2488, 2478, 2489, + 2478, 2479, 2489, 2489, 2479, 2490, + 2479, 2480, 2490, 2490, 2480, 2491, + 2480, 2481, 2491, 2491, 2481, 2492, + 2481, 2482, 2492, 2492, 2482, 2493, + 2482, 2483, 2493, 2493, 2483, 2494, + 2483, 2484, 2494, 2494, 2484, 2495, + 2484, 2485, 2495, 2495, 2485, 2496, + 2486, 2487, 2497, 2497, 2487, 2498, + 2487, 2488, 2498, 2498, 2488, 2499, + 2488, 2489, 2499, 2499, 2489, 2500, + 2489, 2490, 2500, 2500, 2490, 2501, + 2490, 2491, 2501, 2501, 2491, 2502, + 2491, 2492, 2502, 2502, 2492, 2503, + 2492, 2493, 2503, 2503, 2493, 2504, + 2493, 2494, 2504, 2504, 2494, 2505, + 2494, 2495, 2505, 2505, 2495, 2506, + 2495, 2496, 2506, 2506, 2496, 2507, + 2497, 2498, 2508, 2508, 2498, 2509, + 2498, 2499, 2509, 2509, 2499, 2510, + 2499, 2500, 2510, 2510, 2500, 2511, + 2500, 2501, 2511, 2511, 2501, 2512, + 2501, 2502, 2512, 2512, 2502, 2513, + 2502, 2503, 2513, 2513, 2503, 2514, + 2503, 2504, 2514, 2514, 2504, 2515, + 2504, 2505, 2515, 2515, 2505, 2516, + 2505, 2506, 2516, 2516, 2506, 2517, + 2506, 2507, 2517, 2517, 2507, 2518, + 2508, 2509, 2519, 2519, 2509, 2520, + 2509, 2510, 2520, 2520, 2510, 2521, + 2510, 2511, 2521, 2521, 2511, 2522, + 2511, 2512, 2522, 2522, 2512, 2523, + 2512, 2513, 2523, 2523, 2513, 2524, + 2513, 2514, 2524, 2524, 2514, 2525, + 2514, 2515, 2525, 2525, 2515, 2526, + 2515, 2516, 2526, 2526, 2516, 2527, + 2516, 2517, 2527, 2527, 2517, 2528, + 2517, 2518, 2528, 2528, 2518, 2529, + 2519, 2520, 2530, 2530, 2520, 2531, + 2520, 2521, 2531, 2531, 2521, 2532, + 2521, 2522, 2532, 2532, 2522, 2533, + 2522, 2523, 2533, 2533, 2523, 2534, + 2523, 2524, 2534, 2534, 2524, 2535, + 2524, 2525, 2535, 2535, 2525, 2536, + 2525, 2526, 2536, 2536, 2526, 2537, + 2526, 2527, 2537, 2537, 2527, 2538, + 2527, 2528, 2538, 2538, 2528, 2539, + 2528, 2529, 2539, 2539, 2529, 2540, + 2541, 2542, 2552, 2552, 2542, 2553, + 2542, 2543, 2553, 2553, 2543, 2554, + 2543, 2544, 2554, 2554, 2544, 2555, + 2544, 2545, 2555, 2555, 2545, 2556, + 2545, 2546, 2556, 2556, 2546, 2557, + 2546, 2547, 2557, 2557, 2547, 2558, + 2547, 2548, 2558, 2558, 2548, 2559, + 2548, 2549, 2559, 2559, 2549, 2560, + 2549, 2550, 2560, 2560, 2550, 2561, + 2550, 2551, 2561, 2561, 2551, 2562, + 2552, 2553, 2563, 2563, 2553, 2564, + 2553, 2554, 2564, 2564, 2554, 2565, + 2554, 2555, 2565, 2565, 2555, 2566, + 2555, 2556, 2566, 2566, 2556, 2567, + 2556, 2557, 2567, 2567, 2557, 2568, + 2557, 2558, 2568, 2568, 2558, 2569, + 2558, 2559, 2569, 2569, 2559, 2570, + 2559, 2560, 2570, 2570, 2560, 2571, + 2560, 2561, 2571, 2571, 2561, 2572, + 2561, 2562, 2572, 2572, 2562, 2573, + 2563, 2564, 2574, 2574, 2564, 2575, + 2564, 2565, 2575, 2575, 2565, 2576, + 2565, 2566, 2576, 2576, 2566, 2577, + 2566, 2567, 2577, 2577, 2567, 2578, + 2567, 2568, 2578, 2578, 2568, 2579, + 2568, 2569, 2579, 2579, 2569, 2580, + 2569, 2570, 2580, 2580, 2570, 2581, + 2570, 2571, 2581, 2581, 2571, 2582, + 2571, 2572, 2582, 2582, 2572, 2583, + 2572, 2573, 2583, 2583, 2573, 2584, + 2574, 2575, 2585, 2585, 2575, 2586, + 2575, 2576, 2586, 2586, 2576, 2587, + 2576, 2577, 2587, 2587, 2577, 2588, + 2577, 2578, 2588, 2588, 2578, 2589, + 2578, 2579, 2589, 2589, 2579, 2590, + 2579, 2580, 2590, 2590, 2580, 2591, + 2580, 2581, 2591, 2591, 2581, 2592, + 2581, 2582, 2592, 2592, 2582, 2593, + 2582, 2583, 2593, 2593, 2583, 2594, + 2583, 2584, 2594, 2594, 2584, 2595, + 2585, 2586, 2596, 2596, 2586, 2597, + 2586, 2587, 2597, 2597, 2587, 2598, + 2587, 2588, 2598, 2598, 2588, 2599, + 2588, 2589, 2599, 2599, 2589, 2600, + 2589, 2590, 2600, 2600, 2590, 2601, + 2590, 2591, 2601, 2601, 2591, 2602, + 2591, 2592, 2602, 2602, 2592, 2603, + 2592, 2593, 2603, 2603, 2593, 2604, + 2593, 2594, 2604, 2604, 2594, 2605, + 2594, 2595, 2605, 2605, 2595, 2606, + 2596, 2597, 2607, 2607, 2597, 2608, + 2597, 2598, 2608, 2608, 2598, 2609, + 2598, 2599, 2609, 2609, 2599, 2610, + 2599, 2600, 2610, 2610, 2600, 2611, + 2600, 2601, 2611, 2611, 2601, 2612, + 2601, 2602, 2612, 2612, 2602, 2613, + 2602, 2603, 2613, 2613, 2603, 2614, + 2603, 2604, 2614, 2614, 2604, 2615, + 2604, 2605, 2615, 2615, 2605, 2616, + 2605, 2606, 2616, 2616, 2606, 2617, + 2607, 2608, 2618, 2618, 2608, 2619, + 2608, 2609, 2619, 2619, 2609, 2620, + 2609, 2610, 2620, 2620, 2610, 2621, + 2610, 2611, 2621, 2621, 2611, 2622, + 2611, 2612, 2622, 2622, 2612, 2623, + 2612, 2613, 2623, 2623, 2613, 2624, + 2613, 2614, 2624, 2624, 2614, 2625, + 2614, 2615, 2625, 2625, 2615, 2626, + 2615, 2616, 2626, 2626, 2616, 2627, + 2616, 2617, 2627, 2627, 2617, 2628, + 2618, 2619, 2629, 2629, 2619, 2630, + 2619, 2620, 2630, 2630, 2620, 2631, + 2620, 2621, 2631, 2631, 2621, 2632, + 2621, 2622, 2632, 2632, 2622, 2633, + 2622, 2623, 2633, 2633, 2623, 2634, + 2623, 2624, 2634, 2634, 2624, 2635, + 2624, 2625, 2635, 2635, 2625, 2636, + 2625, 2626, 2636, 2636, 2626, 2637, + 2626, 2627, 2637, 2637, 2627, 2638, + 2627, 2628, 2638, 2638, 2628, 2639, + 2629, 2630, 2640, 2640, 2630, 2641, + 2630, 2631, 2641, 2641, 2631, 2642, + 2631, 2632, 2642, 2642, 2632, 2643, + 2632, 2633, 2643, 2643, 2633, 2644, + 2633, 2634, 2644, 2644, 2634, 2645, + 2634, 2635, 2645, 2645, 2635, 2646, + 2635, 2636, 2646, 2646, 2636, 2647, + 2636, 2637, 2647, 2647, 2637, 2648, + 2637, 2638, 2648, 2648, 2638, 2649, + 2638, 2639, 2649, 2649, 2639, 2650, + 2640, 2641, 2651, 2651, 2641, 2652, + 2641, 2642, 2652, 2652, 2642, 2653, + 2642, 2643, 2653, 2653, 2643, 2654, + 2643, 2644, 2654, 2654, 2644, 2655, + 2644, 2645, 2655, 2655, 2645, 2656, + 2645, 2646, 2656, 2656, 2646, 2657, + 2646, 2647, 2657, 2657, 2647, 2658, + 2647, 2648, 2658, 2658, 2648, 2659, + 2648, 2649, 2659, 2659, 2649, 2660, + 2649, 2650, 2660, 2660, 2650, 2661, + 2662, 2663, 2673, 2673, 2663, 2674, + 2663, 2664, 2674, 2674, 2664, 2675, + 2664, 2665, 2675, 2675, 2665, 2676, + 2665, 2666, 2676, 2676, 2666, 2677, + 2666, 2667, 2677, 2677, 2667, 2678, + 2667, 2668, 2678, 2678, 2668, 2679, + 2668, 2669, 2679, 2679, 2669, 2680, + 2669, 2670, 2680, 2680, 2670, 2681, + 2670, 2671, 2681, 2681, 2671, 2682, + 2671, 2672, 2682, 2682, 2672, 2683, + 2673, 2674, 2684, 2684, 2674, 2685, + 2674, 2675, 2685, 2685, 2675, 2686, + 2675, 2676, 2686, 2686, 2676, 2687, + 2676, 2677, 2687, 2687, 2677, 2688, + 2677, 2678, 2688, 2688, 2678, 2689, + 2678, 2679, 2689, 2689, 2679, 2690, + 2679, 2680, 2690, 2690, 2680, 2691, + 2680, 2681, 2691, 2691, 2681, 2692, + 2681, 2682, 2692, 2692, 2682, 2693, + 2682, 2683, 2693, 2693, 2683, 2694, + 2684, 2685, 2695, 2695, 2685, 2696, + 2685, 2686, 2696, 2696, 2686, 2697, + 2686, 2687, 2697, 2697, 2687, 2698, + 2687, 2688, 2698, 2698, 2688, 2699, + 2688, 2689, 2699, 2699, 2689, 2700, + 2689, 2690, 2700, 2700, 2690, 2701, + 2690, 2691, 2701, 2701, 2691, 2702, + 2691, 2692, 2702, 2702, 2692, 2703, + 2692, 2693, 2703, 2703, 2693, 2704, + 2693, 2694, 2704, 2704, 2694, 2705, + 2695, 2696, 2706, 2706, 2696, 2707, + 2696, 2697, 2707, 2707, 2697, 2708, + 2697, 2698, 2708, 2708, 2698, 2709, + 2698, 2699, 2709, 2709, 2699, 2710, + 2699, 2700, 2710, 2710, 2700, 2711, + 2700, 2701, 2711, 2711, 2701, 2712, + 2701, 2702, 2712, 2712, 2702, 2713, + 2702, 2703, 2713, 2713, 2703, 2714, + 2703, 2704, 2714, 2714, 2704, 2715, + 2704, 2705, 2715, 2715, 2705, 2716, + 2706, 2707, 2717, 2717, 2707, 2718, + 2707, 2708, 2718, 2718, 2708, 2719, + 2708, 2709, 2719, 2719, 2709, 2720, + 2709, 2710, 2720, 2720, 2710, 2721, + 2710, 2711, 2721, 2721, 2711, 2722, + 2711, 2712, 2722, 2722, 2712, 2723, + 2712, 2713, 2723, 2723, 2713, 2724, + 2713, 2714, 2724, 2724, 2714, 2725, + 2714, 2715, 2725, 2725, 2715, 2726, + 2715, 2716, 2726, 2726, 2716, 2727, + 2717, 2718, 2728, 2728, 2718, 2729, + 2718, 2719, 2729, 2729, 2719, 2730, + 2719, 2720, 2730, 2730, 2720, 2731, + 2720, 2721, 2731, 2731, 2721, 2732, + 2721, 2722, 2732, 2732, 2722, 2733, + 2722, 2723, 2733, 2733, 2723, 2734, + 2723, 2724, 2734, 2734, 2724, 2735, + 2724, 2725, 2735, 2735, 2725, 2736, + 2725, 2726, 2736, 2736, 2726, 2737, + 2726, 2727, 2737, 2737, 2727, 2738, + 2728, 2729, 2739, 2739, 2729, 2740, + 2729, 2730, 2740, 2740, 2730, 2741, + 2730, 2731, 2741, 2741, 2731, 2742, + 2731, 2732, 2742, 2742, 2732, 2743, + 2732, 2733, 2743, 2743, 2733, 2744, + 2733, 2734, 2744, 2744, 2734, 2745, + 2734, 2735, 2745, 2745, 2735, 2746, + 2735, 2736, 2746, 2746, 2736, 2747, + 2736, 2737, 2747, 2747, 2737, 2748, + 2737, 2738, 2748, 2748, 2738, 2749, + 2739, 2740, 2750, 2750, 2740, 2751, + 2740, 2741, 2751, 2751, 2741, 2752, + 2741, 2742, 2752, 2752, 2742, 2753, + 2742, 2743, 2753, 2753, 2743, 2754, + 2743, 2744, 2754, 2754, 2744, 2755, + 2744, 2745, 2755, 2755, 2745, 2756, + 2745, 2746, 2756, 2756, 2746, 2757, + 2746, 2747, 2757, 2757, 2747, 2758, + 2747, 2748, 2758, 2758, 2748, 2759, + 2748, 2749, 2759, 2759, 2749, 2760, + 2750, 2751, 2761, 2761, 2751, 2762, + 2751, 2752, 2762, 2762, 2752, 2763, + 2752, 2753, 2763, 2763, 2753, 2764, + 2753, 2754, 2764, 2764, 2754, 2765, + 2754, 2755, 2765, 2765, 2755, 2766, + 2755, 2756, 2766, 2766, 2756, 2767, + 2756, 2757, 2767, 2767, 2757, 2768, + 2757, 2758, 2768, 2768, 2758, 2769, + 2758, 2759, 2769, 2769, 2759, 2770, + 2759, 2760, 2770, 2770, 2760, 2771, + 2761, 2762, 2772, 2772, 2762, 2773, + 2762, 2763, 2773, 2773, 2763, 2774, + 2763, 2764, 2774, 2774, 2764, 2775, + 2764, 2765, 2775, 2775, 2765, 2776, + 2765, 2766, 2776, 2776, 2766, 2777, + 2766, 2767, 2777, 2777, 2767, 2778, + 2767, 2768, 2778, 2778, 2768, 2779, + 2768, 2769, 2779, 2779, 2769, 2780, + 2769, 2770, 2780, 2780, 2770, 2781, + 2770, 2771, 2781, 2781, 2771, 2782, + 2783, 2784, 2794, 2794, 2784, 2795, + 2784, 2785, 2795, 2795, 2785, 2796, + 2785, 2786, 2796, 2796, 2786, 2797, + 2786, 2787, 2797, 2797, 2787, 2798, + 2787, 2788, 2798, 2798, 2788, 2799, + 2788, 2789, 2799, 2799, 2789, 2800, + 2789, 2790, 2800, 2800, 2790, 2801, + 2790, 2791, 2801, 2801, 2791, 2802, + 2791, 2792, 2802, 2802, 2792, 2803, + 2792, 2793, 2803, 2803, 2793, 2804, + 2794, 2795, 2805, 2805, 2795, 2806, + 2795, 2796, 2806, 2806, 2796, 2807, + 2796, 2797, 2807, 2807, 2797, 2808, + 2797, 2798, 2808, 2808, 2798, 2809, + 2798, 2799, 2809, 2809, 2799, 2810, + 2799, 2800, 2810, 2810, 2800, 2811, + 2800, 2801, 2811, 2811, 2801, 2812, + 2801, 2802, 2812, 2812, 2802, 2813, + 2802, 2803, 2813, 2813, 2803, 2814, + 2803, 2804, 2814, 2814, 2804, 2815, + 2805, 2806, 2816, 2816, 2806, 2817, + 2806, 2807, 2817, 2817, 2807, 2818, + 2807, 2808, 2818, 2818, 2808, 2819, + 2808, 2809, 2819, 2819, 2809, 2820, + 2809, 2810, 2820, 2820, 2810, 2821, + 2810, 2811, 2821, 2821, 2811, 2822, + 2811, 2812, 2822, 2822, 2812, 2823, + 2812, 2813, 2823, 2823, 2813, 2824, + 2813, 2814, 2824, 2824, 2814, 2825, + 2814, 2815, 2825, 2825, 2815, 2826, + 2816, 2817, 2827, 2827, 2817, 2828, + 2817, 2818, 2828, 2828, 2818, 2829, + 2818, 2819, 2829, 2829, 2819, 2830, + 2819, 2820, 2830, 2830, 2820, 2831, + 2820, 2821, 2831, 2831, 2821, 2832, + 2821, 2822, 2832, 2832, 2822, 2833, + 2822, 2823, 2833, 2833, 2823, 2834, + 2823, 2824, 2834, 2834, 2824, 2835, + 2824, 2825, 2835, 2835, 2825, 2836, + 2825, 2826, 2836, 2836, 2826, 2837, + 2827, 2828, 2838, 2838, 2828, 2839, + 2828, 2829, 2839, 2839, 2829, 2840, + 2829, 2830, 2840, 2840, 2830, 2841, + 2830, 2831, 2841, 2841, 2831, 2842, + 2831, 2832, 2842, 2842, 2832, 2843, + 2832, 2833, 2843, 2843, 2833, 2844, + 2833, 2834, 2844, 2844, 2834, 2845, + 2834, 2835, 2845, 2845, 2835, 2846, + 2835, 2836, 2846, 2846, 2836, 2847, + 2836, 2837, 2847, 2847, 2837, 2848, + 2838, 2839, 2849, 2849, 2839, 2850, + 2839, 2840, 2850, 2850, 2840, 2851, + 2840, 2841, 2851, 2851, 2841, 2852, + 2841, 2842, 2852, 2852, 2842, 2853, + 2842, 2843, 2853, 2853, 2843, 2854, + 2843, 2844, 2854, 2854, 2844, 2855, + 2844, 2845, 2855, 2855, 2845, 2856, + 2845, 2846, 2856, 2856, 2846, 2857, + 2846, 2847, 2857, 2857, 2847, 2858, + 2847, 2848, 2858, 2858, 2848, 2859, + 2849, 2850, 2860, 2860, 2850, 2861, + 2850, 2851, 2861, 2861, 2851, 2862, + 2851, 2852, 2862, 2862, 2852, 2863, + 2852, 2853, 2863, 2863, 2853, 2864, + 2853, 2854, 2864, 2864, 2854, 2865, + 2854, 2855, 2865, 2865, 2855, 2866, + 2855, 2856, 2866, 2866, 2856, 2867, + 2856, 2857, 2867, 2867, 2857, 2868, + 2857, 2858, 2868, 2868, 2858, 2869, + 2858, 2859, 2869, 2869, 2859, 2870, + 2860, 2861, 2871, 2871, 2861, 2872, + 2861, 2862, 2872, 2872, 2862, 2873, + 2862, 2863, 2873, 2873, 2863, 2874, + 2863, 2864, 2874, 2874, 2864, 2875, + 2864, 2865, 2875, 2875, 2865, 2876, + 2865, 2866, 2876, 2876, 2866, 2877, + 2866, 2867, 2877, 2877, 2867, 2878, + 2867, 2868, 2878, 2878, 2868, 2879, + 2868, 2869, 2879, 2879, 2869, 2880, + 2869, 2870, 2880, 2880, 2870, 2881, + 2871, 2872, 2882, 2882, 2872, 2883, + 2872, 2873, 2883, 2883, 2873, 2884, + 2873, 2874, 2884, 2884, 2874, 2885, + 2874, 2875, 2885, 2885, 2875, 2886, + 2875, 2876, 2886, 2886, 2876, 2887, + 2876, 2877, 2887, 2887, 2877, 2888, + 2877, 2878, 2888, 2888, 2878, 2889, + 2878, 2879, 2889, 2889, 2879, 2890, + 2879, 2880, 2890, 2890, 2880, 2891, + 2880, 2881, 2891, 2891, 2881, 2892, + 2882, 2883, 2893, 2893, 2883, 2894, + 2883, 2884, 2894, 2894, 2884, 2895, + 2884, 2885, 2895, 2895, 2885, 2896, + 2885, 2886, 2896, 2896, 2886, 2897, + 2886, 2887, 2897, 2897, 2887, 2898, + 2887, 2888, 2898, 2898, 2888, 2899, + 2888, 2889, 2899, 2899, 2889, 2900, + 2889, 2890, 2900, 2900, 2890, 2901, + 2890, 2891, 2901, 2901, 2891, 2902, + 2891, 2892, 2902, 2902, 2892, 2903, + 2904, 2905, 2915, 2915, 2905, 2916, + 2905, 2906, 2916, 2916, 2906, 2917, + 2906, 2907, 2917, 2917, 2907, 2918, + 2907, 2908, 2918, 2918, 2908, 2919, + 2908, 2909, 2919, 2919, 2909, 2920, + 2909, 2910, 2920, 2920, 2910, 2921, + 2910, 2911, 2921, 2921, 2911, 2922, + 2911, 2912, 2922, 2922, 2912, 2923, + 2912, 2913, 2923, 2923, 2913, 2924, + 2913, 2914, 2924, 2924, 2914, 2925, + 2915, 2916, 2926, 2926, 2916, 2927, + 2916, 2917, 2927, 2927, 2917, 2928, + 2917, 2918, 2928, 2928, 2918, 2929, + 2918, 2919, 2929, 2929, 2919, 2930, + 2919, 2920, 2930, 2930, 2920, 2931, + 2920, 2921, 2931, 2931, 2921, 2932, + 2921, 2922, 2932, 2932, 2922, 2933, + 2922, 2923, 2933, 2933, 2923, 2934, + 2923, 2924, 2934, 2934, 2924, 2935, + 2924, 2925, 2935, 2935, 2925, 2936, + 2926, 2927, 2937, 2937, 2927, 2938, + 2927, 2928, 2938, 2938, 2928, 2939, + 2928, 2929, 2939, 2939, 2929, 2940, + 2929, 2930, 2940, 2940, 2930, 2941, + 2930, 2931, 2941, 2941, 2931, 2942, + 2931, 2932, 2942, 2942, 2932, 2943, + 2932, 2933, 2943, 2943, 2933, 2944, + 2933, 2934, 2944, 2944, 2934, 2945, + 2934, 2935, 2945, 2945, 2935, 2946, + 2935, 2936, 2946, 2946, 2936, 2947, + 2937, 2938, 2948, 2948, 2938, 2949, + 2938, 2939, 2949, 2949, 2939, 2950, + 2939, 2940, 2950, 2950, 2940, 2951, + 2940, 2941, 2951, 2951, 2941, 2952, + 2941, 2942, 2952, 2952, 2942, 2953, + 2942, 2943, 2953, 2953, 2943, 2954, + 2943, 2944, 2954, 2954, 2944, 2955, + 2944, 2945, 2955, 2955, 2945, 2956, + 2945, 2946, 2956, 2956, 2946, 2957, + 2946, 2947, 2957, 2957, 2947, 2958, + 2948, 2949, 2959, 2959, 2949, 2960, + 2949, 2950, 2960, 2960, 2950, 2961, + 2950, 2951, 2961, 2961, 2951, 2962, + 2951, 2952, 2962, 2962, 2952, 2963, + 2952, 2953, 2963, 2963, 2953, 2964, + 2953, 2954, 2964, 2964, 2954, 2965, + 2954, 2955, 2965, 2965, 2955, 2966, + 2955, 2956, 2966, 2966, 2956, 2967, + 2956, 2957, 2967, 2967, 2957, 2968, + 2957, 2958, 2968, 2968, 2958, 2969, + 2959, 2960, 2970, 2970, 2960, 2971, + 2960, 2961, 2971, 2971, 2961, 2972, + 2961, 2962, 2972, 2972, 2962, 2973, + 2962, 2963, 2973, 2973, 2963, 2974, + 2963, 2964, 2974, 2974, 2964, 2975, + 2964, 2965, 2975, 2975, 2965, 2976, + 2965, 2966, 2976, 2976, 2966, 2977, + 2966, 2967, 2977, 2977, 2967, 2978, + 2967, 2968, 2978, 2978, 2968, 2979, + 2968, 2969, 2979, 2979, 2969, 2980, + 2970, 2971, 2981, 2981, 2971, 2982, + 2971, 2972, 2982, 2982, 2972, 2983, + 2972, 2973, 2983, 2983, 2973, 2984, + 2973, 2974, 2984, 2984, 2974, 2985, + 2974, 2975, 2985, 2985, 2975, 2986, + 2975, 2976, 2986, 2986, 2976, 2987, + 2976, 2977, 2987, 2987, 2977, 2988, + 2977, 2978, 2988, 2988, 2978, 2989, + 2978, 2979, 2989, 2989, 2979, 2990, + 2979, 2980, 2990, 2990, 2980, 2991, + 2981, 2982, 2992, 2992, 2982, 2993, + 2982, 2983, 2993, 2993, 2983, 2994, + 2983, 2984, 2994, 2994, 2984, 2995, + 2984, 2985, 2995, 2995, 2985, 2996, + 2985, 2986, 2996, 2996, 2986, 2997, + 2986, 2987, 2997, 2997, 2987, 2998, + 2987, 2988, 2998, 2998, 2988, 2999, + 2988, 2989, 2999, 2999, 2989, 3000, + 2989, 2990, 3000, 3000, 2990, 3001, + 2990, 2991, 3001, 3001, 2991, 3002, + 2992, 2993, 3003, 3003, 2993, 3004, + 2993, 2994, 3004, 3004, 2994, 3005, + 2994, 2995, 3005, 3005, 2995, 3006, + 2995, 2996, 3006, 3006, 2996, 3007, + 2996, 2997, 3007, 3007, 2997, 3008, + 2997, 2998, 3008, 3008, 2998, 3009, + 2998, 2999, 3009, 3009, 2999, 3010, + 2999, 3000, 3010, 3010, 3000, 3011, + 3000, 3001, 3011, 3011, 3001, 3012, + 3001, 3002, 3012, 3012, 3002, 3013, + 3003, 3004, 3014, 3014, 3004, 3015, + 3004, 3005, 3015, 3015, 3005, 3016, + 3005, 3006, 3016, 3016, 3006, 3017, + 3006, 3007, 3017, 3017, 3007, 3018, + 3007, 3008, 3018, 3018, 3008, 3019, + 3008, 3009, 3019, 3019, 3009, 3020, + 3009, 3010, 3020, 3020, 3010, 3021, + 3010, 3011, 3021, 3021, 3011, 3022, + 3011, 3012, 3022, 3022, 3012, 3023, + 3012, 3013, 3023, 3023, 3013, 3024, + 3025, 3026, 3036, 3036, 3026, 3037, + 3026, 3027, 3037, 3037, 3027, 3038, + 3027, 3028, 3038, 3038, 3028, 3039, + 3028, 3029, 3039, 3039, 3029, 3040, + 3029, 3030, 3040, 3040, 3030, 3041, + 3030, 3031, 3041, 3041, 3031, 3042, + 3031, 3032, 3042, 3042, 3032, 3043, + 3032, 3033, 3043, 3043, 3033, 3044, + 3033, 3034, 3044, 3044, 3034, 3045, + 3034, 3035, 3045, 3045, 3035, 3046, + 3036, 3037, 3047, 3047, 3037, 3048, + 3037, 3038, 3048, 3048, 3038, 3049, + 3038, 3039, 3049, 3049, 3039, 3050, + 3039, 3040, 3050, 3050, 3040, 3051, + 3040, 3041, 3051, 3051, 3041, 3052, + 3041, 3042, 3052, 3052, 3042, 3053, + 3042, 3043, 3053, 3053, 3043, 3054, + 3043, 3044, 3054, 3054, 3044, 3055, + 3044, 3045, 3055, 3055, 3045, 3056, + 3045, 3046, 3056, 3056, 3046, 3057, + 3047, 3048, 3058, 3058, 3048, 3059, + 3048, 3049, 3059, 3059, 3049, 3060, + 3049, 3050, 3060, 3060, 3050, 3061, + 3050, 3051, 3061, 3061, 3051, 3062, + 3051, 3052, 3062, 3062, 3052, 3063, + 3052, 3053, 3063, 3063, 3053, 3064, + 3053, 3054, 3064, 3064, 3054, 3065, + 3054, 3055, 3065, 3065, 3055, 3066, + 3055, 3056, 3066, 3066, 3056, 3067, + 3056, 3057, 3067, 3067, 3057, 3068, + 3058, 3059, 3069, 3069, 3059, 3070, + 3059, 3060, 3070, 3070, 3060, 3071, + 3060, 3061, 3071, 3071, 3061, 3072, + 3061, 3062, 3072, 3072, 3062, 3073, + 3062, 3063, 3073, 3073, 3063, 3074, + 3063, 3064, 3074, 3074, 3064, 3075, + 3064, 3065, 3075, 3075, 3065, 3076, + 3065, 3066, 3076, 3076, 3066, 3077, + 3066, 3067, 3077, 3077, 3067, 3078, + 3067, 3068, 3078, 3078, 3068, 3079, + 3069, 3070, 3080, 3080, 3070, 3081, + 3070, 3071, 3081, 3081, 3071, 3082, + 3071, 3072, 3082, 3082, 3072, 3083, + 3072, 3073, 3083, 3083, 3073, 3084, + 3073, 3074, 3084, 3084, 3074, 3085, + 3074, 3075, 3085, 3085, 3075, 3086, + 3075, 3076, 3086, 3086, 3076, 3087, + 3076, 3077, 3087, 3087, 3077, 3088, + 3077, 3078, 3088, 3088, 3078, 3089, + 3078, 3079, 3089, 3089, 3079, 3090, + 3080, 3081, 3091, 3091, 3081, 3092, + 3081, 3082, 3092, 3092, 3082, 3093, + 3082, 3083, 3093, 3093, 3083, 3094, + 3083, 3084, 3094, 3094, 3084, 3095, + 3084, 3085, 3095, 3095, 3085, 3096, + 3085, 3086, 3096, 3096, 3086, 3097, + 3086, 3087, 3097, 3097, 3087, 3098, + 3087, 3088, 3098, 3098, 3088, 3099, + 3088, 3089, 3099, 3099, 3089, 3100, + 3089, 3090, 3100, 3100, 3090, 3101, + 3091, 3092, 3102, 3102, 3092, 3103, + 3092, 3093, 3103, 3103, 3093, 3104, + 3093, 3094, 3104, 3104, 3094, 3105, + 3094, 3095, 3105, 3105, 3095, 3106, + 3095, 3096, 3106, 3106, 3096, 3107, + 3096, 3097, 3107, 3107, 3097, 3108, + 3097, 3098, 3108, 3108, 3098, 3109, + 3098, 3099, 3109, 3109, 3099, 3110, + 3099, 3100, 3110, 3110, 3100, 3111, + 3100, 3101, 3111, 3111, 3101, 3112, + 3102, 3103, 3113, 3113, 3103, 3114, + 3103, 3104, 3114, 3114, 3104, 3115, + 3104, 3105, 3115, 3115, 3105, 3116, + 3105, 3106, 3116, 3116, 3106, 3117, + 3106, 3107, 3117, 3117, 3107, 3118, + 3107, 3108, 3118, 3118, 3108, 3119, + 3108, 3109, 3119, 3119, 3109, 3120, + 3109, 3110, 3120, 3120, 3110, 3121, + 3110, 3111, 3121, 3121, 3111, 3122, + 3111, 3112, 3122, 3122, 3112, 3123, + 3113, 3114, 3124, 3124, 3114, 3125, + 3114, 3115, 3125, 3125, 3115, 3126, + 3115, 3116, 3126, 3126, 3116, 3127, + 3116, 3117, 3127, 3127, 3117, 3128, + 3117, 3118, 3128, 3128, 3118, 3129, + 3118, 3119, 3129, 3129, 3119, 3130, + 3119, 3120, 3130, 3130, 3120, 3131, + 3120, 3121, 3131, 3131, 3121, 3132, + 3121, 3122, 3132, 3132, 3122, 3133, + 3122, 3123, 3133, 3133, 3123, 3134, + 3124, 3125, 3135, 3135, 3125, 3136, + 3125, 3126, 3136, 3136, 3126, 3137, + 3126, 3127, 3137, 3137, 3127, 3138, + 3127, 3128, 3138, 3138, 3128, 3139, + 3128, 3129, 3139, 3139, 3129, 3140, + 3129, 3130, 3140, 3140, 3130, 3141, + 3130, 3131, 3141, 3141, 3131, 3142, + 3131, 3132, 3142, 3142, 3132, 3143, + 3132, 3133, 3143, 3143, 3133, 3144, + 3133, 3134, 3144, 3144, 3134, 3145, + 3146, 3147, 3157, 3157, 3147, 3158, + 3147, 3148, 3158, 3158, 3148, 3159, + 3148, 3149, 3159, 3159, 3149, 3160, + 3149, 3150, 3160, 3160, 3150, 3161, + 3150, 3151, 3161, 3161, 3151, 3162, + 3151, 3152, 3162, 3162, 3152, 3163, + 3152, 3153, 3163, 3163, 3153, 3164, + 3153, 3154, 3164, 3164, 3154, 3165, + 3154, 3155, 3165, 3165, 3155, 3166, + 3155, 3156, 3166, 3166, 3156, 3167, + 3157, 3158, 3168, 3168, 3158, 3169, + 3158, 3159, 3169, 3169, 3159, 3170, + 3159, 3160, 3170, 3170, 3160, 3171, + 3160, 3161, 3171, 3171, 3161, 3172, + 3161, 3162, 3172, 3172, 3162, 3173, + 3162, 3163, 3173, 3173, 3163, 3174, + 3163, 3164, 3174, 3174, 3164, 3175, + 3164, 3165, 3175, 3175, 3165, 3176, + 3165, 3166, 3176, 3176, 3166, 3177, + 3166, 3167, 3177, 3177, 3167, 3178, + 3168, 3169, 3179, 3179, 3169, 3180, + 3169, 3170, 3180, 3180, 3170, 3181, + 3170, 3171, 3181, 3181, 3171, 3182, + 3171, 3172, 3182, 3182, 3172, 3183, + 3172, 3173, 3183, 3183, 3173, 3184, + 3173, 3174, 3184, 3184, 3174, 3185, + 3174, 3175, 3185, 3185, 3175, 3186, + 3175, 3176, 3186, 3186, 3176, 3187, + 3176, 3177, 3187, 3187, 3177, 3188, + 3177, 3178, 3188, 3188, 3178, 3189, + 3179, 3180, 3190, 3190, 3180, 3191, + 3180, 3181, 3191, 3191, 3181, 3192, + 3181, 3182, 3192, 3192, 3182, 3193, + 3182, 3183, 3193, 3193, 3183, 3194, + 3183, 3184, 3194, 3194, 3184, 3195, + 3184, 3185, 3195, 3195, 3185, 3196, + 3185, 3186, 3196, 3196, 3186, 3197, + 3186, 3187, 3197, 3197, 3187, 3198, + 3187, 3188, 3198, 3198, 3188, 3199, + 3188, 3189, 3199, 3199, 3189, 3200, + 3190, 3191, 3201, 3201, 3191, 3202, + 3191, 3192, 3202, 3202, 3192, 3203, + 3192, 3193, 3203, 3203, 3193, 3204, + 3193, 3194, 3204, 3204, 3194, 3205, + 3194, 3195, 3205, 3205, 3195, 3206, + 3195, 3196, 3206, 3206, 3196, 3207, + 3196, 3197, 3207, 3207, 3197, 3208, + 3197, 3198, 3208, 3208, 3198, 3209, + 3198, 3199, 3209, 3209, 3199, 3210, + 3199, 3200, 3210, 3210, 3200, 3211, + 3201, 3202, 3212, 3212, 3202, 3213, + 3202, 3203, 3213, 3213, 3203, 3214, + 3203, 3204, 3214, 3214, 3204, 3215, + 3204, 3205, 3215, 3215, 3205, 3216, + 3205, 3206, 3216, 3216, 3206, 3217, + 3206, 3207, 3217, 3217, 3207, 3218, + 3207, 3208, 3218, 3218, 3208, 3219, + 3208, 3209, 3219, 3219, 3209, 3220, + 3209, 3210, 3220, 3220, 3210, 3221, + 3210, 3211, 3221, 3221, 3211, 3222, + 3212, 3213, 3223, 3223, 3213, 3224, + 3213, 3214, 3224, 3224, 3214, 3225, + 3214, 3215, 3225, 3225, 3215, 3226, + 3215, 3216, 3226, 3226, 3216, 3227, + 3216, 3217, 3227, 3227, 3217, 3228, + 3217, 3218, 3228, 3228, 3218, 3229, + 3218, 3219, 3229, 3229, 3219, 3230, + 3219, 3220, 3230, 3230, 3220, 3231, + 3220, 3221, 3231, 3231, 3221, 3232, + 3221, 3222, 3232, 3232, 3222, 3233, + 3223, 3224, 3234, 3234, 3224, 3235, + 3224, 3225, 3235, 3235, 3225, 3236, + 3225, 3226, 3236, 3236, 3226, 3237, + 3226, 3227, 3237, 3237, 3227, 3238, + 3227, 3228, 3238, 3238, 3228, 3239, + 3228, 3229, 3239, 3239, 3229, 3240, + 3229, 3230, 3240, 3240, 3230, 3241, + 3230, 3231, 3241, 3241, 3231, 3242, + 3231, 3232, 3242, 3242, 3232, 3243, + 3232, 3233, 3243, 3243, 3233, 3244, + 3234, 3235, 3245, 3245, 3235, 3246, + 3235, 3236, 3246, 3246, 3236, 3247, + 3236, 3237, 3247, 3247, 3237, 3248, + 3237, 3238, 3248, 3248, 3238, 3249, + 3238, 3239, 3249, 3249, 3239, 3250, + 3239, 3240, 3250, 3250, 3240, 3251, + 3240, 3241, 3251, 3251, 3241, 3252, + 3241, 3242, 3252, 3252, 3242, 3253, + 3242, 3243, 3253, 3253, 3243, 3254, + 3243, 3244, 3254, 3254, 3244, 3255, + 3245, 3246, 3256, 3256, 3246, 3257, + 3246, 3247, 3257, 3257, 3247, 3258, + 3247, 3248, 3258, 3258, 3248, 3259, + 3248, 3249, 3259, 3259, 3249, 3260, + 3249, 3250, 3260, 3260, 3250, 3261, + 3250, 3251, 3261, 3261, 3251, 3262, + 3251, 3252, 3262, 3262, 3252, 3263, + 3252, 3253, 3263, 3263, 3253, 3264, + 3253, 3254, 3264, 3264, 3254, 3265, + 3254, 3255, 3265, 3265, 3255, 3266, + 3267, 3268, 3278, 3278, 3268, 3279, + 3268, 3269, 3279, 3279, 3269, 3280, + 3269, 3270, 3280, 3280, 3270, 3281, + 3270, 3271, 3281, 3281, 3271, 3282, + 3271, 3272, 3282, 3282, 3272, 3283, + 3272, 3273, 3283, 3283, 3273, 3284, + 3273, 3274, 3284, 3284, 3274, 3285, + 3274, 3275, 3285, 3285, 3275, 3286, + 3275, 3276, 3286, 3286, 3276, 3287, + 3276, 3277, 3287, 3287, 3277, 3288, + 3278, 3279, 3289, 3289, 3279, 3290, + 3279, 3280, 3290, 3290, 3280, 3291, + 3280, 3281, 3291, 3291, 3281, 3292, + 3281, 3282, 3292, 3292, 3282, 3293, + 3282, 3283, 3293, 3293, 3283, 3294, + 3283, 3284, 3294, 3294, 3284, 3295, + 3284, 3285, 3295, 3295, 3285, 3296, + 3285, 3286, 3296, 3296, 3286, 3297, + 3286, 3287, 3297, 3297, 3287, 3298, + 3287, 3288, 3298, 3298, 3288, 3299, + 3289, 3290, 3300, 3300, 3290, 3301, + 3290, 3291, 3301, 3301, 3291, 3302, + 3291, 3292, 3302, 3302, 3292, 3303, + 3292, 3293, 3303, 3303, 3293, 3304, + 3293, 3294, 3304, 3304, 3294, 3305, + 3294, 3295, 3305, 3305, 3295, 3306, + 3295, 3296, 3306, 3306, 3296, 3307, + 3296, 3297, 3307, 3307, 3297, 3308, + 3297, 3298, 3308, 3308, 3298, 3309, + 3298, 3299, 3309, 3309, 3299, 3310, + 3300, 3301, 3311, 3311, 3301, 3312, + 3301, 3302, 3312, 3312, 3302, 3313, + 3302, 3303, 3313, 3313, 3303, 3314, + 3303, 3304, 3314, 3314, 3304, 3315, + 3304, 3305, 3315, 3315, 3305, 3316, + 3305, 3306, 3316, 3316, 3306, 3317, + 3306, 3307, 3317, 3317, 3307, 3318, + 3307, 3308, 3318, 3318, 3308, 3319, + 3308, 3309, 3319, 3319, 3309, 3320, + 3309, 3310, 3320, 3320, 3310, 3321, + 3311, 3312, 3322, 3322, 3312, 3323, + 3312, 3313, 3323, 3323, 3313, 3324, + 3313, 3314, 3324, 3324, 3314, 3325, + 3314, 3315, 3325, 3325, 3315, 3326, + 3315, 3316, 3326, 3326, 3316, 3327, + 3316, 3317, 3327, 3327, 3317, 3328, + 3317, 3318, 3328, 3328, 3318, 3329, + 3318, 3319, 3329, 3329, 3319, 3330, + 3319, 3320, 3330, 3330, 3320, 3331, + 3320, 3321, 3331, 3331, 3321, 3332, + 3322, 3323, 3333, 3333, 3323, 3334, + 3323, 3324, 3334, 3334, 3324, 3335, + 3324, 3325, 3335, 3335, 3325, 3336, + 3325, 3326, 3336, 3336, 3326, 3337, + 3326, 3327, 3337, 3337, 3327, 3338, + 3327, 3328, 3338, 3338, 3328, 3339, + 3328, 3329, 3339, 3339, 3329, 3340, + 3329, 3330, 3340, 3340, 3330, 3341, + 3330, 3331, 3341, 3341, 3331, 3342, + 3331, 3332, 3342, 3342, 3332, 3343, + 3333, 3334, 3344, 3344, 3334, 3345, + 3334, 3335, 3345, 3345, 3335, 3346, + 3335, 3336, 3346, 3346, 3336, 3347, + 3336, 3337, 3347, 3347, 3337, 3348, + 3337, 3338, 3348, 3348, 3338, 3349, + 3338, 3339, 3349, 3349, 3339, 3350, + 3339, 3340, 3350, 3350, 3340, 3351, + 3340, 3341, 3351, 3351, 3341, 3352, + 3341, 3342, 3352, 3352, 3342, 3353, + 3342, 3343, 3353, 3353, 3343, 3354, + 3344, 3345, 3355, 3355, 3345, 3356, + 3345, 3346, 3356, 3356, 3346, 3357, + 3346, 3347, 3357, 3357, 3347, 3358, + 3347, 3348, 3358, 3358, 3348, 3359, + 3348, 3349, 3359, 3359, 3349, 3360, + 3349, 3350, 3360, 3360, 3350, 3361, + 3350, 3351, 3361, 3361, 3351, 3362, + 3351, 3352, 3362, 3362, 3352, 3363, + 3352, 3353, 3363, 3363, 3353, 3364, + 3353, 3354, 3364, 3364, 3354, 3365, + 3355, 3356, 3366, 3366, 3356, 3367, + 3356, 3357, 3367, 3367, 3357, 3368, + 3357, 3358, 3368, 3368, 3358, 3369, + 3358, 3359, 3369, 3369, 3359, 3370, + 3359, 3360, 3370, 3370, 3360, 3371, + 3360, 3361, 3371, 3371, 3361, 3372, + 3361, 3362, 3372, 3372, 3362, 3373, + 3362, 3363, 3373, 3373, 3363, 3374, + 3363, 3364, 3374, 3374, 3364, 3375, + 3364, 3365, 3375, 3375, 3365, 3376, + 3366, 3367, 3377, 3377, 3367, 3378, + 3367, 3368, 3378, 3378, 3368, 3379, + 3368, 3369, 3379, 3379, 3369, 3380, + 3369, 3370, 3380, 3380, 3370, 3381, + 3370, 3371, 3381, 3381, 3371, 3382, + 3371, 3372, 3382, 3382, 3372, 3383, + 3372, 3373, 3383, 3383, 3373, 3384, + 3373, 3374, 3384, 3384, 3374, 3385, + 3374, 3375, 3385, 3385, 3375, 3386, + 3375, 3376, 3386, 3386, 3376, 3387, + 3388, 3389, 3399, 3399, 3389, 3400, + 3389, 3390, 3400, 3400, 3390, 3401, + 3390, 3391, 3401, 3401, 3391, 3402, + 3391, 3392, 3402, 3402, 3392, 3403, + 3392, 3393, 3403, 3403, 3393, 3404, + 3393, 3394, 3404, 3404, 3394, 3405, + 3394, 3395, 3405, 3405, 3395, 3406, + 3395, 3396, 3406, 3406, 3396, 3407, + 3396, 3397, 3407, 3407, 3397, 3408, + 3397, 3398, 3408, 3408, 3398, 3409, + 3399, 3400, 3410, 3410, 3400, 3411, + 3400, 3401, 3411, 3411, 3401, 3412, + 3401, 3402, 3412, 3412, 3402, 3413, + 3402, 3403, 3413, 3413, 3403, 3414, + 3403, 3404, 3414, 3414, 3404, 3415, + 3404, 3405, 3415, 3415, 3405, 3416, + 3405, 3406, 3416, 3416, 3406, 3417, + 3406, 3407, 3417, 3417, 3407, 3418, + 3407, 3408, 3418, 3418, 3408, 3419, + 3408, 3409, 3419, 3419, 3409, 3420, + 3410, 3411, 3421, 3421, 3411, 3422, + 3411, 3412, 3422, 3422, 3412, 3423, + 3412, 3413, 3423, 3423, 3413, 3424, + 3413, 3414, 3424, 3424, 3414, 3425, + 3414, 3415, 3425, 3425, 3415, 3426, + 3415, 3416, 3426, 3426, 3416, 3427, + 3416, 3417, 3427, 3427, 3417, 3428, + 3417, 3418, 3428, 3428, 3418, 3429, + 3418, 3419, 3429, 3429, 3419, 3430, + 3419, 3420, 3430, 3430, 3420, 3431, + 3421, 3422, 3432, 3432, 3422, 3433, + 3422, 3423, 3433, 3433, 3423, 3434, + 3423, 3424, 3434, 3434, 3424, 3435, + 3424, 3425, 3435, 3435, 3425, 3436, + 3425, 3426, 3436, 3436, 3426, 3437, + 3426, 3427, 3437, 3437, 3427, 3438, + 3427, 3428, 3438, 3438, 3428, 3439, + 3428, 3429, 3439, 3439, 3429, 3440, + 3429, 3430, 3440, 3440, 3430, 3441, + 3430, 3431, 3441, 3441, 3431, 3442, + 3432, 3433, 3443, 3443, 3433, 3444, + 3433, 3434, 3444, 3444, 3434, 3445, + 3434, 3435, 3445, 3445, 3435, 3446, + 3435, 3436, 3446, 3446, 3436, 3447, + 3436, 3437, 3447, 3447, 3437, 3448, + 3437, 3438, 3448, 3448, 3438, 3449, + 3438, 3439, 3449, 3449, 3439, 3450, + 3439, 3440, 3450, 3450, 3440, 3451, + 3440, 3441, 3451, 3451, 3441, 3452, + 3441, 3442, 3452, 3452, 3442, 3453, + 3443, 3444, 3454, 3454, 3444, 3455, + 3444, 3445, 3455, 3455, 3445, 3456, + 3445, 3446, 3456, 3456, 3446, 3457, + 3446, 3447, 3457, 3457, 3447, 3458, + 3447, 3448, 3458, 3458, 3448, 3459, + 3448, 3449, 3459, 3459, 3449, 3460, + 3449, 3450, 3460, 3460, 3450, 3461, + 3450, 3451, 3461, 3461, 3451, 3462, + 3451, 3452, 3462, 3462, 3452, 3463, + 3452, 3453, 3463, 3463, 3453, 3464, + 3454, 3455, 3465, 3465, 3455, 3466, + 3455, 3456, 3466, 3466, 3456, 3467, + 3456, 3457, 3467, 3467, 3457, 3468, + 3457, 3458, 3468, 3468, 3458, 3469, + 3458, 3459, 3469, 3469, 3459, 3470, + 3459, 3460, 3470, 3470, 3460, 3471, + 3460, 3461, 3471, 3471, 3461, 3472, + 3461, 3462, 3472, 3472, 3462, 3473, + 3462, 3463, 3473, 3473, 3463, 3474, + 3463, 3464, 3474, 3474, 3464, 3475, + 3465, 3466, 3476, 3476, 3466, 3477, + 3466, 3467, 3477, 3477, 3467, 3478, + 3467, 3468, 3478, 3478, 3468, 3479, + 3468, 3469, 3479, 3479, 3469, 3480, + 3469, 3470, 3480, 3480, 3470, 3481, + 3470, 3471, 3481, 3481, 3471, 3482, + 3471, 3472, 3482, 3482, 3472, 3483, + 3472, 3473, 3483, 3483, 3473, 3484, + 3473, 3474, 3484, 3484, 3474, 3485, + 3474, 3475, 3485, 3485, 3475, 3486, + 3476, 3477, 3487, 3487, 3477, 3488, + 3477, 3478, 3488, 3488, 3478, 3489, + 3478, 3479, 3489, 3489, 3479, 3490, + 3479, 3480, 3490, 3490, 3480, 3491, + 3480, 3481, 3491, 3491, 3481, 3492, + 3481, 3482, 3492, 3492, 3482, 3493, + 3482, 3483, 3493, 3493, 3483, 3494, + 3483, 3484, 3494, 3494, 3484, 3495, + 3484, 3485, 3495, 3495, 3485, 3496, + 3485, 3486, 3496, 3496, 3486, 3497, + 3487, 3488, 3498, 3498, 3488, 3499, + 3488, 3489, 3499, 3499, 3489, 3500, + 3489, 3490, 3500, 3500, 3490, 3501, + 3490, 3491, 3501, 3501, 3491, 3502, + 3491, 3492, 3502, 3502, 3492, 3503, + 3492, 3493, 3503, 3503, 3493, 3504, + 3493, 3494, 3504, 3504, 3494, 3505, + 3494, 3495, 3505, 3505, 3495, 3506, + 3495, 3496, 3506, 3506, 3496, 3507, + 3496, 3497, 3507, 3507, 3497, 3508, + 3509, 3510, 3520, 3520, 3510, 3521, + 3510, 3511, 3521, 3521, 3511, 3522, + 3511, 3512, 3522, 3522, 3512, 3523, + 3512, 3513, 3523, 3523, 3513, 3524, + 3513, 3514, 3524, 3524, 3514, 3525, + 3514, 3515, 3525, 3525, 3515, 3526, + 3515, 3516, 3526, 3526, 3516, 3527, + 3516, 3517, 3527, 3527, 3517, 3528, + 3517, 3518, 3528, 3528, 3518, 3529, + 3518, 3519, 3529, 3529, 3519, 3530, + 3520, 3521, 3531, 3531, 3521, 3532, + 3521, 3522, 3532, 3532, 3522, 3533, + 3522, 3523, 3533, 3533, 3523, 3534, + 3523, 3524, 3534, 3534, 3524, 3535, + 3524, 3525, 3535, 3535, 3525, 3536, + 3525, 3526, 3536, 3536, 3526, 3537, + 3526, 3527, 3537, 3537, 3527, 3538, + 3527, 3528, 3538, 3538, 3528, 3539, + 3528, 3529, 3539, 3539, 3529, 3540, + 3529, 3530, 3540, 3540, 3530, 3541, + 3531, 3532, 3542, 3542, 3532, 3543, + 3532, 3533, 3543, 3543, 3533, 3544, + 3533, 3534, 3544, 3544, 3534, 3545, + 3534, 3535, 3545, 3545, 3535, 3546, + 3535, 3536, 3546, 3546, 3536, 3547, + 3536, 3537, 3547, 3547, 3537, 3548, + 3537, 3538, 3548, 3548, 3538, 3549, + 3538, 3539, 3549, 3549, 3539, 3550, + 3539, 3540, 3550, 3550, 3540, 3551, + 3540, 3541, 3551, 3551, 3541, 3552, + 3542, 3543, 3553, 3553, 3543, 3554, + 3543, 3544, 3554, 3554, 3544, 3555, + 3544, 3545, 3555, 3555, 3545, 3556, + 3545, 3546, 3556, 3556, 3546, 3557, + 3546, 3547, 3557, 3557, 3547, 3558, + 3547, 3548, 3558, 3558, 3548, 3559, + 3548, 3549, 3559, 3559, 3549, 3560, + 3549, 3550, 3560, 3560, 3550, 3561, + 3550, 3551, 3561, 3561, 3551, 3562, + 3551, 3552, 3562, 3562, 3552, 3563, + 3553, 3554, 3564, 3564, 3554, 3565, + 3554, 3555, 3565, 3565, 3555, 3566, + 3555, 3556, 3566, 3566, 3556, 3567, + 3556, 3557, 3567, 3567, 3557, 3568, + 3557, 3558, 3568, 3568, 3558, 3569, + 3558, 3559, 3569, 3569, 3559, 3570, + 3559, 3560, 3570, 3570, 3560, 3571, + 3560, 3561, 3571, 3571, 3561, 3572, + 3561, 3562, 3572, 3572, 3562, 3573, + 3562, 3563, 3573, 3573, 3563, 3574, + 3564, 3565, 3575, 3575, 3565, 3576, + 3565, 3566, 3576, 3576, 3566, 3577, + 3566, 3567, 3577, 3577, 3567, 3578, + 3567, 3568, 3578, 3578, 3568, 3579, + 3568, 3569, 3579, 3579, 3569, 3580, + 3569, 3570, 3580, 3580, 3570, 3581, + 3570, 3571, 3581, 3581, 3571, 3582, + 3571, 3572, 3582, 3582, 3572, 3583, + 3572, 3573, 3583, 3583, 3573, 3584, + 3573, 3574, 3584, 3584, 3574, 3585, + 3575, 3576, 3586, 3586, 3576, 3587, + 3576, 3577, 3587, 3587, 3577, 3588, + 3577, 3578, 3588, 3588, 3578, 3589, + 3578, 3579, 3589, 3589, 3579, 3590, + 3579, 3580, 3590, 3590, 3580, 3591, + 3580, 3581, 3591, 3591, 3581, 3592, + 3581, 3582, 3592, 3592, 3582, 3593, + 3582, 3583, 3593, 3593, 3583, 3594, + 3583, 3584, 3594, 3594, 3584, 3595, + 3584, 3585, 3595, 3595, 3585, 3596, + 3586, 3587, 3597, 3597, 3587, 3598, + 3587, 3588, 3598, 3598, 3588, 3599, + 3588, 3589, 3599, 3599, 3589, 3600, + 3589, 3590, 3600, 3600, 3590, 3601, + 3590, 3591, 3601, 3601, 3591, 3602, + 3591, 3592, 3602, 3602, 3592, 3603, + 3592, 3593, 3603, 3603, 3593, 3604, + 3593, 3594, 3604, 3604, 3594, 3605, + 3594, 3595, 3605, 3605, 3595, 3606, + 3595, 3596, 3606, 3606, 3596, 3607, + 3597, 3598, 3608, 3608, 3598, 3609, + 3598, 3599, 3609, 3609, 3599, 3610, + 3599, 3600, 3610, 3610, 3600, 3611, + 3600, 3601, 3611, 3611, 3601, 3612, + 3601, 3602, 3612, 3612, 3602, 3613, + 3602, 3603, 3613, 3613, 3603, 3614, + 3603, 3604, 3614, 3614, 3604, 3615, + 3604, 3605, 3615, 3615, 3605, 3616, + 3605, 3606, 3616, 3616, 3606, 3617, + 3606, 3607, 3617, 3617, 3607, 3618, + 3608, 3609, 3619, 3619, 3609, 3620, + 3609, 3610, 3620, 3620, 3610, 3621, + 3610, 3611, 3621, 3621, 3611, 3622, + 3611, 3612, 3622, 3622, 3612, 3623, + 3612, 3613, 3623, 3623, 3613, 3624, + 3613, 3614, 3624, 3624, 3614, 3625, + 3614, 3615, 3625, 3625, 3615, 3626, + 3615, 3616, 3626, 3626, 3616, 3627, + 3616, 3617, 3627, 3627, 3617, 3628, + 3617, 3618, 3628, 3628, 3618, 3629, + 3630, 3631, 3641, 3641, 3631, 3642, + 3631, 3632, 3642, 3642, 3632, 3643, + 3632, 3633, 3643, 3643, 3633, 3644, + 3633, 3634, 3644, 3644, 3634, 3645, + 3634, 3635, 3645, 3645, 3635, 3646, + 3635, 3636, 3646, 3646, 3636, 3647, + 3636, 3637, 3647, 3647, 3637, 3648, + 3637, 3638, 3648, 3648, 3638, 3649, + 3638, 3639, 3649, 3649, 3639, 3650, + 3639, 3640, 3650, 3650, 3640, 3651, + 3641, 3642, 3652, 3652, 3642, 3653, + 3642, 3643, 3653, 3653, 3643, 3654, + 3643, 3644, 3654, 3654, 3644, 3655, + 3644, 3645, 3655, 3655, 3645, 3656, + 3645, 3646, 3656, 3656, 3646, 3657, + 3646, 3647, 3657, 3657, 3647, 3658, + 3647, 3648, 3658, 3658, 3648, 3659, + 3648, 3649, 3659, 3659, 3649, 3660, + 3649, 3650, 3660, 3660, 3650, 3661, + 3650, 3651, 3661, 3661, 3651, 3662, + 3652, 3653, 3663, 3663, 3653, 3664, + 3653, 3654, 3664, 3664, 3654, 3665, + 3654, 3655, 3665, 3665, 3655, 3666, + 3655, 3656, 3666, 3666, 3656, 3667, + 3656, 3657, 3667, 3667, 3657, 3668, + 3657, 3658, 3668, 3668, 3658, 3669, + 3658, 3659, 3669, 3669, 3659, 3670, + 3659, 3660, 3670, 3670, 3660, 3671, + 3660, 3661, 3671, 3671, 3661, 3672, + 3661, 3662, 3672, 3672, 3662, 3673, + 3663, 3664, 3674, 3674, 3664, 3675, + 3664, 3665, 3675, 3675, 3665, 3676, + 3665, 3666, 3676, 3676, 3666, 3677, + 3666, 3667, 3677, 3677, 3667, 3678, + 3667, 3668, 3678, 3678, 3668, 3679, + 3668, 3669, 3679, 3679, 3669, 3680, + 3669, 3670, 3680, 3680, 3670, 3681, + 3670, 3671, 3681, 3681, 3671, 3682, + 3671, 3672, 3682, 3682, 3672, 3683, + 3672, 3673, 3683, 3683, 3673, 3684, + 3674, 3675, 3685, 3685, 3675, 3686, + 3675, 3676, 3686, 3686, 3676, 3687, + 3676, 3677, 3687, 3687, 3677, 3688, + 3677, 3678, 3688, 3688, 3678, 3689, + 3678, 3679, 3689, 3689, 3679, 3690, + 3679, 3680, 3690, 3690, 3680, 3691, + 3680, 3681, 3691, 3691, 3681, 3692, + 3681, 3682, 3692, 3692, 3682, 3693, + 3682, 3683, 3693, 3693, 3683, 3694, + 3683, 3684, 3694, 3694, 3684, 3695, + 3685, 3686, 3696, 3696, 3686, 3697, + 3686, 3687, 3697, 3697, 3687, 3698, + 3687, 3688, 3698, 3698, 3688, 3699, + 3688, 3689, 3699, 3699, 3689, 3700, + 3689, 3690, 3700, 3700, 3690, 3701, + 3690, 3691, 3701, 3701, 3691, 3702, + 3691, 3692, 3702, 3702, 3692, 3703, + 3692, 3693, 3703, 3703, 3693, 3704, + 3693, 3694, 3704, 3704, 3694, 3705, + 3694, 3695, 3705, 3705, 3695, 3706, + 3696, 3697, 3707, 3707, 3697, 3708, + 3697, 3698, 3708, 3708, 3698, 3709, + 3698, 3699, 3709, 3709, 3699, 3710, + 3699, 3700, 3710, 3710, 3700, 3711, + 3700, 3701, 3711, 3711, 3701, 3712, + 3701, 3702, 3712, 3712, 3702, 3713, + 3702, 3703, 3713, 3713, 3703, 3714, + 3703, 3704, 3714, 3714, 3704, 3715, + 3704, 3705, 3715, 3715, 3705, 3716, + 3705, 3706, 3716, 3716, 3706, 3717, + 3707, 3708, 3718, 3718, 3708, 3719, + 3708, 3709, 3719, 3719, 3709, 3720, + 3709, 3710, 3720, 3720, 3710, 3721, + 3710, 3711, 3721, 3721, 3711, 3722, + 3711, 3712, 3722, 3722, 3712, 3723, + 3712, 3713, 3723, 3723, 3713, 3724, + 3713, 3714, 3724, 3724, 3714, 3725, + 3714, 3715, 3725, 3725, 3715, 3726, + 3715, 3716, 3726, 3726, 3716, 3727, + 3716, 3717, 3727, 3727, 3717, 3728, + 3718, 3719, 3729, 3729, 3719, 3730, + 3719, 3720, 3730, 3730, 3720, 3731, + 3720, 3721, 3731, 3731, 3721, 3732, + 3721, 3722, 3732, 3732, 3722, 3733, + 3722, 3723, 3733, 3733, 3723, 3734, + 3723, 3724, 3734, 3734, 3724, 3735, + 3724, 3725, 3735, 3735, 3725, 3736, + 3725, 3726, 3736, 3736, 3726, 3737, + 3726, 3727, 3737, 3737, 3727, 3738, + 3727, 3728, 3738, 3738, 3728, 3739, + 3729, 3730, 3740, 3740, 3730, 3741, + 3730, 3731, 3741, 3741, 3731, 3742, + 3731, 3732, 3742, 3742, 3732, 3743, + 3732, 3733, 3743, 3743, 3733, 3744, + 3733, 3734, 3744, 3744, 3734, 3745, + 3734, 3735, 3745, 3745, 3735, 3746, + 3735, 3736, 3746, 3746, 3736, 3747, + 3736, 3737, 3747, 3747, 3737, 3748, + 3737, 3738, 3748, 3748, 3738, 3749, + 3738, 3739, 3749, 3749, 3739, 3750, + 3751, 3752, 3762, 3762, 3752, 3763, + 3752, 3753, 3763, 3763, 3753, 3764, + 3753, 3754, 3764, 3764, 3754, 3765, + 3754, 3755, 3765, 3765, 3755, 3766, + 3755, 3756, 3766, 3766, 3756, 3767, + 3756, 3757, 3767, 3767, 3757, 3768, + 3757, 3758, 3768, 3768, 3758, 3769, + 3758, 3759, 3769, 3769, 3759, 3770, + 3759, 3760, 3770, 3770, 3760, 3771, + 3760, 3761, 3771, 3771, 3761, 3772, + 3762, 3763, 3773, 3773, 3763, 3774, + 3763, 3764, 3774, 3774, 3764, 3775, + 3764, 3765, 3775, 3775, 3765, 3776, + 3765, 3766, 3776, 3776, 3766, 3777, + 3766, 3767, 3777, 3777, 3767, 3778, + 3767, 3768, 3778, 3778, 3768, 3779, + 3768, 3769, 3779, 3779, 3769, 3780, + 3769, 3770, 3780, 3780, 3770, 3781, + 3770, 3771, 3781, 3781, 3771, 3782, + 3771, 3772, 3782, 3782, 3772, 3783, + 3773, 3774, 3784, 3784, 3774, 3785, + 3774, 3775, 3785, 3785, 3775, 3786, + 3775, 3776, 3786, 3786, 3776, 3787, + 3776, 3777, 3787, 3787, 3777, 3788, + 3777, 3778, 3788, 3788, 3778, 3789, + 3778, 3779, 3789, 3789, 3779, 3790, + 3779, 3780, 3790, 3790, 3780, 3791, + 3780, 3781, 3791, 3791, 3781, 3792, + 3781, 3782, 3792, 3792, 3782, 3793, + 3782, 3783, 3793, 3793, 3783, 3794, + 3784, 3785, 3795, 3795, 3785, 3796, + 3785, 3786, 3796, 3796, 3786, 3797, + 3786, 3787, 3797, 3797, 3787, 3798, + 3787, 3788, 3798, 3798, 3788, 3799, + 3788, 3789, 3799, 3799, 3789, 3800, + 3789, 3790, 3800, 3800, 3790, 3801, + 3790, 3791, 3801, 3801, 3791, 3802, + 3791, 3792, 3802, 3802, 3792, 3803, + 3792, 3793, 3803, 3803, 3793, 3804, + 3793, 3794, 3804, 3804, 3794, 3805, + 3795, 3796, 3806, 3806, 3796, 3807, + 3796, 3797, 3807, 3807, 3797, 3808, + 3797, 3798, 3808, 3808, 3798, 3809, + 3798, 3799, 3809, 3809, 3799, 3810, + 3799, 3800, 3810, 3810, 3800, 3811, + 3800, 3801, 3811, 3811, 3801, 3812, + 3801, 3802, 3812, 3812, 3802, 3813, + 3802, 3803, 3813, 3813, 3803, 3814, + 3803, 3804, 3814, 3814, 3804, 3815, + 3804, 3805, 3815, 3815, 3805, 3816, + 3806, 3807, 3817, 3817, 3807, 3818, + 3807, 3808, 3818, 3818, 3808, 3819, + 3808, 3809, 3819, 3819, 3809, 3820, + 3809, 3810, 3820, 3820, 3810, 3821, + 3810, 3811, 3821, 3821, 3811, 3822, + 3811, 3812, 3822, 3822, 3812, 3823, + 3812, 3813, 3823, 3823, 3813, 3824, + 3813, 3814, 3824, 3824, 3814, 3825, + 3814, 3815, 3825, 3825, 3815, 3826, + 3815, 3816, 3826, 3826, 3816, 3827, + 3817, 3818, 3828, 3828, 3818, 3829, + 3818, 3819, 3829, 3829, 3819, 3830, + 3819, 3820, 3830, 3830, 3820, 3831, + 3820, 3821, 3831, 3831, 3821, 3832, + 3821, 3822, 3832, 3832, 3822, 3833, + 3822, 3823, 3833, 3833, 3823, 3834, + 3823, 3824, 3834, 3834, 3824, 3835, + 3824, 3825, 3835, 3835, 3825, 3836, + 3825, 3826, 3836, 3836, 3826, 3837, + 3826, 3827, 3837, 3837, 3827, 3838, + 3828, 3829, 3839, 3839, 3829, 3840, + 3829, 3830, 3840, 3840, 3830, 3841, + 3830, 3831, 3841, 3841, 3831, 3842, + 3831, 3832, 3842, 3842, 3832, 3843, + 3832, 3833, 3843, 3843, 3833, 3844, + 3833, 3834, 3844, 3844, 3834, 3845, + 3834, 3835, 3845, 3845, 3835, 3846, + 3835, 3836, 3846, 3846, 3836, 3847, + 3836, 3837, 3847, 3847, 3837, 3848, + 3837, 3838, 3848, 3848, 3838, 3849, + 3839, 3840, 3850, 3850, 3840, 3851, + 3840, 3841, 3851, 3851, 3841, 3852, + 3841, 3842, 3852, 3852, 3842, 3853, + 3842, 3843, 3853, 3853, 3843, 3854, + 3843, 3844, 3854, 3854, 3844, 3855, + 3844, 3845, 3855, 3855, 3845, 3856, + 3845, 3846, 3856, 3856, 3846, 3857, + 3846, 3847, 3857, 3857, 3847, 3858, + 3847, 3848, 3858, 3858, 3848, 3859, + 3848, 3849, 3859, 3859, 3849, 3860, + 3850, 3851, 3861, 3861, 3851, 3862, + 3851, 3852, 3862, 3862, 3852, 3863, + 3852, 3853, 3863, 3863, 3853, 3864, + 3853, 3854, 3864, 3864, 3854, 3865, + 3854, 3855, 3865, 3865, 3855, 3866, + 3855, 3856, 3866, 3866, 3856, 3867, + 3856, 3857, 3867, 3867, 3857, 3868, + 3857, 3858, 3868, 3868, 3858, 3869, + 3858, 3859, 3869, 3869, 3859, 3870, + 3859, 3860, 3870, 3870, 3860, 3871 + }; + + std::vector<glm::vec3> teapotTangents; + teapotTangents.resize(teapotVertices.size() / 3, glm::vec3(0.0f)); + + std::vector<size_t> teapotTangentWeights; + teapotTangentWeights.resize(teapotTangents.size(), 0); + + for (size_t i = 0; i < teapotIndices.size(); i += 3) { + const auto index0 = teapotIndices[i + 0]; + const auto index1 = teapotIndices[i + 1]; + const auto index2 = teapotIndices[i + 2]; + + const std::array<glm::vec3, 3> positions = { + glm::vec3( + teapotVertices[index0 * 3 + 0], + teapotVertices[index0 * 3 + 1], + teapotVertices[index0 * 3 + 2] + ), + glm::vec3( + teapotVertices[index1 * 3 + 0], + teapotVertices[index1 * 3 + 1], + teapotVertices[index1 * 3 + 2] + ), + glm::vec3( + teapotVertices[index2 * 3 + 0], + teapotVertices[index2 * 3 + 1], + teapotVertices[index2 * 3 + 2] + ) + }; + + const std::array<glm::vec2, 3> uvs = { + glm::vec2( + teapotUVCoords[index0 * 3 + 0], + teapotUVCoords[index0 * 3 + 1] + ), + glm::vec2( + teapotUVCoords[index1 * 3 + 0], + teapotUVCoords[index1 * 3 + 1] + ), + glm::vec2( + teapotUVCoords[index2 * 3 + 0], + teapotUVCoords[index2 * 3 + 1] + ) + }; + + const glm::vec3 tangent = generateTangent(positions, uvs); + + teapotTangents[index0] += tangent; + teapotTangents[index1] += tangent; + teapotTangents[index2] += tangent; + + teapotTangentWeights[index0]++; + teapotTangentWeights[index1]++; + teapotTangentWeights[index2]++; + } + + for (size_t i = 0; i < teapotTangents.size(); i++) { + if (teapotTangentWeights[i] <= 0) { + continue; + } + + teapotTangents[i] /= teapotTangentWeights[i]; + } + + const auto& position = getPosition(); + const auto scale = getScale(); + + for (size_t i = 0; i < vertexCount; i++) { + teapotVertices[i * 3 + 0] = teapotVertices[i * 3 + 0] * scale + position.x; + teapotVertices[i * 3 + 1] = teapotVertices[i * 3 + 1] * scale + position.y; + teapotVertices[i * 3 + 2] = teapotVertices[i * 3 + 2] * scale + position.z; + } + + auto positionBuffer = buffer<float>(core, BufferType::VERTEX, teapotVertices.size()); + positionBuffer.fill(teapotVertices); + + auto normalBuffer = buffer<float>(core, BufferType::VERTEX, teapotNormals.size()); + normalBuffer.fill(teapotNormals); + + auto uvBuffer = buffer<float>(core, BufferType::VERTEX, teapotUVCoords.size()); + uvBuffer.fill(teapotUVCoords); + + auto tangentBuffer = buffer<glm::vec3>(core, BufferType::VERTEX, teapotTangents.size()); + tangentBuffer.fill(teapotTangents); + + auto indexBuffer = buffer<uint16_t>(core, BufferType::INDEX, teapotIndices.size()); + indexBuffer.fill(teapotIndices); + + VertexData data ({ + vkcv::vertexBufferBinding(positionBuffer.getHandle()), + vkcv::vertexBufferBinding(normalBuffer.getHandle()), + vkcv::vertexBufferBinding(uvBuffer.getHandle()), + vkcv::vertexBufferBinding(tangentBuffer.getHandle()) + }); + + data.setIndexBuffer(indexBuffer.getHandle()); + data.setCount(indexBuffer.getCount()); + + return data; + } + +} diff --git a/modules/geometry/src/vkcv/geometry/Volume.cpp b/modules/geometry/src/vkcv/geometry/Volume.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b503eb907aea6cc0aaeb24affb485d4d101bf4d --- /dev/null +++ b/modules/geometry/src/vkcv/geometry/Volume.cpp @@ -0,0 +1,13 @@ + +#include "vkcv/geometry/Volume.hpp" + +namespace vkcv::geometry { + + Volume::Volume(const glm::vec3 &position) + : Geometry(position) {} + + bool Volume::contains(const glm::vec3 &point) { + return (this->distanceTo(point) <= 0.0f); + } + +} diff --git a/modules/gui/CMakeLists.txt b/modules/gui/CMakeLists.txt index 3b5202ccfe454f38745c53ac711cc05095ef88a1..34d80c934f0444e3afb02ae3a22ec3c4a49b8eff 100644 --- a/modules/gui/CMakeLists.txt +++ b/modules/gui/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(vkcv_gui) # setting c++ standard for the module -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(vkcv_gui_source ${PROJECT_SOURCE_DIR}/src) @@ -22,7 +22,7 @@ set(vkcv_gui_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_gui_lib}) include(config/ImGui.cmake) # adding source files to the module -add_library(vkcv_gui STATIC ${vkcv_gui_sources} ${vkcv_imgui_sources}) +add_library(vkcv_gui ${vkcv_build_attribute} ${vkcv_gui_sources} ${vkcv_imgui_sources}) # link the required libraries to the module target_link_libraries(vkcv_gui ${vkcv_gui_libraries} vkcv ${vkcv_libraries}) @@ -34,3 +34,11 @@ target_include_directories(vkcv_gui SYSTEM BEFORE PRIVATE ${vkcv_gui_includes} $ target_include_directories(vkcv_gui BEFORE PUBLIC ${vkcv_gui_include} ${vkcv_imgui_includes}) target_compile_definitions(vkcv_gui PUBLIC ${vkcv_gui_defines}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_gui_include}) + list(APPEND vkcv_modules_libraries vkcv_gui) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/gui/README.md b/modules/gui/README.md new file mode 100644 index 0000000000000000000000000000000000000000..823900829d5a3ca7b2f64dc1c05bda3e9dd55b87 --- /dev/null +++ b/modules/gui/README.md @@ -0,0 +1,16 @@ +# GUI + +A VkCV module to integrate GUI rendering to your application as additional pass + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|----------------------------------------------------|---| +| [ImGUI](https://github.com/ocornut/imgui/) | ✅ | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__gui.html) to this module. + diff --git a/modules/gui/config/ImGui.cmake b/modules/gui/config/ImGui.cmake index 90cdafdeee355af9e63723632572799e135b04da..624ea7701e245ab8c20731d2b226896667c18f89 100644 --- a/modules/gui/config/ImGui.cmake +++ b/modules/gui/config/ImGui.cmake @@ -1,5 +1,7 @@ -if (EXISTS "${vkcv_gui_lib_path}/imgui") +use_git_submodule("${vkcv_gui_lib_path}/imgui" imgui_status) + +if (${imgui_status}) list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_glfw.cpp) list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_glfw.h) list(APPEND vkcv_imgui_sources ${vkcv_gui_lib_path}/imgui/backends/imgui_impl_vulkan.cpp) @@ -22,6 +24,4 @@ if (EXISTS "${vkcv_gui_lib_path}/imgui") list(APPEND vkcv_gui_include ${vkcv_gui_lib}) list(APPEND vkcv_gui_defines IMGUI_DISABLE_WIN32_FUNCTIONS=1) -else() - message(WARNING "IMGUI is required..! Update the submodules!") endif () diff --git a/modules/gui/include/vkcv/gui/GUI.hpp b/modules/gui/include/vkcv/gui/GUI.hpp index d1a9986c5f69bfd9d4392bd5ae50f0b1f8b60642..303fbb2c0920b400cb7637641a70e26665b4dabd 100644 --- a/modules/gui/include/vkcv/gui/GUI.hpp +++ b/modules/gui/include/vkcv/gui/GUI.hpp @@ -9,31 +9,75 @@ namespace vkcv::gui { + /** + * @defgroup vkcv_gui GUI Module + * A module to manage ImGUI integration for VkCV applications. + * @{ + */ + + /** + * Class to manage ImGui integration for VkCV. + */ class GUI final { private: - Window& m_window; + /** + * Window handle of the currently used window to draw user interface on. + */ + WindowHandle m_windowHandle; + + /** + * Reference to the current Core instance. + */ Core& m_core; - + + /** + * Reference to the current Context instance. + */ const Context& m_context; - + + /** + * ImGui context for drawing GUI. + */ ImGuiContext* m_gui_context; - + + /** + * Vulkan handle for the ImGui descriptor pool. + */ vk::DescriptorPool m_descriptor_pool; + + /** + * Vulkan handle for the ImGui render pass. + */ vk::RenderPass m_render_pass; - + + /** + * Event handle for pressing a mouse button. + */ event_handle<int,int,int> f_mouseButton; + + /** + * Event handle for scrolling with the mouse or touchpad. + */ event_handle<double,double> f_mouseScroll; + + /** + * Event handle for pressing a key. + */ event_handle<int,int,int,int> f_key; + + /** + * Event handle for typing a character. + */ event_handle<unsigned int> f_char; public: /** - * Constructor of a new instance of ImGui management + * Constructor of a new instance for ImGui management. * - * @param core Valid #Core instance of the framework - * @param window Valid #Window instance of the framework + * @param[in,out] core Valid Core instance of the framework + * @param[in,out] windowHandle Valid Window handle of the framework */ - GUI(Core& core, Window& window); + GUI(Core& core, WindowHandle& windowHandle); GUI(const GUI& other) = delete; GUI(GUI&& other) = delete; @@ -42,12 +86,12 @@ namespace vkcv::gui { GUI& operator=(GUI&& other) = delete; /** - * Destructor of a #GUI instance + * Destructor of a GUI instance. */ virtual ~GUI(); /** - * Sets up a new frame for ImGui to draw + * Sets up a new frame for ImGui to draw. */ void beginGUI(); @@ -59,4 +103,6 @@ namespace vkcv::gui { }; + /** @} */ + } diff --git a/modules/gui/lib/imgui b/modules/gui/lib/imgui index d5828cd988db525f27128edeadb1a689cd2d7461..08752b372e5ebeb39adec59387590dac9d9e68f7 160000 --- a/modules/gui/lib/imgui +++ b/modules/gui/lib/imgui @@ -1 +1 @@ -Subproject commit d5828cd988db525f27128edeadb1a689cd2d7461 +Subproject commit 08752b372e5ebeb39adec59387590dac9d9e68f7 diff --git a/modules/gui/src/vkcv/gui/GUI.cpp b/modules/gui/src/vkcv/gui/GUI.cpp index 38bb6894fb2b40c6ab10445f19431f87f7370afc..15a1b049aa5496bdcb9ecb81c00f82b66ad86d71 100644 --- a/modules/gui/src/vkcv/gui/GUI.cpp +++ b/modules/gui/src/vkcv/gui/GUI.cpp @@ -6,6 +6,9 @@ namespace vkcv::gui { + const static vk::ImageLayout initialImageLayout = vk::ImageLayout::eColorAttachmentOptimal; + const static vk::ImageLayout finalImageLayout = vk::ImageLayout::ePresentSrcKHR; + static void checkVulkanResult(VkResult resultCode) { if (resultCode == 0) return; @@ -15,31 +18,33 @@ namespace vkcv::gui { vkcv_log(LogLevel::ERROR, "ImGui has a problem with Vulkan! (%s)", vk::to_string(result).c_str()); } - GUI::GUI(Core& core, Window& window) : - m_window(window), + GUI::GUI(Core& core, WindowHandle& windowHandle) : + m_windowHandle(windowHandle), m_core(core), m_context(m_core.getContext()), m_gui_context(nullptr) { IMGUI_CHECKVERSION(); m_gui_context = ImGui::CreateContext(); + + Window& window = m_core.getWindow(windowHandle); + + ImGui_ImplGlfw_InitForVulkan(window.getWindow(), false); - ImGui_ImplGlfw_InitForVulkan(m_window.getWindow(), false); - - f_mouseButton = m_window.e_mouseButton.add([&](int button, int action, int mods) { - ImGui_ImplGlfw_MouseButtonCallback(m_window.getWindow(), button, action, mods); + f_mouseButton = window.e_mouseButton.add([&](int button, int action, int mods) { + ImGui_ImplGlfw_MouseButtonCallback(window.getWindow(), button, action, mods); }); - f_mouseScroll = m_window.e_mouseScroll.add([&](double xoffset, double yoffset) { - ImGui_ImplGlfw_ScrollCallback(m_window.getWindow(), xoffset, yoffset); + f_mouseScroll = window.e_mouseScroll.add([&](double xoffset, double yoffset) { + ImGui_ImplGlfw_ScrollCallback(window.getWindow(), xoffset, yoffset); }); - f_key = m_window.e_key.add([&](int key, int scancode, int action, int mods) { - ImGui_ImplGlfw_KeyCallback(m_window.getWindow(), key, scancode, action, mods); + f_key = window.e_key.add([&](int key, int scancode, int action, int mods) { + ImGui_ImplGlfw_KeyCallback(window.getWindow(), key, scancode, action, mods); }); - f_char = m_window.e_char.add([&](unsigned int c) { - ImGui_ImplGlfw_CharCallback(m_window.getWindow(), c); + f_char = window.e_char.add([&](unsigned int c) { + ImGui_ImplGlfw_CharCallback(window.getWindow(), c); }); vk::DescriptorPoolSize pool_sizes[] = { @@ -66,7 +71,8 @@ namespace vkcv::gui { m_descriptor_pool = m_context.getDevice().createDescriptorPool(descriptorPoolCreateInfo); const vk::PhysicalDevice& physicalDevice = m_context.getPhysicalDevice(); - const Swapchain& swapchain = m_core.getSwapchain(); + const SwapchainHandle& swapchainHandle = m_core.getWindow(m_windowHandle).getSwapchain(); + const uint32_t swapchainImageCount = m_core.getSwapchainImageCount(swapchainHandle); const uint32_t graphicsQueueFamilyIndex = ( m_context.getQueueManager().getGraphicsQueues()[0].familyIndex @@ -81,20 +87,20 @@ namespace vkcv::gui { init_info.PipelineCache = 0; init_info.DescriptorPool = static_cast<VkDescriptorPool>(m_descriptor_pool); init_info.Allocator = nullptr; - init_info.MinImageCount = swapchain.getImageCount(); - init_info.ImageCount = swapchain.getImageCount(); + init_info.MinImageCount = swapchainImageCount; + init_info.ImageCount = swapchainImageCount; init_info.CheckVkResultFn = checkVulkanResult; const vk::AttachmentDescription attachment ( vk::AttachmentDescriptionFlags(), - swapchain.getFormat(), + m_core.getSwapchainFormat(swapchainHandle), vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, - vk::ImageLayout::eUndefined, - vk::ImageLayout::ePresentSrcKHR + initialImageLayout, + finalImageLayout ); const vk::AttachmentReference attachmentReference ( @@ -139,31 +145,33 @@ namespace vkcv::gui { ImGui_ImplVulkan_Init(&init_info, static_cast<VkRenderPass>(m_render_pass)); - const SubmitInfo submitInfo { QueueType::Graphics, {}, {} }; + auto stream = m_core.createCommandStream(QueueType::Graphics); - m_core.recordAndSubmitCommandsImmediate(submitInfo, [](const vk::CommandBuffer& commandBuffer) { + m_core.recordCommandsToStream(stream, [](const vk::CommandBuffer& commandBuffer) { ImGui_ImplVulkan_CreateFontsTexture(static_cast<VkCommandBuffer>(commandBuffer)); }, []() { ImGui_ImplVulkan_DestroyFontUploadObjects(); }); + m_core.submitCommandStream(stream, false); m_context.getDevice().waitIdle(); } GUI::~GUI() { m_context.getDevice().waitIdle(); - + Window& window = m_core.getWindow(m_windowHandle); + ImGui_ImplVulkan_Shutdown(); m_context.getDevice().destroyRenderPass(m_render_pass); m_context.getDevice().destroyDescriptorPool(m_descriptor_pool); ImGui_ImplGlfw_Shutdown(); - - m_window.e_mouseButton.remove(f_mouseButton); - m_window.e_mouseScroll.remove(f_mouseScroll); - m_window.e_key.remove(f_key); - m_window.e_char.remove(f_char); + + window.e_mouseButton.remove(f_mouseButton); + window.e_mouseScroll.remove(f_mouseScroll); + window.e_key.remove(f_key); + window.e_char.remove(f_char); if (m_gui_context) { ImGui::DestroyContext(m_gui_context); @@ -171,11 +179,11 @@ namespace vkcv::gui { } void GUI::beginGUI() { - const Swapchain& swapchain = m_core.getSwapchain(); - const auto extent = swapchain.getExtent(); + const auto swapchainHandle = m_core.getWindow(m_windowHandle).getSwapchain(); + const auto& extent = m_core.getSwapchainExtent(swapchainHandle); if ((extent.width > 0) && (extent.height > 0)) { - ImGui_ImplVulkan_SetMinImageCount(swapchain.getImageCount()); + ImGui_ImplVulkan_SetMinImageCount(m_core.getSwapchainImageCount(swapchainHandle)); } ImGui_ImplVulkan_NewFrame(); @@ -194,9 +202,9 @@ namespace vkcv::gui { return; } - const Swapchain& swapchain = m_core.getSwapchain(); - const auto extent = swapchain.getExtent(); - + const auto swapchainHandle = m_core.getWindow(m_windowHandle).getSwapchain(); + const auto& extent = m_core.getSwapchainExtent(swapchainHandle); + const vk::ImageView swapchainImageView = m_core.getSwapchainImageView(); const vk::FramebufferCreateInfo framebufferCreateInfo ( @@ -210,11 +218,13 @@ namespace vkcv::gui { ); const vk::Framebuffer framebuffer = m_context.getDevice().createFramebuffer(framebufferCreateInfo); + auto stream = m_core.createCommandStream(QueueType::Graphics); - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Graphics; - - m_core.recordAndSubmitCommandsImmediate(submitInfo, [&](const vk::CommandBuffer& commandBuffer) { + m_core.recordCommandsToStream(stream, [&](const vk::CommandBuffer& commandBuffer) { + + assert(initialImageLayout == vk::ImageLayout::eColorAttachmentOptimal); + m_core.prepareImageForAttachmentManually(commandBuffer, vkcv::ImageHandle::createSwapchainImageHandle()); + const vk::Rect2D renderArea ( vk::Offset2D(0, 0), extent @@ -227,15 +237,22 @@ namespace vkcv::gui { 0, nullptr ); - + commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eInline); ImGui_ImplVulkan_RenderDrawData(drawData, static_cast<VkCommandBuffer>(commandBuffer)); commandBuffer.endRenderPass(); + + // executing the renderpass changed the image layout without going through the image manager + // therefore the layout must be updated manually + m_core.updateImageLayoutManual(vkcv::ImageHandle::createSwapchainImageHandle(), finalImageLayout); + }, [&]() { m_context.getDevice().destroyFramebuffer(framebuffer); }); + + m_core.submitCommandStream(stream, false); } } diff --git a/modules/material/CMakeLists.txt b/modules/material/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ee3b36e8f411f3d4d6d86a19e498f2faa0513f86 --- /dev/null +++ b/modules/material/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_material) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_material_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_material_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_material_sources + ${vkcv_material_include}/vkcv/material/Material.hpp + ${vkcv_material_source}/vkcv/material/Material.cpp +) + +# adding source files to the module +add_library(vkcv_material ${vkcv_build_attribute} ${vkcv_material_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_material vkcv ${vkcv_libraries}) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_material SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes}) + +# add the own include directory for public headers +target_include_directories(vkcv_material BEFORE PUBLIC ${vkcv_material_include}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_material_include}) + list(APPEND vkcv_modules_libraries vkcv_material) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/material/README.md b/modules/material/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a3cff07cc25900f4d06ee9059dcd7175d14f3e3d --- /dev/null +++ b/modules/material/README.md @@ -0,0 +1,7 @@ +# Material + +A VkCV module to abstract typical kinds of materials for rendering + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__asset.html) to this module. diff --git a/modules/material/include/vkcv/material/Material.hpp b/modules/material/include/vkcv/material/Material.hpp new file mode 100644 index 0000000000000000000000000000000000000000..345d22912b368ba5bb84e96d6ce8b1dbe49e8337 --- /dev/null +++ b/modules/material/include/vkcv/material/Material.hpp @@ -0,0 +1,212 @@ +#pragma once + +#include <vector> + +#include <vkcv/Core.hpp> +#include <vkcv/Downsampler.hpp> +#include <vkcv/Handles.hpp> + +namespace vkcv::material { + + /** + * @defgroup vkcv_material Material Module + * A module to manage standardized materials for rendering. + * @{ + */ + + /** + * Enum to handle standardized material types. + */ + enum class MaterialType { + /** + * The material can be used for physically based rendering. + */ + PBR_MATERIAL = 1, + + /** + * The type is unknown. + */ + UNKNOWN = 0 + }; + + /** + * Class to manage required handles for materials using + * a wide range of textures with separate samplers and factors. + */ + class Material { + private: + + /** + * A nested structure for textures used by a material. + */ + struct Texture { + /** + * The image handle of a texture. + */ + ImageHandle m_Image; + + /** + * The sampler handle of a texture. + */ + SamplerHandle m_Sampler; + + /** + * The list of custom factors for a given texture. + */ + std::vector<float> m_Factors; + }; + + /** + * The type of a material. + */ + MaterialType m_Type; + + /** + * The descriptor set handle of a material. + */ + DescriptorSetHandle m_DescriptorSet; + + /** + * The descriptor set layout used by a material. + */ + DescriptorSetLayoutHandle m_DescriptorSetLayout; + + /** + * The list of textures used by a material. + */ + std::vector<Texture> m_Textures; + + public: + /** + * Default constructor to create an invalid material instance. + */ + Material(); + + /** + * Destructor to release handles and resources of a material instance. + */ + ~Material() = default; + + /** + * Copy-constructor to copy a given material instance. + * @param other Other material + */ + Material(const Material& other) = default; + + /** + * Move-constructor to move a given material instance. + * @param other Other material + */ + Material(Material&& other) = default; + + /** + * Copy-operator to copy a given material instance. + * @param other Other material + * @return Reference to the material + */ + Material& operator=(const Material& other) = default; + + /** + * Move-operator to move a given material instance. + * @param other Other material + * @return Reference to the material + */ + Material& operator=(Material&& other) = default; + + /** + * Returns the type of a material as MaterialType. + * @return Type of material + */ + [[nodiscard]] + MaterialType getType() const; + + /** + * Returns the descriptor set handle of the material. + * @return Descriptor set handle + */ + [[nodiscard]] + const DescriptorSetHandle& getDescriptorSet() const; + + /** + * Returns the descriptor set layout handle used by the material. + * @return Descriptor set layout handle + */ + [[nodiscard]] + const DescriptorSetLayoutHandle& getDescriptorSetLayout() const; + + /** + * Checks if the material is valid and returns the status + * as boolean value. + * @return true if the material is valid, otherwise false + */ + explicit operator bool() const; + + /** + * Checks if the material is invalid and returns the status + * as boolean value. + * @return true if the material is invalid, otherwise false + */ + bool operator!() const; + + /** + * @brief Records mip chain generation to command stream of the whole material. + * + * @param[out] cmdStream Command stream that the commands are recorded into + * @param[in,out] downsampler Downsampler to generate mip levels with + */ + void recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream, + Downsampler &downsampler); + + /** + * Returns the descriptor bindings required by a given material + * type to create the descriptor set layout. + * @param[in] type Type of material + * @return Descriptor bindings of a material type + */ + static const DescriptorBindings& getDescriptorBindings(MaterialType type); + + /** + * Creates a new valid material which supports physically based + * rendering. + * @param[in,out] core Reference to Core instance + * @param[in] colorImg Base color image handle + * @param[in] colorSmp Base color sampler handle + * @param[in] normalImg Normal map image handle + * @param[in] normalSmp Normal map sampler handle + * @param[in] metRoughImg Metallic and roughness image handle + * @param[in] metRoughSmp Metallic and roughness sampler handle + * @param[in] occlusionImg Occlusion map image handle + * @param[in] occlusionSmp Occlusion map sampler handle + * @param[in] emissiveImg Emissive image handle + * @param[in] emissiveSmp Emissive sampler handle + * @param[in] baseColorFactor 4D vector of base color factors + * @param[in] metallicFactor Metallic factor + * @param[in] roughnessFactor Roughness factor + * @param[in] normalScale Scale of normal map + * @param[in] occlusionStrength Strength of occlusion + * @param[in] emissiveFactor 3D vector of emmisive factors + * @return New material instance + */ + static Material createPBR(Core &core, + const ImageHandle &colorImg, + const SamplerHandle &colorSmp, + const ImageHandle &normalImg, + const SamplerHandle &normalSmp, + const ImageHandle &metRoughImg, + const SamplerHandle &metRoughSmp, + const ImageHandle &occlusionImg, + const SamplerHandle &occlusionSmp, + const ImageHandle &emissiveImg, + const SamplerHandle &emissiveSmp, + const float baseColorFactor [4], + float metallicFactor, + float roughnessFactor, + float normalScale, + float occlusionStrength, + const float emissiveFactor [3]); + + }; + + /** @} */ + +} diff --git a/modules/material/src/vkcv/material/Material.cpp b/modules/material/src/vkcv/material/Material.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8187b5de9205ba18ca32cd23dba9d5b457696712 --- /dev/null +++ b/modules/material/src/vkcv/material/Material.cpp @@ -0,0 +1,197 @@ + +#include "vkcv/material/Material.hpp" + +#include <vkcv/Image.hpp> +#include <vkcv/Sampler.hpp> + +namespace vkcv::material { + + Material::Material() { + m_Type = MaterialType::UNKNOWN; + } + + MaterialType Material::getType() const { + return m_Type; + } + + const DescriptorSetHandle & Material::getDescriptorSet() const { + return m_DescriptorSet; + } + + const DescriptorSetLayoutHandle & Material::getDescriptorSetLayout() const { + return m_DescriptorSetLayout; + } + + Material::operator bool() const { + return (m_Type != MaterialType::UNKNOWN); + } + + bool Material::operator!() const { + return (m_Type == MaterialType::UNKNOWN); + } + + void Material::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream, + Downsampler &downsampler) { + for (auto& texture : m_Textures) { + downsampler.recordDownsampling(cmdStream, texture.m_Image); + } + } + + const DescriptorBindings& Material::getDescriptorBindings(MaterialType type) + { + static DescriptorBindings pbr_bindings = {}; + static DescriptorBindings default_bindings = {}; + + switch (type) { + case MaterialType::PBR_MATERIAL: + if (pbr_bindings.empty()) + { + pbr_bindings.insert(std::make_pair(0, DescriptorBinding { + 0, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(1, DescriptorBinding { + 1, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(2, DescriptorBinding { + 2, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(3, DescriptorBinding { + 3, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(4, DescriptorBinding { + 4, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(5, DescriptorBinding { + 5, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(6, DescriptorBinding { + 6, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(7, DescriptorBinding { + 7, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(8, DescriptorBinding { + 8, DescriptorType::IMAGE_SAMPLED, 1, ShaderStage::FRAGMENT, false + })); + pbr_bindings.insert(std::make_pair(9, DescriptorBinding { + 9, DescriptorType::SAMPLER, 1, ShaderStage::FRAGMENT, false + })); + } + + return pbr_bindings; + default: + return default_bindings; + } + } + + static void fillImage(Image& image, float data [4]) { + std::vector<float> vec (image.getWidth() * image.getHeight() * image.getDepth() * 4); + + for (size_t i = 0; i < vec.size(); i++) { + vec[i] = data[i % 4]; + } + + image.fill(data); + } + + Material Material::createPBR(Core &core, + const ImageHandle &colorImg, const SamplerHandle &colorSmp, + const ImageHandle &normalImg, const SamplerHandle &normalSmp, + const ImageHandle &metRoughImg, const SamplerHandle &metRoughSmp, + const ImageHandle &occlusionImg, const SamplerHandle &occlusionSmp, + const ImageHandle &emissiveImg, const SamplerHandle &emissiveSmp, + const float baseColorFactor [4], + float metallicFactor, + float roughnessFactor, + float normalScale, + float occlusionStrength, + const float emissiveFactor [3]) { + ImageHandle images [5] = { + colorImg, normalImg, metRoughImg, occlusionImg, emissiveImg + }; + + SamplerHandle samplers [5] = { + colorSmp, normalSmp, metRoughSmp, occlusionSmp, emissiveSmp + }; + + if (!colorImg) { + vkcv::Image defaultColor = image(core, vk::Format::eR8G8B8A8Srgb, 2, 2); + float colorData [4] = { 228, 51, 255, 1 }; + fillImage(defaultColor, colorData); + images[0] = defaultColor.getHandle(); + } + + if (!normalImg) { + vkcv::Image defaultNormal = image(core, vk::Format::eR8G8B8A8Unorm, 2, 2); + float normalData [4] = { 0, 0, 1, 0 }; + fillImage(defaultNormal, normalData); + images[1] = defaultNormal.getHandle(); + } + + if (!metRoughImg) { + vkcv::Image defaultRough = image(core, vk::Format::eR8G8B8A8Unorm, 2, 2); + float roughData [4] = { 228, 51, 255, 1 }; + fillImage(defaultRough, roughData); + images[2] = defaultRough.getHandle(); + } + + if (!occlusionImg) { + vkcv::Image defaultOcclusion = image(core, vk::Format::eR8G8B8A8Unorm, 2, 2); + float occlusionData [4] = { 228, 51, 255, 1 }; + fillImage(defaultOcclusion, occlusionData); + images[3] = defaultOcclusion.getHandle(); + } + + if (!emissiveImg) { + vkcv::Image defaultEmissive = image(core, vk::Format::eR8G8B8A8Srgb, 2, 2); + float emissiveData [4] = { 0, 0, 0, 1 }; + fillImage(defaultEmissive, emissiveData); + images[4] = defaultEmissive.getHandle(); + } + + if (!colorSmp) { + samplers[0] = samplerLinear(core); + } + + if (!normalSmp) { + samplers[1] = samplerLinear(core); + } + + if (!metRoughSmp) { + samplers[2] = samplerLinear(core); + } + + if (!occlusionSmp) { + samplers[3] = samplerLinear(core); + } + + if (!emissiveSmp) { + samplers[4] = samplerLinear(core); + } + + Material material; + material.m_Type = MaterialType::PBR_MATERIAL; + + const auto& bindings = getDescriptorBindings(material.m_Type); + material.m_DescriptorSetLayout = core.createDescriptorSetLayout(bindings); + material.m_DescriptorSet = core.createDescriptorSet(material.m_DescriptorSetLayout);; + + material.m_Textures.reserve(bindings.size()); + material.m_Textures.push_back({ images[0], samplers[0], std::vector<float>(baseColorFactor, baseColorFactor+4) }); + material.m_Textures.push_back({ images[1], samplers[1], { normalScale } }); + material.m_Textures.push_back({ images[2], samplers[2], { metallicFactor, roughnessFactor } }); + material.m_Textures.push_back({ images[3], samplers[3], { occlusionStrength } }); + material.m_Textures.push_back({ images[4], samplers[4], std::vector<float>(emissiveFactor, emissiveFactor+3) }); + + vkcv::DescriptorWrites setWrites; + + for (size_t i = 0; i < material.m_Textures.size(); i++) { + setWrites.writeSampledImage(i * 2, material.m_Textures[i].m_Image); + setWrites.writeSampler(i * 2 + 1, material.m_Textures[i].m_Sampler); + } + + core.writeDescriptorSet(material.m_DescriptorSet, setWrites); + return material; + } + +} diff --git a/modules/meshlet/CMakeLists.txt b/modules/meshlet/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a3b232e56ec86f65e1114c6ee570f512e7d3b13b --- /dev/null +++ b/modules/meshlet/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_meshlet) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_meshlet_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_meshlet_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_meshlet_sources + ${vkcv_meshlet_include}/vkcv/meshlet/Meshlet.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Meshlet.cpp + + ${vkcv_meshlet_include}/vkcv/meshlet/Tipsify.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Tipsify.cpp + + ${vkcv_meshlet_include}/vkcv/meshlet/Forsyth.hpp + ${vkcv_meshlet_source}/vkcv/meshlet/Forsyth.cpp) + +# adding source files to the module +add_library(vkcv_meshlet ${vkcv_build_attribute} ${vkcv_meshlet_sources}) + + +# link the required libraries to the module +target_link_libraries(vkcv_meshlet vkcv ${vkcv_libraries}) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_meshlet SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_meshlet BEFORE PUBLIC ${vkcv_meshlet_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(vkcv_meshlet vkcv vkcv_asset_loader vkcv_camera) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_meshlet_include}) + list(APPEND vkcv_modules_libraries vkcv_meshlet) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/meshlet/README.md b/modules/meshlet/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4ef209723aa95b8d2e50ff6804f19819f3e11d54 --- /dev/null +++ b/modules/meshlet/README.md @@ -0,0 +1,7 @@ +# Meshlet + +A VkCV module to divide vertex data of a mesh into meshlets + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__meshlet.html) to this module. diff --git a/modules/meshlet/include/vkcv/meshlet/Forsyth.hpp b/modules/meshlet/include/vkcv/meshlet/Forsyth.hpp new file mode 100644 index 0000000000000000000000000000000000000000..90625bd470ac4ecbc27f2e6f95ab9e18734557f8 --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Forsyth.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "Meshlet.hpp" + +namespace vkcv::meshlet +{ + + /** + * @addtogroup vkcv_meshlet + * @{ + */ + + /** + * Reorders the index buffer, simulating a LRU cache, so that vertices are grouped together in close triangle patches + * @param idxBuf current IndexBuffer + * @param vertexCount of the mesh + * @return new reordered index buffer to replace the input index buffer + * References: + * https://tomforsyth1000.github.io/papers/fast_vert_cache_opt.html + * https://www.martin.st/thesis/efficient_triangle_reordering.pdf + * https://github.com/vivkin/forsyth/blob/master/forsyth.h + */ + VertexCacheReorderResult forsythReorder(const std::vector<uint32_t> &idxBuf, const size_t vertexCount); + + /** @} */ + +} diff --git a/modules/meshlet/include/vkcv/meshlet/Meshlet.hpp b/modules/meshlet/include/vkcv/meshlet/Meshlet.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ee32878726123a8d51c3816c9df2644da4ae9b6c --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Meshlet.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include <vector> +#include <map> +#include <glm/glm.hpp> +#include <vkcv/asset/asset_loader.hpp> + +namespace vkcv::meshlet { + + /** + * @defgroup vkcv_meshlet Meshlet Module + * A module to convert meshes into meshlets for rendering via mesh shaders. + * @{ + */ + + struct Vertex { + glm::vec3 position; + float padding0; + glm::vec3 normal; + float padding1; + }; + + struct Meshlet { + uint32_t vertexOffset; + uint32_t vertexCount; + uint32_t indexOffset; + uint32_t indexCount; + glm::vec3 meanPosition; + float boundingSphereRadius; + }; + + struct VertexCacheReorderResult { + /** + * @param indexBuffer new indexBuffer + * @param skippedIndices indices that have a spacial break + */ + VertexCacheReorderResult(const std::vector<uint32_t> indexBuffer, const std::vector<uint32_t> skippedIndices) + :indexBuffer(indexBuffer), skippedIndices(skippedIndices) {} + + std::vector<uint32_t> indexBuffer; + std::vector<uint32_t> skippedIndices; + }; + + struct MeshShaderModelData { + std::vector<Vertex> vertices; + std::vector<uint32_t> localIndices; + std::vector<Meshlet> meshlets; + }; + + std::vector<Vertex> convertToVertices( + const std::vector<uint8_t>& vertexData, + const uint64_t vertexCount, + const vkcv::asset::VertexAttribute& positionAttribute, + const vkcv::asset::VertexAttribute& normalAttribute); + + MeshShaderModelData createMeshShaderModelData( + const std::vector<Vertex>& inVertices, + const std::vector<uint32_t>& inIndices, + const std::vector<uint32_t>& deadEndIndices = {}); + + std::vector<uint32_t> assetLoaderIndicesTo32BitIndices( + const std::vector<uint8_t>& indexData, + vkcv::asset::IndexType indexType); + + /** @} */ + +} \ No newline at end of file diff --git a/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp b/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3c808f9565ee1e73b14356dc0f4c8f4cd7c6dd9c --- /dev/null +++ b/modules/meshlet/include/vkcv/meshlet/Tipsify.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "Meshlet.hpp" +#include <algorithm> +#include <iostream> + +namespace vkcv::meshlet { + + /** + * @addtogroup vkcv_meshlet + * @{ + */ + + /** + * reorders the IndexBuffer, so all usages of vertices to triangle are as close as possible + * @param indexBuffer32Bit current IndexBuffer + * @param vertexCount of the mesh + * @param cacheSize of the priority cache <br> + * Recommended: 20. Keep the value between 5 and 50 <br> + * low: more random and patchy<br> + * high: closer vertices have higher chance -> leads to sinuous lines + * @return new IndexBuffer that replaces the input IndexBuffer, and the indices that are skipped + * + * https://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf + * https://www.martin.st/thesis/efficient_triangle_reordering.pdf + */ + VertexCacheReorderResult tipsifyMesh(const std::vector<uint32_t> &indexBuffer32Bit, + const int vertexCount, const unsigned int cacheSize = 20); + + /** @} */ + +} \ No newline at end of file diff --git a/modules/meshlet/src/vkcv/meshlet/Forsyth.cpp b/modules/meshlet/src/vkcv/meshlet/Forsyth.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd0f160d65b8db81102f9eb6a9d60cf735999d44 --- /dev/null +++ b/modules/meshlet/src/vkcv/meshlet/Forsyth.cpp @@ -0,0 +1,317 @@ +#include "vkcv/meshlet/Forsyth.hpp" +#include <vkcv/Logger.hpp> +#include <array> +#include <cmath> + +namespace vkcv::meshlet +{ + /* + * CACHE AND VALENCE + * SIZE AND SCORE CONSTANTS + * CHANGE AS NEEDED + */ + + // set these to adjust performance and result quality + const size_t VERTEX_CACHE_SIZE = 8; + const size_t CACHE_FUNCTION_LENGTH = 32; + + // score function constants + const float CACHE_DECAY_POWER = 1.5f; + const float LAST_TRI_SCORE = 0.75f; + + const float VALENCE_BOOST_SCALE = 2.0f; + const float VALENCE_BOOST_POWER = 0.5f; + + // sizes for precalculated tables + // make sure that cache score is always >= vertex_cache_size + const size_t CACHE_SCORE_TABLE_SIZE = 32; + const size_t VALENCE_SCORE_TABLE_SIZE = 32; + + // precalculated tables + std::array<float, CACHE_SCORE_TABLE_SIZE> cachePositionScore = {}; + std::array<float, VALENCE_SCORE_TABLE_SIZE> valenceScore = {}; + + // function to populate the cache position and valence score tables + void initScoreTables() + { + for(size_t i = 0; i < CACHE_SCORE_TABLE_SIZE; i++) + { + float score = 0.0f; + if (i < 3) + { + score = LAST_TRI_SCORE; + } + else + { + const float scaler = 1.0f / static_cast<float>(CACHE_FUNCTION_LENGTH - 3); + score = 1.0f - (i - 3) * scaler; + score = std::pow(score, CACHE_DECAY_POWER); + } + cachePositionScore[i] = score; + } + + for(size_t i = 0; i < VALENCE_SCORE_TABLE_SIZE; i++) + { + const float valenceBoost = std::pow(i, -VALENCE_BOOST_POWER); + const float score = VALENCE_BOOST_SCALE * valenceBoost; + + valenceScore[i] = score; + } + } + + /** + * Return the vertex' score, depending on its current active triangle count and cache position + * Add a valence boost to score, if active triangles are below VALENCE_SCORE_TABLE_SIZE + * @param numActiveTris the active triangles on this vertex + * @param cachePos the vertex' position in the cache + * @return vertex' score + */ + float findVertexScore(uint32_t numActiveTris, int32_t cachePos) + { + if(numActiveTris == 0) + return 0.0f; + + float score = 0.0f; + + if (cachePos >= 0) + score = cachePositionScore[cachePos]; + + if (numActiveTris < VALENCE_SCORE_TABLE_SIZE) + score += valenceScore[numActiveTris]; + + return score; + } + + VertexCacheReorderResult forsythReorder(const std::vector<uint32_t> &idxBuf, const size_t vertexCount) + { + std::vector<uint32_t> skippedIndices; + + initScoreTables(); + + // get the total triangle count from the index buffer + const size_t triangleCount = idxBuf.size() / 3; + + // per-vertex active triangle count + std::vector<uint8_t> numActiveTris(vertexCount, 0); + // iterate over indices, count total occurrences of each vertex + for(const auto index : idxBuf) + { + if(numActiveTris[index] == UINT8_MAX) + { + vkcv_log(LogLevel::ERROR, "Unsupported mesh."); + vkcv_log(LogLevel::ERROR, "Vertex shared by too many triangles."); + return VertexCacheReorderResult({}, {}); + } + + numActiveTris[index]++; + } + + + // allocate remaining vectors + /** + * offsets: contains the vertices' offset into the triangleIndices vector + * Offset itself is the sum of triangles required by the previous vertices + * + * lastScore: the vertices' most recent calculated score + * + * cacheTag: the vertices' most recent cache score + * + * triangleAdded: boolean flags to denote whether a triangle has been processed or not + * + * triangleScore: total score of the three vertices making up the triangle + * + * triangleIndices: indices for the triangles + */ + std::vector<uint32_t> offsets(vertexCount, 0); + std::vector<float> lastScore(vertexCount, 0.0f); + std::vector<int8_t> cacheTag(vertexCount, -1); + + std::vector<bool> triangleAdded(triangleCount, false); + std::vector<float> triangleScore(triangleCount, 0.0f); + + std::vector<int32_t> triangleIndices(idxBuf.size(), 0); + + + // sum the number of active triangles for all previous vertices + // null the number of active triangles afterwards for recalculation in second loop + uint32_t sum = 0; + for(size_t i = 0; i < vertexCount; i++) + { + offsets[i] = sum; + sum += numActiveTris[i]; + numActiveTris[i] = 0; + } + // create the triangle indices, using the newly calculated offsets, and increment numActiveTris + // every vertex should be referenced by a triangle index now + for(size_t i = 0; i < triangleCount; i++) + { + for(size_t j = 0; j < 3; j++) + { + uint32_t v = idxBuf[3 * i + j]; + triangleIndices[offsets[v] + numActiveTris[v]] = static_cast<int32_t>(i); + numActiveTris[v]++; + } + } + + // calculate and initialize the triangle score, by summing the vertices' score + for (size_t i = 0; i < vertexCount; i++) + { + lastScore[i] = findVertexScore(numActiveTris[i], static_cast<int32_t>(cacheTag[i])); + + for(size_t j = 0; j < numActiveTris[i]; j++) + { + triangleScore[triangleIndices[offsets[i] + j]] += lastScore[i]; + } + } + + // find best triangle to start reordering with + int32_t bestTriangle = -1; + float bestScore = -1.0f; + for(size_t i = 0; i < triangleCount; i++) + { + if(triangleScore[i] > bestScore) + { + bestScore = triangleScore[i]; + bestTriangle = static_cast<int32_t>(i); + } + } + + // allocate output triangles + std::vector<int32_t> outTriangles(triangleCount, 0); + uint32_t outPos = 0; + + // initialize cache (with -1) + std::array<int32_t, VERTEX_CACHE_SIZE + 3> cache = {}; + for(auto &element : cache) + { + element = -1; + } + + uint32_t scanPos = 0; + + // begin reordering routine + // output the currently best triangle, as long as there are triangles left to output + while(bestTriangle >= 0) + { + // mark best triangle as added + triangleAdded[bestTriangle] = true; + // output this triangle + outTriangles[outPos++] = bestTriangle; + + // push best triangle's vertices into the cache + for(size_t i = 0; i < 3; i++) + { + uint32_t v = idxBuf[3 * bestTriangle + i]; + + // get vertex' cache position, if its -1, set its position to the end + int8_t endPos = cacheTag[v]; + if(endPos < 0) + endPos = static_cast<int8_t>(VERTEX_CACHE_SIZE + i); + + // shift vertices' cache entries forward by one + for(int8_t j = endPos; j > i; j--) + { + cache[j] = cache[j - 1]; + + // if cache slot is valid vertex, + // update the vertex cache tag accordingly + if (cache[j] >= 0) + cacheTag[cache[j]]++; + } + + // insert current vertex into its new target slot + cache[i] = static_cast<int32_t>(v); + cacheTag[v] = static_cast<int8_t>(i); + + // find current triangle in the list of active triangles + // remove it by moving the last triangle into the slot the current triangle is holding. + for (size_t j = 0; j < numActiveTris[v]; j++) + { + if(triangleIndices[offsets[v] + j] == bestTriangle) + { + triangleIndices[offsets[v] + j] = triangleIndices[offsets[v] + numActiveTris[v] - 1]; + break; + } + } + // shorten the list + numActiveTris[v]--; + } + + // update scores of all triangles in cache + for (size_t i = 0; i < cache.size(); i++) + { + int32_t v = cache[i]; + if (v < 0) + break; + + // this vertex has been pushed outside of the actual cache + if(i >= VERTEX_CACHE_SIZE) + { + cacheTag[v] = -1; + cache[i] = -1; + } + + float newScore = findVertexScore(numActiveTris[v], cacheTag[v]); + float diff = newScore - lastScore[v]; + + for(size_t j = 0; j < numActiveTris[v]; j++) + { + triangleScore[triangleIndices[offsets[v] + j]] += diff; + } + lastScore[v] = newScore; + } + + // find best triangle reference by vertices in cache + bestTriangle = -1; + bestScore = -1.0f; + for(size_t i = 0; i < VERTEX_CACHE_SIZE; i++) + { + if (cache[i] < 0) + break; + + int32_t v = cache[i]; + for(size_t j = 0; j < numActiveTris[v]; j++) + { + int32_t t = triangleIndices[offsets[v] + j]; + if(triangleScore[t] > bestScore) + { + bestTriangle = t; + bestScore = triangleScore[t]; + } + } + } + + // if no triangle was found at all, continue scanning whole list of triangles + if (bestTriangle < 0) + { + for(; scanPos < triangleCount; scanPos++) + { + if(!triangleAdded[scanPos]) + { + bestTriangle = scanPos; + + skippedIndices.push_back(3 * outPos); + + break; + } + } + } + } + + + // convert triangle index array into full triangle list + std::vector<uint32_t> outIndices(idxBuf.size(), 0); + outPos = 0; + for(size_t i = 0; i < triangleCount; i++) + { + int32_t t = outTriangles[i]; + for(size_t j = 0; j < 3; j++) + { + int32_t v = idxBuf[3 * t + j]; + outIndices[outPos++] = static_cast<uint32_t>(v); + } + } + + return VertexCacheReorderResult(outIndices, skippedIndices); + } +} \ No newline at end of file diff --git a/modules/meshlet/src/vkcv/meshlet/Meshlet.cpp b/modules/meshlet/src/vkcv/meshlet/Meshlet.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abcad7207ed5a6f80cb292ab2f7e855d3b4c7797 --- /dev/null +++ b/modules/meshlet/src/vkcv/meshlet/Meshlet.cpp @@ -0,0 +1,167 @@ + +#include "vkcv/meshlet/Meshlet.hpp" +#include <vkcv/Logger.hpp> +#include <cassert> +#include <iostream> + +namespace vkcv::meshlet { + +std::vector<vkcv::meshlet::Vertex> convertToVertices( + const std::vector<uint8_t>& vertexData, + const uint64_t vertexCount, + const vkcv::asset::VertexAttribute& positionAttribute, + const vkcv::asset::VertexAttribute& normalAttribute) { + + assert(positionAttribute.type == vkcv::asset::PrimitiveType::POSITION); + assert(normalAttribute.type == vkcv::asset::PrimitiveType::NORMAL); + + std::vector<vkcv::meshlet::Vertex> vertices; + vertices.reserve(vertexCount); + + const size_t positionStepSize = positionAttribute.stride == 0 ? sizeof(glm::vec3) : positionAttribute.stride; + const size_t normalStepSize = normalAttribute.stride == 0 ? sizeof(glm::vec3) : normalAttribute.stride; + + for (int i = 0; i < vertexCount; i++) { + Vertex v; + + const size_t positionOffset = positionAttribute.offset + positionStepSize * i; + const size_t normalOffset = normalAttribute.offset + normalStepSize * i; + + v.position = *reinterpret_cast<const glm::vec3*>(&(vertexData[positionOffset])); + v.normal = *reinterpret_cast<const glm::vec3*>(&(vertexData[normalOffset])); + vertices.push_back(v); + } + return vertices; +} + +MeshShaderModelData createMeshShaderModelData( + const std::vector<Vertex>& inVertices, + const std::vector<uint32_t>& inIndices, + const std::vector<uint32_t>& deadEndIndices) { + + MeshShaderModelData data; + size_t currentIndex = 0; + + const size_t maxVerticesPerMeshlet = 64; + const size_t maxIndicesPerMeshlet = 126 * 3; + + bool indicesAreLeft = true; + + size_t deadEndIndicesIndex = 0; + + while (indicesAreLeft) { + Meshlet meshlet; + + meshlet.indexCount = 0; + meshlet.vertexCount = 0; + + meshlet.indexOffset = data.localIndices.size(); + meshlet.vertexOffset = data.vertices.size(); + + std::map<uint32_t, uint32_t> globalToLocalIndexMap; + std::vector<uint32_t> globalIndicesOrdered; + + while (true) { + + if (deadEndIndicesIndex < deadEndIndices.size()) { + const uint32_t deadEndIndex = deadEndIndices[deadEndIndicesIndex]; + if (deadEndIndex == currentIndex) { + deadEndIndicesIndex++; + break; + } + } + + indicesAreLeft = currentIndex + 1 <= inIndices.size(); + if (!indicesAreLeft) { + break; + } + + bool enoughSpaceForIndices = meshlet.indexCount + 3 < maxIndicesPerMeshlet; + if (!enoughSpaceForIndices) { + break; + } + + size_t vertexCountToAdd = 0; + for (int i = 0; i < 3; i++) { + const uint32_t globalIndex = inIndices[currentIndex + i]; + const bool containsVertex = globalToLocalIndexMap.find(globalIndex) != globalToLocalIndexMap.end(); + if (!containsVertex) { + vertexCountToAdd++; + } + } + + bool enoughSpaceForVertices = meshlet.vertexCount + vertexCountToAdd < maxVerticesPerMeshlet; + if (!enoughSpaceForVertices) { + break; + } + + for (int i = 0; i < 3; i++) { + const uint32_t globalIndex = inIndices[currentIndex + i]; + + uint32_t localIndex; + const bool indexAlreadyExists = globalToLocalIndexMap.find(globalIndex) != globalToLocalIndexMap.end(); + if (indexAlreadyExists) { + localIndex = globalToLocalIndexMap[globalIndex]; + } + else { + localIndex = globalToLocalIndexMap.size(); + globalToLocalIndexMap[globalIndex] = localIndex; + globalIndicesOrdered.push_back(globalIndex); + } + + data.localIndices.push_back(localIndex); + } + + meshlet.indexCount += 3; + currentIndex += 3; + meshlet.vertexCount += vertexCountToAdd; + } + + for (const uint32_t globalIndex : globalIndicesOrdered) { + const Vertex v = inVertices[globalIndex]; + data.vertices.push_back(v); + } + + // compute mean position + meshlet.meanPosition = glm::vec3(0); + const uint32_t meshletLastVertexIndex = meshlet.vertexOffset + meshlet.vertexCount; + + for (uint32_t vertexIndex = meshlet.vertexOffset; vertexIndex < meshletLastVertexIndex; vertexIndex++) { + const Vertex& v = data.vertices[vertexIndex]; + meshlet.meanPosition += v.position; + } + meshlet.meanPosition /= meshlet.vertexCount; + + // compute bounding sphere radius + meshlet.boundingSphereRadius = 0.f; + for (uint32_t vertexIndex = meshlet.vertexOffset; vertexIndex < meshletLastVertexIndex; vertexIndex++) { + const Vertex& v = data.vertices[vertexIndex]; + const float d = glm::distance(v.position, meshlet.meanPosition); + meshlet.boundingSphereRadius = glm::max(meshlet.boundingSphereRadius, d); + } + + data.meshlets.push_back(meshlet); + } + + return data; +} + +std::vector<uint32_t> assetLoaderIndicesTo32BitIndices(const std::vector<uint8_t>& indexData, vkcv::asset::IndexType indexType) { + std::vector<uint32_t> indices; + if (indexType == vkcv::asset::IndexType::UINT16) { + for (int i = 0; i < indexData.size(); i += 2) { + const uint16_t index16Bit = *reinterpret_cast<const uint16_t *>(&(indexData[i])); + const uint32_t index32Bit = static_cast<uint32_t>(index16Bit); + indices.push_back(index32Bit); + } + } else if (indexType == vkcv::asset::IndexType::UINT32) { + for (int i = 0; i < indexData.size(); i += 4) { + const uint32_t index32Bit = *reinterpret_cast<const uint32_t *>(&(indexData[i])); + indices.push_back(index32Bit); + } + } else { + vkcv_log(vkcv::LogLevel::ERROR, "Unsupported index type"); + } + return indices; +} +} \ No newline at end of file diff --git a/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp b/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp new file mode 100644 index 0000000000000000000000000000000000000000..101a777d4ef52d31579355f52c562a88e058cbb5 --- /dev/null +++ b/modules/meshlet/src/vkcv/meshlet/Tipsify.cpp @@ -0,0 +1,288 @@ + +#include <vkcv/Logger.hpp> +#include "vkcv/meshlet/Tipsify.hpp" +#include <iostream> + +namespace vkcv::meshlet { + + const int maxUsedVertices = 128; + + /** + * modulo operation with maxUsedVertices + * @param number for modulo operation + * @return number between 0 and maxUsedVertices - 1 + */ + int mod( int number ){ + return (number + maxUsedVertices) % maxUsedVertices; + } + + /** + * searches for the next VertexIndex that was used before or returns any vertexIndex if no used was found + * @param livingVertices vertice that still has a living triangle + * @param usedVerticeStack stack of vertices that are recently used + * @param usedVerticeCount number of last used vertices that maxes at @param maxUsedVertices + * @param usedVerticeOffset the @param usedVerticeStack wraps around and overrides old vertices, this offset keeps the wrapping intact + * @param vertexCount total vertex count to terminate correctly + * @param lowestLivingVertexIndex vertexIndex with the lowest number + * @param currentTriangleIndex index of the currently fanned triangle + * @param skippedIndices indices that define a new meshlet, even if they are close in memory + * @return a VertexIndex to be used as fanningVertexIndex + */ + int skipDeadEnd( + const std::vector<uint8_t> &livingVertices, + const std::vector<uint32_t> &usedVerticeStack, + int &usedVerticeCount, + int &usedVerticeOffset, + int vertexCount, + int &lowestLivingVertexIndex, + int ¤tTriangleIndex, + std::vector<uint32_t> &skippedIndices) { + + // returns the latest vertex used that has a living triangle + while (mod(usedVerticeCount) != usedVerticeOffset) { + // iterate from the latest to the oldest. + maxUsedVertices to always make it a positive number in the range 0 to maxUsedVertices -1 + int nextVertex = usedVerticeStack[mod(--usedVerticeCount)]; + + if (livingVertices[nextVertex] > 0) { + return nextVertex; + } + } + // returns any vertexIndex since no last used has a living triangle + while (lowestLivingVertexIndex + 1 < vertexCount) { + lowestLivingVertexIndex++; + if (livingVertices[lowestLivingVertexIndex] > 0) { + // add index of the vertex to skippedIndices + skippedIndices.push_back(static_cast<uint32_t>(currentTriangleIndex * 3)); + return lowestLivingVertexIndex; + } + } + return -1; + } + + /** + * searches for the best next candidate as a fanningVertexIndex + * @param vertexCount total vertexCount of the mesh + * @param lowestLivingVertexIndex vertexIndex with the lowest number + * @param cacheSize number to determine in which range vertices are prioritized to me reused + * @param possibleCandidates candidates for the next vertex to be fanned out + * @param numPossibleCandidates number of all possible candidates + * @param lastTimestampCache number of the last iteration the vertex was used + * @param currentTimeStamp current iteration of all vertices + * @param livingVertices vertice that still has a living triangle + * @param usedVerticeStack stack of vertices that are recently used + * @param usedVerticeCount number of last used vertices that maxes at @param maxUsedVertices + * @param usedVerticeOffset the @param usedVerticeStack wraps around and overrides old vertices, this offset keeps the wrapping intact + * @param currentTriangleIndex index of the currently fanned triangle + * @param skippedIndices indices that define a new meshlet, even if they are close in memory + * @return a VertexIndex to be used as fanningVertexIndex + */ + int getNextVertexIndex(int vertexCount, + int &lowestLivingVertexIndex, + int cacheSize, + const std::vector<uint32_t> &possibleCandidates, + int numPossibleCandidates, + const std::vector<uint32_t> &lastTimestampCache, + int currentTimeStamp, + const std::vector<uint8_t> &livingVertices, + const std::vector<uint32_t> &usedVerticeStack, + int &usedVerticeCount, + int &usedVerticeOffset, + int ¤tTriangleIndex, + std::vector<uint32_t> &skippedIndices) { + int nextVertexIndex = -1; + int maxPriority = -1; + // calculates the next possibleCandidates that is recently used + for (int j = 0; j < numPossibleCandidates; j++) { + int vertexIndex = possibleCandidates[j]; + + // the candidate needs to be not fanned out yet + if (livingVertices[vertexIndex] > 0) { + int priority = -1; + + // prioritizes recent used vertices, but tries not to pick one that has many triangles -> fills holes better + if ( currentTimeStamp - lastTimestampCache[vertexIndex] + 2 * livingVertices[vertexIndex] <= + cacheSize) { + priority = currentTimeStamp - lastTimestampCache[vertexIndex]; + } + // select the vertexIndex with the highest priority + if (priority > maxPriority) { + maxPriority = priority; + nextVertexIndex = vertexIndex; + } + } + } + + // if no candidate is alive, try and find another one + if (nextVertexIndex == -1) { + nextVertexIndex = skipDeadEnd( + livingVertices, + usedVerticeStack, + usedVerticeCount, + usedVerticeOffset, + vertexCount, + lowestLivingVertexIndex, + currentTriangleIndex, + skippedIndices); + } + return nextVertexIndex; + } + + VertexCacheReorderResult tipsifyMesh( + const std::vector<uint32_t> &indexBuffer32Bit, + const int vertexCount, + const unsigned int cacheSize) { + + if (indexBuffer32Bit.empty() || vertexCount <= 0) { + vkcv_log(LogLevel::ERROR, "Invalid Input."); + return VertexCacheReorderResult(indexBuffer32Bit , {}); + } + int triangleCount = indexBuffer32Bit.size() / 3; + + // dynamic array for vertexOccurrence + std::vector<uint8_t> vertexOccurrence(vertexCount, 0); + // count the occurrence of a vertex in all among all triangles + for (size_t i = 0; i < triangleCount * 3; i++) { + vertexOccurrence[indexBuffer32Bit[i]]++; + } + + int sum = 0; + std::vector<uint32_t> offsetVertexOccurrence(vertexCount + 1, 0); + // highest offset for later iteration + int maxOffset = 0; + // calculate the offset of each vertex from the start + for (int i = 0; i < vertexCount; i++) { + offsetVertexOccurrence[i] = sum; + sum += vertexOccurrence[i]; + + if (vertexOccurrence[i] > maxOffset) { + maxOffset = vertexOccurrence[i]; + } + // reset for reuse + vertexOccurrence[i] = 0; + } + offsetVertexOccurrence[vertexCount] = sum; + + // vertexIndexToTriangle = which vertex belongs to which triangle + std::vector<uint32_t> vertexIndexToTriangle(3 * triangleCount, 0); + // vertexOccurrence functions as number of usages in all triangles + // lowestLivingVertexIndex = number of a triangle + for (int i = 0; i < triangleCount; i++) { + // get the pointer to the first vertex of the triangle + // this allows us to iterate over the indexBuffer with the first vertex of the triangle as start + const uint32_t *vertexIndexOfTriangle = &indexBuffer32Bit[i * 3]; + + vertexIndexToTriangle[offsetVertexOccurrence[vertexIndexOfTriangle[0]] + vertexOccurrence[vertexIndexOfTriangle[0]]] = i; + vertexOccurrence[vertexIndexOfTriangle[0]]++; + + vertexIndexToTriangle[offsetVertexOccurrence[vertexIndexOfTriangle[1]] + vertexOccurrence[vertexIndexOfTriangle[1]]] = i; + vertexOccurrence[vertexIndexOfTriangle[1]]++; + + vertexIndexToTriangle[offsetVertexOccurrence[vertexIndexOfTriangle[2]] + vertexOccurrence[vertexIndexOfTriangle[2]]] = i; + vertexOccurrence[vertexIndexOfTriangle[2]]++; + } + + // counts if a triangle still uses this vertex + std::vector<uint8_t> livingVertices = vertexOccurrence; + std::vector<uint32_t> lastTimestampCache(vertexCount, 0); + + // stack of already used vertices, if it'currentTimeStamp full it will write to 0 again + std::vector<uint32_t> usedVerticeStack(maxUsedVertices, 0); + + //currently used vertices + int usedVerticeCount = 0; + // offset if maxUsedVertices was reached and it loops back to 0 + int usedVerticeOffset = 0; + + // saves if a triangle was emitted (used in the IndexBuffer) + std::vector<bool> isEmittedTriangles(triangleCount, false); + + // reordered Triangles that get rewritten to the new IndexBuffer + std::vector<uint32_t> reorderedTriangleIndexBuffer(triangleCount, 0); + + // offset to the latest not used triangleIndex + int triangleOutputOffset = 0; + // vertexIndex to fan out from (fanning VertexIndex) + int currentVertexIndex = 0; + int currentTimeStamp = cacheSize + 1; + int lowestLivingVertexIndex = 0; + + std::vector<uint32_t> possibleCandidates(3 * maxOffset); + + int currentTriangleIndex = 0; + // list of vertex indices where a deadEnd was reached + // useful to know where the mesh is potentially not contiguous + std::vector<uint32_t> skippedIndices; + + // run while not all indices are fanned out, -1 equals all are fanned out + while (currentVertexIndex >= 0) { + // number of possible candidates for a fanning VertexIndex + int numPossibleCandidates = 0; + // offset of currentVertexIndex and the next VertexIndex + int startOffset = offsetVertexOccurrence[currentVertexIndex]; + int endOffset = offsetVertexOccurrence[currentVertexIndex + 1]; + // iterates over every triangle of currentVertexIndex + for (int offset = startOffset; offset < endOffset; offset++) { + int triangleIndex = vertexIndexToTriangle[offset]; + + // checks if the triangle is already emitted + if (!isEmittedTriangles[triangleIndex]) { + + // get the pointer to the first vertex of the triangle + // this allows us to iterate over the indexBuffer with the first vertex of the triangle as start + const uint32_t *vertexIndexOfTriangle = &indexBuffer32Bit[3 * triangleIndex]; + + currentTriangleIndex++; + + // save emitted vertexIndexOfTriangle to reorderedTriangleIndexBuffer and set it to emitted + reorderedTriangleIndexBuffer[triangleOutputOffset++] = triangleIndex; + isEmittedTriangles[triangleIndex] = true; + + // save all vertexIndices of the triangle to reuse as soon as possible + for (int j = 0; j < 3; j++) { + int vertexIndex = vertexIndexOfTriangle[j]; + + //save vertexIndex to reuseStack + usedVerticeStack[mod(usedVerticeCount++)] = vertexIndex; + + // after looping back increase the start, so it only overrides the oldest vertexIndex + if ((mod(usedVerticeCount)) == + (mod(usedVerticeOffset))) { + usedVerticeOffset = mod(usedVerticeOffset + 1); + } + // add vertex to next possibleCandidates as fanning vertex + possibleCandidates[numPossibleCandidates++] = vertexIndex; + + // remove one occurrence of the vertex, since the triangle is used + livingVertices[vertexIndex]--; + + // writes the timestamp (number of iteration) of the last usage, if it wasn't used within the last cacheSize iterations + if (currentTimeStamp - lastTimestampCache[vertexIndex] > cacheSize) { + lastTimestampCache[vertexIndex] = currentTimeStamp; + currentTimeStamp++; + } + } + } + } + + // search for the next vertexIndex to fan out + currentVertexIndex = getNextVertexIndex( + vertexCount, lowestLivingVertexIndex, cacheSize, possibleCandidates, numPossibleCandidates, lastTimestampCache, currentTimeStamp, + livingVertices, usedVerticeStack, usedVerticeCount, usedVerticeOffset, currentTriangleIndex, skippedIndices); + } + + std::vector<uint32_t> reorderedIndexBuffer(3 * triangleCount); + + triangleOutputOffset = 0; + // rewriting the TriangleIndexBuffer to the new IndexBuffer + for (int i = 0; i < triangleCount; i++) { + int triangleIndex = reorderedTriangleIndexBuffer[i]; + // rewriting the triangle index to vertices + for (int j = 0; j < 3; j++) { + int vertexIndex = indexBuffer32Bit[(3 * triangleIndex) + j]; + reorderedIndexBuffer[triangleOutputOffset++] = vertexIndex; + } + } + + return VertexCacheReorderResult(reorderedIndexBuffer, skippedIndices); + } +} \ No newline at end of file diff --git a/modules/scene/CMakeLists.txt b/modules/scene/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..39effd0f802bb9d8ef4b318d5f1d233fa93db492 --- /dev/null +++ b/modules/scene/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_scene) + +# setting c++ standard for the module +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_scene_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_scene_include ${PROJECT_SOURCE_DIR}/include) + +# Add source and header files to the module +set(vkcv_scene_sources + ${vkcv_scene_include}/vkcv/scene/Bounds.hpp + ${vkcv_scene_source}/vkcv/scene/Bounds.cpp + + ${vkcv_scene_include}/vkcv/scene/Frustum.hpp + ${vkcv_scene_source}/vkcv/scene/Frustum.cpp + + ${vkcv_scene_include}/vkcv/scene/MeshPart.hpp + ${vkcv_scene_source}/vkcv/scene/MeshPart.cpp + + ${vkcv_scene_include}/vkcv/scene/Mesh.hpp + ${vkcv_scene_source}/vkcv/scene/Mesh.cpp + + ${vkcv_scene_include}/vkcv/scene/Node.hpp + ${vkcv_scene_source}/vkcv/scene/Node.cpp + + ${vkcv_scene_include}/vkcv/scene/Scene.hpp + ${vkcv_scene_source}/vkcv/scene/Scene.cpp +) + +# adding source files to the module +add_library(vkcv_scene ${vkcv_build_attribute} ${vkcv_scene_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_scene vkcv) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_scene SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_asset_loader_include} + ${vkcv_material_include} + ${vkcv_camera_include} + ${vkcv_algorithm_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_scene BEFORE PUBLIC ${vkcv_scene_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(vkcv_scene + vkcv + vkcv_asset_loader + vkcv_material + vkcv_camera + vkcv_algorithm) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_scene_include}) + list(APPEND vkcv_modules_libraries vkcv_scene) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/scene/README.md b/modules/scene/README.md new file mode 100644 index 0000000000000000000000000000000000000000..77837163d7de34fb6ce280697e84c8b047180369 --- /dev/null +++ b/modules/scene/README.md @@ -0,0 +1,7 @@ +# Scene + +A VkCV module to load and manage a scene, simplify its rendering and potentially optimize it + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__scene.html) to this module. diff --git a/modules/scene/include/vkcv/scene/Bounds.hpp b/modules/scene/include/vkcv/scene/Bounds.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8ad5806506610dbef6e4f1e0e26d66dfe0dbb63b --- /dev/null +++ b/modules/scene/include/vkcv/scene/Bounds.hpp @@ -0,0 +1,209 @@ +#pragma once + +#include <array> +#include <iostream> +#include <glm/vec3.hpp> + +namespace vkcv::scene { + + /** + * @addtogroup vkcv_scene + * @{ + */ + + /** + * A basic class to represent axis aligned bounding boxes. + */ + class Bounds { + private: + /** + * Minimum values of the box as 3D vector. + */ + glm::vec3 m_min; + + /** + * Maximum values of the box as 3D vector. + */ + glm::vec3 m_max; + + public: + /** + * Default constructor creating a zero volume axis aligned bounding + * box. + */ + Bounds(); + + /** + * Constructor creating a zero volume axis aligned bounding + * box around a certain point. + * @param[in] point Center of the box as 3D vector + */ + Bounds(const glm::vec3& point); + + /** + * Constructor creating an axis aligned bounding box with given + * boundaries, defined through minimum and maximum values. + * @param[in] min Minimum values of the box as 3D vector + * @param[in] max Maximum values of the box as 3D vector + */ + Bounds(const glm::vec3& min, const glm::vec3& max); + + /** + * Destructor of an axis aligned bounding box. + */ + ~Bounds() = default; + + /** + * Copy-constructor of an axis aligned bounding box. + * @param[in] other Other box as Bounds + */ + Bounds(const Bounds& other) = default; + + /** + * Move-constructor of an axis aligned bounding box. + * @param[in,out] other Other box as Bounds + */ + Bounds(Bounds&& other) = default; + + /** + * Copy-operator of an axis aligned bounding box. + * @param[in] other Other box as Bounds + * @return Reference to this box + */ + Bounds& operator=(const Bounds& other) = default; + + /** + * Move-operator of an axis aligned bounding box. + * @param[in,out] other Other box as Bounds + * @return Reference to this box + */ + Bounds& operator=(Bounds&& other) = default; + + /** + * Set and replace the minimum values of the box + * via a 3D vector. + * @param[in] min New minimum values of the box as 3D vector + */ + void setMin(const glm::vec3& min); + + /** + * Return the current minimum values of the box as + * 3D vector. + * @return Minimum values of the box as 3D vector + */ + [[nodiscard]] + const glm::vec3& getMin() const; + + /** + * Set and replace the maximum values of the box + * via a 3D vector. + * @param[in] max New maximum values of the box as 3D vector + */ + void setMax(const glm::vec3& max); + + /** + * Return the current maximum values of the box as + * 3D vector. + * @return Maximum values of the box as 3D vector + */ + [[nodiscard]] + const glm::vec3& getMax() const; + + /** + * Set the new center of the box by moving it and keeping + * its current volume. + * @param[in] center New center as 3D vector + */ + void setCenter(const glm::vec3& center); + + /** + * Return the current center of the box as 3D vector. + * @return Center as 3D vector + */ + [[nodiscard]] + glm::vec3 getCenter() const; + + /** + * Set the new size of the box by scaling it from its center, + * keeping the center position but replacing its volume. + * @param[in] size New size as 3D vector + */ + void setSize(const glm::vec3& size); + + /** + * Return the current size of the box as 3D vector. + * @return Size as 3D vector + */ + [[nodiscard]] + glm::vec3 getSize() const; + + /** + * Return a fixed size array containing all corners of + * the box as 3D vectors in absolute positions. + * @return Array of corners as 3D vectors + */ + [[nodiscard]] + std::array<glm::vec3, 8> getCorners() const; + + /** + * Resize the box to include a given point, provided + * in absolute position as 3D vector. + * @param[in] point Point as 3D vector + */ + void extend(const glm::vec3& point); + + /** + * Return if a given point, provided in absolute position, + * is inside this box or not. + * @param[in] point Point as 3D vector + * @return true if the point is inside, otherwise false + */ + [[nodiscard]] + bool contains(const glm::vec3& point) const; + + /** + * Return if a given other axis aligned bounding box is + * inside this box or not. + * @param[in] other Other box as Bounds + * @return true if the box is inside, otherwise false + */ + [[nodiscard]] + bool contains(const Bounds& other) const; + + /** + * Return if a given other axis aligned bounding box is + * intersecting this box or not. + * @param[in] other Other box as Bounds + * @return true if the boxes are intersecting, otherwise false + */ + [[nodiscard]] + bool intersects(const Bounds& other) const; + + /** + * Return if the defined bounding box is valid (min <= max). + * @return true if the box is valid, otherwise false + */ + [[nodiscard]] + explicit operator bool() const; + + /** + * Return if the defined bounding box is invalid (min > max). + * @return true if the box is invalid, otherwise false + */ + [[nodiscard]] + bool operator!() const; + + }; + + /** + * Stream-operator of the axis aligned bounding box to print + * it as readable output to logs or the console. + * @param[out] out Output stream + * @param[in] bounds Axis aligned bounding box + * @return Accessed output stream + */ + std::ostream& operator << (std::ostream& out, const Bounds& bounds); + + /** @} */ + +} diff --git a/modules/scene/include/vkcv/scene/Frustum.hpp b/modules/scene/include/vkcv/scene/Frustum.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e40092240614a5ee76fe3f5aa5a43db98d39970a --- /dev/null +++ b/modules/scene/include/vkcv/scene/Frustum.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <glm/mat4x4.hpp> +#include "vkcv/scene/Bounds.hpp" + +namespace vkcv::scene { + + /** + * @addtogroup vkcv_scene + * @{ + */ + + /** + * Transform a given axis aligned bounding box into frustum coordinates + * using a given 4x4 transformation matrix to return a new axis aligned + * bounding box containing that transformed box. + * @param[in] transform Frustum defining 4x4 matrix + * @param[in] bounds Axis aligned bounding box + * @return Bounds containing box in frustum coordinates + */ + Bounds transformBounds(const glm::mat4& transform, const Bounds& bounds); + + /** + * Transform a given axis aligned bounding box into frustum coordinates + * using a given 4x4 transformation matrix to return a new axis aligned + * bounding box containing that transformed box. + * @param[in] transform Frustum defining 4x4 matrix + * @param[in] bounds Axis aligned bounding box + * @param[out] negative_w Flag if w-coordinate is negative + * @return Bounds containing box in frustum coordinates + */ + Bounds transformBounds(const glm::mat4& transform, const Bounds& bounds, bool& negative_w); + + /** + * Check if an axis aligned bounding box is intersecting a given frustum + * defined by a 4x4 transformation matrix. + * @param[in] transform Frustum defining 4x4 matrix + * @param[in] bounds Axis aligned bounding box + * @return true if the box and frustum are intersecting, otherwise false + */ + bool checkFrustum(const glm::mat4& transform, const Bounds& bounds); + + /** @} */ + +} diff --git a/modules/scene/include/vkcv/scene/Mesh.hpp b/modules/scene/include/vkcv/scene/Mesh.hpp new file mode 100644 index 0000000000000000000000000000000000000000..059eb6bcabb3cecb287b3a5bbdba86029fe97c9f --- /dev/null +++ b/modules/scene/include/vkcv/scene/Mesh.hpp @@ -0,0 +1,146 @@ +#pragma once + +#include <glm/mat4x4.hpp> + +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/camera/Camera.hpp> + +#include "MeshPart.hpp" + +namespace vkcv::scene { + + /** + * @addtogroup vkcv_scene + * @{ + */ + + /** + * An event function type to be called on per drawcall recording level to adjust data + * like push constants with provided matrices. + */ + typedef typename event_function<const glm::mat4&, const glm::mat4&, PushConstants&, vkcv::Drawcall&>::type RecordMeshDrawcallFunction; + + class Node; + + /** + * A class to represent a whole mesh to render. + */ + class Mesh { + friend class Node; + + private: + /** + * Parent scene of the mesh. + */ + Scene& m_scene; + + /** + * List of the meshes parts. + */ + std::vector<MeshPart> m_parts; + + /** + * List of the meshes drawcalls to render. + */ + std::vector<InstanceDrawcall> m_drawcalls; + + /** + * Local transformation matrix of the mesh. + */ + glm::mat4 m_transform; + + /** + * Axis aligned bounding box of the mesh. + */ + Bounds m_bounds; + + /** + * Constructor of a new mesh with a given scene as parent. + * + * @param[in,out] scene Scene + */ + explicit Mesh(Scene& scene); + + /** + * Load mesh data from a scene structure from the asset loader + * creating and loading all mesh parts being required. + * + * @param[in] scene Scene structure from asset loader + * @param[in] mesh Mesh structure from asset loader + * @param[in] types Primitive type order of vertex attributes + */ + void load(const asset::Scene& scene, + const asset::Mesh& mesh, + const std::vector<asset::PrimitiveType>& types); + + /** + * Record drawcalls of the mesh, equally to all its visible parts. + * + * @param[in] viewProjection View-transformation and projection as 4x4 matrix + * @param[out] pushConstants Structure to store push constants per drawcall + * @param[out] drawcalls List of drawcall structures + * @param[in] record Drawcall recording event function + */ + void recordDrawcalls(const glm::mat4& viewProjection, + PushConstants& pushConstants, + std::vector<InstanceDrawcall>& drawcalls, + const RecordMeshDrawcallFunction& record); + + /** + * Return the amount of drawcalls of the mesh + * as sum of all its parts. + * + * @return Amount of drawcalls + */ + [[nodiscard]] + size_t getDrawcallCount() const; + + public: + /** + * Destructor of a mesh. + */ + ~Mesh(); + + /** + * Copy-constructor of a mesh. + * + * @param[in] other Other mesh instance + */ + Mesh(const Mesh& other) = default; + + /** + * Move-constructor of a mesh. + * + * @param[in,out] other Other mesh instance + */ + Mesh(Mesh&& other) = default; + + /** + * Copy-operator of a mesh. + * + * @param[in] other Other mesh instance + * @return Reference to this mesh instance + */ + Mesh& operator=(const Mesh& other); + + /** + * Move-operator of a mesh. + * + * @param[in,out] other Other mesh instance + * @return Reference to this mesh instance + */ + Mesh& operator=(Mesh&& other) noexcept; + + /** + * Return the axis aligned bounding box of the mesh. + * + * @return Axis aligned bounding box of this mesh + */ + [[nodiscard]] + const Bounds& getBounds() const; + + }; + + /** @} */ + +} diff --git a/modules/scene/include/vkcv/scene/MeshPart.hpp b/modules/scene/include/vkcv/scene/MeshPart.hpp new file mode 100644 index 0000000000000000000000000000000000000000..72fd6151f64c31377fb9f38b3c8dedae44905d4d --- /dev/null +++ b/modules/scene/include/vkcv/scene/MeshPart.hpp @@ -0,0 +1,145 @@ +#pragma once + +#include <vector> + +#include <vkcv/Buffer.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/material/Material.hpp> + +#include "Bounds.hpp" + +namespace vkcv::scene { + + /** + * @addtogroup vkcv_scene + * @{ + */ + + class Scene; + class Mesh; + + /** + * A class to represent a group of vertices to render + * a part of a mesh. + */ + class MeshPart { + friend class Mesh; + + private: + /** + * Parent scene of the mesh part. + */ + Scene& m_scene; + + /** + * The vertex data containing its part of the mesh. + */ + VertexData m_data; + + /** + * Axis aligned bounding box of the mesh part. + */ + Bounds m_bounds; + + /** + * The index of the material used to render + * this part of the mesh. + */ + size_t m_materialIndex; + + /** + * Constructor of a new mesh part with a given scene as parent. + * + * @param[in,out] scene Scene + */ + explicit MeshPart(Scene& scene); + + /** + * Load vertex and index data from structures provided by the asset loader + * and add a matching drawcall to the list if the loaded mesh part is valid. + * + * @param[in] scene Scene structure from asset loader + * @param[in] vertexGroup Vertex group structure from asset loader + * @param[in] types Primitive type order of vertex attributes + * @param[out] drawcalls List of drawcalls + */ + void load(const asset::Scene& scene, + const asset::VertexGroup& vertexGroup, + const std::vector<asset::PrimitiveType>& types, + std::vector<InstanceDrawcall>& drawcalls); + + public: + /** + * Destructor of a mesh part. + */ + ~MeshPart(); + + /** + * Copy-constructor of a mesh part. + * + * @param[in] other Other mesh part + */ + MeshPart(const MeshPart& other); + + /** + * Move-constructor of a mesh part. + * + * @param[in,out] other Other mesh part + */ + MeshPart(MeshPart&& other) noexcept; + + /** + * Copy-operator of a mesh part. + * + * @param[in] other Other mesh part + * @return Reference to this mesh part + */ + MeshPart& operator=(const MeshPart& other); + + /** + * Move-operator of a mesh part. + * + * @param[in,out] other Other mesh part + * @return Reference to this mesh part + */ + MeshPart& operator=(MeshPart&& other) noexcept; + + /** + * Get the material used by this specific part of + * the mesh for rendering. + * + * @return Material + */ + [[nodiscard]] + const material::Material& getMaterial() const; + + /** + * Return the axis aligned bounding box of this + * specific part of the mesh. + * + * @return Axis aligned bounding box of this mesh part + */ + [[nodiscard]] + const Bounds& getBounds() const; + + /** + * Return the status if this part of the mesh is valid + * as boolean value. + * + * @return true if the mesh part is valid, otherwise false + */ + explicit operator bool() const; + + /** + * Return the status if this part of the mesh is invalid + * as boolean value. + * + * @return true if the mesh part is invalid, otherwise false + */ + bool operator!() const; + + }; + + /** @} */ + +} diff --git a/modules/scene/include/vkcv/scene/Node.hpp b/modules/scene/include/vkcv/scene/Node.hpp new file mode 100644 index 0000000000000000000000000000000000000000..de40adcfb5e9c71c233465b44ad07c5bb30b2a78 --- /dev/null +++ b/modules/scene/include/vkcv/scene/Node.hpp @@ -0,0 +1,176 @@ +#pragma once + +#include <vector> + +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/camera/Camera.hpp> + +#include "Bounds.hpp" +#include "Mesh.hpp" + +namespace vkcv::scene { + + /** + * @addtogroup vkcv_scene + * @{ + */ + + class Scene; + + /** + * A class to represent a graph node in a scene graph. + */ + class Node { + friend class Scene; + + private: + /** + * Parent scene of the node. + */ + Scene& m_scene; + + /** + * List of meshes added the node. + */ + std::vector<Mesh> m_meshes; + + /** + * List of child nodes added the node. + */ + std::vector<Node> m_nodes; + + /** + * Axis aligned bounding box of the node. + */ + Bounds m_bounds; + + /** + * Constructor of a new node with a given scene as parent. + * + * @param[in,out] scene Scene + */ + explicit Node(Scene& scene); + + /** + * Add a given mesh to this node for drawcall recording. + * + * @param[in] mesh Mesh + */ + void addMesh(const Mesh& mesh); + + /** + * Load and add a mesh from a scene preloaded with the asset loader. + * + * @param[in] asset_scene Scene structure from asset loader + * @param[in] asset_mesh Mesh structure from asset loader + * @param[in] types Primitive type order of vertex attributes + */ + void loadMesh(const asset::Scene& asset_scene, + const asset::Mesh& asset_mesh, + const std::vector<asset::PrimitiveType>& types); + + /** + * Record drawcalls of all meshes of this node and its child nodes. + * + * @param[in] viewProjection View-transformation and projection as 4x4 matrix + * @param[out] pushConstants Structure to store push constants per drawcall + * @param[out] drawcalls List of drawcall structures + * @param[in] record Drawcall recording event function + */ + void recordDrawcalls(const glm::mat4& viewProjection, + PushConstants& pushConstants, + std::vector<InstanceDrawcall>& drawcalls, + const RecordMeshDrawcallFunction& record); + + /** + * Splits child nodes into tree based graphs of nodes + * until all nodes contain an amount of meshes below + * a given maximum. + * + * @param[in] maxMeshesPerNode Maximum amount of meshes per node + */ + void splitMeshesToSubNodes(size_t maxMeshesPerNode); + + /** + * Return the sum of drawcalls in the graph of this node. + * + * @return Amount of drawcalls + */ + [[nodiscard]] + size_t getDrawcallCount() const; + + /** + * Add a new node as child to the scene graph with this node + * as parent and return its index. + * + * @return Index of the new node + */ + size_t addNode(); + + /** + * Get a reference to the child node with a given index. + * + * @param[in] index Valid index of a child node + * @return Matching child node + */ + Node& getNode(size_t index); + + /** + * Get a const reference to the child node with a given index. + * + * @param[in] index Valid index of a child node + * @return Matching child node + */ + [[nodiscard]] + const Node& getNode(size_t index) const; + + public: + /** + * Destructor of a scene node. + */ + ~Node(); + + /** + * Copy-constructor of a scene node. + * + * @param[in] other Other scene node + */ + Node(const Node& other) = default; + + /** + * Move-constructor of a scene node. + * + * @param[in,out] other Other scene node + */ + Node(Node&& other) = default; + + /** + * Copy-operator of a scene node. + * + * @param[in] other Other scene node + * @return Reference of this node + */ + Node& operator=(const Node& other); + + /** + * Move-operator of a scene node. + * + * @param[in,out] other Other scene node + * @return Reference of this node + */ + Node& operator=(Node&& other) noexcept; + + /** + * Return the axis aligned bounding box of the + * scene node. + * + * @return Axis aligned bounding box of this node + */ + [[nodiscard]] + const Bounds& getBounds() const; + + }; + + /** @} */ + +} diff --git a/modules/scene/include/vkcv/scene/Scene.hpp b/modules/scene/include/vkcv/scene/Scene.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d6db062c823f1d8746179bc0fde61535c16f8fe8 --- /dev/null +++ b/modules/scene/include/vkcv/scene/Scene.hpp @@ -0,0 +1,216 @@ +#pragma once + +#include <filesystem> +#include <mutex> + +#include <vkcv/Core.hpp> +#include <vkcv/Event.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/camera/Camera.hpp> +#include <vkcv/material/Material.hpp> + +#include "Node.hpp" + +namespace vkcv::scene { + + /** + * @defgroup vkcv_scene Scene Module + * A module to manage basic scene rendering with CPU-side frustum culling. + * @{ + */ + + /** + * A class to represent a scene graph. + */ + class Scene { + friend class MeshPart; + + private: + /** + * A nested structure to manage material storage. + */ + struct Material { + /** + * The amount of active usages of the material. + */ + size_t m_usages; + + /** + * The actual material data. + */ + material::Material m_data; + }; + + /** + * A pointer to the current Core instance. + */ + Core* m_core; + + /** + * A list of currently used materials. + */ + std::vector<Material> m_materials; + + /** + * A list of nodes in the first level of the scene graph. + */ + std::vector<Node> m_nodes; + + /** + * Constructor of a scene instance with a given Core instance. + * + * @param[in,out] core Pointer to valid Core instance + */ + explicit Scene(Core* core); + + /** + * Add a new node to the first level of the scene graph with + * this scene as parent and return its index. + * + * @return Index of the new node + */ + size_t addNode(); + + /** + * Get a reference to the first-level node with a given index. + * + * @param[in] index Valid index of a first-level node + * @return Matching first-level node + */ + Node& getNode(size_t index); + + /** + * Get a const reference to the first-level node with a given index. + * + * @param[in] index Valid index of a first-level node + * @return Matching first-level node + */ + [[nodiscard]] + const Node& getNode(size_t index) const; + + /** + * Increase the amount of usages for a certain material via its index. + * + * @param[in] index Index of a material + */ + void increaseMaterialUsage(size_t index); + + /** + * Decrease the amount of usages for a certain material via its index. + * + * @param[in] index Index of a material + */ + void decreaseMaterialUsage(size_t index); + + /** + * Load a material from a scene preloaded with the asset loader via + * its index. + * + * @param[in] index Valid index of a material + * @param[in] scene Scene structure from asset loader + * @param[in] material Material structure from asset loader + */ + void loadMaterial(size_t index, const asset::Scene& scene, + const asset::Material& material); + + public: + /** + * Destructor of a scene instance. + */ + ~Scene(); + + /** + * Copy-constructor of a scene instance. + * + * @param[in] other Other scene instance + */ + Scene(const Scene& other); + + /** + * Move-constructor of a scene instance. + * + * @param[in,out] other Other scene instance + */ + Scene(Scene&& other) noexcept; + + /** + * Copy-operator of a scene instance. + * + * @param[in] other Other scene instance + * @return Reference to this scene + */ + Scene& operator=(const Scene& other); + + /** + * Move-operator of a scene instance. + * + * @param[in,out] other Other scene instance + * @return Reference to this scene + */ + Scene& operator=(Scene&& other) noexcept; + + /** + * Return the amount of materials managed by this scene. + * + * @return Amount of materials + */ + [[nodiscard]] + size_t getMaterialCount() const; + + /** + * Get the material data by its certain index. + * The material can still be invalid if it was not loaded properly. + * + * @param[in] index Valid index of material + * @return Material + */ + [[nodiscard]] + const material::Material& getMaterial(size_t index) const; + + /** + * Record drawcalls of all meshes of this scene with CPU-side frustum culling. + * + * @param cmdStream Command stream handle + * @param camera Scene viewing camera + * @param pass Render pass handle + * @param pipeline Graphics pipeline handle + * @param pushConstantsSizePerDrawcall Size of push constants per drawcall + * @param record Drawcall recording event function + * @param renderTargets Actual render targets + * @param windowHandle Window handle to use + */ + void recordDrawcalls(CommandStreamHandle &cmdStream, + const camera::Camera &camera, + const PassHandle &pass, + const GraphicsPipelineHandle &pipeline, + size_t pushConstantsSizePerDrawcall, + const RecordMeshDrawcallFunction &record, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle); + + /** + * Instantiation function to create a new scene instance. + * + * @param[in,out] core Current Core instance + * @return New scene instance + */ + static Scene create(Core& core); + + /** + * Load function to create a new scene instance with materials and meshes + * loaded from a file using the asset loader. + * + * @param[in,out] core Current Core instance + * @param[in] path Path of a valid file to load via asset loader + * @param[in] types Primitive type order of vertex attributes + * @return New scene instance + */ + static Scene load(Core& core, + const std::filesystem::path &path, + const std::vector<asset::PrimitiveType>& types); + + }; + + /** @} */ + +} \ No newline at end of file diff --git a/modules/scene/src/vkcv/scene/Bounds.cpp b/modules/scene/src/vkcv/scene/Bounds.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9614208d2d523117eef09e1432ff755005b5c51f --- /dev/null +++ b/modules/scene/src/vkcv/scene/Bounds.cpp @@ -0,0 +1,131 @@ + +#include "vkcv/scene/Bounds.hpp" + +namespace vkcv::scene { + + Bounds::Bounds() : + m_min(glm::vec3(0)), + m_max(glm::vec3(0)) {} + + Bounds::Bounds(const glm::vec3 &point) : + m_min(point), + m_max(point) + {} + + Bounds::Bounds(const glm::vec3 &min, const glm::vec3 &max) : + m_min(min), + m_max(max) + {} + + void Bounds::setMin(const glm::vec3 &min) { + m_min = min; + } + + const glm::vec3 & Bounds::getMin() const { + return m_min; + } + + void Bounds::setMax(const glm::vec3 &max) { + m_max = max; + } + + const glm::vec3 & Bounds::getMax() const { + return m_max; + } + + void Bounds::setCenter(const glm::vec3 ¢er) { + const glm::vec3 size = getSize(); + m_min = center - size / 2.0f; + m_max = center + size / 2.0f; + } + + glm::vec3 Bounds::getCenter() const { + return (m_min + m_max) / 2.0f; + } + + void Bounds::setSize(const glm::vec3 &size) { + const glm::vec3 center = getCenter(); + m_min = center - size / 2.0f; + m_max = center + size / 2.0f; + } + + glm::vec3 Bounds::getSize() const { + return (m_max - m_min); + } + + std::array<glm::vec3, 8> Bounds::getCorners() const { + return { + m_min, + glm::vec3(m_min[0], m_min[1], m_max[2]), + glm::vec3(m_min[0], m_max[1], m_min[2]), + glm::vec3(m_min[0], m_max[1], m_max[2]), + glm::vec3(m_max[0], m_min[1], m_min[2]), + glm::vec3(m_max[0], m_min[1], m_max[2]), + glm::vec3(m_max[0], m_max[1], m_min[2]), + m_max + }; + } + + void Bounds::extend(const glm::vec3 &point) { + m_min = glm::vec3( + std::min(m_min[0], point[0]), + std::min(m_min[1], point[1]), + std::min(m_min[2], point[2]) + ); + + m_max = glm::vec3( + std::max(m_max[0], point[0]), + std::max(m_max[1], point[1]), + std::max(m_max[2], point[2]) + ); + } + + bool Bounds::contains(const glm::vec3 &point) const { + return ( + (point[0] >= m_min[0]) && (point[0] <= m_max[0]) && + (point[1] >= m_min[1]) && (point[1] <= m_max[1]) && + (point[2] >= m_min[2]) && (point[2] <= m_max[2]) + ); + } + + bool Bounds::contains(const Bounds &other) const { + return ( + (other.m_min[0] >= m_min[0]) && (other.m_max[0] <= m_max[0]) && + (other.m_min[1] >= m_min[1]) && (other.m_max[1] <= m_max[1]) && + (other.m_min[2] >= m_min[2]) && (other.m_max[2] <= m_max[2]) + ); + } + + bool Bounds::intersects(const Bounds &other) const { + return ( + (other.m_max[0] >= m_min[0]) && (other.m_min[0] <= m_max[0]) && + (other.m_max[1] >= m_min[1]) && (other.m_min[1] <= m_max[1]) && + (other.m_max[2] >= m_min[2]) && (other.m_min[2] <= m_max[2]) + ); + } + + Bounds::operator bool() const { + return ( + (m_min[0] <= m_max[0]) && + (m_min[1] <= m_max[1]) && + (m_min[2] <= m_max[2]) + ); + } + + bool Bounds::operator!() const { + return ( + (m_min[0] > m_max[0]) || + (m_min[1] > m_max[1]) || + (m_min[2] > m_max[2]) + ); + } + + std::ostream& operator << (std::ostream& out, const Bounds& bounds) { + const auto& min = bounds.getMin(); + const auto& max = bounds.getMax(); + + return out << "[Bounds: (" << min[0] << ", " << min[1] << ", " << min[2] << ") (" + << max[0] << ", " << max[1] << ", " << max[2] << ") ]"; + } + +} diff --git a/modules/scene/src/vkcv/scene/Frustum.cpp b/modules/scene/src/vkcv/scene/Frustum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10af3bc44e4f585f990577bd9d95ecfade7acca8 --- /dev/null +++ b/modules/scene/src/vkcv/scene/Frustum.cpp @@ -0,0 +1,68 @@ + +#include "vkcv/scene/Frustum.hpp" + +namespace vkcv::scene { + + static glm::vec3 transformPoint(const glm::mat4& transform, const glm::vec3& point, bool& negative_w) { + const glm::vec4 position = transform * glm::vec4(point, 1.0f); + + /* + * We divide by the absolute of the 4th coorditnate because + * clipping is weird and points have to move to the other + * side of the camera. + * + * We also need to collect if the 4th coordinate was negative + * to know if all corners are behind the camera. So these can + * be culled as well + */ + const float perspective = std::abs(position[3]); + negative_w &= (position[3] < 0.0f); + return glm::vec3( + position[0] / perspective, + position[1] / perspective, + position[2] / perspective + ); + } + + Bounds transformBounds(const glm::mat4& transform, const Bounds& bounds) { + const auto corners = bounds.getCorners(); + + Bounds result (glm::vec3(transform * glm::vec4(corners[0], 1.0f))); + + for (size_t j = 1; j < corners.size(); j++) { + result.extend(glm::vec3(transform * glm::vec4(corners[j], 1.0f))); + } + + return result; + } + + Bounds transformBounds(const glm::mat4& transform, const Bounds& bounds, bool& negative_w) { + const auto corners = bounds.getCorners(); + negative_w = true; + + Bounds result (transformPoint(transform, corners[0], negative_w)); + + for (size_t j = 1; j < corners.size(); j++) { + result.extend(transformPoint(transform, corners[j], negative_w)); + } + + return result; + } + + bool checkFrustum(const glm::mat4& transform, const Bounds& bounds) { + static Bounds frustum ( + glm::vec3(-1.0f, -1.0f, -0.0f), + glm::vec3(+1.0f, +1.0f, +1.0f) + ); + + bool negative_w; + auto box = transformBounds(transform, bounds, negative_w); + + if (negative_w) { + return false; + } else { + return box.intersects(frustum); + } + } + +} diff --git a/modules/scene/src/vkcv/scene/Mesh.cpp b/modules/scene/src/vkcv/scene/Mesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c6249fb556c9c2d7e08b41636718be9ef80f829 --- /dev/null +++ b/modules/scene/src/vkcv/scene/Mesh.cpp @@ -0,0 +1,134 @@ + +#include "vkcv/scene/Mesh.hpp" +#include "vkcv/scene/Scene.hpp" +#include "vkcv/scene/Frustum.hpp" + +namespace vkcv::scene { + + Mesh::Mesh(Scene& scene) : + m_scene(scene) {} + + static glm::mat4 arrayTo4x4Matrix(const std::array<float,16>& array){ + glm::mat4 matrix; + + for (int i = 0; i < 4; i++){ + for (int j = 0; j < 4; j++){ + matrix[i][j] = array[j * 4 + i]; + } + } + + return matrix; + } + + void Mesh::load(const asset::Scene &scene, + const asset::Mesh &mesh, + const std::vector<asset::PrimitiveType>& types) { + m_parts.clear(); + m_drawcalls.clear(); + + m_transform = arrayTo4x4Matrix(mesh.modelMatrix); + + for (const auto& vertexGroupIndex : mesh.vertexGroups) { + if ((vertexGroupIndex < 0) || (vertexGroupIndex >= scene.vertexGroups.size())) { + continue; + } + + MeshPart part (m_scene); + part.load(scene, scene.vertexGroups[vertexGroupIndex], types, m_drawcalls); + + if (!part) { + continue; + } + + auto bounds = transformBounds(m_transform, part.getBounds()); + + if (m_parts.empty()) { + m_bounds = bounds; + } else { + m_bounds.extend(bounds.getMin()); + m_bounds.extend(bounds.getMax()); + } + + m_parts.push_back(part); + } + } + + Mesh::~Mesh() { + m_drawcalls.clear(); + m_parts.clear(); + } + + Mesh &Mesh::operator=(const Mesh &other) { + if (&other == this) { + return *this; + } + + m_parts.resize(other.m_parts.size(), MeshPart(m_scene)); + + for (size_t i = 0; i < m_parts.size(); i++) { + m_parts[i] = other.m_parts[i]; + } + + m_drawcalls = std::vector<InstanceDrawcall>(other.m_drawcalls); + m_transform = other.m_transform; + m_bounds = other.m_bounds; + + return *this; + } + + Mesh &Mesh::operator=(Mesh &&other) noexcept { + m_parts.resize(other.m_parts.size(), MeshPart(m_scene)); + + for (size_t i = 0; i < m_parts.size(); i++) { + m_parts[i] = std::move(other.m_parts[i]); + } + + m_drawcalls = std::move(other.m_drawcalls); + m_transform = other.m_transform; + m_bounds = other.m_bounds; + + return *this; + } + + void Mesh::recordDrawcalls(const glm::mat4& viewProjection, + PushConstants& pushConstants, + std::vector<InstanceDrawcall>& drawcalls, + const RecordMeshDrawcallFunction& record) { + const glm::mat4 transform = viewProjection * m_transform; + + if (!checkFrustum(viewProjection, m_bounds)) { + return; + } + + if (m_drawcalls.size() == 1) { + drawcalls.push_back(m_drawcalls[0]); + + if (record) { + record(transform, m_transform, pushConstants, drawcalls.back()); + } + } else { + for (size_t i = 0; i < m_parts.size(); i++) { + const MeshPart& part = m_parts[i]; + + if (!checkFrustum(transform, part.getBounds())) { + continue; + } + + drawcalls.push_back(m_drawcalls[i]); + + if (record) { + record(transform, m_transform, pushConstants, drawcalls.back()); + } + } + } + } + + size_t Mesh::getDrawcallCount() const { + return m_drawcalls.size(); + } + + const Bounds& Mesh::getBounds() const { + return m_bounds; + } + +} diff --git a/modules/scene/src/vkcv/scene/MeshPart.cpp b/modules/scene/src/vkcv/scene/MeshPart.cpp new file mode 100644 index 0000000000000000000000000000000000000000..959148b0c82f9ddc4068e669f56f9936f5462055 --- /dev/null +++ b/modules/scene/src/vkcv/scene/MeshPart.cpp @@ -0,0 +1,161 @@ + +#include "vkcv/scene/MeshPart.hpp" +#include "vkcv/scene/Scene.hpp" + +#include <vkcv/Buffer.hpp> + +namespace vkcv::scene { + + MeshPart::MeshPart(Scene& scene) : + m_scene(scene), + m_data(), + m_bounds(), + m_materialIndex(std::numeric_limits<size_t>::max()) {} + + void MeshPart::load(const asset::Scene& scene, + const asset::VertexGroup &vertexGroup, + const std::vector<asset::PrimitiveType>& types, + std::vector<InstanceDrawcall>& drawcalls) { + Core& core = *(m_scene.m_core); + + auto vertexBuffer = buffer<uint8_t>( + core, BufferType::VERTEX, vertexGroup.vertexBuffer.data.size() + ); + + vertexBuffer.fill(vertexGroup.vertexBuffer.data); + + m_data = VertexData( + asset::loadVertexBufferBindings( + vertexGroup.vertexBuffer.attributes, + vertexBuffer.getHandle(), + types + ) + ); + + if (!vertexGroup.indexBuffer.data.empty()) { + auto indexBuffer = buffer<uint8_t>( + core, BufferType::INDEX, vertexGroup.indexBuffer.data.size() + ); + + indexBuffer.fill(vertexGroup.indexBuffer.data); + + IndexBitCount indexBitCount; + switch (vertexGroup.indexBuffer.type) { + case asset::IndexType::UINT8: + indexBitCount = IndexBitCount::Bit8; + break; + case asset::IndexType::UINT16: + indexBitCount = IndexBitCount::Bit16; + break; + case asset::IndexType::UINT32: + indexBitCount = IndexBitCount::Bit32; + break; + default: + indexBitCount = IndexBitCount::Bit16; + vkcv_log(LogLevel::WARNING, "Unsupported index type!"); + break; + } + + m_data.setIndexBuffer(indexBuffer.getHandle(), indexBitCount); + m_data.setCount(vertexGroup.numIndices); + } else { + m_data.setCount(vertexGroup.numVertices); + } + + m_bounds.setMin(glm::vec3( + vertexGroup.min.x, + vertexGroup.min.y, + vertexGroup.min.z + )); + + m_bounds.setMax(glm::vec3( + vertexGroup.max.x, + vertexGroup.max.y, + vertexGroup.max.z + )); + + if ((vertexGroup.materialIndex >= 0) && + (vertexGroup.materialIndex < scene.materials.size())) { + m_materialIndex = vertexGroup.materialIndex; + + if (!getMaterial()) { + m_scene.loadMaterial(m_materialIndex, scene, scene.materials[vertexGroup.materialIndex]); + } + + m_scene.increaseMaterialUsage(m_materialIndex); + } else { + m_materialIndex = std::numeric_limits<size_t>::max(); + } + + if (*this) { + const auto& material = getMaterial(); + + InstanceDrawcall drawcall (m_data); + drawcall.useDescriptorSet(0, material.getDescriptorSet()); + drawcalls.push_back(drawcall); + } + } + + MeshPart::~MeshPart() { + m_scene.decreaseMaterialUsage(m_materialIndex); + } + + MeshPart::MeshPart(const MeshPart &other) : + m_scene(other.m_scene), + m_data(other.m_data), + m_bounds(other.m_bounds), + m_materialIndex(other.m_materialIndex) { + m_scene.increaseMaterialUsage(m_materialIndex); + } + + MeshPart::MeshPart(MeshPart &&other) noexcept : + m_scene(other.m_scene), + m_data(other.m_data), + m_bounds(other.m_bounds), + m_materialIndex(other.m_materialIndex) { + m_scene.increaseMaterialUsage(m_materialIndex); + } + + MeshPart &MeshPart::operator=(const MeshPart &other) { + if (&other == this) { + return *this; + } + + m_data = other.m_data; + m_bounds = other.m_bounds; + m_materialIndex = other.m_materialIndex; + + return *this; + } + + MeshPart &MeshPart::operator=(MeshPart &&other) noexcept { + m_data = other.m_data; + m_bounds = other.m_bounds; + m_materialIndex = other.m_materialIndex; + + return *this; + } + + const material::Material & MeshPart::getMaterial() const { + return m_scene.getMaterial(m_materialIndex); + } + + MeshPart::operator bool() const { + return ( + (getMaterial()) && + (m_data.getCount() > 0) + ); + } + + bool MeshPart::operator!() const { + return ( + (!getMaterial()) || + (m_data.getCount() <= 0) + ); + } + + const Bounds &MeshPart::getBounds() const { + return m_bounds; + } + +} diff --git a/modules/scene/src/vkcv/scene/Node.cpp b/modules/scene/src/vkcv/scene/Node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5234dd461537c491c0fa5580c78fbfec695a310 --- /dev/null +++ b/modules/scene/src/vkcv/scene/Node.cpp @@ -0,0 +1,191 @@ + +#include "vkcv/scene/Node.hpp" +#include "vkcv/scene/Scene.hpp" +#include "vkcv/scene/Frustum.hpp" + +#include <algorithm> + +namespace vkcv::scene { + + Node::Node(Scene& scene) : + m_scene(scene), + m_meshes(), + m_nodes(), + m_bounds() {} + + Node::~Node() { + m_nodes.clear(); + m_meshes.clear(); + } + + Node &Node::operator=(const Node &other) { + if (&other == this) { + return *this; + } + + m_meshes.resize(other.m_meshes.size(), Mesh(m_scene)); + + for (size_t i = 0; i < m_meshes.size(); i++) { + m_meshes[i] = other.m_meshes[i]; + } + + m_nodes.resize(other.m_nodes.size(), Node(m_scene)); + + for (size_t i = 0; i < m_nodes.size(); i++) { + m_nodes[i] = other.m_nodes[i]; + } + + m_bounds = other.m_bounds; + + return *this; + } + + Node &Node::operator=(Node &&other) noexcept { + m_meshes.resize(other.m_meshes.size(), Mesh(m_scene)); + + for (size_t i = 0; i < m_meshes.size(); i++) { + m_meshes[i] = std::move(other.m_meshes[i]); + } + + m_nodes.resize(other.m_nodes.size(), Node(m_scene)); + + for (size_t i = 0; i < m_nodes.size(); i++) { + m_nodes[i] = std::move(other.m_nodes[i]); + } + + m_bounds = other.m_bounds; + + return *this; + } + + void Node::addMesh(const Mesh& mesh) { + if (m_meshes.empty()) { + m_bounds = mesh.getBounds(); + } else { + m_bounds.extend(mesh.getBounds().getMin()); + m_bounds.extend(mesh.getBounds().getMax()); + } + + m_meshes.push_back(mesh); + } + + void Node::loadMesh(const asset::Scene &asset_scene, + const asset::Mesh &asset_mesh, + const std::vector<asset::PrimitiveType>& types) { + Mesh mesh (m_scene); + mesh.load(asset_scene, asset_mesh, types); + addMesh(mesh); + } + + size_t Node::addNode() { + const Node node (m_scene); + const size_t index = m_nodes.size(); + m_nodes.push_back(node); + return index; + } + + Node& Node::getNode(size_t index) { + return m_nodes[index]; + } + + const Node& Node::getNode(size_t index) const { + return m_nodes[index]; + } + + void Node::recordDrawcalls(const glm::mat4& viewProjection, + PushConstants& pushConstants, + std::vector<InstanceDrawcall>& drawcalls, + const RecordMeshDrawcallFunction& record) { + if (!checkFrustum(viewProjection, m_bounds)) { + return; + } + + for (auto& mesh : m_meshes) { + mesh.recordDrawcalls(viewProjection, pushConstants, drawcalls, record); + } + + for (auto& node : m_nodes) { + node.recordDrawcalls(viewProjection, pushConstants, drawcalls, record); + } + } + + void Node::splitMeshesToSubNodes(size_t maxMeshesPerNode) { + if (m_meshes.size() <= maxMeshesPerNode) { + return; + } + + const auto split = m_bounds.getCenter(); + int axis = 0; + + const auto size = m_bounds.getSize(); + + if (size[1] > size[0]) { + if (size[2] > size[1]) { + axis = 2; + } else { + axis = 1; + } + } else + if (size[2] > size[0]) { + axis = 2; + } + + std::vector<size_t> left_meshes; + std::vector<size_t> right_meshes; + + for (size_t i = 0; i < m_meshes.size(); i++) { + const auto& bounds = m_meshes[i].getBounds(); + + if (bounds.getMax()[axis] <= split[axis]) { + left_meshes.push_back(i); + } else + if (bounds.getMin()[axis] >= split[axis]) { + right_meshes.push_back(i); + } + } + + if ((left_meshes.empty()) || (right_meshes.empty())) { + return; + } + + const size_t left = addNode(); + const size_t right = addNode(); + + for (size_t i : left_meshes) { + getNode(left).addMesh(m_meshes[i]); + } + + for (size_t i : right_meshes) { + getNode(right).addMesh(m_meshes[i]); + left_meshes.push_back(i); + } + + std::sort(left_meshes.begin(), left_meshes.end(), std::greater()); + + for (size_t i : left_meshes) { + m_meshes.erase(m_meshes.begin() + static_cast<long>(i)); + } + + getNode(left).splitMeshesToSubNodes(maxMeshesPerNode); + getNode(right).splitMeshesToSubNodes(maxMeshesPerNode); + } + + size_t Node::getDrawcallCount() const { + size_t count = 0; + + for (auto& mesh : m_meshes) { + count += mesh.getDrawcallCount(); + } + + for (auto& node : m_nodes) { + count += node.getDrawcallCount(); + } + + return count; + } + + const Bounds& Node::getBounds() const { + return m_bounds; + } + +} diff --git a/modules/scene/src/vkcv/scene/Scene.cpp b/modules/scene/src/vkcv/scene/Scene.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dfe3ac54e6f63922218edd607432d9b291dcc168 --- /dev/null +++ b/modules/scene/src/vkcv/scene/Scene.cpp @@ -0,0 +1,361 @@ + +#include "vkcv/scene/Scene.hpp" + +#include <vkcv/Image.hpp> +#include <vkcv/Logger.hpp> +#include <vkcv/Sampler.hpp> +#include <vkcv/asset/asset_loader.hpp> + +#include <vkcv/algorithm/SinglePassDownsampler.hpp> + +namespace vkcv::scene { + + Scene::Scene(Core* core) : + m_core(core), + m_materials(), + m_nodes() {} + + Scene::~Scene() { + m_nodes.clear(); + m_materials.clear(); + } + + Scene::Scene(const Scene &other) : + m_core(other.m_core), + m_materials(other.m_materials), + m_nodes() { + m_nodes.resize(other.m_nodes.size(), Node(*this)); + + for (size_t i = 0; i < m_nodes.size(); i++) { + m_nodes[i] = other.m_nodes[i]; + } + } + + Scene::Scene(Scene &&other) noexcept : + m_core(other.m_core), + m_materials(other.m_materials), + m_nodes() { + m_nodes.resize(other.m_nodes.size(), Node(*this)); + + for (size_t i = 0; i < m_nodes.size(); i++) { + m_nodes[i] = std::move(other.m_nodes[i]); + } + } + + Scene &Scene::operator=(const Scene &other) { + if (&other == this) { + return *this; + } + + m_core = other.m_core; + m_materials = std::vector<Material>(other.m_materials); + + m_nodes.resize(other.m_nodes.size(), Node(*this)); + + for (size_t i = 0; i < m_nodes.size(); i++) { + m_nodes[i] = other.m_nodes[i]; + } + + return *this; + } + + Scene &Scene::operator=(Scene &&other) noexcept { + m_core = other.m_core; + m_materials = std::move(other.m_materials); + + m_nodes.resize(other.m_nodes.size(), Node(*this)); + + for (size_t i = 0; i < m_nodes.size(); i++) { + m_nodes[i] = std::move(other.m_nodes[i]); + } + + return *this; + } + + size_t Scene::addNode() { + const Node node (*this); + const size_t index = m_nodes.size(); + m_nodes.push_back(node); + return index; + } + + Node& Scene::getNode(size_t index) { + return m_nodes[index]; + } + + const Node& Scene::getNode(size_t index) const { + return m_nodes[index]; + } + + void Scene::increaseMaterialUsage(size_t index) { + if (index < m_materials.size()) { + m_materials[index].m_usages++; + } + } + + void Scene::decreaseMaterialUsage(size_t index) { + if ((index < m_materials.size()) && (m_materials[index].m_usages > 0)) { + m_materials[index].m_usages--; + } + } + + size_t Scene::getMaterialCount() const { + return m_materials.size(); + } + + const material::Material & Scene::getMaterial(size_t index) const { + static material::Material noMaterial; + + if (index >= m_materials.size()) { + return noMaterial; + } + + return m_materials[index].m_data; + } + + void Scene::recordDrawcalls(CommandStreamHandle &cmdStream, + const camera::Camera &camera, + const PassHandle &pass, + const GraphicsPipelineHandle &pipeline, + size_t pushConstantsSizePerDrawcall, + const RecordMeshDrawcallFunction &record, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle) { + m_core->recordBeginDebugLabel(cmdStream, "vkcv::scene::Scene", { + 0.0f, 1.0f, 0.0f, 1.0f + }); + + PushConstants pushConstants (pushConstantsSizePerDrawcall); + std::vector<InstanceDrawcall> drawcalls; + size_t count = 0; + + const glm::mat4 viewProjection = camera.getMVP(); + + for (auto& node : m_nodes) { + count += node.getDrawcallCount(); + node.recordDrawcalls(viewProjection, pushConstants, drawcalls, record); + } + + vkcv_log(LogLevel::RAW_INFO, "Frustum culling: %lu / %lu", drawcalls.size(), count); + + m_core->recordDrawcallsToCmdStream( + cmdStream, + pipeline, + pushConstants, + drawcalls, + renderTargets, + windowHandle + ); + + m_core->recordEndDebugLabel(cmdStream); + } + + Scene Scene::create(Core& core) { + return Scene(&core); + } + + static void loadImage(Core& core, const asset::Scene& asset_scene, + const asset::Texture& asset_texture, + const vk::Format& format, + ImageHandle& image, SamplerHandle& sampler) { + const asset::Sampler* asset_sampler = nullptr; + + if ((asset_texture.sampler >= 0) && (asset_texture.sampler < asset_scene.samplers.size())) { + asset_sampler = &(asset_scene.samplers[asset_texture.sampler]); + } + + Image img = vkcv::image(core, format, asset_texture.w, asset_texture.h, 1, true); + img.fill(asset_texture.data.data()); + image = img.getHandle(); + + SamplerFilterType magFilter = SamplerFilterType::LINEAR; + SamplerFilterType minFilter = SamplerFilterType::LINEAR; + SamplerMipmapMode mipmapMode = SamplerMipmapMode::LINEAR; + SamplerAddressMode addressMode = SamplerAddressMode::REPEAT; + + float mipLodBias = 0.0f; + + if (asset_sampler) { + switch (asset_sampler->magFilter) { + case VK_FILTER_NEAREST: + magFilter = SamplerFilterType::NEAREST; + break; + case VK_FILTER_LINEAR: + magFilter = SamplerFilterType::LINEAR; + break; + default: + break; + } + + switch (asset_sampler->minFilter) { + case VK_FILTER_NEAREST: + minFilter = SamplerFilterType::NEAREST; + break; + case VK_FILTER_LINEAR: + minFilter = SamplerFilterType::LINEAR; + break; + default: + break; + } + + switch (asset_sampler->mipmapMode) { + case VK_SAMPLER_MIPMAP_MODE_NEAREST: + mipmapMode = SamplerMipmapMode::NEAREST; + break; + case VK_SAMPLER_MIPMAP_MODE_LINEAR: + mipmapMode = SamplerMipmapMode::LINEAR; + break; + default: + break; + } + + switch (asset_sampler->addressModeU) { + case VK_SAMPLER_ADDRESS_MODE_REPEAT: + addressMode = SamplerAddressMode::REPEAT; + break; + case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: + addressMode = SamplerAddressMode::MIRRORED_REPEAT; + break; + case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: + addressMode = SamplerAddressMode::CLAMP_TO_EDGE; + break; + case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: + addressMode = SamplerAddressMode::MIRROR_CLAMP_TO_EDGE; + break; + case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: + addressMode = SamplerAddressMode::CLAMP_TO_BORDER; + break; + default: + break; + } + + mipLodBias = asset_sampler->minLOD; + } + + sampler = core.createSampler( + magFilter, + minFilter, + mipmapMode, + addressMode, + mipLodBias + ); + } + + void Scene::loadMaterial(size_t index, const asset::Scene& scene, + const asset::Material& material) { + if (index >= m_materials.size()) { + return; + } + + ImageHandle diffuseImg; + SamplerHandle diffuseSmp; + + if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) { + loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb, + diffuseImg, diffuseSmp); + } + + ImageHandle normalImg; + SamplerHandle normalSmp; + + if ((material.normal >= 0) && (material.normal < scene.textures.size())) { + loadImage(*m_core, scene, scene.textures[material.normal], vk::Format::eR8G8B8A8Unorm, + normalImg, normalSmp); + } + + ImageHandle metalRoughImg; + SamplerHandle metalRoughSmp; + + if ((material.metalRough >= 0) && (material.metalRough < scene.textures.size())) { + loadImage(*m_core, scene, scene.textures[material.metalRough], vk::Format::eR8G8B8A8Unorm, + metalRoughImg, metalRoughSmp); + } + + ImageHandle occlusionImg; + SamplerHandle occlusionSmp; + + if ((material.occlusion >= 0) && (material.occlusion < scene.textures.size())) { + loadImage(*m_core, scene, scene.textures[material.occlusion], vk::Format::eR8G8B8A8Unorm, + occlusionImg, occlusionSmp); + } + + ImageHandle emissionImg; + SamplerHandle emissionSmp; + + if ((material.emissive >= 0) && (material.emissive < scene.textures.size())) { + loadImage(*m_core, scene, scene.textures[material.emissive], vk::Format::eR8G8B8A8Srgb, + emissionImg, emissionSmp); + } + + const float colorFactors [4] = { + material.baseColorFactor.r, + material.baseColorFactor.g, + material.baseColorFactor.b, + material.baseColorFactor.a + }; + + const float emissionFactors[4] = { + material.emissiveFactor.r, + material.emissiveFactor.g, + material.emissiveFactor.b, + 0.0f + }; + + m_materials[index].m_data = material::Material::createPBR( + *m_core, + diffuseImg, diffuseSmp, + normalImg, normalSmp, + metalRoughImg, metalRoughSmp, + occlusionImg, occlusionSmp, + emissionImg, emissionSmp, + colorFactors, + material.normalScale, + material.metallicFactor, + material.roughnessFactor, + material.occlusionStrength, + emissionFactors + ); + } + + Scene Scene::load(Core& core, + const std::filesystem::path &path, + const std::vector<asset::PrimitiveType>& types) { + asset::Scene asset_scene; + + if (!asset::loadScene(path.string(), asset_scene)) { + vkcv_log(LogLevel::ERROR, "Scene could not be loaded (%s)", path.c_str()); + return create(core); + } + + Scene scene = create(core); + + for (const auto& material : asset_scene.materials) { + scene.m_materials.push_back({ + 0, material::Material() + }); + } + + const size_t root = scene.addNode(); + + for (const auto& mesh : asset_scene.meshes) { + scene.getNode(root).loadMesh(asset_scene, mesh, types); + } + + vkcv::SamplerHandle sampler = samplerLinear(core); + + const vkcv::FeatureManager& featureManager = core.getContext().getFeatureManager(); + + vkcv::algorithm::SinglePassDownsampler spdDownsampler (core, sampler); + auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics); + + for (auto& material : scene.m_materials) { + material.m_data.recordMipChainGeneration(mipStream, spdDownsampler); + } + + core.submitCommandStream(mipStream, false); + + scene.getNode(root).splitMeshesToSubNodes(128); + return scene; + } + +} diff --git a/modules/shader_compiler/CMakeLists.txt b/modules/shader_compiler/CMakeLists.txt index 4b674ec41ed4ea5f42dc73187c212e6a69952cec..63182aaf34ad9d09d3f9a3dca7360cbed1875c04 100644 --- a/modules/shader_compiler/CMakeLists.txt +++ b/modules/shader_compiler/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(vkcv_shader_compiler) # setting c++ standard for the module -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(vkcv_shader_compiler_source ${PROJECT_SOURCE_DIR}/src) @@ -10,12 +10,15 @@ set(vkcv_shader_compiler_include ${PROJECT_SOURCE_DIR}/include) # Add source and header files to the module set(vkcv_shader_compiler_sources + ${vkcv_shader_compiler_include}/vkcv/shader/Compiler.hpp + ${vkcv_shader_compiler_source}/vkcv/shader/Compiler.cpp + ${vkcv_shader_compiler_include}/vkcv/shader/GLSLCompiler.hpp ${vkcv_shader_compiler_source}/vkcv/shader/GLSLCompiler.cpp ) # adding source files to the module -add_library(vkcv_shader_compiler STATIC ${vkcv_shader_compiler_sources}) +add_library(vkcv_shader_compiler ${vkcv_build_attribute} ${vkcv_shader_compiler_sources}) # Setup some path variables to load libraries set(vkcv_shader_compiler_lib lib) @@ -28,7 +31,15 @@ include(config/GLSLANG.cmake) target_link_libraries(vkcv_shader_compiler ${vkcv_shader_compiler_libraries} vkcv) # including headers of dependencies and the VkCV framework -target_include_directories(vkcv_shader_compiler SYSTEM BEFORE PRIVATE ${vkcv_shader_compiler_includes} ${vkcv_include}) +target_include_directories(vkcv_shader_compiler SYSTEM BEFORE PRIVATE ${vkcv_shader_compiler_includes} ${vkcv_include} ${vkcv_includes}) # add the own include directory for public headers target_include_directories(vkcv_shader_compiler BEFORE PUBLIC ${vkcv_shader_compiler_include}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_shader_compiler_include}) + list(APPEND vkcv_modules_libraries vkcv_shader_compiler) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/shader_compiler/README.md b/modules/shader_compiler/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3b64e10e5eb8de251de3ce2695e499e6e15f746b --- /dev/null +++ b/modules/shader_compiler/README.md @@ -0,0 +1,15 @@ +# Shader-Compiler + +A VkCV module to compile shaders at runtime + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|----------------------------------------------------|---| +| [Glslang](https://github.com/KhronosGroup/glslang/) | ✅ | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__shader.html) to this module. diff --git a/modules/shader_compiler/config/GLSLANG.cmake b/modules/shader_compiler/config/GLSLANG.cmake index 50b9fd46bd0db9421c632aa0b80fb8df7e3f2123..98bd45497f9d7ed5196dbed486921f8e6ada12da 100644 --- a/modules/shader_compiler/config/GLSLANG.cmake +++ b/modules/shader_compiler/config/GLSLANG.cmake @@ -1,5 +1,7 @@ -if (EXISTS "${vkcv_shader_compiler_lib_path}/glslang") +use_git_submodule("${vkcv_shader_compiler_lib_path}/glslang" glslang_status) + +if (${glslang_status}) set(SKIP_GLSLANG_INSTALL ON CACHE INTERNAL "") set(ENABLE_SPVREMAPPER OFF CACHE INTERNAL "") set(ENABLE_GLSLANG_BINARIES OFF CACHE INTERNAL "") @@ -23,6 +25,4 @@ if (EXISTS "${vkcv_shader_compiler_lib_path}/glslang") list(APPEND vkcv_shader_compiler_libraries glslang SPIRV) list(APPEND vkcv_shader_compiler_includes ${vkcv_shader_compiler_lib}) -else() - message(WARNING "GLSLANG is required..! Update the submodules!") endif () diff --git a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp index d7b7af7178531aea358cecbc8b86a29527173014..f62190753ec440c7d732551b6229276c5a42cfc1 100644 --- a/modules/shader_compiler/include/vkcv/shader/Compiler.hpp +++ b/modules/shader_compiler/include/vkcv/shader/Compiler.hpp @@ -1,17 +1,101 @@ #pragma once +#include <filesystem> +#include <string> +#include <unordered_map> + #include <vkcv/Event.hpp> +#include <vkcv/ShaderStage.hpp> +#include <vkcv/ShaderProgram.hpp> namespace vkcv::shader { - + + /** + * @defgroup vkcv_shader Shader Compiler Module + * A module to use runtime shader compilation. + * @{ + */ + + /** + * An event function type to be called on compilation completion. + */ typedef typename event_function<ShaderStage, const std::filesystem::path&>::type ShaderCompiledFunction; + /** + * An event function type to be called on program compilation completion. + */ + typedef typename event_function<ShaderProgram&>::type ShaderProgramCompiledFunction; + + /** + * An abstract class to handle runtime shader compilation. + */ class Compiler { private: + protected: + /** + * A map containing macros for shader compilation. + */ + std::unordered_map<std::string, std::string> m_defines; + public: + /** + * Compile a shader from source for a target stage with a custom shader + * include path and an event function called if the compilation completes. + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderSource Source of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @return Result if the compilation succeeds + */ + virtual bool compileSource(ShaderStage shaderStage, const char* shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath) = 0; + + /** + * Compile a shader from a specific file path for a target stage with + * a custom shader include path and an event function called if the + * compilation completes. + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderPath Filepath of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @param[in] update Flag to update shaders during runtime + */ virtual void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath, - const ShaderCompiledFunction& compiled, bool update = false) = 0; + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath, bool update) = 0; + + /** + * Compile a shader program from a specific map of given file paths for + * target pipeline stages with a custom shader include path and an event + * function called if the compilation completes. + * @param[in,out] program Shader program + * @param[in] stages Shader pipeline stages + * @param[in] compiled Shader program compilation event + * @param[in] includePath Include path for shaders + * @param[in] update Flag to update shaders during runtime + */ + void compileProgram(ShaderProgram& program, + const std::unordered_map<ShaderStage, const std::filesystem::path>& stages, + const ShaderProgramCompiledFunction& compiled, + const std::filesystem::path& includePath = "", bool update = false); + + /** + * Return the definition value of a macro for shader compilation. + * @param[in] name Macro definition name + * @return Macro definition value + */ + std::string getDefine(const std::string& name) const; + + /** + * Set a macro for shader compilation. + * @param[in] name Macro definition name + * @param[in] value Macro definition value + */ + void setDefine(const std::string& name, const std::string& value); }; + + /** @} */ } diff --git a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp index 7105d93a0c3e153bf3abe1d624d0c13c6f09ac6d..face4cba350122f22feee6bae878397dccf4a070 100644 --- a/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp +++ b/modules/shader_compiler/include/vkcv/shader/GLSLCompiler.hpp @@ -6,23 +6,94 @@ #include "Compiler.hpp" namespace vkcv::shader { - - class GLSLCompiler { + + /** + * @addtogroup vkcv_shader + * @{ + */ + + enum class GLSLCompileTarget { + SUBGROUP_OP, + RAY_TRACING, + + UNKNOWN + }; + + /** + * A class to handle GLSL runtime shader compilation. + */ + class GLSLCompiler : public Compiler { private: - public: - GLSLCompiler(); + GLSLCompileTarget m_target; + public: + /** + * The constructor of a runtime GLSL shader compiler instance. + * + * @param[in] target Compile target (optional) + */ + GLSLCompiler(GLSLCompileTarget target = GLSLCompileTarget::UNKNOWN); + + /** + * The copy-constructor of a runtime GLSL shader compiler instance. + * @param[in] other Other instance of a GLSL shader compiler instance + */ GLSLCompiler(const GLSLCompiler& other); + + /** + * The move-constructor of a runtime GLSL shader compiler instance. + * @param[out] other Other instance of a GLSL shader compiler instance + */ GLSLCompiler(GLSLCompiler&& other) = default; - + + /** + * The destructor of a runtime GLSL shader compiler instance. + */ ~GLSLCompiler(); - + + /** + * The copy-operator of a runtime GLSL shader compiler instance. + * @param[in] other Other instance of a GLSL shader compiler instance + * @return Reference to this instance + */ GLSLCompiler& operator=(const GLSLCompiler& other); - GLSLCompiler& operator=(GLSLCompiler&& other) = default; - + + /** + * The copy-operator of a runtime GLSL shader compiler instance. + * @param[out] other Other instance of a GLSL shader compiler instance + * @return Reference to this instance + */ + GLSLCompiler& operator=(GLSLCompiler&& other) = default; + + /** + * Compile a GLSL shader from source for a target stage with a custom shader + * include path and an event function called if the compilation completes. + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderSource Source of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @return Result if the compilation succeeds + */ + bool compileSource(ShaderStage shaderStage, const char* shaderSource, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath) override; + + /** + * Compile a GLSL shader from a specific file path for a target stage with + * a custom shader include path and an event function called if the + * compilation completes. + * @param[in] shaderStage Shader pipeline stage + * @param[in] shaderPath Filepath of shader + * @param[in] compiled Shader compilation event + * @param[in] includePath Include path for shaders + * @param[in] update Flag to update shaders during runtime + */ void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath, - const ShaderCompiledFunction& compiled, bool update = false); + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath = "", bool update = false) override; }; + + /** @} */ } diff --git a/modules/shader_compiler/lib/glslang b/modules/shader_compiler/lib/glslang index fe15158676657bf965e41c32e15ae5db7ea2ab6a..5755de46b07e4374c05fb1081f65f7ae1f8cca81 160000 --- a/modules/shader_compiler/lib/glslang +++ b/modules/shader_compiler/lib/glslang @@ -1 +1 @@ -Subproject commit fe15158676657bf965e41c32e15ae5db7ea2ab6a +Subproject commit 5755de46b07e4374c05fb1081f65f7ae1f8cca81 diff --git a/modules/shader_compiler/src/vkcv/shader/Compiler.cpp b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dcf9e5167c2b2fdd251e4e245a8ea7a6d7e62712 --- /dev/null +++ b/modules/shader_compiler/src/vkcv/shader/Compiler.cpp @@ -0,0 +1,48 @@ + +#include "vkcv/shader/Compiler.hpp" + +namespace vkcv::shader { + + void Compiler::compileProgram(ShaderProgram& program, + const std::unordered_map<ShaderStage, const std::filesystem::path>& stages, + const ShaderProgramCompiledFunction& compiled, + const std::filesystem::path& includePath, bool update) { + std::vector<std::pair<ShaderStage, const std::filesystem::path>> stageList; + size_t i; + + stageList.reserve(stages.size()); + for (const auto& stage : stages) { + stageList.push_back(stage); + } + + /* Compile a shader programs stages in parallel to improve performance */ + #pragma omp parallel for shared(stageList, includePath, update) private(i) + for (i = 0; i < stageList.size(); i++) { + const auto& stage = stageList[i]; + + compile( + stage.first, + stage.second, + [&program](ShaderStage shaderStage, const std::filesystem::path& path) { + #pragma omp critical + program.addShader(shaderStage, path); + }, + includePath, + update + ); + } + + if (compiled) { + compiled(program); + } + } + + std::string Compiler::getDefine(const std::string &name) const { + return m_defines.at(name); + } + + void Compiler::setDefine(const std::string &name, const std::string &value) { + m_defines[name] = value; + } + +} diff --git a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp index ec358188b8e871da6f4d62ffd397f32bfb795ee2..78a82113f89bd54914471d64fee6e11700548431 100644 --- a/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp +++ b/modules/shader_compiler/src/vkcv/shader/GLSLCompiler.cpp @@ -2,16 +2,20 @@ #include "vkcv/shader/GLSLCompiler.hpp" #include <fstream> +#include <sstream> #include <glslang/SPIRV/GlslangToSpv.h> #include <glslang/StandAlone/DirStackFileIncluder.h> +#include <vkcv/File.hpp> #include <vkcv/Logger.hpp> namespace vkcv::shader { static uint32_t s_CompilerCount = 0; - GLSLCompiler::GLSLCompiler() { + GLSLCompiler::GLSLCompiler(GLSLCompileTarget target) : + Compiler(), + m_target(target) { if (s_CompilerCount == 0) { glslang::InitializeProcess(); } @@ -19,7 +23,7 @@ namespace vkcv::shader { s_CompilerCount++; } - GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) { + GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) : Compiler(other) { s_CompilerCount++; } @@ -50,6 +54,22 @@ namespace vkcv::shader { return EShLangFragment; case ShaderStage::COMPUTE: return EShLangCompute; + case ShaderStage::TASK: + return EShLangTaskNV; + case ShaderStage::MESH: + return EShLangMeshNV; + case ShaderStage::RAY_GEN: + return EShLangRayGen; + case ShaderStage::RAY_CLOSEST_HIT: + return EShLangClosestHit; + case ShaderStage::RAY_MISS: + return EShLangMiss; + case ShaderStage::RAY_INTERSECTION: + return EShLangIntersect; + case ShaderStage::RAY_ANY_HIT: + return EShLangAnyHit; + case ShaderStage::RAY_CALLABLE: + return EShLangCallable; default: return EShLangCount; } @@ -197,22 +217,57 @@ namespace vkcv::shader { return true; } - void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath, - const ShaderCompiledFunction& compiled, bool update) { + bool GLSLCompiler::compileSource(ShaderStage shaderStage, const char* shaderSource, + const ShaderCompiledFunction &compiled, + const std::filesystem::path& includePath) { const EShLanguage language = findShaderLanguage(shaderStage); if (language == EShLangCount) { - vkcv_log(LogLevel::ERROR, "Shader stage not supported (%s)", shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "Shader stage not supported"); + return false; } - const std::vector<char> code = readShaderCode(shaderPath); - glslang::TShader shader (language); + switch (m_target) { + case GLSLCompileTarget::SUBGROUP_OP: + shader.setEnvClient(glslang::EShClientVulkan,glslang::EShTargetVulkan_1_1); + shader.setEnvTarget(glslang::EShTargetSpv,glslang::EShTargetSpv_1_3); + break; + case GLSLCompileTarget::RAY_TRACING: + shader.setEnvClient(glslang::EShClientVulkan,glslang::EShTargetVulkan_1_2); + shader.setEnvTarget(glslang::EShTargetSpv,glslang::EShTargetSpv_1_4); + break; + default: + break; + } + glslang::TProgram program; + std::string source (shaderSource); + + if (!m_defines.empty()) { + std::ostringstream defines; + for (const auto& define : m_defines) { + defines << "#define " << define.first << " " << define.second << std::endl; + } + + size_t pos = source.find("#version") + 8; + if (pos >= source.length()) { + pos = 0; + } + + const size_t epos = source.find_last_of("#extension", pos) + 10; + if (epos < source.length()) { + pos = epos; + } + + const auto defines_str = defines.str(); + + pos = source.find('\n', pos) + 1; + source = source.insert(pos, defines_str); + } const char *shaderStrings [1]; - shaderStrings[0] = code.data(); + shaderStrings[0] = source.c_str(); shader.setStrings(shaderStrings, 1); @@ -222,51 +277,53 @@ namespace vkcv::shader { const auto messages = (EShMessages)( EShMsgSpvRules | EShMsgVulkanRules - ); + ); std::string preprocessedGLSL; DirStackFileIncluder includer; - includer.pushExternalLocalDirectory(shaderPath.parent_path().string()); + includer.pushExternalLocalDirectory(includePath.string()); - if (!shader.preprocess(&resources, 100, ENoProfile, false, false, messages, &preprocessedGLSL, includer)) { - vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n} (%s)", - shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str()); - return; + if (!shader.preprocess(&resources, 100, ENoProfile, + false, false, + messages, &preprocessedGLSL, includer)) { + vkcv_log(LogLevel::ERROR, "Shader preprocessing failed {\n%s\n%s\n}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return false; } const char* preprocessedCString = preprocessedGLSL.c_str(); shader.setStrings(&preprocessedCString, 1); if (!shader.parse(&resources, 100, false, messages)) { - vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n} (%s)", - shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return false; } program.addShader(&shader); if (!program.link(messages)) { - vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n} (%s)", - shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n}", + shader.getInfoLog(), shader.getInfoDebugLog()); + return false; } const glslang::TIntermediate* intermediate = program.getIntermediate(language); if (!intermediate) { - vkcv_log(LogLevel::ERROR, "No valid intermediate representation (%s)", shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "No valid intermediate representation"); + return false; } std::vector<uint32_t> spirv; glslang::GlslangToSpv(*intermediate, spirv); - const std::filesystem::path tmp_path (std::tmpnam(nullptr)); + const std::filesystem::path tmp_path = generateTemporaryFilePath(); if (!writeSpirvCode(tmp_path, spirv)) { - vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk (%s)", shaderPath.string().c_str()); - return; + vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk"); + return false; } if (compiled) { @@ -274,6 +331,24 @@ namespace vkcv::shader { } std::filesystem::remove(tmp_path); + return true; + } + + void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath, + const ShaderCompiledFunction& compiled, + const std::filesystem::path& includePath, bool update) { + const std::vector<char> code = readShaderCode(shaderPath); + bool result; + + if (!includePath.empty()) { + result = compileSource(shaderStage, code.data(), compiled, includePath); + } else { + result = compileSource(shaderStage, code.data(), compiled, shaderPath.parent_path()); + } + + if (!result) { + vkcv_log(LogLevel::ERROR, "Shader compilation failed: (%s)", shaderPath.string().c_str()); + } if (update) { // TODO: Shader hot compilation during runtime diff --git a/modules/testing/CMakeLists.txt b/modules/testing/CMakeLists.txt index a22e547646fd4ef59860245d51365b98df59b578..60f5a82732b9e095f05b0d6a551b825a6d05f3dd 100644 --- a/modules/testing/CMakeLists.txt +++ b/modules/testing/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16) project(vkcv_testing) # setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(vkcv_testing_source ${PROJECT_SOURCE_DIR}/src) @@ -14,8 +14,15 @@ set(vkcv_testing_sources ) # adding source files to the project -add_library(vkcv_testing STATIC ${vkcv_testing_sources}) +add_library(vkcv_testing ${vkcv_build_attribute} ${vkcv_testing_sources}) # add the own include directory for public headers target_include_directories(vkcv_testing BEFORE PUBLIC ${vkcv_testing_include}) +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_testing_include}) + list(APPEND vkcv_modules_libraries vkcv_testing) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/upscaling/CMakeLists.txt b/modules/upscaling/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0a5ee06dd695a3f3596c238bc0ef4383c011cf24 --- /dev/null +++ b/modules/upscaling/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 3.16) +project(vkcv_upscaling) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(vkcv_upscaling_source ${PROJECT_SOURCE_DIR}/src) +set(vkcv_upscaling_include ${PROJECT_SOURCE_DIR}/include) + +set(vkcv_upscaling_sources + ${vkcv_upscaling_include}/vkcv/upscaling/Upscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/Upscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/BilinearUpscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/BilinearUpscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/FSRUpscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/FSRUpscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/NISUpscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/NISUpscaling.cpp + + ${vkcv_upscaling_include}/vkcv/upscaling/FSR2Upscaling.hpp + ${vkcv_upscaling_source}/vkcv/upscaling/FSR2Upscaling.cpp +) + +# Setup some path variables to load libraries +set(vkcv_upscaling_lib lib) +set(vkcv_upscaling_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_upscaling_lib}) + +# Check and load FidelityFX_FSR +include(config/FidelityFX_FSR.cmake) + +# Check and load FidelityFX_FSR2 +include(config/FidelityFX_FSR2.cmake) + +# Check and load NVIDIAImageScaling +include(config/NVIDIAImageScaling.cmake) + +# Add compile definitions depending on the build context of the module +add_compile_definitions(${vkcv_upscaling_definitions}) + +# adding source files to the project +add_library(vkcv_upscaling ${vkcv_build_attribute} ${vkcv_upscaling_sources}) + +# link the required libraries to the module +target_link_libraries(vkcv_upscaling ${vkcv_upscaling_libraries} vkcv vkcv_shader_compiler) + +# including headers of dependencies and the VkCV framework +target_include_directories(vkcv_upscaling SYSTEM BEFORE PRIVATE ${vkcv_upscaling_includes} ${vkcv_include} ${vkcv_includes} ${vkcv_shader_compiler_include}) + +# add the own include directory for public headers +target_include_directories(vkcv_upscaling BEFORE PUBLIC ${vkcv_upscaling_include}) + +if (vkcv_parent_scope) + list(APPEND vkcv_modules_includes ${vkcv_upscaling_include}) + list(APPEND vkcv_modules_libraries vkcv_upscaling) + + set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE) + set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE) +endif() diff --git a/modules/upscaling/README.md b/modules/upscaling/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9c4bed679f5a7a756e8b25edc85360672f8fabd0 --- /dev/null +++ b/modules/upscaling/README.md @@ -0,0 +1,17 @@ +# Upscaling + +A VkCV module to upscale images in realtime + +## Build + +### Dependencies (required): + +| Name of dependency | Used as submodule | +|------------------------------------------------------------------------------|---| +| [FidelityFX-FSR](https://github.com/GPUOpen-Effects/FidelityFX-FSR/) | ✅ | +| [NVIDIAImageScaling](https://github.com/NVIDIAGameWorks/NVIDIAImageScaling/) | ✅ | +| [FidelityFX-FSR2](https://github.com/GPUOpen-Effects/FidelityFX-FSR2/) | ✅ | + +## Docs + +Here is a [link](https://userpages.uni-koblenz.de/~vkcv/doc/group__vkcv__upscaling.html) to this module. diff --git a/modules/upscaling/config/FidelityFX_FSR.cmake b/modules/upscaling/config/FidelityFX_FSR.cmake new file mode 100644 index 0000000000000000000000000000000000000000..780f1d441abe2a567db7491ce5e2f4821a396b89 --- /dev/null +++ b/modules/upscaling/config/FidelityFX_FSR.cmake @@ -0,0 +1,18 @@ + +use_git_submodule("${vkcv_upscaling_lib_path}/FidelityFX-FSR" ffx_fsr_status) + +if (${ffx_fsr_status}) + include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/ffx-fsr/ffx_a.h ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/sample/src/VK/FSR_Pass.glsl ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + + list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR/ffx-fsr) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/ffx_a.h.cxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/ffx_fsr1.h.cxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/FSR_Pass.glsl.cxx) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/ffx_a.h.hxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/ffx_fsr1.h.hxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/FSR_Pass.glsl.hxx) +endif () diff --git a/modules/upscaling/config/FidelityFX_FSR2.cmake b/modules/upscaling/config/FidelityFX_FSR2.cmake new file mode 100644 index 0000000000000000000000000000000000000000..1c1cb1339a7e7f93cb42994c3532f3128ad206ac --- /dev/null +++ b/modules/upscaling/config/FidelityFX_FSR2.cmake @@ -0,0 +1,30 @@ + +set(vkcv_upscaling_fsr2_override ON) + +if (WIN32) + set(vkcv_upscaling_fsr2_override OFF) +else() + find_program(wine_program "wine") + + if (EXISTS ${wine_program}) + set(vkcv_upscaling_fsr2_override OFF) + endif() +endif() + +if (vkcv_upscaling_fsr2_override) + list(APPEND vkcv_upscaling_definitions VKCV_OVERRIDE_FSR2_WITH_FSR1=1) +else() + use_git_submodule("${vkcv_upscaling_lib_path}/FidelityFX-FSR2" ffx_fsr2_status) + + if (${ffx_fsr2_status}) + set(FFX_FSR2_API_DX12 OFF CACHE INTERNAL "") + set(FFX_FSR2_API_VK ON CACHE INTERNAL "") + + add_subdirectory(${vkcv_upscaling_lib}/FidelityFX-FSR2/src/ffx-fsr2-api) + + list(APPEND vkcv_upscaling_libraries ${FFX_FSR2_API} ${FFX_FSR2_API_VK}) + + list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR2/src/ffx-fsr2-api) + list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR2/src/ffx-fsr2-api/vk) + endif () +endif() \ No newline at end of file diff --git a/modules/upscaling/config/NVIDIAImageScaling.cmake b/modules/upscaling/config/NVIDIAImageScaling.cmake new file mode 100644 index 0000000000000000000000000000000000000000..083891c67e369dc783c79f5ac4386ae15e6e3261 --- /dev/null +++ b/modules/upscaling/config/NVIDIAImageScaling.cmake @@ -0,0 +1,15 @@ + +use_git_submodule("${vkcv_upscaling_lib_path}/NVIDIAImageScaling" nvidia_nis_status) + +if (${nvidia_nis_status}) + include_shader(${vkcv_upscaling_lib_path}/NVIDIAImageScaling/NIS/NIS_Scaler.h ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + include_shader(${vkcv_upscaling_lib_path}/NVIDIAImageScaling/NIS/NIS_Main.glsl ${vkcv_upscaling_include} ${vkcv_upscaling_source}) + + list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/NVIDIAImageScaling/NIS) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/NIS_Scaler.h.cxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/NIS_Main.glsl.cxx) + + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/NIS_Scaler.h.hxx) + list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/NIS_Main.glsl.hxx) +endif () diff --git a/modules/upscaling/include/vkcv/upscaling/BilinearUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/BilinearUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a3f29276aa95e17e11ce2a044721d47d558dd869 --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/BilinearUpscaling.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "Upscaling.hpp" + +namespace vkcv::upscaling { + + /** + * @addtogroup vkcv_upscaling + * @{ + */ + + /** + * A class to handle upscaling via bilinear interpolation. + */ + class BilinearUpscaling : public Upscaling { + private: + public: + /** + * Constructor to create instance for bilinear upscaling. + * + * @param[in,out] core Reference to a Core instance + */ + explicit BilinearUpscaling(Core& core); + + /** + * Record the commands of the bilinear upscaling instance to + * scale the image of the input handle to the resolution of + * the output image handle via bilinear interpolation. + * + * @param[in] cmdStream Command stream handle to record commands + * @param[in] input Input image handle + * @param[in] output Output image handle + */ + void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) override; + + }; + + /** @} */ + +} diff --git a/modules/upscaling/include/vkcv/upscaling/FSR2Upscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSR2Upscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..447f16ed6bc3f84862c252db2103c7734659b5d2 --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/FSR2Upscaling.hpp @@ -0,0 +1,244 @@ +#pragma once + +#include "Upscaling.hpp" + +#include <memory> +#include <vector> + +#ifdef VKCV_OVERRIDE_FSR2_WITH_FSR1 +#include "FSRUpscaling.hpp" +#else +struct FfxFsr2ContextDescription; +struct FfxFsr2Context; +#endif + +namespace vkcv::upscaling { + + /** + * @addtogroup vkcv_upscaling + * @{ + */ + + /** + * Enum to set the mode of quality for + * FSR2 upscaling. + */ + enum class FSR2QualityMode : int { + /** + * Don't upscale anything. + */ + NONE = 0, + + /** + * High quality of FSR upscaling: + * 1.5x per dimension + */ + QUALITY = 2, + + /** + * Medium quality of FSR upscaling: + * 1.7x per dimension + */ + BALANCED = 3, + + /** + * Low quality of FSR upscaling: + * 2.0x per dimension + */ + PERFORMANCE = 4, + + /** + * Lowest quality of FSR upscaling: + * 3.0x per dimension + */ + ULTRA_PERFORMANCE = 5, + }; + + /** + * Calculates the internal resolution for actual rendering if + * a specific mode of quality is used for upscaling with FSR2. + * + * @param[in] mode Mode of quality + * @param[in] outputWidth Final resolution width + * @param[in] outputHeight Final resolution height + * @param[out] inputWidth Internal resolution width + * @param[out] inputHeight Internal resolution height + */ + void getFSR2Resolution(FSR2QualityMode mode, + uint32_t outputWidth, uint32_t outputHeight, + uint32_t &inputWidth, uint32_t &inputHeight); + + /** + * Returns the matching negative lod bias to reduce artifacts + * upscaling with FSR2 under a given mode of quality. + * + * @param mode Mode of quality + * @return Lod bias + */ + float getFSR2LodBias(FSR2QualityMode mode); + + /** + * A class to handle upscaling via FidelityFX Super Resolution. + * https://github.com/GPUOpen-Effects/FidelityFX-FSR2 + */ + class FSR2Upscaling : public Upscaling { + private: +#ifdef VKCV_OVERRIDE_FSR2_WITH_FSR1 + std::unique_ptr<FSRUpscaling> m_fsr1; +#else + std::vector<char> m_scratchBuffer; + + std::unique_ptr<FfxFsr2ContextDescription> m_description; + std::unique_ptr<FfxFsr2Context> m_context; + + ImageHandle m_depth; + ImageHandle m_velocity; + + uint32_t m_frameIndex; + + float m_frameDeltaTime; + bool m_reset; + + float m_near; + float m_far; + float m_fov; + + /** + * Current state of HDR support. + */ + bool m_hdr; + + /** + * Sharpness will improve the upscaled image quality with + * a factor between 0.0f for no sharpening and 1.0f for + * maximum sharpening. + * + * The default value for sharpness should be 0.875f. + * + * Beware that 0.0f or any negative value of sharpness will + * disable the sharpening pass completely. + */ + float m_sharpness; + + void createFSR2Context(uint32_t displayWidth, + uint32_t displayHeight, + uint32_t renderWidth, + uint32_t renderHeight); + + void destroyFSR2Context(); +#endif + + public: + /** + * Constructor to create an instance for FSR upscaling. + * + * @param[in,out] core Reference to a Core instance + */ + explicit FSR2Upscaling(Core& core); + + /** + * Destructor to free the instance for FSR upscaling. + */ + ~FSR2Upscaling(); + + /** + * Update the upscaling instance with current frame + * delta time and whether the temporal data needs to + * be reset (for example because the camera switched). + * + * @param[in] deltaTime Current frame delta time + * @param[in] reset Reset temporal frame data + */ + void update(float deltaTime, bool reset = false); + + /** + * Calculates the jitter offset for the projection + * matrix of the camera to use in the current frame. + * + * @param[in] renderWidth Render resolution width + * @param[in] renderHeight Render resolution height + * @param[out] jitterOffsetX Jitter offset x-coordinate + * @param[out] jitterOffsetY Jitter offset y-coordinate + */ + void calcJitterOffset(uint32_t renderWidth, + uint32_t renderHeight, + float& jitterOffsetX, + float& jitterOffsetY) const; + + /** + * Bind the depth buffer image to use with the FSR2 + * upscaling instance for utilizing depth information. + * + * @param[in] depthInput Depth input image handle + */ + void bindDepthBuffer(const ImageHandle& depthInput); + + /** + * Bind the velocity buffer image to use with the FSR2 + * upscaling instance for utilizing 2D motion vectors. + * + * @param[in] velocityInput Velocity input image handle + */ + void bindVelocityBuffer(const ImageHandle& velocityInput); + + /** + * Record the commands of the FSR2 upscaling instance to + * scale the image of the input handle to the resolution of + * the output image handle via FidelityFX Super Resolution. + * + * @param[in] cmdStream Command stream handle to record commands + * @param[in] colorInput Color input image handle + * @param[in] output Output image handle + */ + void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& colorInput, + const ImageHandle& output) override; + + /** + * Set the required camera values for the FSR2 upscaling + * instance including near- and far-plane as well as + * the FOV angle vertical. + * + * @param[in] near Camera near plane + * @param[in] far Camera far plane + * @param[in] fov Camera field of view angle vertical + */ + void setCamera(float near, float far, float fov); + + /** + * Checks if HDR support is enabled and returns the status as boolean. + * + * @return true if HDR is supported, otherwise false + */ + [[nodiscard]] + bool isHdrEnabled() const; + + /** + * Changes the status of HDR support of the FSR upscaling instance. + * + * @param[in] enabled New status of HDR support + */ + void setHdrEnabled(bool enabled); + + /** + * Returns the amount of sharpness the FSR upscaling instance is using. + * + * @return The amount of sharpness + */ + [[nodiscard]] + float getSharpness() const; + + /** + * Changes the amount of sharpness of the FSR upscaling instance. + * The new sharpness value is restricted by 0.0f as lower and 1.0f + * as upper boundary. + * + * @param[in] sharpness New sharpness value + */ + void setSharpness(float sharpness); + + }; + + /** @} */ + +} \ No newline at end of file diff --git a/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5a4c59ef02c2f17ed017535ca79b834e7391546a --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/FSRUpscaling.hpp @@ -0,0 +1,239 @@ +#pragma once + +#include "Upscaling.hpp" + +#include <vkcv/Buffer.hpp> +#include <vkcv/ShaderProgram.hpp> + +namespace vkcv::upscaling { + + /** + * @addtogroup vkcv_upscaling + * @{ + */ + + /** + * Enum to set the mode of quality for + * FSR upscaling. + */ + enum class FSRQualityMode : int { + /** + * Don't upscale anything. + */ + NONE = 0, + + /** + * Highest quality of FSR upscaling: + * 1.3x per dimension + */ + ULTRA_QUALITY = 1, + + /** + * High quality of FSR upscaling: + * 1.5x per dimension + */ + QUALITY = 2, + + /** + * Medium quality of FSR upscaling: + * 1.7x per dimension + */ + BALANCED = 3, + + /** + * Low quality of FSR upscaling: + * 2.0x per dimension + */ + PERFORMANCE = 4, + }; + + /** + * Calculates the internal resolution for actual rendering if + * a specific mode of quality is used for upscaling with FSR. + * + * @param[in] mode Mode of quality + * @param[in] outputWidth Final resolution width + * @param[in] outputHeight Final resolution height + * @param[out] inputWidth Internal resolution width + * @param[out] inputHeight Internal resolution height + */ + void getFSRResolution(FSRQualityMode mode, + uint32_t outputWidth, uint32_t outputHeight, + uint32_t &inputWidth, uint32_t &inputHeight); + + /** + * Returns the matching negative lod bias to reduce artifacts + * upscaling with FSR under a given mode of quality. + * + * @param mode Mode of quality + * @return Lod bias + */ + float getFSRLodBias(FSRQualityMode mode); + + /** + * A structure to exchange required configuration + * with the shaders used by FSR upscaling. + */ + struct FSRConstants { + /** + * 0th FSR constant. + */ + uint32_t Const0 [4]; + + /** + * 1st FSR constant. + */ + uint32_t Const1 [4]; + + /** + * 2nd FSR constant. + */ + uint32_t Const2 [4]; + + /** + * 3rd FSR constant. + */ + uint32_t Const3 [4]; + + /** + * 4th FSR constant. + */ + uint32_t Sample [4]; + }; + + /** + * A class to handle upscaling via FidelityFX Super Resolution. + * https://github.com/GPUOpen-Effects/FidelityFX-FSR + */ + class FSRUpscaling : public Upscaling { + private: + /** + * The EASU compute pipeline of the FSR upscaling. + */ + ComputePipelineHandle m_easuPipeline; + + /** + * The RCAS compute pipeline of the FSR upscaling. + */ + ComputePipelineHandle m_rcasPipeline; + + /** + * The descriptor set layout of the EASU pipeline. + */ + DescriptorSetLayoutHandle m_easuDescriptorSetLayout; + + /** + * The descriptor set for the EASU pipeline. + */ + DescriptorSetHandle m_easuDescriptorSet; + + /** + * The descriptor set layout of the RCAS pipeline. + */ + DescriptorSetLayoutHandle m_rcasDescriptorSetLayout; + + /** + * The descriptor set for the RCAS pipeline. + */ + DescriptorSetHandle m_rcasDescriptorSet; + + /** + * The buffer template to handle FSR constants for + * the EASU pipeline. + */ + Buffer<FSRConstants> m_easuConstants; + + /** + * The buffer template to handle FSR constants for + * the RCAS pipeline. + */ + Buffer<FSRConstants> m_rcasConstants; + + /** + * The image handle to store the intermidiate state of + * the FSR upscaling. + */ + ImageHandle m_intermediateImage; + + /** + * The sampler handle to use for accessing the images + * in the FSR upscaling process. + */ + SamplerHandle m_sampler; + + /** + * Current state of HDR support. + */ + bool m_hdr; + + /** + * Sharpness will calculate the rcasAttenuation value + * which should be between 0.0f and 2.0f (default: 0.25f). + * + * rcasAttenuation = (1.0f - sharpness) * 2.0f + * + * So the default value for sharpness should be 0.875f. + * + * Beware that 0.0f or any negative value of sharpness will + * disable the rcas pass completely. + */ + float m_sharpness; + + public: + /** + * Constructor to create instance for FSR upscaling. + * + * @param[in,out] core Reference to a Core instance + */ + explicit FSRUpscaling(Core& core); + + /** + * Record the commands of the FSR upscaling instance to + * scale the image of the input handle to the resolution of + * the output image handle via FidelityFX Super Resolution. + * + * @param[in] cmdStream Command stream handle to record commands + * @param[in] input Input image handle + * @param[in] output Output image handle + */ + void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) override; + + /** + * Checks if HDR support is enabled and returns the status as boolean. + * + * @return true if HDR is supported, otherwise false + */ + [[nodiscard]] + bool isHdrEnabled() const; + + /** + * Changes the status of HDR support of the FSR upscaling instance. + * + * @param[in] enabled New status of HDR support + */ + void setHdrEnabled(bool enabled); + + /** + * Returns the amount of sharpness the FSR upscaling instance is using. + * + * @return The amount of sharpness + */ + [[nodiscard]] + float getSharpness() const; + + /** + * Changes the amount of sharpness of the FSR upscaling instance. + * The new sharpness value is restricted by 0.0f as lower and 1.0f + * as upper boundary. + * + * @param[in] sharpness New sharpness value + */ + void setSharpness(float sharpness); + + }; + + /** @} */ + +} diff --git a/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp b/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..62fadf8332afd78ad7cb9dbe1eadda75de35d1c3 --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/NISUpscaling.hpp @@ -0,0 +1,135 @@ +#pragma once + +#include "Upscaling.hpp" + +#include <vkcv/Buffer.hpp> +#include <vkcv/ShaderProgram.hpp> + +namespace vkcv::upscaling { + + /** + * @addtogroup vkcv_upscaling + * @{ + */ + + /** + * A class to handle upscaling via NVIDIA Image Scaling. + * https://github.com/NVIDIAGameWorks/NVIDIAImageScaling + */ + class NISUpscaling : public Upscaling { + private: + /** + * The compute pipeline of the NIS upscaling. + */ + ComputePipelineHandle m_scalerPipeline; + + /** + * The descriptor set layout of the upscaling pipeline. + */ + DescriptorSetLayoutHandle m_scalerDescriptorSetLayout; + + /** + * The descriptor set for the upscaling pipeline. + */ + DescriptorSetHandle m_scalerDescriptorSet; + + /** + * The buffer template to handle NIS constants for + * the upscaling pipeline. + */ + Buffer<uint8_t> m_scalerConstants; + + /** + * The sampler handle to use for accessing the images + * in the NIS upscaling process. + */ + SamplerHandle m_sampler; + + /** + * The image handle to store the upscaling coefficients. + */ + ImageHandle m_coefScaleImage; + + /** + * The image handle to store the USM coefficients. + */ + ImageHandle m_coefUsmImage; + + /** + * The amount of pixels per block width. + */ + uint32_t m_blockWidth; + + /** + * The amount of pixels per block height. + */ + uint32_t m_blockHeight; + + /** + * Current state of HDR support. + */ + bool m_hdr; + + /** + * The current value of sharpness. + */ + float m_sharpness; + + public: + /** + * Constructor to create instance for NIS upscaling. + * + * @param[in,out] core Reference to a Core instance + */ + explicit NISUpscaling(Core &core); + + /** + * Record the commands of the NIS upscaling instance to + * scale the image of the input handle to the resolution of + * the output image handle via NVIDIA Image Scaling. + * + * @param[in] cmdStream Command stream handle to record commands + * @param[in] input Input image handle + * @param[in] output Output image handle + */ + void recordUpscaling(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &output) override; + + /** + * Checks if HDR support is enabled and returns the status as boolean. + * + * @return true if HDR is supported, otherwise false + */ + [[nodiscard]] + bool isHdrEnabled() const; + + /** + * Changes the status of HDR support of the NIS upscaling instance. + * + * @param[in] enabled New status of HDR support + */ + void setHdrEnabled(bool enabled); + + /** + * Returns the amount of sharpness the NIS upscaling instance is using. + * + * @return The amount of sharpness + */ + [[nodiscard]] + float getSharpness() const; + + /** + * Changes the amount of sharpness of the NIS upscaling instance. + * The new sharpness value is restricted by 0.0f as lower and 1.0f + * as upper boundary. + * + * @param[in] sharpness New sharpness value + */ + void setSharpness(float sharpness); + + }; + + /** @} */ + +} diff --git a/modules/upscaling/include/vkcv/upscaling/Upscaling.hpp b/modules/upscaling/include/vkcv/upscaling/Upscaling.hpp new file mode 100644 index 0000000000000000000000000000000000000000..06e35208744734f903c8a48aa6abcd9478685c1e --- /dev/null +++ b/modules/upscaling/include/vkcv/upscaling/Upscaling.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <vkcv/Core.hpp> +#include <vkcv/Handles.hpp> + +namespace vkcv::upscaling { + + /** + * @defgroup vkcv_upscaling Upscaling Module + * A module to upscale an image from an internal resolution to a final resolution in realtime. + * @{ + */ + + /** + * An abstract class to handle upscaling of images in realtime. + */ + class Upscaling { + protected: + /** + * Reference to the current Core instance. + */ + Core& m_core; + + public: + /** + * Constructor to create an upscaling instance. + * + * @param[in,out] core Reference to a Core instance + */ + explicit Upscaling(Core& core); + + ~Upscaling() = default; + + /** + * Record the commands of the given upscaling instance to + * scale the image of the input handle to the resolution of + * the output image handle. + * + * @param[in] cmdStream Command stream handle to record commands + * @param[in] input Input image handle + * @param[in] output Output image handle + */ + virtual void recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) = 0; + + }; + + /** @} */ + +} diff --git a/modules/upscaling/lib/FidelityFX-FSR b/modules/upscaling/lib/FidelityFX-FSR new file mode 160000 index 0000000000000000000000000000000000000000..a21ffb8f6c13233ba336352bdff293894c706575 --- /dev/null +++ b/modules/upscaling/lib/FidelityFX-FSR @@ -0,0 +1 @@ +Subproject commit a21ffb8f6c13233ba336352bdff293894c706575 diff --git a/modules/upscaling/lib/FidelityFX-FSR2 b/modules/upscaling/lib/FidelityFX-FSR2 new file mode 160000 index 0000000000000000000000000000000000000000..59950a85247baa4e099537324912a1f0e3a7b5d5 --- /dev/null +++ b/modules/upscaling/lib/FidelityFX-FSR2 @@ -0,0 +1 @@ +Subproject commit 59950a85247baa4e099537324912a1f0e3a7b5d5 diff --git a/modules/upscaling/lib/NVIDIAImageScaling b/modules/upscaling/lib/NVIDIAImageScaling new file mode 160000 index 0000000000000000000000000000000000000000..35e13ba316c98eeecf16f37eae70ce88019911f6 --- /dev/null +++ b/modules/upscaling/lib/NVIDIAImageScaling @@ -0,0 +1 @@ +Subproject commit 35e13ba316c98eeecf16f37eae70ce88019911f6 diff --git a/modules/upscaling/src/vkcv/upscaling/BilinearUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/BilinearUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54df1829006964b30cc1831dc7115e9d5d222a51 --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/BilinearUpscaling.cpp @@ -0,0 +1,19 @@ + +#include "vkcv/upscaling/BilinearUpscaling.hpp" + +namespace vkcv::upscaling { + + BilinearUpscaling::BilinearUpscaling(Core &core) : Upscaling(core) {} + + void BilinearUpscaling::recordUpscaling(const CommandStreamHandle &cmdStream, const ImageHandle &input, + const ImageHandle &output) { + m_core.recordBeginDebugLabel(cmdStream, "vkcv::upscaling::BilinearUpscaling", { + 0.0f, 0.0f, 1.0f, 1.0f + }); + + m_core.recordBlitImage(cmdStream, input, output, SamplerFilterType::LINEAR); + + m_core.recordEndDebugLabel(cmdStream); + } + +} diff --git a/modules/upscaling/src/vkcv/upscaling/FSR2Upscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSR2Upscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e523a11db3512f30ab1f7df421da9b7838a1ea5b --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/FSR2Upscaling.cpp @@ -0,0 +1,399 @@ + +#include "vkcv/upscaling/FSR2Upscaling.hpp" + +#include <cmath> + +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 +#ifndef _MSVC_LANG +#define FFX_GCC +#endif + +#include <ffx_fsr2.h> +#include <ffx_fsr2_vk.h> + +#ifdef FFX_GCC +#undef FFX_GCC +#endif +#endif + +namespace vkcv::upscaling { + + void getFSR2Resolution(FSR2QualityMode mode, + uint32_t outputWidth, uint32_t outputHeight, + uint32_t &inputWidth, uint32_t &inputHeight) { + float scale; + + switch (mode) { + case FSR2QualityMode::QUALITY: + scale = 1.5f; + break; + case FSR2QualityMode::BALANCED: + scale = 1.7f; + break; + case FSR2QualityMode::PERFORMANCE: + scale = 2.0f; + break; + case FSR2QualityMode::ULTRA_PERFORMANCE: + scale = 3.0f; + break; + default: + scale = 1.0f; + break; + } + + inputWidth = static_cast<uint32_t>( + std::round(static_cast<float>(outputWidth) / scale) + ); + + inputHeight = static_cast<uint32_t>( + std::round(static_cast<float>(outputHeight) / scale) + ); + } + + float getFSR2LodBias(FSR2QualityMode mode) { + switch (mode) { + case FSR2QualityMode::QUALITY: + return -1.58f; + case FSR2QualityMode::BALANCED: + return -1.76f; + case FSR2QualityMode::PERFORMANCE: + return -2.0f; + case FSR2QualityMode::ULTRA_PERFORMANCE: + return -2.58f; + default: + return 0.0f; + } + } + +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + void FSR2Upscaling::createFSR2Context(uint32_t displayWidth, + uint32_t displayHeight, + uint32_t renderWidth, + uint32_t renderHeight) { + m_description->displaySize.width = displayWidth; + m_description->displaySize.height = displayHeight; + + m_description->maxRenderSize.width = renderWidth; + m_description->maxRenderSize.height = renderHeight; + + m_description->flags = FFX_FSR2_ENABLE_AUTO_EXPOSURE; + + if (m_hdr) { + m_description->flags |= FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE; + } + + if ((m_description->displaySize.width * m_description->displaySize.height <= 1) || + (m_description->maxRenderSize.width * m_description->maxRenderSize.height <= 1)) { + return; + } + + if (!m_context) { + m_context.reset(new FfxFsr2Context()); + } + + memset(m_context.get(), 0, sizeof(*m_context)); + assert(ffxFsr2ContextCreate(m_context.get(), m_description.get()) == FFX_OK); + } + + void FSR2Upscaling::destroyFSR2Context() { + m_core.getContext().getDevice().waitIdle(); + + if (m_context) { + assert(ffxFsr2ContextDestroy(m_context.get()) == FFX_OK); + m_context.reset(nullptr); + } + + m_frameIndex = 0; + } + + FSR2Upscaling::FSR2Upscaling(Core &core) : + Upscaling(core), + m_scratchBuffer(), + + m_description(new FfxFsr2ContextDescription()), + m_context(nullptr), + + m_depth(), + m_velocity(), + + m_frameIndex(0), + + m_frameDeltaTime(0.0f), + m_reset(false), + + m_near(0.0f), + m_far(0.0f), + m_fov(0.0f), + + m_hdr(false), + m_sharpness(0.875f) { + const auto& physicalDevice = core.getContext().getPhysicalDevice(); + + memset(m_description.get(), 0, sizeof(*m_description)); + + m_scratchBuffer.resize(ffxFsr2GetScratchMemorySizeVK(physicalDevice)); + + assert(ffxFsr2GetInterfaceVK( + &(m_description->callbacks), + m_scratchBuffer.data(), + m_scratchBuffer.size(), + physicalDevice, + vkGetDeviceProcAddr + ) == FFX_OK); + + m_description->device = ffxGetDeviceVK(core.getContext().getDevice()); + + createFSR2Context(1, 1, 1, 1); + } + + FSR2Upscaling::~FSR2Upscaling() { + destroyFSR2Context(); + + m_scratchBuffer.clear(); + m_description->callbacks.scratchBuffer = nullptr; + } +#else + FSR2Upscaling::FSR2Upscaling(vkcv::Core &core) : + Upscaling(core), m_fsr1(new FSRUpscaling(m_core)) {} + + FSR2Upscaling::~FSR2Upscaling() {} +#endif + + void FSR2Upscaling::update(float deltaTime, bool reset) { +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + if (reset) { + m_frameIndex = 0; + } + + m_frameDeltaTime = deltaTime; + m_reset = reset; +#endif + } + + void FSR2Upscaling::calcJitterOffset(uint32_t renderWidth, + uint32_t renderHeight, + float &jitterOffsetX, + float &jitterOffsetY) const { +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + const int32_t phaseCount = ffxFsr2GetJitterPhaseCount( + static_cast<int32_t>(renderWidth), + static_cast<int32_t>(renderHeight) + ); + + const int32_t phaseIndex = (static_cast<int32_t>(m_frameIndex) % phaseCount); + + assert(ffxFsr2GetJitterOffset( + &jitterOffsetX, + &jitterOffsetY, + phaseIndex, + phaseCount + ) == FFX_OK); + + jitterOffsetX *= +2.0f / renderWidth; + jitterOffsetY *= -2.0f / renderHeight; +#else + jitterOffsetX = 0.0f; + jitterOffsetY = 0.0f; +#endif + } + + void FSR2Upscaling::bindDepthBuffer(const ImageHandle &depthInput) { +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + m_depth = depthInput; +#endif + } + + void FSR2Upscaling::bindVelocityBuffer(const ImageHandle &velocityInput) { +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + m_velocity = velocityInput; +#endif + } + + void FSR2Upscaling::recordUpscaling(const CommandStreamHandle &cmdStream, + const ImageHandle &colorInput, + const ImageHandle &output) { +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + m_core.recordBeginDebugLabel(cmdStream, "vkcv::upscaling::FSR2Upscaling", { + 1.0f, 0.05f, 0.05f, 1.0f + }); + + m_core.prepareImageForSampling(cmdStream, output); + + FfxFsr2DispatchDescription dispatch; + memset(&dispatch, 0, sizeof(dispatch)); + + const uint32_t inputWidth = m_core.getImageWidth(colorInput); + const uint32_t inputHeight = m_core.getImageHeight(colorInput); + + const uint32_t outputWidth = m_core.getImageWidth(output); + const uint32_t outputHeight = m_core.getImageHeight(output); + + if ((m_description->displaySize.width != outputWidth) || + (m_description->displaySize.height != outputHeight) || + (m_description->maxRenderSize.width < inputWidth) || + (m_description->maxRenderSize.height < inputHeight) || + (m_hdr != ((m_description->flags & FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE) != 0))) { + destroyFSR2Context(); + + createFSR2Context( + outputWidth, + outputHeight, + inputWidth, + inputHeight + ); + } + + if (m_context) { + const bool sharpeningEnabled = ( + (m_sharpness > +0.0f) && + ((inputWidth < outputWidth) || (inputHeight < outputHeight)) + ); + + dispatch.color = ffxGetTextureResourceVK( + m_context.get(), + m_core.getVulkanImage(colorInput), + m_core.getVulkanImageView(colorInput), + inputWidth, + inputHeight, + static_cast<VkFormat>(m_core.getImageFormat(colorInput)) + ); + + dispatch.depth = ffxGetTextureResourceVK( + m_context.get(), + m_core.getVulkanImage(m_depth), + m_core.getVulkanImageView(m_depth), + m_core.getImageWidth(m_depth), + m_core.getImageHeight(m_depth), + static_cast<VkFormat>(m_core.getImageFormat(m_depth)) + ); + + dispatch.motionVectors = ffxGetTextureResourceVK( + m_context.get(), + m_core.getVulkanImage(m_velocity), + m_core.getVulkanImageView(m_velocity), + m_core.getImageWidth(m_velocity), + m_core.getImageHeight(m_velocity), + static_cast<VkFormat>(m_core.getImageFormat(m_velocity)) + ); + + dispatch.exposure = ffxGetTextureResourceVK( + m_context.get(), + nullptr, + nullptr, + 1, + 1, + VK_FORMAT_UNDEFINED + ); + + dispatch.reactive = ffxGetTextureResourceVK( + m_context.get(), + nullptr, + nullptr, + 1, + 1, + VK_FORMAT_UNDEFINED + ); + + dispatch.transparencyAndComposition = ffxGetTextureResourceVK( + m_context.get(), + nullptr, + nullptr, + 1, + 1, + VK_FORMAT_UNDEFINED + ); + + dispatch.output = ffxGetTextureResourceVK( + m_context.get(), + m_core.getVulkanImage(output), + m_core.getVulkanImageView(output), + outputWidth, + outputHeight, + static_cast<VkFormat>(m_core.getImageFormat(output)) + ); + + calcJitterOffset( + inputWidth, + inputHeight, + dispatch.jitterOffset.x, + dispatch.jitterOffset.y + ); + + dispatch.motionVectorScale.x = static_cast<float>(+2.0f); + dispatch.motionVectorScale.y = static_cast<float>(-2.0f); + + dispatch.renderSize.width = inputWidth; + dispatch.renderSize.height = inputHeight; + + dispatch.enableSharpening = sharpeningEnabled; + dispatch.sharpness = m_sharpness; + + dispatch.frameTimeDelta = m_frameDeltaTime * 1000.0f; // from seconds to milliseconds + dispatch.preExposure = 1.0f; + dispatch.reset = m_reset; + + dispatch.cameraNear = m_near; + dispatch.cameraFar = m_far; + dispatch.cameraFovAngleVertical = m_fov; + + m_core.recordCommandsToStream(cmdStream, [&](const vk::CommandBuffer& cmdBuffer) { + dispatch.commandList = ffxGetCommandListVK(cmdBuffer); + + assert(ffxFsr2ContextDispatch( + m_context.get(), + &dispatch + ) == FFX_OK); + + m_frameIndex++; + m_reset = false; + }, nullptr); + } + + m_core.updateImageLayoutManual(output, vk::ImageLayout::eGeneral); + m_core.recordEndDebugLabel(cmdStream); +#else + m_fsr1->recordUpscaling(cmdStream, colorInput, output); +#endif + } + + void FSR2Upscaling::setCamera(float near, float far, float fov) { +#ifndef VKCV_OVERRIDE_FSR2_WITH_FSR1 + m_near = near; + m_far = far; + m_fov = fov; +#endif + } + + bool FSR2Upscaling::isHdrEnabled() const { +#ifdef VKCV_OVERRIDE_FSR2_WITH_FSR1 + return m_fsr1->isHdrEnabled(); +#else + return m_hdr; +#endif + } + + void FSR2Upscaling::setHdrEnabled(bool enabled) { +#ifdef VKCV_OVERRIDE_FSR2_WITH_FSR1 + m_fsr1->setHdrEnabled(true); +#else + m_hdr = enabled; +#endif + } + + float FSR2Upscaling::getSharpness() const { +#ifdef VKCV_OVERRIDE_FSR2_WITH_FSR1 + return m_fsr1->getSharpness(); +#else + return m_sharpness; +#endif + } + + void FSR2Upscaling::setSharpness(float sharpness) { +#ifdef VKCV_OVERRIDE_FSR2_WITH_FSR1 + m_fsr1->setSharpness(sharpness); +#else + m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness)); +#endif + } + +} \ No newline at end of file diff --git a/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bacb4ce0814ff0caec0c3d4d353ecf5019e0eda4 --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/FSRUpscaling.cpp @@ -0,0 +1,399 @@ + +#include "vkcv/upscaling/FSRUpscaling.hpp" + +#include <cstdint> +#include <cmath> + +#define A_CPU 1 +#include <ffx_a.h> +#include <ffx_fsr1.h> + +#include "ffx_a.h.hxx" +#include "ffx_fsr1.h.hxx" +#include "FSR_Pass.glsl.hxx" + +#include <vkcv/File.hpp> +#include <vkcv/Logger.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +namespace vkcv::upscaling { + + void getFSRResolution(FSRQualityMode mode, + uint32_t outputWidth, uint32_t outputHeight, + uint32_t &inputWidth, uint32_t &inputHeight) { + float scale; + + switch (mode) { + case FSRQualityMode::ULTRA_QUALITY: + scale = 1.3f; + break; + case FSRQualityMode::QUALITY: + scale = 1.5f; + break; + case FSRQualityMode::BALANCED: + scale = 1.7f; + break; + case FSRQualityMode::PERFORMANCE: + scale = 2.0f; + break; + default: + scale = 1.0f; + break; + } + + inputWidth = static_cast<uint32_t>( + std::round(static_cast<float>(outputWidth) / scale) + ); + + inputHeight = static_cast<uint32_t>( + std::round(static_cast<float>(outputHeight) / scale) + ); + } + + float getFSRLodBias(FSRQualityMode mode) { + switch (mode) { + case FSRQualityMode::ULTRA_QUALITY: + return -0.38f; + case FSRQualityMode::QUALITY: + return -0.58f; + case FSRQualityMode::BALANCED: + return -0.79f; + case FSRQualityMode::PERFORMANCE: + return -1.0f; + default: + return 0.0f; + } + } + + static DescriptorBindings getDescriptorBindings() { + DescriptorBindings descriptorBindings = {}; + + auto binding_0 = DescriptorBinding { + 0, + DescriptorType::UNIFORM_BUFFER_DYNAMIC, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_1 = DescriptorBinding { + 1, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_2 = DescriptorBinding{ + 2, + DescriptorType::IMAGE_STORAGE, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_3 = DescriptorBinding{ + 3, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE + }; + + descriptorBindings.insert(std::make_pair(0, binding_0)); + descriptorBindings.insert(std::make_pair(1, binding_1)); + descriptorBindings.insert(std::make_pair(2, binding_2)); + descriptorBindings.insert(std::make_pair(3, binding_3)); + + return descriptorBindings; + } + + static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) { + std::ofstream file (shaderPath.string(), std::ios::out); + + if (!file.is_open()) { + vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str()); + return false; + } + + file.seekp(0); + file.write(code.c_str(), static_cast<std::streamsize>(code.length())); + file.close(); + + return true; + } + + static bool compileFSRShader(vkcv::shader::GLSLCompiler& compiler, + const shader::ShaderCompiledFunction& compiled) { + std::filesystem::path directory = generateTemporaryDirectoryPath(); + + if (!std::filesystem::create_directory(directory)) { + vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str()); + return false; + } + + if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) { + return false; + } + + if (!writeShaderCode(directory / "ffx_fsr1.h", FFX_FSR1_H_SHADER)) { + return false; + } + + return compiler.compileSource( + vkcv::ShaderStage::COMPUTE, + FSR_PASS_GLSL_SHADER.c_str(), + [&directory, &compiled] (vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + if (compiled) { + compiled(shaderStage, path); + } + + std::filesystem::remove_all(directory); + }, directory + ); + } + + FSRUpscaling::FSRUpscaling(Core& core) : + Upscaling(core), + m_easuPipeline(), + m_rcasPipeline(), + + m_easuDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_easuDescriptorSet(m_core.createDescriptorSet(m_easuDescriptorSetLayout)), + + m_rcasDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_rcasDescriptorSet(m_core.createDescriptorSet(m_rcasDescriptorSetLayout)), + + m_easuConstants(buffer<FSRConstants>( + m_core, + BufferType::UNIFORM, + 1, + BufferMemoryType::HOST_VISIBLE + )), + m_rcasConstants(buffer<FSRConstants>( + m_core, + BufferType::UNIFORM, + 1, + BufferMemoryType::HOST_VISIBLE + )), + m_intermediateImage(), + m_sampler(m_core.createSampler( + SamplerFilterType::LINEAR, + SamplerFilterType::LINEAR, + SamplerMipmapMode::NEAREST, + SamplerAddressMode::CLAMP_TO_EDGE + )), + + m_hdr(false), + m_sharpness(0.875f) { + vkcv::shader::GLSLCompiler easuCompiler; + vkcv::shader::GLSLCompiler rcasCompiler; + + const auto& featureManager = m_core.getContext().getFeatureManager(); + + const bool float16Support = ( + featureManager.checkFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>( + vk::StructureType::ePhysicalDeviceShaderFloat16Int8FeaturesKHR, + [](const vk::PhysicalDeviceFloat16Int8FeaturesKHR& features) { + return features.shaderFloat16; + } + ) && + featureManager.checkFeatures<vk::PhysicalDevice16BitStorageFeaturesKHR>( + vk::StructureType::ePhysicalDevice16BitStorageFeaturesKHR, + [](const vk::PhysicalDevice16BitStorageFeaturesKHR& features) { + return features.storageBuffer16BitAccess; + } + ) + ); + + if (!float16Support) { + easuCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1"); + rcasCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1"); + } + + easuCompiler.setDefine("SAMPLE_EASU", "1"); + rcasCompiler.setDefine("SAMPLE_RCAS", "1"); + + { + ShaderProgram program; + compileFSRShader(easuCompiler, [&program](vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }); + + m_easuPipeline = m_core.createComputePipeline({program,{ + m_easuDescriptorSetLayout + }}); + + + DescriptorWrites writes; + writes.writeUniformBuffer( + 0, m_easuConstants.getHandle(),true + ); + + writes.writeSampler(3, m_sampler); + + m_core.writeDescriptorSet(m_easuDescriptorSet, writes); + } + + { + ShaderProgram program; + compileFSRShader(rcasCompiler, [&program](vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }); + + m_rcasPipeline = m_core.createComputePipeline({ program, { + m_rcasDescriptorSetLayout + }}); + + DescriptorWrites writes; + writes.writeUniformBuffer( + 0, m_rcasConstants.getHandle(),true + ); + + writes.writeSampler(3, m_sampler); + + m_core.writeDescriptorSet(m_rcasDescriptorSet, writes); + } + } + + void FSRUpscaling::recordUpscaling(const CommandStreamHandle& cmdStream, + const ImageHandle& input, + const ImageHandle& output) { + m_core.recordBeginDebugLabel(cmdStream, "vkcv::upscaling::FSRUpscaling", { + 1.0f, 0.0f, 0.0f, 1.0f + }); + + const uint32_t inputWidth = m_core.getImageWidth(input); + const uint32_t inputHeight = m_core.getImageHeight(input); + + const uint32_t outputWidth = m_core.getImageWidth(output); + const uint32_t outputHeight = m_core.getImageHeight(output); + + if ((!m_intermediateImage) || + (outputWidth != m_core.getImageWidth(m_intermediateImage)) || + (outputHeight != m_core.getImageHeight(m_intermediateImage))) { + ImageConfig imageConfig (outputWidth, outputHeight); + imageConfig.setSupportingStorage(true); + + m_intermediateImage = m_core.createImage( + m_core.getImageFormat(output), + imageConfig + ); + + m_core.prepareImageForStorage(cmdStream, m_intermediateImage); + } + + const bool rcasEnabled = ( + (m_sharpness > +0.0f) && + ((inputWidth < outputWidth) || (inputHeight < outputHeight)) + ); + + { + FSRConstants consts = {}; + + FsrEasuCon( + consts.Const0, consts.Const1, consts.Const2, consts.Const3, + static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight), + static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight), + static_cast<AF1>(outputWidth), static_cast<AF1>(outputHeight) + ); + + consts.Sample[0] = (((m_hdr) && (!rcasEnabled)) ? 1 : 0); + + m_easuConstants.fill(&consts); + } + + static const uint32_t threadGroupWorkRegionDim = 16; + + DispatchSize dispatch = dispatchInvocations( + DispatchSize(outputWidth, outputHeight), + DispatchSize(threadGroupWorkRegionDim, threadGroupWorkRegionDim) + ); + + m_core.recordBufferMemoryBarrier(cmdStream, m_easuConstants.getHandle()); + + if (rcasEnabled) { + { + DescriptorWrites writes; + writes.writeSampledImage(1, input); + writes.writeStorageImage(2, m_intermediateImage); + + m_core.writeDescriptorSet(m_easuDescriptorSet, writes); + } + { + DescriptorWrites writes; + writes.writeSampledImage(1, m_intermediateImage); + writes.writeStorageImage(2, output); + + m_core.writeDescriptorSet(m_rcasDescriptorSet, writes); + } + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_easuPipeline, + dispatch, + { useDescriptorSet(0, m_easuDescriptorSet, { 0 }) }, + PushConstants(0) + ); + + { + FSRConstants consts = {}; + + FsrRcasCon(consts.Const0, (1.0f - m_sharpness) * 2.0f); + consts.Sample[0] = (m_hdr ? 1 : 0); + + m_rcasConstants.fill(&consts); + } + + m_core.recordBufferMemoryBarrier(cmdStream, m_rcasConstants.getHandle()); + m_core.prepareImageForSampling(cmdStream, m_intermediateImage); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_rcasPipeline, + dispatch, + { useDescriptorSet(0,m_rcasDescriptorSet, { 0 }) }, + PushConstants(0) + ); + + m_core.prepareImageForStorage(cmdStream, m_intermediateImage); + } else { + { + DescriptorWrites writes; + writes.writeSampledImage(1, input); + writes.writeStorageImage(2, output); + + m_core.writeDescriptorSet(m_easuDescriptorSet, writes); + } + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_easuPipeline, + dispatch, + { useDescriptorSet(0, m_easuDescriptorSet, { 0 }) }, + PushConstants(0) + ); + } + + m_core.recordEndDebugLabel(cmdStream); + } + + bool FSRUpscaling::isHdrEnabled() const { + return m_hdr; + } + + void FSRUpscaling::setHdrEnabled(bool enabled) { + m_hdr = enabled; + } + + float FSRUpscaling::getSharpness() const { + return m_sharpness; + } + + void FSRUpscaling::setSharpness(float sharpness) { + m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness)); + } + +} diff --git a/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8c3a973e6914f4cb9b31eef1acf805e6448237eb --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/NISUpscaling.cpp @@ -0,0 +1,281 @@ + +#include "vkcv/upscaling/NISUpscaling.hpp" + +#include <NIS_Config.h> + +#include "NIS_Main.glsl.hxx" +#include "NIS_Scaler.h.hxx" + +#include <vkcv/File.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/Logger.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +namespace vkcv::upscaling { + + static DescriptorBindings getDescriptorBindings() { + DescriptorBindings descriptorBindings = {}; + + auto binding_0 = DescriptorBinding { + 0, + DescriptorType::UNIFORM_BUFFER_DYNAMIC, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_1 = DescriptorBinding{ + 1, + DescriptorType::SAMPLER, + 1, + ShaderStage::COMPUTE + }; + + auto binding_2 = DescriptorBinding { + 2, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_3 = DescriptorBinding{ + 3, + DescriptorType::IMAGE_STORAGE, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_4 = DescriptorBinding { + 4, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + auto binding_5 = DescriptorBinding { + 5, + DescriptorType::IMAGE_SAMPLED, + 1, + ShaderStage::COMPUTE, + false + }; + + descriptorBindings.insert(std::make_pair(0, binding_0)); + descriptorBindings.insert(std::make_pair(1, binding_1)); + descriptorBindings.insert(std::make_pair(2, binding_2)); + descriptorBindings.insert(std::make_pair(3, binding_3)); + descriptorBindings.insert(std::make_pair(4, binding_4)); + descriptorBindings.insert(std::make_pair(5, binding_5)); + + return descriptorBindings; + } + + static ImageHandle createFilterImage(Core &core, + const void* data) { + const size_t rowPitch = kFilterSize * 2; + const size_t imageSize = rowPitch * kPhaseCount; + + Image image = vkcv::image( + core, + vk::Format::eR16G16B16A16Sfloat, + kFilterSize / 4, + kPhaseCount + ); + + image.fill(data, imageSize); + return image.getHandle(); + } + + static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) { + std::ofstream file (shaderPath.string(), std::ios::out); + + if (!file.is_open()) { + vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str()); + return false; + } + + file.seekp(0); + file.write(code.c_str(), static_cast<std::streamsize>(code.length())); + file.close(); + + return true; + } + + static bool compileNISShader(vkcv::shader::GLSLCompiler& compiler, + const shader::ShaderCompiledFunction& compiled) { + std::filesystem::path directory = generateTemporaryDirectoryPath(); + + if (!std::filesystem::create_directory(directory)) { + vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str()); + return false; + } + + if (!writeShaderCode(directory / "NIS_Scaler.h", NIS_SCALER_H_SHADER)) { + return false; + } + + return compiler.compileSource( + vkcv::ShaderStage::COMPUTE, + NIS_MAIN_GLSL_SHADER.c_str(), + [&directory, &compiled] (vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + if (compiled) { + compiled(shaderStage, path); + } + + std::filesystem::remove_all(directory); + }, directory + ); + } + + NISUpscaling::NISUpscaling(Core &core) : + Upscaling(core), + m_scalerPipeline(), + + m_scalerDescriptorSetLayout(m_core.createDescriptorSetLayout(getDescriptorBindings())), + m_scalerDescriptorSet(m_core.createDescriptorSet(m_scalerDescriptorSetLayout)), + + m_scalerConstants(buffer<uint8_t>( + m_core, + BufferType::UNIFORM, + sizeof(NISConfig), + BufferMemoryType::HOST_VISIBLE + )), + m_sampler(m_core.createSampler( + SamplerFilterType::LINEAR, + SamplerFilterType::LINEAR, + SamplerMipmapMode::NEAREST, + SamplerAddressMode::CLAMP_TO_EDGE + )), + + m_coefScaleImage(createFilterImage(m_core, coef_scale_fp16)), + m_coefUsmImage(createFilterImage(m_core, coef_usm_fp16)), + + m_blockWidth(0), + m_blockHeight(0), + + m_hdr(false), + m_sharpness(0.875f) { + vkcv::shader::GLSLCompiler scalerCompiler; + + scalerCompiler.setDefine("NIS_SCALER", "1"); + scalerCompiler.setDefine("NIS_GLSL", "1"); + + NISOptimizer optimizer (true, NISGPUArchitecture::NVIDIA_Generic); + + m_blockWidth = optimizer.GetOptimalBlockWidth(); + m_blockHeight = optimizer.GetOptimalBlockHeight(); + + scalerCompiler.setDefine("NIS_BLOCK_WIDTH", std::to_string(m_blockWidth)); + scalerCompiler.setDefine("NIS_BLOCK_HEIGHT", std::to_string(m_blockHeight)); + + const uint32_t threadGroupSize = optimizer.GetOptimalThreadGroupSize(); + + scalerCompiler.setDefine("NIS_THREAD_GROUP_SIZE", std::to_string(threadGroupSize)); + + { + ShaderProgram program; + compileNISShader(scalerCompiler, [&program](vkcv::ShaderStage shaderStage, + const std::filesystem::path& path) { + program.addShader(shaderStage, path); + }); + + m_scalerPipeline = m_core.createComputePipeline({program,{ + m_scalerDescriptorSetLayout + }}); + + + DescriptorWrites writes; + writes.writeUniformBuffer( + 0, m_scalerConstants.getHandle(), true + ); + + writes.writeSampler(1, m_sampler); + writes.writeSampledImage(4, m_coefScaleImage); + writes.writeSampledImage(5, m_coefUsmImage); + + m_core.writeDescriptorSet(m_scalerDescriptorSet, writes); + } + } + + void NISUpscaling::recordUpscaling(const CommandStreamHandle &cmdStream, + const ImageHandle &input, + const ImageHandle &output) { + m_core.recordBeginDebugLabel(cmdStream, "vkcv::upscaling::NISUpscaling", { + 0.0f, 1.0f, 0.0f, 1.0f + }); + + const uint32_t inputWidth = m_core.getImageWidth(input); + const uint32_t inputHeight = m_core.getImageHeight(input); + + const uint32_t outputWidth = m_core.getImageWidth(output); + const uint32_t outputHeight = m_core.getImageHeight(output); + + NISConfig config {}; + NVScalerUpdateConfig( + config, + m_sharpness, + 0, 0, + inputWidth, + inputHeight, + inputWidth, + inputHeight, + 0, 0, + outputWidth, + outputHeight, + outputWidth, + outputHeight, + m_hdr? NISHDRMode::PQ : NISHDRMode::None + ); + + m_scalerConstants.fill( + reinterpret_cast<uint8_t*>(&config), + sizeof(config) + ); + + DispatchSize dispatch = dispatchInvocations( + DispatchSize(outputWidth, outputHeight), + DispatchSize(m_blockWidth, m_blockHeight) + ); + + m_core.recordBufferMemoryBarrier(cmdStream, m_scalerConstants.getHandle()); + + { + DescriptorWrites writes; + writes.writeSampledImage(2, input); + writes.writeStorageImage(3, output); + + m_core.writeDescriptorSet(m_scalerDescriptorSet, writes); + } + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_scalerPipeline, + dispatch, + { useDescriptorSet(0, m_scalerDescriptorSet, { 0 }) }, + PushConstants(0) + ); + + m_core.recordEndDebugLabel(cmdStream); + } + + bool NISUpscaling::isHdrEnabled() const { + return m_hdr; + } + + void NISUpscaling::setHdrEnabled(bool enabled) { + m_hdr = enabled; + } + + float NISUpscaling::getSharpness() const { + return m_sharpness; + } + + void NISUpscaling::setSharpness(float sharpness) { + m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness)); + } + +} diff --git a/modules/upscaling/src/vkcv/upscaling/Upscaling.cpp b/modules/upscaling/src/vkcv/upscaling/Upscaling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b0c3dee9b1c799c0e1f07b59b03d3ad46bd453ed --- /dev/null +++ b/modules/upscaling/src/vkcv/upscaling/Upscaling.cpp @@ -0,0 +1,8 @@ + +#include "vkcv/upscaling/Upscaling.hpp" + +namespace vkcv::upscaling { + + Upscaling::Upscaling(Core &core) : m_core(core) {} + +} diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index 34fbcb0cf8dd3f1d34efd2cc8424994c7da76e32..3860ec7a2135a9ecc39db6e0f7a1ddd167e3b391 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -1,7 +1,20 @@ +include(${vkcv_config_ext}/Project.cmake) + # Add new projects/examples here: -add_subdirectory(bloom) +add_subdirectory(bindless_textures) +add_subdirectory(fire_works) add_subdirectory(first_triangle) add_subdirectory(first_mesh) add_subdirectory(first_scene) +add_subdirectory(head_demo) +add_subdirectory(indirect_dispatch) +add_subdirectory(indirect_draw) +add_subdirectory(mesh_shader) +add_subdirectory(mpm) +add_subdirectory(particle_simulation) +add_subdirectory(path_tracer) +add_subdirectory(ray_tracer) +add_subdirectory(rtx_ambient_occlusion) +add_subdirectory(sph) add_subdirectory(voxelization) \ No newline at end of file diff --git a/projects/bindless_textures/.gitignore b/projects/bindless_textures/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..cd45650aee8ab49ad568556452dde2d9d51d5f13 --- /dev/null +++ b/projects/bindless_textures/.gitignore @@ -0,0 +1 @@ +bindless_textures \ No newline at end of file diff --git a/projects/bindless_textures/CMakeLists.txt b/projects/bindless_textures/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..86a93e04427d7ff103bda1dbd5a4602ff337d639 --- /dev/null +++ b/projects/bindless_textures/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(bindless_textures) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(bindless_textures src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(bindless_textures SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(bindless_textures vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) diff --git a/projects/bindless_textures/README.md b/projects/bindless_textures/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0c5498551ae55802010bbb4295d35ec2ba43978a --- /dev/null +++ b/projects/bindless_textures/README.md @@ -0,0 +1,16 @@ +# Bindless textures +An example project to show usage of bindless descriptor indexing with the VkCV framework + + + +## Details + +The project utilizes a Vulkan extension to access an array of descriptors with arbitrary size. The +size does not need to be known during shader compilation and can be adjusted during runtime when +creating the graphics pipeline. + +## Extensions + +Here is a list of the used extensions: + + - [VK_EXT_descriptor_indexing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html) diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2217fb53744f9166232a40b78ee9518e04b0ded5 --- /dev/null +++ b/projects/bindless_textures/resources/cube/Grass001_1K_AmbientOcclusion.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f6c88f8f3facd9e91f8dd160bd8c4a602e433872ed18e08015a9fa9dfff889de +size 901465 diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8aa1533ae5a023a5fc8457f30ed60efe3bda32d --- /dev/null +++ b/projects/bindless_textures/resources/cube/Grass001_1K_Color.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:878b8fe4747d9ce693220edea1915de550e8f14d402d26a0915f162d40f84e91 +size 1763328 diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg new file mode 100644 index 0000000000000000000000000000000000000000..89789cba150eea7c7abbdc1851090f6021af978a --- /dev/null +++ b/projects/bindless_textures/resources/cube/Grass001_1K_Displacement.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1e8300e1107bee7e681059d9da0a7e3ca422977e8b6f496e16452a4c94b3d385 +size 912347 diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3163d6391592ace10446cb71141a2192e63e9660 --- /dev/null +++ b/projects/bindless_textures/resources/cube/Grass001_1K_Normal.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:684426b49e841e267f12f06dc955b5b1d01b3ba3659bea0c5d73be889700929f +size 2336471 diff --git a/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg b/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10e6ac33badf2a4795766a66546a62c67eb8b558 --- /dev/null +++ b/projects/bindless_textures/resources/cube/Grass001_1K_Roughness.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d952ffb098faf9ac5eb25134eabac08f0c65d927a448b5e7b4f9c72510cfcbe0 +size 822659 diff --git a/projects/first_mesh/resources/Szene/boards2_vcyc_jpg.jpg b/projects/bindless_textures/resources/cube/boards2_vcyc_jpg.jpg similarity index 100% rename from projects/first_mesh/resources/Szene/boards2_vcyc_jpg.jpg rename to projects/bindless_textures/resources/cube/boards2_vcyc_jpg.jpg diff --git a/projects/first_mesh/resources/cube/cube.bin b/projects/bindless_textures/resources/cube/cube.bin similarity index 100% rename from projects/first_mesh/resources/cube/cube.bin rename to projects/bindless_textures/resources/cube/cube.bin diff --git a/projects/first_mesh/resources/cube/cube.blend b/projects/bindless_textures/resources/cube/cube.blend similarity index 100% rename from projects/first_mesh/resources/cube/cube.blend rename to projects/bindless_textures/resources/cube/cube.blend diff --git a/projects/first_mesh/resources/cube/cube.blend1 b/projects/bindless_textures/resources/cube/cube.blend1 similarity index 100% rename from projects/first_mesh/resources/cube/cube.blend1 rename to projects/bindless_textures/resources/cube/cube.blend1 diff --git a/projects/first_mesh/resources/cube/cube.glb b/projects/bindless_textures/resources/cube/cube.glb similarity index 100% rename from projects/first_mesh/resources/cube/cube.glb rename to projects/bindless_textures/resources/cube/cube.glb diff --git a/projects/first_mesh/resources/cube/cube.gltf b/projects/bindless_textures/resources/cube/cube.gltf similarity index 100% rename from projects/first_mesh/resources/cube/cube.gltf rename to projects/bindless_textures/resources/cube/cube.gltf diff --git a/projects/bindless_textures/resources/shaders/shader.frag b/projects/bindless_textures/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..c855eb407944c415dc4055716aa64a531c830ef3 --- /dev/null +++ b/projects/bindless_textures/resources/shaders/shader.frag @@ -0,0 +1,16 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_EXT_nonuniform_qualifier : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; +layout(location = 2) in flat int passTextureIndex; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform sampler textureSampler; +layout(set=0, binding=1) uniform texture2D materialTextures[]; + +void main() { + outColor = texture(sampler2D(materialTextures[nonuniformEXT(passTextureIndex)], textureSampler), passUV).rgb; +} \ No newline at end of file diff --git a/projects/bindless_textures/resources/shaders/shader.vert b/projects/bindless_textures/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..6bc918c6a186dcfb965719cd1e08cb448a49b44e --- /dev/null +++ b/projects/bindless_textures/resources/shaders/shader.vert @@ -0,0 +1,43 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec2 passUV; +layout(location = 2) out flat int passTextureIndex; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() +{ + gl_Position = mvp * vec4(inPosition, 1.0); + passNormal = inNormal; + passUV = inUV; + + passTextureIndex = (gl_VertexIndex / 4); + + /* + if(inNormal.x > 0.9) + passTextureIndex = 0; + + if(inNormal.x < -0.9) + passTextureIndex = 1; + + if(inNormal.y > 0.9) + passTextureIndex = 2; + + if(inNormal.y < -0.9) + passTextureIndex = 3; + + if(inNormal.z > 0.9) + passTextureIndex = 4; + + if(inNormal.z < -0.9) + passTextureIndex = 5; + */ +} \ No newline at end of file diff --git a/projects/bindless_textures/src/main.cpp b/projects/bindless_textures/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5021de5ca081f009ab39806c5648662f86482418 --- /dev/null +++ b/projects/bindless_textures/src/main.cpp @@ -0,0 +1,254 @@ +#include <iostream> +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Sampler.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +int main(int argc, const char** argv) { + const std::string applicationName = "Bindless Textures"; + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>( + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, [](vk::PhysicalDeviceDescriptorIndexingFeatures &features) { + features.setShaderInputAttachmentArrayDynamicIndexing(true); + features.setShaderUniformTexelBufferArrayDynamicIndexing(true); + features.setShaderStorageTexelBufferArrayDynamicIndexing(true); + features.setShaderUniformBufferArrayNonUniformIndexing(true); + features.setShaderSampledImageArrayNonUniformIndexing(true); + features.setShaderStorageBufferArrayNonUniformIndexing(true); + features.setShaderStorageImageArrayNonUniformIndexing(true); + features.setShaderInputAttachmentArrayNonUniformIndexing(true); + features.setShaderUniformTexelBufferArrayNonUniformIndexing(true); + features.setShaderStorageTexelBufferArrayNonUniformIndexing(true); + + features.setDescriptorBindingUniformBufferUpdateAfterBind(true); + features.setDescriptorBindingSampledImageUpdateAfterBind(true); + features.setDescriptorBindingStorageImageUpdateAfterBind(true); + features.setDescriptorBindingStorageBufferUpdateAfterBind(true); + features.setDescriptorBindingUniformTexelBufferUpdateAfterBind(true); + features.setDescriptorBindingStorageTexelBufferUpdateAfterBind(true); + + features.setDescriptorBindingUpdateUnusedWhilePending(true); + features.setDescriptorBindingPartiallyBound(true); + features.setDescriptorBindingVariableDescriptorCount(true); + features.setRuntimeDescriptorArray(true); + } + ); + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, + features + ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true); + vkcv::Window& window = core.getWindow(windowHandle); + + vkcv::asset::Scene mesh; + + // TEST DATA + std::vector<vkcv::Image> texturesArray; + const std::string grassPaths[5] = { "resources/cube/Grass001_1K_AmbientOcclusion.jpg", + "resources/cube/Grass001_1K_Color.jpg", + "resources/cube/Grass001_1K_Displacement.jpg", + "resources/cube/Grass001_1K_Normal.jpg", + "resources/cube/Grass001_1K_Roughness.jpg" }; + + for (const auto &path : grassPaths) { + std::filesystem::path grassPath(path); + vkcv::asset::Texture grassTexture = vkcv::asset::loadTexture(grassPath); + + vkcv::Image texture = vkcv::image( + core, + vk::Format::eR8G8B8A8Srgb, + grassTexture.width, + grassTexture.height + ); + + texture.fill(grassTexture.data.data()); + texturesArray.push_back(texture); + } + + const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; + int result = vkcv::asset::loadScene(path, mesh); + + if (result == 1) { + std::cout << "Mesh loading successful!" << std::endl; + } else { + std::cerr << "Mesh loading failed: " << result << std::endl; + return 1; + } + + assert(!mesh.vertexGroups.empty()); + auto vertexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::VERTEX, + mesh.vertexGroups[0].vertexBuffer.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + + vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data); + + auto indexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::INDEX, + mesh.vertexGroups[0].indexBuffer.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + + indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data); + + vkcv::PassHandle firstMeshPass = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } + ); + + if (!firstMeshPass) { + std::cerr << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram firstMeshProgram; + vkcv::shader::GLSLCompiler compiler; + + compiler.compileProgram(firstMeshProgram, { + { vkcv::ShaderStage::VERTEX, "resources/shaders/shader.vert" }, + { vkcv::ShaderStage::FRAGMENT, "resources/shaders/shader.frag" } + }, nullptr); + + const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings( + mesh.vertexGroups[0].vertexBuffer.attributes, + vertexBuffer.getHandle(), + { + vkcv::asset::PrimitiveType::POSITION, + vkcv::asset::PrimitiveType::NORMAL, + vkcv::asset::PrimitiveType::TEXCOORD_0 + } + ); + + std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings( + firstMeshProgram.getVertexAttachments() + ); + + const vkcv::VertexLayout firstMeshLayout { bindings }; + const std::unordered_map<uint32_t, vkcv::DescriptorBinding> &descriptorBindings = firstMeshProgram.getReflectedDescriptors().at(0); + + std::unordered_map<uint32_t, vkcv::DescriptorBinding> adjustedBindings = descriptorBindings; + adjustedBindings[1].descriptorCount = 6; + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(adjustedBindings); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + firstMeshProgram, + firstMeshPass, + { firstMeshLayout }, + { descriptorSetLayout } + ) + ); + + if (!firstMeshPipeline) { + std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + if (mesh.textures.empty()) { + std::cerr << "Error. No textures found. Exiting." << std::endl; + return EXIT_FAILURE; + } + + { + vkcv::asset::Texture &tex = mesh.textures[0]; + vkcv::Image texture = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, tex.w, tex.h); + texture.fill(tex.data.data()); + texturesArray.push_back(texture); + } + + auto downsampleStream = core.createCommandStream(vkcv::QueueType::Graphics); + + for (auto& texture : texturesArray) { + texture.recordMipChainGeneration(downsampleStream, core.getDownsampler()); + } + + core.submitCommandStream(downsampleStream, false); + + vkcv::SamplerHandle sampler = vkcv::samplerLinear(core); + vkcv::DescriptorWrites setWrites; + + for(uint32_t i = 0; i < 6; i++) { + setWrites.writeSampledImage( + 1, + texturesArray[i].getHandle(), + 0, + false, + i + ); + } + + setWrites.writeSampler(0, sampler); + + core.writeDescriptorSet(descriptorSet, setWrites); + + vkcv::ImageHandle depthBuffer; + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::VertexData vertexData (vertexBufferBindings); + vertexData.setIndexBuffer(indexBuffer.getHandle()); + vertexData.setCount(mesh.vertexGroups[0].numIndices); + + vkcv::InstanceDrawcall drawcall (vertexData); + drawcall.useDescriptorSet(0, descriptorSet); + + vkcv::camera::CameraManager cameraManager(window); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(0, 0, -3)); + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0, 0, -3)); + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + (swapchainHeight != core.getImageHeight(depthBuffer))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig(swapchainWidth, swapchainHeight) + ); + } + + cameraManager.update(dt); + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); + + vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>(); + pushConstants.appendDrawcall(mvp); + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + core.recordDrawcallsToCmdStream( + cmdStream, + firstMeshPipeline, + pushConstants, + { drawcall }, + renderTargets, + windowHandle + ); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + }); + + return 0; +} diff --git a/projects/bloom/.gitignore b/projects/bloom/.gitignore deleted file mode 100644 index 3643183e0628e666abab193e1dd1d92c1774ac61..0000000000000000000000000000000000000000 --- a/projects/bloom/.gitignore +++ /dev/null @@ -1 +0,0 @@ -bloom \ No newline at end of file diff --git a/projects/bloom/CMakeLists.txt b/projects/bloom/CMakeLists.txt deleted file mode 100644 index 8171938e7cb430aacce5562af44f628c11c97c54..0000000000000000000000000000000000000000 --- a/projects/bloom/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(bloom) - -# setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# this should fix the execution path to load local files from the project -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - -# adding source files to the project -add_executable(bloom src/main.cpp) - -target_sources(bloom PRIVATE - src/BloomAndFlares.cpp - src/BloomAndFlares.hpp) - -# this should fix the execution path to load local files from the project (for MSVC) -if(MSVC) - set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - set_target_properties(bloom PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - - # in addition to setting the output directory, the working directory has to be set - # by default visual studio sets the working directory to the build directory, when using the debugger - set_target_properties(bloom PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -endif() - -# including headers of dependencies and the VkCV framework -target_include_directories(bloom SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) - -# linking with libraries from all dependencies and the VkCV framework -target_link_libraries(bloom vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) diff --git a/projects/bloom/resources/shaders/comp.spv b/projects/bloom/resources/shaders/comp.spv deleted file mode 100644 index 85c7e74cfc0a89917bf6dd1a7ec449368274c1d3..0000000000000000000000000000000000000000 Binary files a/projects/bloom/resources/shaders/comp.spv and /dev/null differ diff --git a/projects/bloom/resources/shaders/composite.comp b/projects/bloom/resources/shaders/composite.comp deleted file mode 100644 index 190bed0657d70e0217bf654820d0b2b2c58f12c2..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/composite.comp +++ /dev/null @@ -1,38 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(set=0, binding=0) uniform texture2D blurImage; -layout(set=0, binding=1) uniform texture2D lensImage; -layout(set=0, binding=2) uniform sampler linearSampler; -layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D colorBuffer; - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - - -void main() -{ - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(colorBuffer)))){ - return; - } - - ivec2 pixel_coord = ivec2(gl_GlobalInvocationID.xy); - vec2 pixel_size = vec2(1.0f) / textureSize(sampler2D(blurImage, linearSampler), 0); - vec2 UV = pixel_coord.xy * pixel_size; - - vec4 composite_color = vec4(0.0f); - - vec3 blur_color = texture(sampler2D(blurImage, linearSampler), UV).rgb; - vec3 lens_color = texture(sampler2D(lensImage, linearSampler), UV).rgb; - vec3 main_color = imageLoad(colorBuffer, pixel_coord).rgb; - - // composite blur and lens features - float bloom_weight = 0.25f; - float lens_weight = 0.25f; - float main_weight = 1 - (bloom_weight + lens_weight); - - composite_color.rgb = blur_color * bloom_weight + - lens_color * lens_weight + - main_color * main_weight; - - imageStore(colorBuffer, pixel_coord, composite_color); -} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/gammaCorrection.comp b/projects/bloom/resources/shaders/gammaCorrection.comp deleted file mode 100644 index f89ad167c846cca8e80f69d33eda83bd6ed00d46..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/gammaCorrection.comp +++ /dev/null @@ -1,20 +0,0 @@ -#version 440 - -layout(set=0, binding=0, r11f_g11f_b10f) uniform image2D inImage; -layout(set=0, binding=1, rgba8) uniform image2D outImage; - - -layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; - -void main(){ - - if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ - return; - } - ivec2 uv = ivec2(gl_GlobalInvocationID.xy); - vec3 linearColor = imageLoad(inImage, uv).rgb; - // cheap Reinhard tone mapping - linearColor = linearColor/(linearColor + 1.0f); - vec3 gammaCorrected = pow(linearColor, vec3(1.f / 2.2f)); - imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); -} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/perMeshResources.inc b/projects/bloom/resources/shaders/perMeshResources.inc deleted file mode 100644 index 95e4fb7c27009965659d14a9c72acfec950c37e3..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/perMeshResources.inc +++ /dev/null @@ -1,2 +0,0 @@ -layout(set=1, binding=0) uniform texture2D albedoTexture; -layout(set=1, binding=1) uniform sampler textureSampler; \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shader.frag b/projects/bloom/resources/shaders/shader.frag deleted file mode 100644 index 3e95b4508f112c1ed9aa4a7050a98fa789dccd09..0000000000000000000000000000000000000000 --- a/projects/bloom/resources/shaders/shader.frag +++ /dev/null @@ -1,45 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_GOOGLE_include_directive : enable - -#include "perMeshResources.inc" - -layout(location = 0) in vec3 passNormal; -layout(location = 1) in vec2 passUV; -layout(location = 2) in vec3 passPos; - -layout(location = 0) out vec3 outColor; - -layout(set=0, binding=0) uniform sunBuffer { - vec3 L; float padding; - mat4 lightMatrix; -}; -layout(set=0, binding=1) uniform texture2D shadowMap; -layout(set=0, binding=2) uniform sampler shadowMapSampler; - -float shadowTest(vec3 worldPos){ - vec4 lightPos = lightMatrix * vec4(worldPos, 1); - lightPos /= lightPos.w; - lightPos.xy = lightPos.xy * 0.5 + 0.5; - - if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){ - return 1; - } - - lightPos.z = clamp(lightPos.z, 0, 1); - - float shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy).r; - float bias = 0.01f; - shadowMapSample += bias; - return shadowMapSample < lightPos.z ? 0 : 1; -} - -void main() { - vec3 N = normalize(passNormal); - vec3 sunColor = vec3(10); - vec3 sun = sunColor * clamp(dot(N, L), 0, 1); - sun *= shadowTest(passPos); - vec3 ambient = vec3(0.05); - vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; - outColor = albedo * (sun + ambient); -} \ No newline at end of file diff --git a/projects/bloom/src/BloomAndFlares.cpp b/projects/bloom/src/BloomAndFlares.cpp deleted file mode 100644 index 6f26db9de0f2c8334b6dd7e5dd6cf4b6f48baedc..0000000000000000000000000000000000000000 --- a/projects/bloom/src/BloomAndFlares.cpp +++ /dev/null @@ -1,274 +0,0 @@ -#include "BloomAndFlares.hpp" -#include <vkcv/shader/GLSLCompiler.hpp> - -BloomAndFlares::BloomAndFlares( - vkcv::Core *p_Core, - vk::Format colorBufferFormat, - uint32_t width, - uint32_t height) : - - p_Core(p_Core), - m_ColorBufferFormat(colorBufferFormat), - m_Width(width), - m_Height(height), - m_LinearSampler(p_Core->createSampler(vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE)), - m_Blur(p_Core->createImage(colorBufferFormat, width, height, 1, true, true, false)), - m_LensFeatures(p_Core->createImage(colorBufferFormat, width, height, 1, false, true, false)) -{ - vkcv::shader::GLSLCompiler compiler; - - // DOWNSAMPLE - vkcv::ShaderProgram dsProg; - compiler.compile(vkcv::ShaderStage::COMPUTE, - "resources/shaders/downsample.comp", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) - { - dsProg.addShader(shaderStage, path); - }); - for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) - { - m_DownsampleDescSets.push_back( - p_Core->createDescriptorSet(dsProg.getReflectedDescriptors()[0])); - } - m_DownsamplePipe = p_Core->createComputePipeline( - dsProg, { p_Core->getDescriptorSet(m_DownsampleDescSets[0]).layout }); - - // UPSAMPLE - vkcv::ShaderProgram usProg; - compiler.compile(vkcv::ShaderStage::COMPUTE, - "resources/shaders/upsample.comp", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) - { - usProg.addShader(shaderStage, path); - }); - for(uint32_t mipLevel = 0; mipLevel < m_Blur.getMipCount(); mipLevel++) - { - m_UpsampleDescSets.push_back( - p_Core->createDescriptorSet(usProg.getReflectedDescriptors()[0])); - } - m_UpsamplePipe = p_Core->createComputePipeline( - usProg, { p_Core->getDescriptorSet(m_UpsampleDescSets[0]).layout }); - - // LENS FEATURES - vkcv::ShaderProgram lensProg; - compiler.compile(vkcv::ShaderStage::COMPUTE, - "resources/shaders/lensFlares.comp", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) - { - lensProg.addShader(shaderStage, path); - }); - m_LensFlareDescSet = p_Core->createDescriptorSet(lensProg.getReflectedDescriptors()[0]); - m_LensFlarePipe = p_Core->createComputePipeline( - lensProg, { p_Core->getDescriptorSet(m_LensFlareDescSet).layout }); - - // COMPOSITE - vkcv::ShaderProgram compProg; - compiler.compile(vkcv::ShaderStage::COMPUTE, - "resources/shaders/composite.comp", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) - { - compProg.addShader(shaderStage, path); - }); - m_CompositeDescSet = p_Core->createDescriptorSet(compProg.getReflectedDescriptors()[0]); - m_CompositePipe = p_Core->createComputePipeline( - compProg, { p_Core->getDescriptorSet(m_CompositeDescSet).layout }); -} - -void BloomAndFlares::execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, - const vkcv::ImageHandle &colorAttachment) -{ - auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; - auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; - // blur dispatch - uint32_t initialDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(dispatchCountX)), - static_cast<uint32_t>(glm::ceil(dispatchCountY)), - 1 - }; - - // downsample dispatch of original color attachment - p_Core->prepareImageForSampling(cmdStream, colorAttachment); - p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); - - vkcv::DescriptorWrites initialDownsampleWrites; - initialDownsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, colorAttachment)}; - initialDownsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; - initialDownsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), 0) }; - p_Core->writeDescriptorSet(m_DownsampleDescSets[0], initialDownsampleWrites); - - p_Core->recordComputeDispatchToCmdStream( - cmdStream, - m_DownsamplePipe, - initialDispatchCount, - {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[0]).vulkanHandle)}, - vkcv::PushConstantData(nullptr, 0)); - - // downsample dispatches of blur buffer's mip maps - float mipDispatchCountX = dispatchCountX; - float mipDispatchCountY = dispatchCountY; - for(uint32_t mipLevel = 1; mipLevel < m_DownsampleDescSets.size(); mipLevel++) - { - // mip descriptor writes - vkcv::DescriptorWrites mipDescriptorWrites; - mipDescriptorWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel - 1, true)}; - mipDescriptorWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; - mipDescriptorWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel) }; - p_Core->writeDescriptorSet(m_DownsampleDescSets[mipLevel], mipDescriptorWrites); - - // mip dispatch calculation - mipDispatchCountX /= 2.0f; - mipDispatchCountY /= 2.0f; - - uint32_t mipDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(mipDispatchCountX)), - static_cast<uint32_t>(glm::ceil(mipDispatchCountY)), - 1 - }; - - if(mipDispatchCount[0] == 0) - mipDispatchCount[0] = 1; - if(mipDispatchCount[1] == 0) - mipDispatchCount[1] = 1; - - // mip blur dispatch - p_Core->recordComputeDispatchToCmdStream( - cmdStream, - m_DownsamplePipe, - mipDispatchCount, - {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_DownsampleDescSets[mipLevel]).vulkanHandle)}, - vkcv::PushConstantData(nullptr, 0)); - - // image barrier between mips - p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); - } -} - -void BloomAndFlares::execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream) -{ - // upsample dispatch - p_Core->prepareImageForStorage(cmdStream, m_Blur.getHandle()); - - const uint32_t upsampleMipLevels = std::min( - static_cast<uint32_t>(m_UpsampleDescSets.size() - 1), - static_cast<uint32_t>(5) - ); - - // upsample dispatch for each mip map - for(uint32_t mipLevel = upsampleMipLevels; mipLevel > 0; mipLevel--) - { - // mip descriptor writes - vkcv::DescriptorWrites mipUpsampleWrites; - mipUpsampleWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), mipLevel, true)}; - mipUpsampleWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; - mipUpsampleWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_Blur.getHandle(), mipLevel - 1) }; - p_Core->writeDescriptorSet(m_UpsampleDescSets[mipLevel], mipUpsampleWrites); - - auto mipDivisor = glm::pow(2.0f, static_cast<float>(mipLevel) - 1.0f); - - auto upsampleDispatchX = static_cast<float>(m_Width) / mipDivisor; - auto upsampleDispatchY = static_cast<float>(m_Height) / mipDivisor; - upsampleDispatchX /= 8.0f; - upsampleDispatchY /= 8.0f; - - const uint32_t upsampleDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(upsampleDispatchX)), - static_cast<uint32_t>(glm::ceil(upsampleDispatchY)), - 1 - }; - - p_Core->recordComputeDispatchToCmdStream( - cmdStream, - m_UpsamplePipe, - upsampleDispatchCount, - {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_UpsampleDescSets[mipLevel]).vulkanHandle)}, - vkcv::PushConstantData(nullptr, 0) - ); - // image barrier between mips - p_Core->recordImageMemoryBarrier(cmdStream, m_Blur.getHandle()); - } -} - -void BloomAndFlares::execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream) -{ - // lens feature generation descriptor writes - p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); - p_Core->prepareImageForStorage(cmdStream, m_LensFeatures.getHandle()); - - vkcv::DescriptorWrites lensFeatureWrites; - lensFeatureWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle(), 0)}; - lensFeatureWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(1, m_LinearSampler)}; - lensFeatureWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(2, m_LensFeatures.getHandle(), 0)}; - p_Core->writeDescriptorSet(m_LensFlareDescSet, lensFeatureWrites); - - auto dispatchCountX = static_cast<float>(m_Width) / 8.0f; - auto dispatchCountY = static_cast<float>(m_Height) / 8.0f; - // lens feature generation dispatch - uint32_t lensFeatureDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(dispatchCountX)), - static_cast<uint32_t>(glm::ceil(dispatchCountY)), - 1 - }; - p_Core->recordComputeDispatchToCmdStream( - cmdStream, - m_LensFlarePipe, - lensFeatureDispatchCount, - {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_LensFlareDescSet).vulkanHandle)}, - vkcv::PushConstantData(nullptr, 0)); -} - -void BloomAndFlares::execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, - const vkcv::ImageHandle &colorAttachment) -{ - p_Core->prepareImageForSampling(cmdStream, m_Blur.getHandle()); - p_Core->prepareImageForSampling(cmdStream, m_LensFeatures.getHandle()); - p_Core->prepareImageForStorage(cmdStream, colorAttachment); - - // bloom composite descriptor write - vkcv::DescriptorWrites compositeWrites; - compositeWrites.sampledImageWrites = {vkcv::SampledImageDescriptorWrite(0, m_Blur.getHandle()), - vkcv::SampledImageDescriptorWrite(1, m_LensFeatures.getHandle())}; - compositeWrites.samplerWrites = {vkcv::SamplerDescriptorWrite(2, m_LinearSampler)}; - compositeWrites.storageImageWrites = {vkcv::StorageImageDescriptorWrite(3, colorAttachment)}; - p_Core->writeDescriptorSet(m_CompositeDescSet, compositeWrites); - - float dispatchCountX = static_cast<float>(m_Width) / 8.0f; - float dispatchCountY = static_cast<float>(m_Height) / 8.0f; - - uint32_t compositeDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(dispatchCountX)), - static_cast<uint32_t>(glm::ceil(dispatchCountY)), - 1 - }; - - // bloom composite dispatch - p_Core->recordComputeDispatchToCmdStream( - cmdStream, - m_CompositePipe, - compositeDispatchCount, - {vkcv::DescriptorSetUsage(0, p_Core->getDescriptorSet(m_CompositeDescSet).vulkanHandle)}, - vkcv::PushConstantData(nullptr, 0)); -} - -void BloomAndFlares::execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, - const vkcv::ImageHandle &colorAttachment) -{ - execDownsamplePipe(cmdStream, colorAttachment); - execUpsamplePipe(cmdStream); - execLensFeaturePipe(cmdStream); - execCompositePipe(cmdStream, colorAttachment); -} - -void BloomAndFlares::updateImageDimensions(uint32_t width, uint32_t height) -{ - m_Width = width; - m_Height = height; - - p_Core->getContext().getDevice().waitIdle(); - m_Blur = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, true, true, false); - m_LensFeatures = p_Core->createImage(m_ColorBufferFormat, m_Width, m_Height, 1, false, true, false); -} - - diff --git a/projects/bloom/src/BloomAndFlares.hpp b/projects/bloom/src/BloomAndFlares.hpp deleted file mode 100644 index 756b1ca154ea5232df04eb09a88bb743c5bd28aa..0000000000000000000000000000000000000000 --- a/projects/bloom/src/BloomAndFlares.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include <vkcv/Core.hpp> -#include <glm/glm.hpp> - -class BloomAndFlares{ -public: - BloomAndFlares(vkcv::Core *p_Core, - vk::Format colorBufferFormat, - uint32_t width, - uint32_t height); - - void execWholePipeline(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); - - void updateImageDimensions(uint32_t width, uint32_t height); - -private: - vkcv::Core *p_Core; - - vk::Format m_ColorBufferFormat; - uint32_t m_Width; - uint32_t m_Height; - - vkcv::SamplerHandle m_LinearSampler; - vkcv::Image m_Blur; - vkcv::Image m_LensFeatures; - - - vkcv::PipelineHandle m_DownsamplePipe; - std::vector<vkcv::DescriptorSetHandle> m_DownsampleDescSets; // per mip desc set - - vkcv::PipelineHandle m_UpsamplePipe; - std::vector<vkcv::DescriptorSetHandle> m_UpsampleDescSets; // per mip desc set - - vkcv::PipelineHandle m_LensFlarePipe; - vkcv::DescriptorSetHandle m_LensFlareDescSet; - - vkcv::PipelineHandle m_CompositePipe; - vkcv::DescriptorSetHandle m_CompositeDescSet; - - void execDownsamplePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); - void execUpsamplePipe(const vkcv::CommandStreamHandle &cmdStream); - void execLensFeaturePipe(const vkcv::CommandStreamHandle &cmdStream); - void execCompositePipe(const vkcv::CommandStreamHandle &cmdStream, const vkcv::ImageHandle &colorAttachment); -}; - - - diff --git a/projects/bloom/src/main.cpp b/projects/bloom/src/main.cpp deleted file mode 100644 index 7a17a51f1c7d638575c0b5aafcdca49b589533ef..0000000000000000000000000000000000000000 --- a/projects/bloom/src/main.cpp +++ /dev/null @@ -1,419 +0,0 @@ -#include <iostream> -#include <vkcv/Core.hpp> -#include <GLFW/glfw3.h> -#include <vkcv/camera/CameraManager.hpp> -#include <chrono> -#include <vkcv/asset/asset_loader.hpp> -#include <vkcv/shader/GLSLCompiler.hpp> -#include <vkcv/Logger.hpp> -#include "BloomAndFlares.hpp" -#include <glm/glm.hpp> - -int main(int argc, const char** argv) { - const char* applicationName = "Bloom"; - - uint32_t windowWidth = 1920; - uint32_t windowHeight = 1080; - - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true - ); - - vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); - - cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.f, 0.f, 3.f)); - cameraManager.getCamera(camIndex).setNearFar(0.1f, 30.0f); - cameraManager.getCamera(camIndex).setYaw(180.0f); - - cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f); - - vkcv::Core core = vkcv::Core::create( - window, - applicationName, - VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain" } - ); - - const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; - vkcv::asset::Scene scene; - int result = vkcv::asset::loadScene(path, scene); - - if (result == 1) { - std::cout << "Scene loading successful!" << std::endl; - } - else { - std::cout << "Scene loading failed: " << result << std::endl; - return 1; - } - - // build index and vertex buffers - assert(!scene.vertexGroups.empty()); - std::vector<std::vector<uint8_t>> vBuffers; - std::vector<std::vector<uint8_t>> iBuffers; - - std::vector<vkcv::VertexBufferBinding> vBufferBindings; - std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; - std::vector<vkcv::asset::VertexAttribute> vAttributes; - - for (int i = 0; i < scene.vertexGroups.size(); i++) { - - vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); - iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); - - auto& attributes = scene.vertexGroups[i].vertexBuffer.attributes; - - std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { - return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); - }); - } - - std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; - for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { - vertexBuffers.push_back(core.createBuffer<uint8_t>( - vkcv::BufferType::VERTEX, - group.vertexBuffer.data.size())); - vertexBuffers.back().fill(group.vertexBuffer.data); - } - - std::vector<vkcv::Buffer<uint8_t>> indexBuffers; - for (const auto& dataBuffer : iBuffers) { - indexBuffers.push_back(core.createBuffer<uint8_t>( - vkcv::BufferType::INDEX, - dataBuffer.size())); - indexBuffers.back().fill(dataBuffer); - } - - int vertexBufferIndex = 0; - for (const auto& vertexGroup : scene.vertexGroups) { - for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { - vAttributes.push_back(attribute); - vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); - } - vertexBufferBindings.push_back(vBufferBindings); - vBufferBindings.clear(); - vertexBufferIndex++; - } - - const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32; - const vkcv::AttachmentDescription color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - colorBufferFormat - ); - - const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; - const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - depthBufferFormat - ); - - vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }); - vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition); - - vkcv::shader::GLSLCompiler compiler; - - vkcv::ShaderProgram forwardProgram; - compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - forwardProgram.addShader(shaderStage, path); - }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - forwardProgram.addShader(shaderStage, path); - }); - - const std::vector<vkcv::VertexAttachment> vertexAttachments = forwardProgram.getVertexAttachments(); - - std::vector<vkcv::VertexBinding> vertexBindings; - for (size_t i = 0; i < vertexAttachments.size(); i++) { - vertexBindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); - } - const vkcv::VertexLayout vertexLayout (vertexBindings); - - // shadow map - vkcv::SamplerHandle shadowSampler = core.createSampler( - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerMipmapMode::NEAREST, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE - ); - const vk::Format shadowMapFormat = vk::Format::eD16Unorm; - const uint32_t shadowMapResolution = 1024; - const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1); - - // light info buffer - struct LightInfo { - glm::vec3 direction; - float padding; - glm::mat4 lightMatrix; - }; - LightInfo lightInfo; - vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); - - vkcv::DescriptorSetHandle forwardShadingDescriptorSet = - core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] }); - - vkcv::DescriptorWrites forwardDescriptorWrites; - forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) }; - forwardDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) }; - forwardDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, shadowSampler) }; - core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); - - vkcv::SamplerHandle colorSampler = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - - // prepare per mesh descriptor sets - std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; - std::vector<vkcv::Image> sceneImages; - for (const auto& vertexGroup : scene.vertexGroups) { - perMeshDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); - - const auto& material = scene.materials[vertexGroup.materialIndex]; - - int baseColorIndex = material.baseColor; - if (baseColorIndex < 0) { - vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); - baseColorIndex = 0; - } - - vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; - - sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h)); - sceneImages.back().fill(sceneTexture.data.data()); - - vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) - }; - setWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(1, colorSampler), - }; - core.writeDescriptorSet(perMeshDescriptorSets.back(), setWrites); - } - - const vkcv::PipelineConfig forwardPipelineConfig { - forwardProgram, - windowWidth, - windowHeight, - forwardPass, - vertexLayout, - { core.getDescriptorSet(forwardShadingDescriptorSet).layout, - core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, - true - }; - - vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); - - if (!forwardPipeline) { - std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; - return EXIT_FAILURE; - } - - vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); - vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); - - const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - - vkcv::ShaderProgram shadowShader; - compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - shadowShader.addShader(shaderStage, path); - }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow.frag", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - shadowShader.addShader(shaderStage, path); - }); - - const std::vector<vkcv::AttachmentDescription> shadowAttachments = { - vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapFormat) - }; - const vkcv::PassConfig shadowPassConfig(shadowAttachments); - const vkcv::PassHandle shadowPass = core.createPass(shadowPassConfig); - const vkcv::PipelineConfig shadowPipeConfig{ - shadowShader, - shadowMapResolution, - shadowMapResolution, - shadowPass, - vertexLayout, - {}, - false - }; - const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); - - std::vector<std::array<glm::mat4, 2>> mainPassMatrices; - std::vector<glm::mat4> mvpLight; - - // gamma correction compute shader - vkcv::ShaderProgram gammaCorrectionProgram; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/gammaCorrection.comp", - [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - gammaCorrectionProgram.addShader(shaderStage, path); - }); - vkcv::DescriptorSetHandle gammaCorrectionDescriptorSet = core.createDescriptorSet(gammaCorrectionProgram.getReflectedDescriptors()[0]); - vkcv::PipelineHandle gammaCorrectionPipeline = core.createComputePipeline(gammaCorrectionProgram, - { core.getDescriptorSet(gammaCorrectionDescriptorSet).layout }); - - BloomAndFlares baf(&core, colorBufferFormat, windowWidth, windowHeight); - - - // model matrices per mesh - std::vector<glm::mat4> modelMatrices; - modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f)); - for (const auto& mesh : scene.meshes) { - const glm::mat4 m = *reinterpret_cast<const glm::mat4*>(&mesh.modelMatrix[0]); - for (const auto& vertexGroupIndex : mesh.vertexGroups) { - modelMatrices[vertexGroupIndex] = m; - } - } - - // prepare drawcalls - std::vector<vkcv::Mesh> meshes; - for (int i = 0; i < scene.vertexGroups.size(); i++) { - vkcv::Mesh mesh( - vertexBufferBindings[i], - indexBuffers[i].getVulkanHandle(), - scene.vertexGroups[i].numIndices); - meshes.push_back(mesh); - } - - std::vector<vkcv::DrawcallInfo> drawcalls; - std::vector<vkcv::DrawcallInfo> shadowDrawcalls; - for (int i = 0; i < meshes.size(); i++) { - drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { - vkcv::DescriptorSetUsage(0, core.getDescriptorSet(forwardShadingDescriptorSet).vulkanHandle), - vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) })); - shadowDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {})); - } - - auto start = std::chrono::system_clock::now(); - const auto appStartTime = start; - while (window.isWindowOpen()) { - vkcv::Window::pollEvents(); - - uint32_t swapchainWidth, swapchainHeight; - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } - - if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); - colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); - - baf.updateImageDimensions(swapchainWidth, swapchainHeight); - - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; - } - - auto end = std::chrono::system_clock::now(); - auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - - start = end; - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime); - - const float sunTheta = 0.0001f * static_cast<float>(duration.count()); - lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta))); - - const float shadowProjectionSize = 20.f; - glm::mat4 projectionLight = glm::ortho( - -shadowProjectionSize, - shadowProjectionSize, - -shadowProjectionSize, - shadowProjectionSize, - -shadowProjectionSize, - shadowProjectionSize); - - glm::mat4 vulkanCorrectionMatrix(1.f); - vulkanCorrectionMatrix[2][2] = 0.5; - vulkanCorrectionMatrix[3][2] = 0.5; - projectionLight = vulkanCorrectionMatrix * projectionLight; - - const glm::mat4 viewLight = glm::lookAt(glm::vec3(0), -lightInfo.direction, glm::vec3(0, -1, 0)); - - lightInfo.lightMatrix = projectionLight * viewLight; - lightBuffer.fill({ lightInfo }); - - const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP(); - - mainPassMatrices.clear(); - mvpLight.clear(); - for (const auto& m : modelMatrices) { - mainPassMatrices.push_back({ viewProjectionCamera * m, m }); - mvpLight.push_back(lightInfo.lightMatrix * m); - } - - vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4)); - const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer }; - - const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); - - auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - - // shadow map - core.recordDrawcallsToCmdStream( - cmdStream, - shadowPass, - shadowPipe, - shadowPushConstantData, - shadowDrawcalls, - { shadowMap.getHandle() }); - core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); - - // main pass - core.recordDrawcallsToCmdStream( - cmdStream, - forwardPass, - forwardPipeline, - pushConstantData, - drawcalls, - renderTargets); - - const uint32_t gammaCorrectionLocalGroupSize = 8; - const uint32_t gammaCorrectionDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(static_cast<float>(windowWidth) / static_cast<float>(gammaCorrectionLocalGroupSize))), - static_cast<uint32_t>(glm::ceil(static_cast<float>(windowHeight) / static_cast<float>(gammaCorrectionLocalGroupSize))), - 1 - }; - - baf.execWholePipeline(cmdStream, colorBuffer); - - core.prepareImageForStorage(cmdStream, swapchainInput); - - // gamma correction descriptor write - vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; - gammaCorrectionDescriptorWrites.storageImageWrites = { - vkcv::StorageImageDescriptorWrite(0, colorBuffer), - vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; - core.writeDescriptorSet(gammaCorrectionDescriptorSet, gammaCorrectionDescriptorWrites); - - // gamma correction dispatch - core.recordComputeDispatchToCmdStream( - cmdStream, - gammaCorrectionPipeline, - gammaCorrectionDispatchCount, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(gammaCorrectionDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); - - // present and end - core.prepareSwapchainImageForPresent(cmdStream); - core.submitCommandStream(cmdStream); - - core.endFrame(); - } - - return 0; -} diff --git a/projects/cmd_sync_test/src/main.cpp b/projects/cmd_sync_test/src/main.cpp deleted file mode 100644 index eccc0af7331dc140f3a15ddf12c5645e685abc90..0000000000000000000000000000000000000000 --- a/projects/cmd_sync_test/src/main.cpp +++ /dev/null @@ -1,317 +0,0 @@ -#include <iostream> -#include <vkcv/Core.hpp> -#include <GLFW/glfw3.h> -#include <vkcv/camera/CameraManager.hpp> -#include <chrono> -#include <vkcv/asset/asset_loader.hpp> - -int main(int argc, const char** argv) { - const char* applicationName = "First Mesh"; - - uint32_t windowWidth = 800; - uint32_t windowHeight = 600; - - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true - ); - - vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); - - cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.f, 0.f, 3.f)); - cameraManager.getCamera(camIndex).setNearFar(0.1f, 30.0f); - cameraManager.getCamera(camIndex).setYaw(180.0f); - - cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f); - - window.initEvents(); - - vkcv::Core core = vkcv::Core::create( - window, - applicationName, - VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain" } - ); - - vkcv::asset::Scene mesh; - - const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; - int result = vkcv::asset::loadScene(path, mesh); - - if (result == 1) { - std::cout << "Mesh loading successful!" << std::endl; - } - else { - std::cout << "Mesh loading failed: " << result << std::endl; - return 1; - } - - assert(mesh.vertexGroups.size() > 0); - auto vertexBuffer = core.createBuffer<uint8_t>( - vkcv::BufferType::VERTEX, - mesh.vertexGroups[0].vertexBuffer.data.size(), - vkcv::BufferMemoryType::DEVICE_LOCAL - ); - - vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data); - - auto indexBuffer = core.createBuffer<uint8_t>( - vkcv::BufferType::INDEX, - mesh.vertexGroups[0].indexBuffer.data.size(), - vkcv::BufferMemoryType::DEVICE_LOCAL - ); - - indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data); - - auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; - - std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { - return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); - }); - - const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { - vkcv::VertexBufferBinding(attributes[0].offset, vertexBuffer.getVulkanHandle()), - vkcv::VertexBufferBinding(attributes[1].offset, vertexBuffer.getVulkanHandle()), - vkcv::VertexBufferBinding(attributes[2].offset, vertexBuffer.getVulkanHandle()) }; - - const vkcv::Mesh loadedMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices); - - // an example attachment for passes that output to the window - const vkcv::AttachmentDescription present_color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - core.getSwapchain().getFormat() - ); - - const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - vk::Format::eD32Sfloat - ); - - vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); - vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); - - if (!firstMeshPass) { - std::cout << "Error. Could not create renderpass. Exiting." << std::endl; - return EXIT_FAILURE; - } - - vkcv::ShaderProgram firstMeshProgram{}; - firstMeshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); - firstMeshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); - - const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments(); - - std::vector<vkcv::VertexBinding> bindings; - for (size_t i = 0; i < vertexAttachments.size(); i++) { - bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); - } - - const vkcv::VertexLayout firstMeshLayout (bindings); - - std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[0] }; - vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); - - const vkcv::PipelineConfig firstMeshPipelineConfig { - firstMeshProgram, - windowWidth, - windowHeight, - firstMeshPass, - firstMeshLayout, - { core.getDescriptorSet(descriptorSet).layout }, - true - }; - - vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig); - - if (!firstMeshPipeline) { - std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; - return EXIT_FAILURE; - } - - //vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, mesh.texture_hack.w, mesh.texture_hack.h); - //texture.fill(mesh.texture_hack.img); - vkcv::asset::Texture &tex = mesh.textures[0]; - vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h); - texture.fill(tex.data.data()); - - vkcv::SamplerHandle sampler = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - - vkcv::SamplerHandle shadowSampler = core.createSampler( - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerMipmapMode::NEAREST, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE - ); - - vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle(); - - const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - - const vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); - - const std::vector<glm::vec3> instancePositions = { - glm::vec3( 0.f, -2.f, 0.f), - glm::vec3( 3.f, 0.f, 0.f), - glm::vec3(-3.f, 0.f, 0.f), - glm::vec3( 0.f, 2.f, 0.f), - glm::vec3( 0.f, -5.f, 0.f) - }; - - std::vector<glm::mat4> modelMatrices; - std::vector<vkcv::DrawcallInfo> drawcalls; - std::vector<vkcv::DrawcallInfo> shadowDrawcalls; - for (const auto& position : instancePositions) { - modelMatrices.push_back(glm::translate(glm::mat4(1.f), position)); - drawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, { descriptorUsage })); - shadowDrawcalls.push_back(vkcv::DrawcallInfo(loadedMesh, {})); - } - - modelMatrices.back() *= glm::scale(glm::mat4(1.f), glm::vec3(10.f, 1.f, 10.f)); - - std::vector<std::array<glm::mat4, 2>> mainPassMatrices; - std::vector<glm::mat4> mvpLight; - - vkcv::ShaderProgram shadowShader; - shadowShader.addShader(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow_vert.spv"); - shadowShader.addShader(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow_frag.spv"); - - const vk::Format shadowMapFormat = vk::Format::eD16Unorm; - const std::vector<vkcv::AttachmentDescription> shadowAttachments = { - vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapFormat) - }; - const vkcv::PassConfig shadowPassConfig(shadowAttachments); - const vkcv::PassHandle shadowPass = core.createPass(shadowPassConfig); - - const uint32_t shadowMapResolution = 1024; - const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution, 1); - const vkcv::PipelineConfig shadowPipeConfig { - shadowShader, - shadowMapResolution, - shadowMapResolution, - shadowPass, - firstMeshLayout, - {}, - false - }; - - const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); - - struct LightInfo { - glm::vec3 direction; - float padding; - glm::mat4 lightMatrix; - }; - LightInfo lightInfo; - vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); - - vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, texture.getHandle()), - vkcv::SampledImageDescriptorWrite(3, shadowMap.getHandle()) }; - setWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(1, sampler), - vkcv::SamplerDescriptorWrite(4, shadowSampler) }; - setWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(2, lightBuffer.getHandle()) }; - core.writeDescriptorSet(descriptorSet, setWrites); - - auto start = std::chrono::system_clock::now(); - const auto appStartTime = start; - while (window.isWindowOpen()) { - window.pollEvents(); - - uint32_t swapchainWidth, swapchainHeight; - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } - - if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle(); - - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; - } - - auto end = std::chrono::system_clock::now(); - auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - - start = end; - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - - auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - appStartTime); - - const float sunTheta = 0.001f * static_cast<float>(duration.count()); - lightInfo.direction = glm::normalize(glm::vec3(std::cos(sunTheta), 1, std::sin(sunTheta))); - - const float shadowProjectionSize = 5.f; - glm::mat4 projectionLight = glm::ortho( - -shadowProjectionSize, - shadowProjectionSize, - -shadowProjectionSize, - shadowProjectionSize, - -shadowProjectionSize, - shadowProjectionSize); - - glm::mat4 vulkanCorrectionMatrix(1.f); - vulkanCorrectionMatrix[2][2] = 0.5; - vulkanCorrectionMatrix[3][2] = 0.5; - projectionLight = vulkanCorrectionMatrix * projectionLight; - - const glm::mat4 viewLight = glm::lookAt(glm::vec3(0), -lightInfo.direction, glm::vec3(0, -1, 0)); - - lightInfo.lightMatrix = projectionLight * viewLight; - lightBuffer.fill({ lightInfo }); - - const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP(); - - mainPassMatrices.clear(); - mvpLight.clear(); - for (const auto& m : modelMatrices) { - mainPassMatrices.push_back({ viewProjectionCamera * m, m }); - mvpLight.push_back(lightInfo.lightMatrix* m); - } - - vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4)); - const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; - - vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); - - auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - - core.recordDrawcallsToCmdStream( - cmdStream, - shadowPass, - shadowPipe, - shadowPushConstantData, - shadowDrawcalls, - { shadowMap.getHandle() }); - - core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); - - core.recordDrawcallsToCmdStream( - cmdStream, - firstMeshPass, - firstMeshPipeline, - pushConstantData, - drawcalls, - renderTargets); - core.prepareSwapchainImageForPresent(cmdStream); - core.submitCommandStream(cmdStream); - - core.endFrame(); - } - - return 0; -} diff --git a/projects/fire_works/.gitignore b/projects/fire_works/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..a991f1c077c11db780beb6e3d01c5bd561336690 --- /dev/null +++ b/projects/fire_works/.gitignore @@ -0,0 +1 @@ +fire_works diff --git a/projects/fire_works/CMakeLists.txt b/projects/fire_works/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7f9fd1fdd30bff0b331138821f10035f4b7dc64d --- /dev/null +++ b/projects/fire_works/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.16) +project(fire_works) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(fire_works + src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(fire_works SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_camera_include} + ${vkcv_gui_include} + ${vkcv_shader_compiler_include} + ${vkcv_effects_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(fire_works + vkcv + vkcv_camera + vkcv_gui + vkcv_shader_compiler + vkcv_effects) diff --git a/projects/fire_works/README.md b/projects/fire_works/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0357b60bed448a813e0b5812b8716542adebb31a --- /dev/null +++ b/projects/fire_works/README.md @@ -0,0 +1,11 @@ +# Fire works +An example project to show a particle simulation via compute shader to visualize virtual firework + + + +## Details + +The project uses many different compute shaders to calculate different steps of the particles +lifecycle. Particles can dynamically be spread to different events for explosions on the GPU. The +particles will then be rendered as volumetric smoke and geometric smoke using geometry shader +invocations. diff --git a/projects/fire_works/shaders/add.comp b/projects/fire_works/shaders/add.comp new file mode 100644 index 0000000000000000000000000000000000000000..737a1f3fd69e237f7692be8a6b36b1e14f1fd50a --- /dev/null +++ b/projects/fire_works/shaders/add.comp @@ -0,0 +1,64 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D voxelTexture; +layout(set=0, binding=1) uniform sampler voxelSampler; + +layout(set=0, binding=2, rgba16f) restrict readonly uniform image2D inParticles; +layout(set=0, binding=3, rgba16f) restrict readonly uniform image2D inSmoke; +layout(set=0, binding=4, rgba16f) restrict readonly uniform image2D inTrails; +layout(set=0, binding=5, rgba16f) restrict writeonly uniform image2D outImage; + +layout(set=1, binding=0, std430) readonly buffer randomBuffer { + float randomData []; +}; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "physics.inc" +#include "smoke.inc" + +#define NUM_VOXEL_SAMPLES 32 + +shared vec2 sc_data [NUM_VOXEL_SAMPLES]; + +void main() { + const float localRadian = 0.25f * pi * randomData[gl_LocalInvocationIndex % randomData.length()]; + + sc_data[gl_LocalInvocationIndex % NUM_VOXEL_SAMPLES] = vec2( + sin(localRadian), cos(localRadian) + ); + + memoryBarrierShared(); + barrier(); + + const ivec2 res = imageSize(outImage); + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, res))){ + return; + } + + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + vec4 outParticles = imageLoad(inParticles, uv); + vec4 outSmoke = imageLoad(inSmoke, uv); + vec4 outTrails = imageLoad(inTrails, uv); + + vec2 pos = (vec2(uv) + vec2(0.5f)) / vec2(res); + + vec4 outSamples = texture(sampler2D(voxelTexture, voxelSampler), pos); + + vec4 result = vec4(0.0f); + + result = smokeBlend(result, outParticles); + result = smokeBlend(result, outTrails); + result = smokeBlend(result, outSmoke); + result = smokeBlend(result, outSamples * 0.1f); + + result.r = clamp(result.r, 0, 1); + result.g = clamp(result.g, 0, 1); + result.b = clamp(result.b, 0, 1); + result.a = clamp(result.a, 0, 1); + + imageStore(outImage, uv, result); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/clear.comp b/projects/fire_works/shaders/clear.comp new file mode 100644 index 0000000000000000000000000000000000000000..4668538c4a38aefec868f1817a126b5278f8fd78 --- /dev/null +++ b/projects/fire_works/shaders/clear.comp @@ -0,0 +1,25 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "physics.inc" +#include "voxel.inc" + +layout(set=0, binding=0, r32ui) restrict writeonly uniform uimage3D voxelRed; +layout(set=0, binding=1, r32ui) restrict writeonly uniform uimage3D voxelGreen; +layout(set=0, binding=2, r32ui) restrict writeonly uniform uimage3D voxelBlue; +layout(set=0, binding=3, r32ui) restrict writeonly uniform uimage3D voxelDensity; + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +void main() { + if(any(greaterThanEqual(gl_GlobalInvocationID.xyz, imageSize(voxelDensity)))){ + return; + } + + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + + voxel_write(voxelRed, pos, 0.0f); + voxel_write(voxelGreen, pos, 0.0f); + voxel_write(voxelBlue, pos, 0.0f); + voxel_write(voxelDensity, pos, mediumDensity); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/event.inc b/projects/fire_works/shaders/event.inc new file mode 100644 index 0000000000000000000000000000000000000000..a2ab170c0894da68638b0e61e4af90a7a5fcdd64 --- /dev/null +++ b/projects/fire_works/shaders/event.inc @@ -0,0 +1,21 @@ +#ifndef EVENT_INC +#define EVENT_INC + +struct event_t { + vec3 direction; + float startTime; + vec3 color; + float velocity; + + uint count; + uint index; + uint parent; + uint continuous; + + float lifetime; + float mass; + float size; + uint contCount; +}; + +#endif // EVENT_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/fluid.comp b/projects/fire_works/shaders/fluid.comp new file mode 100644 index 0000000000000000000000000000000000000000..076a280739e542c0579a7b80eaeaf4993df13edf --- /dev/null +++ b/projects/fire_works/shaders/fluid.comp @@ -0,0 +1,80 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#include "physics.inc" +#include "voxel.inc" + +layout(set=0, binding=0) uniform texture3D voxelTexture; +layout(set=0, binding=1) uniform sampler voxelSampler; +layout(set=0, binding=2, rgba16) restrict writeonly uniform image3D fluidImage; + +vec4 getDataFrom(vec3 position, vec3 offset) { + return texture( + sampler3D( + voxelTexture, + voxelSampler + ), + position + offset + ); +} + +shared vec4 cachedData [4][4][4]; + +void storeCachedData(vec3 position) { + uvec3 localId = gl_LocalInvocationID; + cachedData[localId.x][localId.y][localId.z] = getDataFrom(position, vec3(0)); +} + +vec4 getCachedData() { + uvec3 localId = gl_LocalInvocationID; + return cachedData[localId.x][localId.y][localId.z]; +} + +vec4 loadCachedData(vec3 position, ivec3 offset, ivec3 size) { + uvec3 localId = gl_LocalInvocationID; + ivec3 index = ivec3(localId) + offset; + + // TOO SPECIAL FOR GPU TO WORK..! + //if ((any(lessThan(index, ivec3(0)))) || (any(greaterThan(index, ivec3(gl_WorkGroupSize))))) { + return getDataFrom(position, vec3(offset) / vec3(size)); + //} else { + // return cachedData[index.x][index.y][index.z]; + //} +} + +void main() { + uvec3 id = gl_GlobalInvocationID; + ivec3 size = imageSize(fluidImage); + + if (any(greaterThanEqual(id, size))) { + return; + } + + vec3 position = (vec3(id) + vec3(0.5f)) / vec3(size); + + storeCachedData(position); + memoryBarrierShared(); + barrier(); + + vec4 extData [6]; + + extData[0] = loadCachedData(position, ivec3(+1, 0, 0), size); + extData[1] = loadCachedData(position, ivec3(-1, 0, 0), size); + extData[2] = loadCachedData(position, ivec3(0, +1, 0), size); + extData[3] = loadCachedData(position, ivec3(0, -1, 0), size); + extData[4] = loadCachedData(position, ivec3(0, 0, +1), size); + extData[5] = loadCachedData(position, ivec3(0, 0, -1), size); + + vec4 data = vec4(0); + + for (uint i = 0; i < 6; i++) { + data += extData[i]; + } + + data = mix(getCachedData(), (data / 6), flowRate); + + imageStore(fluidImage, ivec3(id), data); +} diff --git a/projects/fire_works/shaders/generation.comp b/projects/fire_works/shaders/generation.comp new file mode 100644 index 0000000000000000000000000000000000000000..eb585236d993099350d0c0e6b97adcbfd28fdf57 --- /dev/null +++ b/projects/fire_works/shaders/generation.comp @@ -0,0 +1,179 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) buffer particleBuffer { + particle_t particles []; +}; + +layout(set=0, binding=1, std430) readonly buffer particleBufferCopy { + particle_t particlesCopy []; +}; + +layout(set=1, binding=0, std430) readonly buffer randomBuffer { + float randomData []; +}; + +#include "event.inc" + +layout(set=1, binding=1, std430) buffer eventBuffer { + event_t events []; +}; + +layout(set=1, binding=2, std430) buffer startIndexBuffer { + uint startIndex []; +}; + +#include "smoke.inc" + +layout(set=2, binding=0, std430) writeonly buffer smokeBuffer { + smoke_t smokes []; +}; + +layout(set=2, binding=1, std430) buffer smokeIndexBuffer { + uint smokeIndex; + uint trailIndex; + uint pointIndex; +}; + +#include "trail.inc" + +layout(set=3, binding=0, std430) writeonly buffer trailBuffer { + trail_t trails []; +}; + +#include "point.inc" + +layout(set=3, binding=1, std430) readonly buffer pointBuffer { + point_t points []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= particles.length()) { + return; + } + + float lifetime = particles[id].lifetime; + + if (lifetime > 0.0f) { + return; + } + + uint event_id = events.length(); + uint index = 0; + + for (uint i = 0; i < events.length(); i++) { + const float start = events[i].startTime; + + if ((events[i].continuous < 1) && (t < start)) { + continue; + } + + index = atomicAdd(events[i].index, 1); + + if (events[i].continuous < 1) { + if (events[i].count > index) { + event_id = i; + break; + } else { + atomicAdd(events[i].index, -1); + } + } else { + if (events[i].continuous > index){ + event_id = i; + break; + } else { + if (events[i].contCount > 0) { + atomicAdd(events[i].contCount, -1); + events[i].index = 0; + } + + atomicAdd(events[i].index, -1); + } + } + } + + if (event_id >= events.length()) { + return; + } + + lifetime = events[event_id].lifetime * (1.0f + 0.1f * randomData[(id + 1) % randomData.length()]); + + vec3 direction; + if (dot(events[event_id].direction, events[event_id].direction) <= 0.0f) { + direction = vec3( + randomData[(id * 3 + 0) % randomData.length()], + randomData[(id * 3 + 1) % randomData.length()], + randomData[(id * 3 + 2) % randomData.length()] + ); + } else { + direction = events[event_id].direction; + } + + vec3 color = normalize(events[event_id].color); + const float v = events[event_id].velocity; + + vec3 velocity = vec3(0.0f); + float size = events[event_id].size; + + const uint pid = events[event_id].parent; + + if (pid < events.length()) { + const uint spawnCount = events[pid].count; + const uint spawnId = startIndex[pid] + (id % spawnCount); + + if (spawnId < particlesCopy.length()) { + particles[id].position = particlesCopy[spawnId].position; + velocity += particlesCopy[spawnId].velocity; + size = particlesCopy[spawnId].size; + } + } + + if ((0 == index) && (events[event_id].continuous < 1)) { + const uint sid = atomicAdd(smokeIndex, 1) % smokes.length(); + + smokes[sid].position = particles[id].position; + smokes[sid].size = size * (1.0f + friction); + smokes[sid].velocity = velocity; + smokes[sid].scaling = v; + smokes[sid].color = mix(color, vec3(1.0f), 0.5f); + smokes[sid].eventID = event_id; + } + + velocity += normalize(direction) * v * (1.0f + 0.1f * randomData[(id + 2) % randomData.length()]);; + + const float split = pow(1.0f / events[event_id].count, 1.0f / 3.0f); + + particles[id].lifetime = lifetime; + particles[id].velocity = velocity; + particles[id].size = size * split; + particles[id].color = color; + particles[id].mass = events[event_id].mass / events[event_id].count; + particles[id].eventId = event_id; + + { + const uint tid = atomicAdd(trailIndex, 1) % trails.length(); + const uint trailLen = 96 + int(randomData[(tid + id) % randomData.length()] * 32); + + const uint startIndex = atomicAdd(pointIndex, trailLen) % points.length(); + + trails[tid].particleIndex = id; + trails[tid].startIndex = startIndex; + trails[tid].endIndex = (startIndex + trailLen - 1) % points.length(); + trails[tid].useCount = 0; + trails[tid].color = mix(color, vec3(1.0f), 0.75f); + trails[tid].lifetime = lifetime + (dt * trailLen) * 0.5f; + } +} diff --git a/projects/fire_works/shaders/motion.comp b/projects/fire_works/shaders/motion.comp new file mode 100644 index 0000000000000000000000000000000000000000..51d6fc5f60f2eff380376fead6cc189af68b52e6 --- /dev/null +++ b/projects/fire_works/shaders/motion.comp @@ -0,0 +1,49 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) coherent buffer particleBuffer { + particle_t particles []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= particles.length()) { + return; + } + + vec3 position = particles[id].position; + float lifetime = particles[id].lifetime; + vec3 velocity = particles[id].velocity; + + if (lifetime > dt) { + lifetime -= dt; + } else { + lifetime = 0.0f; + } + + const float fading = 1.0f / (1.0f + friction); + + position = position + velocity * dt; + + if (particles[id].mass > 0){ + velocity = velocity * fading + vec3(0.0f, -g, 0.0f) * dt; + } else { + velocity = velocity * fading; + } + + particles[id].position = position; + particles[id].lifetime = lifetime; + particles[id].velocity = velocity; +} diff --git a/projects/fire_works/shaders/particle.frag b/projects/fire_works/shaders/particle.frag new file mode 100644 index 0000000000000000000000000000000000000000..173c83ef5d7a79bfd8446c5586b7a2f487fe7546 --- /dev/null +++ b/projects/fire_works/shaders/particle.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) in vec2 passPos; +layout(location = 1) in flat vec3 passColor; +layout(location = 2) in flat float passLifetime; + +layout(location = 0) out vec4 outColor; + +void main() { + if (passLifetime <= 0.0f) { + discard; + } + + const float value = length(passPos); + + if (value < 0.5f) { + outColor = vec4(passColor, 1.0f - max(value * 2.0f, 0.0f)); + } else { + discard; + } +} \ No newline at end of file diff --git a/projects/fire_works/shaders/particle.inc b/projects/fire_works/shaders/particle.inc new file mode 100644 index 0000000000000000000000000000000000000000..488c9349d72ac04d48e523d5c9a97057bdfb1f78 --- /dev/null +++ b/projects/fire_works/shaders/particle.inc @@ -0,0 +1,15 @@ +#ifndef PARTICLE_INC +#define PARTICLE_INC + +struct particle_t { + vec3 position; + float lifetime; + vec3 velocity; + float size; + vec3 color; + float mass; + vec3 pad0; + uint eventId; +}; + +#endif // PARTICLE_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/particle.vert b/projects/fire_works/shaders/particle.vert new file mode 100644 index 0000000000000000000000000000000000000000..ff0e862ebcf0f66c3412e9ce04b610cea760151d --- /dev/null +++ b/projects/fire_works/shaders/particle.vert @@ -0,0 +1,40 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +layout(location = 0) in vec2 vertexPos; + +layout(location = 0) out vec2 passPos; +layout(location = 1) out flat vec3 passColor; +layout(location = 2) out flat float passLifetime; + +layout( push_constant ) uniform constants{ + mat4 mvp; + uint width; + uint height; +}; + +void main() { + vec3 position = particles[gl_InstanceIndex].position; + float lifetime = particles[gl_InstanceIndex].lifetime; + float size = particles[gl_InstanceIndex].size; + vec3 color = particles[gl_InstanceIndex].color; + + if (width > height) { + passPos = vertexPos * vec2(1.0f * width / height, 1.0f); + } else { + passPos = vertexPos * vec2(1.0f, 1.0f * height / width); + } + + passColor = color; + passLifetime = lifetime; + + // align particle to face camera + gl_Position = mvp * vec4(position, 1); // transform position into projected view space + gl_Position.xy += vertexPos * size * 2.0f; // move position directly in view space +} \ No newline at end of file diff --git a/projects/fire_works/shaders/physics.inc b/projects/fire_works/shaders/physics.inc new file mode 100644 index 0000000000000000000000000000000000000000..e14c62e33a6fd42ebfa8d31591394989de68a8d2 --- /dev/null +++ b/projects/fire_works/shaders/physics.inc @@ -0,0 +1,13 @@ +#ifndef PHYSICS_INC +#define PHYSICS_INC + +const float pi = 3.14159f; + +const float g = 9.81f; +const float friction = 0.004f; + +const float flowRate = 0.75f; +const float mediumDensity = 0.0002f; +const float trailWidth = 0.25f; + +#endif // PHYSICS_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/point.inc b/projects/fire_works/shaders/point.inc new file mode 100644 index 0000000000000000000000000000000000000000..54663c1e2eae4c641fbe5485d4c0ac41cb323df6 --- /dev/null +++ b/projects/fire_works/shaders/point.inc @@ -0,0 +1,11 @@ +#ifndef POINT_INC +#define POINT_INC + +struct point_t { + vec3 position; + float size; + vec3 velocity; + float scaling; +}; + +#endif // POINT_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/sample.comp b/projects/fire_works/shaders/sample.comp new file mode 100644 index 0000000000000000000000000000000000000000..1eef49df639cfbc9f35c53ace21b1ce50ab62459 --- /dev/null +++ b/projects/fire_works/shaders/sample.comp @@ -0,0 +1,39 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "voxel.inc" +#include "smoke.inc" + +layout(set=0, binding=0, rgba16) restrict readonly uniform image3D voxelImage; +layout(set=1, binding=0, rgba16f) restrict writeonly uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() { + const ivec2 res = imageSize(outImage); + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, res))){ + return; + } + + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + const ivec3 voxelRes = imageSize(voxelImage); + + vec4 voxel = vec4(0.0f); + + for (int i = 0; i < voxelRes.z; i++) { + const ivec3 voxelPos = ivec3(uv, i); + + vec4 data = imageLoad(voxelImage, voxelPos); + + voxel = smokeBlend(voxel, data); + } + + voxel.r = clamp(voxel.r, 0, 1); + voxel.g = clamp(voxel.g, 0, 1); + voxel.b = clamp(voxel.b, 0, 1); + voxel.a = clamp(voxel.a, 0, 1); + + imageStore(outImage, uv, voxel); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/scale.comp b/projects/fire_works/shaders/scale.comp new file mode 100644 index 0000000000000000000000000000000000000000..44cfb8e48ad078f5ee8f63785b513a20f27ef542 --- /dev/null +++ b/projects/fire_works/shaders/scale.comp @@ -0,0 +1,40 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "smoke.inc" + +layout(set=0, binding=0, std430) buffer smokeBuffer { + smoke_t smokes []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= smokes.length()) { + return; + } + + vec3 position = smokes[id].position; + float size = smokes[id].size; + vec3 velocity = smokes[id].velocity; + + const float scaling = smokes[id].scaling; + const float fading = 1.0f / (1.0f + friction); + + position = position + velocity * dt; + velocity = velocity * fading + vec3(0.0f, 0.2f, 0.0f) * dt; //smoke is lighter than air right? + vec3(0.0f, -g, 0.0f) * dt; + size = size + scaling * dt; + + smokes[id].position = position; + smokes[id].size = size; + smokes[id].velocity = velocity; +} diff --git a/projects/fire_works/shaders/smoke.frag b/projects/fire_works/shaders/smoke.frag new file mode 100644 index 0000000000000000000000000000000000000000..8ded98ac5fbae554959a83df5bbf7451d286e832 --- /dev/null +++ b/projects/fire_works/shaders/smoke.frag @@ -0,0 +1,57 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "physics.inc" +#include "smoke.inc" + +layout(location = 0) in vec3 passPos; +layout(location = 1) in vec3 passDir; +layout(location = 2) in vec3 passColor; +layout(location = 3) in float passDensity; +layout(location = 4) in flat int passSmokeIndex; + +layout(location = 0) out vec4 outColor; + +layout(set=1, binding=0, std430) readonly buffer randomBuffer { + float randomData []; +}; + +#define NUM_SMOKE_SAMPLES 16 + +void main() { + if (passDensity <= mediumDensity) { + discard; + } + + vec3 start = passPos; + vec3 end = start + normalize(passDir) * 3.5f; + + vec4 result = vec4(0); + + for (uint i = 0; i < NUM_SMOKE_SAMPLES; i++) { + vec3 position = ( + end + (start - end) * i / (NUM_SMOKE_SAMPLES - 1) + ); + + vec4 data = vec4(passColor, passDensity); + + float fallOff = max(1.0f - length(position), 0.0f); + + const uint randomIndex = (passSmokeIndex * NUM_SMOKE_SAMPLES + i) % randomData.length(); + const float alpha = (1.0f + randomData[randomIndex] * 0.1f) * data.a * fallOff; + + result = smokeBlend(result, vec4(data.rgb, alpha)); + } + + result.r = clamp(result.r, 0, 1); + result.g = clamp(result.g, 0, 1); + result.b = clamp(result.b, 0, 1); + result.a = clamp(result.a, 0, 1); + + if (result.a < 1.0f) { + outColor = result; + } else { + discard; + } +} \ No newline at end of file diff --git a/projects/fire_works/shaders/smoke.inc b/projects/fire_works/shaders/smoke.inc new file mode 100644 index 0000000000000000000000000000000000000000..8886a788273cd5fa5c871749e0c9012f2722ed41 --- /dev/null +++ b/projects/fire_works/shaders/smoke.inc @@ -0,0 +1,31 @@ +#ifndef SMOKE_INC +#define SMOKE_INC + +struct smoke_t { + vec3 position; + float size; + vec3 velocity; + float scaling; + vec3 color; + uint eventID; +}; + +float smokeDensity(float size) { + if (size > 0.0f) { + return 0.025f / size; + } else { + return 0.0f; + } +} + +vec4 smokeBlend(vec4 dst, vec4 src) { + const float f = max(1.0f - dst.a, 0.0f); + const float a = clamp(0.0f, 1.0f, src.a); + + return vec4( + dst.rgb + src.rgb * a * f, + dst.a + a * f + ); +} + +#endif // SMOKE_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/smoke.vert b/projects/fire_works/shaders/smoke.vert new file mode 100644 index 0000000000000000000000000000000000000000..634117f79789797cd68e00cac3dbd59fb6515f79 --- /dev/null +++ b/projects/fire_works/shaders/smoke.vert @@ -0,0 +1,38 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "smoke.inc" + +layout(set=0, binding=0, std430) readonly buffer smokeBuffer { + smoke_t smokes []; +}; + +layout(location = 0) in vec3 vertexPos; + +layout(location = 0) out vec3 passPos; +layout(location = 1) out vec3 passDir; +layout(location = 2) out vec3 passColor; +layout(location = 3) out float passDensity; +layout(location = 4) out flat int passSmokeIndex; + +layout( push_constant ) uniform constants{ + mat4 mvp; + vec3 camera; +}; + +void main() { + vec3 position = smokes[gl_InstanceIndex].position; + float size = smokes[gl_InstanceIndex].size; + vec3 color = smokes[gl_InstanceIndex].color; + + vec3 pos = position + vertexPos * size; + + passPos = vertexPos; + passDir = pos - camera; + passColor = color; + passDensity = smokeDensity(size); + passSmokeIndex = gl_InstanceIndex; + + // transform position into projected view space + gl_Position = mvp * vec4(pos, 1); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/tonemapping.comp b/projects/fire_works/shaders/tonemapping.comp new file mode 100644 index 0000000000000000000000000000000000000000..5e6cc8412a77939888fc8e961ea5e9ef29534a81 --- /dev/null +++ b/projects/fire_works/shaders/tonemapping.comp @@ -0,0 +1,21 @@ +#version 440 + +layout(set=0, binding=0, rgba16f) readonly uniform image2D inImage; +layout(set=0, binding=1, rgba8) writeonly uniform image2D outImage; + + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main() { + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ + return; + } + + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + + vec3 tonemapped = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 1); // reinhard tonemapping + vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); + + imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/trail.comp b/projects/fire_works/shaders/trail.comp new file mode 100644 index 0000000000000000000000000000000000000000..8e811a5251948e979fcacc0d2c1e95e27411fa87 --- /dev/null +++ b/projects/fire_works/shaders/trail.comp @@ -0,0 +1,98 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +#include "trail.inc" + +layout(set=1, binding=0, std430) coherent buffer trailBuffer { + trail_t trails []; +}; + +#include "point.inc" + +layout(set=1, binding=1, std430) buffer pointBuffer { + point_t points []; +}; + +layout( push_constant ) uniform constants{ + float t; + float dt; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= trails.length()) { + return; + } + + const uint particleIndex = trails[id].particleIndex; + const uint startIndex = trails[id].startIndex; + const uint endIndex = trails[id].endIndex; + + uint useCount = trails[id].useCount; + float lifetime = trails[id].lifetime; + + if (lifetime > dt) { + lifetime -= dt; + } else { + lifetime = 0.0f; + } + + const uint available = (endIndex - startIndex) % points.length(); + + float trailLife = dt * available; + float fading = 1.0f / (1.0f + friction); + + if (lifetime <= trailLife) { + fading *= (lifetime / trailLife); + + if (useCount > 0) { + useCount--; + } + } else + if (available > useCount) { + useCount++; + } + + for (uint i = useCount; i > 1; i--) { + const uint x = (startIndex + (i - 1)) % points.length(); + const uint y = (startIndex + (i - 2)) % points.length(); + + vec3 position = points[y].position; + float size = points[y].size; + vec3 velocity = points[y].velocity; + + const float scaling = points[y].scaling; + + size = size * fading + scaling * dt; + + points[x].position = position; + points[x].size = size; + points[x].velocity = velocity; + points[x].scaling = scaling; + } + + vec3 position = particles[particleIndex].position; + float size = particles[particleIndex].size; + vec3 velocity = particles[particleIndex].velocity; + + const float trailFactor = mediumDensity / friction; + + points[startIndex].position = position * fading; + points[startIndex].size = trailWidth * size * fading; + points[startIndex].velocity = velocity * fading; + points[startIndex].scaling = trailFactor * length(velocity) * fading; + + trails[id].useCount = useCount; + trails[id].lifetime = lifetime; +} diff --git a/projects/fire_works/shaders/trail.geom b/projects/fire_works/shaders/trail.geom new file mode 100644 index 0000000000000000000000000000000000000000..027943473d05cddc9db6019c7ee36771fcb91d2e --- /dev/null +++ b/projects/fire_works/shaders/trail.geom @@ -0,0 +1,96 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#define INSTANCE_LEN (16) + +layout(points) in; +layout (triangle_strip, max_vertices = (INSTANCE_LEN * 2)) out; +layout(invocations = 8) in; + +#include "physics.inc" +#include "point.inc" + +layout(set=0, binding=1, std430) readonly buffer pointBuffer { + point_t points []; +}; + +layout(location = 0) in vec3 geomColor [1]; +layout(location = 1) in uint geomTrailIndex [1]; +layout(location = 2) in vec3 geomTrailColor [1]; +layout(location = 3) in uint geomStartIndex [1]; +layout(location = 4) in uint geomUseCount [1]; + +layout(location = 0) out vec3 passPos; +layout(location = 1) out vec3 passDir; +layout(location = 2) out vec3 passColor; +layout(location = 3) out float passDensity; +layout(location = 4) out flat int passSmokeIndex; + +layout( push_constant ) uniform constants{ + mat4 mvp; + vec3 camera; +}; + +void main() { + const vec3 color = geomColor[0]; + const uint id = geomTrailIndex[0]; + + const vec3 trailColor = geomTrailColor[0]; + + const uint startIndex = geomStartIndex[0]; + const uint useCount = geomUseCount[0]; + + const uint indexOffset = (gl_InvocationID * (INSTANCE_LEN - 1)); + const uint instanceIndex = startIndex + indexOffset; + + uint count = min(INSTANCE_LEN, useCount); + + if ((indexOffset >= useCount) && (indexOffset + INSTANCE_LEN > useCount)) { + count = indexOffset - useCount; + } + + if (count <= 1) { + return; + } + + const float trailFactor = mediumDensity / friction; + + for (uint i = 0; i < count; i++) { + const float u = float(indexOffset + i + 1) / float(useCount); + + const uint index = (instanceIndex + i) % points.length(); + + const vec3 position = points[index].position; + const float size = points[index].size; + const vec3 velocity = points[index].velocity; + + const vec3 dir = normalize(cross(abs(velocity), position - camera)); + + vec3 offset = dir * size; + float density = trailFactor * (1.0f - u * u) / size; + + const vec3 p0 = position - offset; + const vec3 p1 = position + offset; + + passPos = vec3(u, -1.0f, -1.0f); + passDir = vec3(-0.1f * u, +0.2f, 2.0f); + passColor = mix(color, trailColor, u); + passDensity = density; + passSmokeIndex = int(id); + + gl_Position = mvp * vec4(p0, 1); + EmitVertex(); + + passPos = vec3(u, +1.0f, -1.0f); + passDir = vec3(-0.1f * u, -0.2f, 2.0f); + passColor = mix(color, trailColor, u); + passDensity = density; + passSmokeIndex = int(id); + + gl_Position = mvp * vec4(p1, 1); + EmitVertex(); + } + + EndPrimitive(); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/trail.inc b/projects/fire_works/shaders/trail.inc new file mode 100644 index 0000000000000000000000000000000000000000..75cc49ad4038f28f27890e088fe4dd4e511d1f6b --- /dev/null +++ b/projects/fire_works/shaders/trail.inc @@ -0,0 +1,13 @@ +#ifndef TRAIL_INC +#define TRAIL_INC + +struct trail_t { + uint particleIndex; + uint startIndex; + uint endIndex; + uint useCount; + vec3 color; + float lifetime; +}; + +#endif // TRAIL_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/trail.vert b/projects/fire_works/shaders/trail.vert new file mode 100644 index 0000000000000000000000000000000000000000..871beb9544c33ec790d079a050b4780efbec363f --- /dev/null +++ b/projects/fire_works/shaders/trail.vert @@ -0,0 +1,40 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "trail.inc" + +layout(set=0, binding=0, std430) readonly buffer trailBuffer { + trail_t trails []; +}; + +#include "particle.inc" + +layout(set=2, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +layout(location = 0) out vec3 geomColor; +layout(location = 1) out uint geomTrailIndex; +layout(location = 2) out vec3 geomTrailColor; +layout(location = 3) out uint geomStartIndex; +layout(location = 4) out uint geomUseCount; + +void main() { + const uint particleIndex = trails[gl_InstanceIndex].particleIndex; + const float lifetime = trails[gl_InstanceIndex].lifetime; + + geomColor = particles[particleIndex].color; + geomTrailIndex = gl_InstanceIndex; + geomTrailColor = trails[gl_InstanceIndex].color; + geomStartIndex = trails[gl_InstanceIndex].startIndex; + + const uint useCount = trails[gl_InstanceIndex].useCount; + + if (lifetime > 0.0f) { + geomUseCount = useCount; + } else { + geomUseCount = 0; + } + + gl_Position = vec4(gl_InstanceIndex, lifetime, useCount, 0.0f); +} \ No newline at end of file diff --git a/projects/fire_works/shaders/voxel.comp b/projects/fire_works/shaders/voxel.comp new file mode 100644 index 0000000000000000000000000000000000000000..459c06f381c5c7c9cc26074cd22ecf2869e30350 --- /dev/null +++ b/projects/fire_works/shaders/voxel.comp @@ -0,0 +1,36 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#include "physics.inc" +#include "voxel.inc" + +layout(set=0, binding=0, r32ui) restrict readonly uniform uimage3D voxelRed; +layout(set=0, binding=1, r32ui) restrict readonly uniform uimage3D voxelGreen; +layout(set=0, binding=2, r32ui) restrict readonly uniform uimage3D voxelBlue; +layout(set=0, binding=3, r32ui) restrict readonly uniform uimage3D voxelDensity; + +layout(set=1, binding=0, rgba16) restrict writeonly uniform image3D voxelImage; + +void main() { + ivec3 pos = ivec3(gl_GlobalInvocationID); + ivec3 size = imageSize(voxelImage); + + if (any(greaterThanEqual(pos, size))) { + return; + } + + const float red = voxel_read(voxelRed, pos); + const float green = voxel_read(voxelGreen, pos); + const float blue = voxel_read(voxelBlue, pos); + const float density = voxel_read(voxelDensity, pos); + + imageStore(voxelImage, pos, vec4( + red, + green, + blue, + density + )); +} diff --git a/projects/fire_works/shaders/voxel.inc b/projects/fire_works/shaders/voxel.inc new file mode 100644 index 0000000000000000000000000000000000000000..9e2c895e4a0c688262de7404039aefe6650191bd --- /dev/null +++ b/projects/fire_works/shaders/voxel.inc @@ -0,0 +1,18 @@ +#ifndef VOXEL_INC +#define VOXEL_INC + +#define VOXEL_NORM_VALUE 0xFF + +#define voxel_add(img, pos, value) imageAtomicAdd(img, ivec3((imageSize(img) - ivec3(1)) * pos), uint(VOXEL_NORM_VALUE * value)) + +#define voxel_write(img, pos, value) imageStore(img, pos, uvec4(VOXEL_NORM_VALUE * value)); +#define voxel_read(img, pos) imageLoad(img, pos).r / float(VOXEL_NORM_VALUE); + +// https://stackoverflow.com/questions/51108596/linearize-depth +float linearize_depth(float d,float zNear,float zFar) { + return zNear * zFar / (zFar + d * (zNear - zFar)); +} + +#define voxel_pos(pos) vec3((pos.xy + vec2(1.0f)) * 0.5f, linearize_depth(pos.y, 0.1f, 50.0f)) + +#endif // VOXEL_INC \ No newline at end of file diff --git a/projects/fire_works/shaders/voxel_particle.comp b/projects/fire_works/shaders/voxel_particle.comp new file mode 100644 index 0000000000000000000000000000000000000000..12b68eed09746f38286a5b1f1b4e9e56d4bfe105 --- /dev/null +++ b/projects/fire_works/shaders/voxel_particle.comp @@ -0,0 +1,59 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + particle_t particles []; +}; + +#include "voxel.inc" + +layout(set=1, binding=0, r32ui) uniform uimage3D voxelRed; +layout(set=1, binding=1, r32ui) uniform uimage3D voxelGreen; +layout(set=1, binding=2, r32ui) uniform uimage3D voxelBlue; +layout(set=1, binding=3, r32ui) uniform uimage3D voxelDensity; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= particles.length()) { + return; + } + + vec3 position = particles[id].position; + float lifetime = particles[id].lifetime; + + if (lifetime <= 0.0f) { + return; + } + + vec4 cs_pos = mvp * vec4(position, 1); + + if (abs(cs_pos.w) <= 0.0f) { + return; + } + + vec3 ndc_pos = cs_pos.xyz / cs_pos.w; + vec3 pos = voxel_pos(ndc_pos); + + if ((any(greaterThanEqual(pos, vec3(1.5f)))) || (any(lessThanEqual(pos, vec3(-0.5f))))) { + return; + } + + float size = particles[id].size; + vec3 color = particles[id].color; + + voxel_add(voxelRed, pos, color.r); + voxel_add(voxelGreen, pos, color.g); + voxel_add(voxelBlue, pos, color.b); + voxel_add(voxelDensity, pos, 1.0f); +} diff --git a/projects/fire_works/shaders/voxel_smoke.comp b/projects/fire_works/shaders/voxel_smoke.comp new file mode 100644 index 0000000000000000000000000000000000000000..de216ab29a6671e1547600d072edaf510b775154 --- /dev/null +++ b/projects/fire_works/shaders/voxel_smoke.comp @@ -0,0 +1,72 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" +#include "smoke.inc" + +layout(set=0, binding=0, std430) readonly buffer smokeBuffer { + smoke_t smokes []; +}; + +#include "voxel.inc" + +layout(set=1, binding=0, r32ui) uniform uimage3D voxelRed; +layout(set=1, binding=1, r32ui) uniform uimage3D voxelGreen; +layout(set=1, binding=2, r32ui) uniform uimage3D voxelBlue; +layout(set=1, binding=3, r32ui) uniform uimage3D voxelDensity; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +#define NUM_SMOKE_SAMPLES 4 + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= smokes.length()) { + return; + } + + vec3 position = smokes[id].position; + float size = smokes[id].size; + + const float density = smokeDensity(size); + + if (density <= mediumDensity) { + return; + } + + vec3 offset = vec3(-size); + + for (;offset.x <= size; offset.x += size / NUM_SMOKE_SAMPLES) { + for (;offset.y <= size; offset.y += size / NUM_SMOKE_SAMPLES) { + for (;offset.z <= size; offset.z += size / NUM_SMOKE_SAMPLES) { + vec4 cs_pos = mvp * vec4(position + offset, 1); + + if (abs(cs_pos.w) <= 0.0f) { + return; + } + + vec3 ndc_pos = cs_pos.xyz / cs_pos.w; + vec3 pos = voxel_pos(ndc_pos); + + if ((any(greaterThanEqual(pos, vec3(1.5f)))) || (any(lessThanEqual(pos, vec3(-0.5f))))) { + return; + } + + vec3 color = smokes[id].color; + + float local_density = density * max(1.0f - length(offset / size), 0.0f); + + voxel_add(voxelRed, pos, color.r); + voxel_add(voxelGreen, pos, color.g); + voxel_add(voxelBlue, pos, color.b); + voxel_add(voxelDensity, pos, local_density); + } + } + } +} diff --git a/projects/fire_works/shaders/voxel_trail.comp b/projects/fire_works/shaders/voxel_trail.comp new file mode 100644 index 0000000000000000000000000000000000000000..56971a1719075e0dd9a6a5bb6b52e89c11f8489c --- /dev/null +++ b/projects/fire_works/shaders/voxel_trail.comp @@ -0,0 +1,87 @@ +#version 450 core +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +#include "physics.inc" + +#include "trail.inc" + +layout(set=0, binding=0, std430) coherent buffer trailBuffer { + trail_t trails []; +}; + +#include "point.inc" + +layout(set=0, binding=1, std430) buffer pointBuffer { + point_t points []; +}; + +#include "voxel.inc" + +layout(set=1, binding=0, r32ui) uniform uimage3D voxelRed; +layout(set=1, binding=1, r32ui) uniform uimage3D voxelGreen; +layout(set=1, binding=2, r32ui) uniform uimage3D voxelBlue; +layout(set=1, binding=3, r32ui) uniform uimage3D voxelDensity; + +#include "smoke.inc" + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= trails.length()) { + return; + } + + const uint particleIndex = trails[id].particleIndex; + const uint startIndex = trails[id].startIndex; + + uint useCount = trails[id].useCount; + + if (useCount <= 0) { + return; + } + + vec3 color = trails[id].color; + float lifetime = trails[id].lifetime; + + if (lifetime <= 0.0f) { + return; + } + + for (uint i = 0; i < useCount; i++) { + const uint x = (startIndex + i) % points.length(); + + vec3 position = points[x].position; + float size = points[x].size; + + const float density = smokeDensity(size); + + if (density <= mediumDensity) { + break; + } + + vec4 cs_pos = mvp * vec4(position, 1); + + if (abs(cs_pos.w) <= 0.0f) { + return; + } + + vec3 ndc_pos = cs_pos.xyz / cs_pos.w; + vec3 pos = voxel_pos(ndc_pos); + + if ((any(greaterThanEqual(pos, vec3(1.5f)))) || (any(lessThanEqual(pos, vec3(-0.5f))))) { + continue; + } + + voxel_add(voxelRed, pos, color.r); + voxel_add(voxelGreen, pos, color.g); + voxel_add(voxelBlue, pos, color.b); + voxel_add(voxelDensity, pos, density); + } +} diff --git a/projects/fire_works/src/main.cpp b/projects/fire_works/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51e29ed5b13182a20e5a069247222a48fe0c90ef --- /dev/null +++ b/projects/fire_works/src/main.cpp @@ -0,0 +1,1386 @@ + +#include <array> + +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Sampler.hpp> + +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/effects/BloomAndFlaresEffect.hpp> + +struct particle_t { + glm::vec3 position; + float lifetime; + glm::vec3 velocity; + float size; + glm::vec3 color; + float mass; + glm::vec3 pad0; + uint32_t eventId; +}; + +struct event_t { + glm::vec3 direction; + float startTime; + glm::vec3 color; + float velocity; + + uint32_t count; + uint32_t index; + uint32_t parent; + uint32_t continuous; + + float lifetime; + float mass; + float size; + uint32_t contCount; +}; + +struct smoke_t { + glm::vec3 position; + float size; + glm::vec3 velocity; + float scaling; + glm::vec3 color; + float eventID; +}; + +struct trail_t { + uint32_t particleIndex; + uint32_t startIndex; + uint32_t endIndex; + uint32_t useCount; + glm::vec3 color; + float lifetime; +}; + +struct point_t { + glm::vec3 position; + float size; + glm::vec3 velocity; + float scaling; +}; + +struct draw_particles_t { + glm::mat4 mvp; + uint32_t width; + uint32_t height; +}; + +struct draw_smoke_t { + glm::mat4 mvp; + glm::vec3 camera; +}; + +#define PARTICLE_COUNT (1024) +#define SMOKE_COUNT (512) +#define TRAIL_COUNT (2048) +#define RANDOM_DATA_LENGTH (4096) +#define POINT_COUNT (2048 * 256) + +void InitializeParticles(std::vector<particle_t> &particles) { + for (size_t i = 0; i < particles.size(); i++) { + particle_t particle; + particle.position = glm::vec3(2.0f * (std::rand() % RAND_MAX) / RAND_MAX - 1.0f, + 2.0f * (std::rand() % RAND_MAX) / RAND_MAX - 1.0f, + 2.0f * (std::rand() % RAND_MAX) / RAND_MAX - 1.0f); + + particle.lifetime = 0.0f; + particle.velocity = glm::vec3(0.0f); + particle.size = 0.01f; + particle.color = glm::vec3(1.0f, 0.0f, 0.0f); + + particles [i] = particle; + } +} + +void InitializeFireworkEvents(std::vector<event_t>& events) { + events.push_back({ + glm::vec3(0, 1, 0), 0.5f, glm::vec3(0.0f, 1.0f, 0.0f), 12.5f, + 1, 0, UINT_MAX, 0, + 1.0f, 1.0f, 0.5f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 1.5f, glm::vec3(0.0f, 1.0f, 1.0f), 10.0f, + 100, 0, 0, 0, + 10.0f, 1.0f, 0.0f, 0 + }); + + events.push_back({ + glm::vec3(0.5, 1, 0), 0.25f, glm::vec3(0.0f, 1.5f, 0.0f), 15.0f, + 1, 0, UINT_MAX, 0, + 0.5f, 1.0f, 0.5f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 0.75f, glm::vec3(0.0f, 1.5f, 1.0f), 8.0f, + 150, 0, 2, 0, + 10.0f, 1.0f, 0.0f, 0 + }); + + events.push_back({ + glm::vec3(-2.5, 3, 0.5), 1.0f, glm::vec3(246.0f, 189.0f, 255.0f), 12.5f, + 1, 0, UINT_MAX, 0, + 1.0f, 1.0f, 0.5f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 2.0f, glm::vec3(235.0f, 137.0f, 250.0f), 8.0f, + 75, 0, 4, 0, + 10.0f, 1.0f, 0.0f, 0 + }); +} + +void InitializeSparklerEvents(std::vector<event_t> &events) { + events.push_back({ + glm::vec3(0, 1, 0), 0.0f, glm::vec3(251.0f, 255.0f, 145.0f), 1.0f, + 1, 0, UINT_MAX, 0, + 8.0f, 0.0f, 0.5f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 0.0f, glm::vec3(251.0f, 255.0f, 145.0f), 10.0f, + 1000, 1, 0, 10, + 0.5f, -1.0f, 0.0f, 100 + }); +} + +void InitializeNestedFireworkEvents(std::vector<event_t>& events) { + events.push_back({ + glm::vec3(0, 2, 0), 0.0f, glm::vec3(0.0f, 1.0f, 0.0f), 12.5f, + 1, 0, UINT_MAX, 0, + 1.0f, 1.0f, 0.5f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 0.9f, glm::vec3(0.0f, 1.0f, 1.0f), 7.0f, + 100, 0, 0, 0, + 10.1f, 1.0f, 0.0f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 2.0f, glm::vec3(0.0f, 0.0f, 0.0f), 10.0f, + 100, 0, 1, 0, + 10.0f, 1.0f, 0.0f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 1.0f, glm::vec3(42.0f,0.0f, 1.0f), 12.5f, + 100, 0, 1, 0, + 1.0f, 1.0f, 0.5f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 1.5f, glm::vec3(42.0f, 0.0f, 1.0f), 10.0f, + 100, 0, 3, 0, + 10.0f, 1.0f, 0.0f, 0 + }); + + events.push_back({ + glm::vec3(0.0f), 2.0f, glm::vec3(42.0f, 0.0f, 1.0f), 10.0f, + 100, 0, 4, 0, + 10.0f, 1.0f, 0.0f, 0 + }); +} + +void ChangeColor(std::vector<event_t>& events, glm::vec3 color) { + for (size_t i = 0; i < events.size(); i++) { + events [i].color = color; + } +} + +int main(int argc, const char **argv) { + vkcv::Features features; + + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + features.requireFeature([](vk::PhysicalDeviceFeatures& features) { + features.setGeometryShader(true); + }); + + vkcv::Core core = vkcv::Core::create( + "Firework", + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + features + ); + + vkcv::WindowHandle windowHandle = core.createWindow("Firework", 800, 600, true); + vkcv::Window& window = core.getWindow (windowHandle); + vkcv::camera::CameraManager cameraManager (window); + + auto trackballHandle = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + auto pilotHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(trackballHandle).setCenter(glm::vec3(0.0f, 0.0f, 0.0f)); // set camera to look at the center of the particle volume + cameraManager.getCamera(trackballHandle).setNearFar(0.1f, 50.0f); + cameraManager.getCamera(trackballHandle).setPosition(glm::vec3(0, 0, 25)); + + cameraManager.getCamera(pilotHandle).setNearFar(0.1f, 50.0f); + cameraManager.getCamera(pilotHandle).setPosition(glm::vec3(0, 0, 25)); + + cameraManager.setActiveCamera(pilotHandle); + + vkcv::gui::GUI gui (core, windowHandle); + vkcv::shader::GLSLCompiler compiler; + + vkcv::DescriptorBindings descriptorBindings0; + vkcv::DescriptorBinding binding0 { + 0, + vkcv::DescriptorType::STORAGE_BUFFER, + 1, + vkcv::ShaderStage::VERTEX | vkcv::ShaderStage::COMPUTE, + false, + false + }; + vkcv::DescriptorBinding binding1 { + 1, + vkcv::DescriptorType::STORAGE_BUFFER, + + 1, + vkcv::ShaderStage::COMPUTE, + false, + false + }; + + descriptorBindings0.insert(std::make_pair(0, binding0)); + descriptorBindings0.insert(std::make_pair(1, binding1)); + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings0); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::ShaderProgram generationShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/generation.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + generationShader.addShader(shaderStage, path); + }); + + auto generationBindings = generationShader.getReflectedDescriptors().at(1); + generationBindings[0].shaderStages |= vkcv::ShaderStage::FRAGMENT; + + vkcv::DescriptorSetLayoutHandle generationDescriptorLayout = core.createDescriptorSetLayout( + generationBindings + ); + + vkcv::DescriptorSetHandle generationDescriptorSet = core.createDescriptorSet(generationDescriptorLayout); + + vkcv::DescriptorBindings descriptorBindings1; + + descriptorBindings1.insert(std::make_pair(0, binding0)); + descriptorBindings1.insert(std::make_pair(1, binding1)); + + vkcv::DescriptorSetLayoutHandle smokeDescriptorLayout = core.createDescriptorSetLayout(descriptorBindings1); + vkcv::DescriptorSetHandle smokeDescriptorSet = core.createDescriptorSet(smokeDescriptorLayout); + + vkcv::DescriptorBindings descriptorBindings2; + vkcv::DescriptorBinding binding2 { + 1, + vkcv::DescriptorType::STORAGE_BUFFER, + 1, + vkcv::ShaderStage::GEOMETRY | vkcv::ShaderStage::COMPUTE, + false, + false + }; + + descriptorBindings2.insert(std::make_pair(0, binding0)); + descriptorBindings2.insert(std::make_pair(1, binding2)); + + vkcv::DescriptorSetLayoutHandle trailDescriptorLayout = core.createDescriptorSetLayout( + descriptorBindings2 + ); + + vkcv::DescriptorSetHandle trailDescriptorSet = core.createDescriptorSet(trailDescriptorLayout); + + vkcv::ComputePipelineHandle generationPipeline = core.createComputePipeline({ + generationShader, + { + descriptorSetLayout, + generationDescriptorLayout, + smokeDescriptorLayout, + trailDescriptorLayout + } + }); + + vkcv::ShaderProgram trailShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/trail.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + trailShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle trailComputePipeline = core.createComputePipeline({ + trailShader, + { descriptorSetLayout, trailDescriptorLayout } + }); + + vkcv::ShaderProgram scaleShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/scale.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + scaleShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle scalePipeline = core.createComputePipeline({ + scaleShader, + { smokeDescriptorLayout } + }); + + auto swapchainHandle = core.getWindow(windowHandle).getSwapchain(); + auto swapchainExtent = core.getSwapchainExtent(swapchainHandle); + + const vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + + std::array<vkcv::ImageHandle, 4> colorBuffers; + for (size_t i = 0; i < colorBuffers.size(); i++) { + vkcv::ImageConfig colorBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + colorBufferConfig.setSupportingStorage(true); + colorBufferConfig.setSupportingColorAttachment(true); + + colorBuffers[i] = core.createImage( + colorFormat, + colorBufferConfig + ); + } + + vkcv::ShaderProgram particleShaderProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/particle.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "shaders/particle.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + + vkcv::ShaderProgram trailShaderProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/trail.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + trailShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::GEOMETRY, "shaders/trail.geom", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + trailShaderProgram.addShader(shaderStage, path); + }); + + vkcv::ShaderProgram smokeShaderProgram; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/smoke.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + smokeShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "shaders/smoke.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + smokeShaderProgram.addShader(shaderStage, path); + trailShaderProgram.addShader(shaderStage, path); + }); + + std::vector<particle_t> particles; + particles.resize(PARTICLE_COUNT); + InitializeParticles(particles); + + auto particleBuffer = vkcv::buffer<particle_t>( + core, + vkcv::BufferType::STORAGE, + particles.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL, + true + ); + + particleBuffer.fill(particles); + + auto particleBufferCopy = vkcv::buffer<particle_t>( + core, + vkcv::BufferType::STORAGE, + particles.size() + ); + + particleBufferCopy.fill(particles); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particleBuffer.getHandle()); + writes.writeStorageBuffer(1, particleBufferCopy.getHandle()); + core.writeDescriptorSet(descriptorSet, writes); + } + + std::vector<float> randomData; + randomData.reserve(RANDOM_DATA_LENGTH); + + for (size_t i = 0; i < RANDOM_DATA_LENGTH; i++) { + randomData.push_back( + 2.0f * static_cast<float>(std::rand() % RAND_MAX) / static_cast<float>(RAND_MAX) - 1.0f + ); + } + + auto randomBuffer = vkcv::buffer<float>( + core, + vkcv::BufferType::STORAGE, + randomData.size() + ); + + randomBuffer.fill(randomData); + + std::vector<event_t> events; + InitializeFireworkEvents(events); + + auto eventBuffer = vkcv::buffer<event_t>( + core, + vkcv::BufferType::STORAGE, + events.size() + ); + + eventBuffer.fill(events); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, randomBuffer.getHandle()); + writes.writeStorageBuffer(1, eventBuffer.getHandle()); + core.writeDescriptorSet(generationDescriptorSet, writes); + } + + auto startIndexBuffer = vkcv::buffer<uint32_t>( + core, + vkcv::BufferType::STORAGE, + eventBuffer.getCount() + ); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(2, startIndexBuffer.getHandle()); + core.writeDescriptorSet(generationDescriptorSet, writes); + } + + std::vector<smoke_t> smokes; + smokes.reserve(SMOKE_COUNT); + + for (size_t i = 0; i < SMOKE_COUNT; i++) { + smoke_t smoke; + smoke.position = glm::vec3(0.0f); + smoke.size = 0.0f; + + smoke.velocity = glm::vec3(0.0f); + smoke.scaling = 0.0f; + + smoke.color = glm::vec3(0.0f); + + smokes.push_back(smoke); + } + + auto smokeBuffer = vkcv::buffer<smoke_t>( + core, + vkcv::BufferType::STORAGE, + smokes.size() + ); + + smokeBuffer.fill(smokes); + + auto smokeIndexBuffer = vkcv::buffer<uint32_t>( + core, + vkcv::BufferType::STORAGE, + 3, + vkcv::BufferMemoryType::HOST_VISIBLE + ); + + uint32_t* smokeIndices = smokeIndexBuffer.map(); + memset(smokeIndices, 0, smokeIndexBuffer.getSize()); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, smokeBuffer.getHandle()); + writes.writeStorageBuffer(1, smokeIndexBuffer.getHandle()); + core.writeDescriptorSet(smokeDescriptorSet, writes); + } + + std::vector<trail_t> trails; + trails.reserve(TRAIL_COUNT); + + for (size_t i = 0; i < TRAIL_COUNT; i++) { + trail_t trail; + + trail.particleIndex = 0; + trail.startIndex = 0; + trail.endIndex = 0; + trail.useCount = 0; + trail.color = glm::vec3(0.0f); + trail.lifetime = 0.0f; + + trails.push_back(trail); + } + + auto trailBuffer = vkcv::buffer<trail_t>( + core, + vkcv::BufferType::STORAGE, + trails.size() + ); + + trailBuffer.fill(trails); + + std::vector<point_t> points; + points.reserve(POINT_COUNT); + + for (size_t i = 0; i < POINT_COUNT; i++) { + point_t point; + + point.position = glm::vec3(0.0f); + point.size = 0.0f; + point.velocity = glm::vec3(0.0f); + point.scaling = 0.0f; + + points.push_back(point); + } + + auto pointBuffer = vkcv::buffer<point_t>( + core, + vkcv::BufferType::STORAGE, + points.size() + ); + + pointBuffer.fill(points); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, trailBuffer.getHandle()); + writes.writeStorageBuffer(1, pointBuffer.getHandle()); + core.writeDescriptorSet(trailDescriptorSet, writes); + } + + auto cubePositions = vkcv::buffer<glm::vec3>( + core, + vkcv::BufferType::VERTEX, + 8 + ); + + cubePositions.fill({ + glm::vec3(-1.0f, -1.0f, -1.0f), + glm::vec3(+1.0f, -1.0f, -1.0f), + glm::vec3(-1.0f, +1.0f, -1.0f), + glm::vec3(+1.0f, +1.0f, -1.0f), + glm::vec3(-1.0f, -1.0f, +1.0f), + glm::vec3(+1.0f, -1.0f, +1.0f), + glm::vec3(-1.0f, +1.0f, +1.0f), + glm::vec3(+1.0f, +1.0f, +1.0f) + }); + + auto cubeIndices = vkcv::buffer<uint16_t>( + core, + vkcv::BufferType::INDEX, + 36 + ); + + cubeIndices.fill({ + 0, 2, 3, + 0, 3, 1, + 1, 3, 7, + 1, 7, 5, + + 5, 7, 6, + 5, 6, 4, + 4, 6, 2, + 4, 2, 0, + + 2, 6, 7, + 2, 7, 3, + 1, 5, 4, + 1, 4, 0 + }); + + vkcv::VertexData cubeData ({ vkcv::vertexBufferBinding(cubePositions.getHandle()) }); + cubeData.setIndexBuffer(cubeIndices.getHandle()); + cubeData.setCount(cubeIndices.getCount()); + + const std::vector<vkcv::VertexAttachment> vaSmoke = smokeShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vbSmoke; + for (size_t i = 0; i < vaSmoke.size(); i++) { + vbSmoke.push_back(vkcv::createVertexBinding(i, { vaSmoke[i] })); + } + + const vkcv::VertexLayout smokeLayout { vbSmoke }; + + vkcv::PassHandle renderPass = vkcv::passFormat(core, colorFormat); + + vkcv::GraphicsPipelineConfig smokePipelineDefinition ( + smokeShaderProgram, + renderPass, + {smokeLayout}, + {smokeDescriptorLayout, generationDescriptorLayout} + ); + + smokePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive); + + vkcv::GraphicsPipelineHandle smokePipeline = core.createGraphicsPipeline(smokePipelineDefinition); + + const std::vector<vkcv::VertexAttachment> vaTrail = trailShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vbTrail; + for (size_t i = 0; i < vaTrail.size(); i++) { + vbTrail.push_back(vkcv::createVertexBinding(i, { vaTrail[i] })); + } + + const vkcv::VertexLayout trailLayout { vbTrail }; + + vkcv::GraphicsPipelineConfig trailPipelineDefinition ( + trailShaderProgram, + renderPass, + {trailLayout}, + {trailDescriptorLayout, generationDescriptorLayout, descriptorSetLayout} + ); + + trailPipelineDefinition.setPrimitiveTopology(vkcv::PrimitiveTopology::PointList); + trailPipelineDefinition.setBlendMode(vkcv::BlendMode::Additive); + + vkcv::GraphicsPipelineHandle trailPipeline = core.createGraphicsPipeline(trailPipelineDefinition); + + vkcv::InstanceDrawcall drawcallSmoke (cubeData, smokeBuffer.getCount()); + drawcallSmoke.useDescriptorSet(0, smokeDescriptorSet); + drawcallSmoke.useDescriptorSet(1, generationDescriptorSet); + + auto trianglePositions = vkcv::buffer<glm::vec2>( + core, + vkcv::BufferType::VERTEX, + 3 + ); + + trianglePositions.fill({ + glm::vec2(-1.0f, -1.0f), + glm::vec2(+0.0f, +1.5f), + glm::vec2(+1.0f, -1.0f) + }); + + auto triangleIndices = vkcv::buffer<uint16_t>( + core, + vkcv::BufferType::INDEX, + 3 + ); + + triangleIndices.fill({ + 0, 1, 2 + }); + + vkcv::VertexData triangleData ({ vkcv::vertexBufferBinding(trianglePositions.getHandle()) }); + triangleData.setIndexBuffer(triangleIndices.getHandle()); + triangleData.setCount(triangleIndices.getCount()); + + vkcv::VertexData trailData; + triangleData.setIndexBuffer(triangleIndices.getHandle()); + trailData.setCount(1); + + vkcv::InstanceDrawcall drawcallTrail (trailData, trailBuffer.getCount()); + drawcallTrail.useDescriptorSet(0, trailDescriptorSet); + drawcallTrail.useDescriptorSet(1, generationDescriptorSet); + drawcallTrail.useDescriptorSet(2, descriptorSet); + + const std::vector<vkcv::VertexAttachment> vaParticles = particleShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> vbParticles; + for (size_t i = 0; i < vaParticles.size(); i++) { + vbParticles.push_back(vkcv::createVertexBinding(i, { vaParticles[i] })); + } + + const vkcv::VertexLayout particleLayout { vbParticles }; + + vkcv::GraphicsPipelineConfig particlePipelineDefinition ( + particleShaderProgram, + renderPass, + {particleLayout}, + {descriptorSetLayout} + ); + + particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive); + + vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); + + vkcv::InstanceDrawcall drawcallParticle (triangleData, particleBuffer.getCount()); + drawcallParticle.useDescriptorSet(0, descriptorSet); + + vkcv::ShaderProgram motionShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/motion.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + motionShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle motionPipeline = core.createComputePipeline({ + motionShader, + { descriptorSetLayout } + }); + + const uint32_t voxelWidth = 160; + const uint32_t voxelHeight = 90; + const uint32_t voxelDepth = 64; + + std::vector<uint32_t> zeroVoxel; + zeroVoxel.resize(voxelWidth * voxelHeight * voxelDepth, 0); + + vkcv::ImageConfig voxelImageConfig ( + voxelWidth, + voxelHeight, + voxelDepth + ); + + voxelImageConfig.setSupportingStorage(true); + + vkcv::Image voxelRed = vkcv::image( + core, + vk::Format::eR32Uint, + voxelImageConfig + ); + + vkcv::Image voxelGreen = vkcv::image( + core, + vk::Format::eR32Uint, + voxelImageConfig + ); + + vkcv::Image voxelBlue = vkcv::image( + core, + vk::Format::eR32Uint, + voxelImageConfig + ); + + vkcv::Image voxelDensity = vkcv::image( + core, + vk::Format::eR32Uint, + voxelImageConfig + ); + + std::array<vkcv::ImageHandle, 2> voxelData { + core.createImage( + vk::Format::eR16G16B16A16Sfloat, + voxelImageConfig + ), + core.createImage( + vk::Format::eR16G16B16A16Sfloat, + voxelImageConfig + ) + }; + + vkcv::ImageConfig voxelSamplesConfig ( + voxelWidth, + voxelHeight + ); + + voxelSamplesConfig.setSupportingStorage(true); + + vkcv::Image voxelSamples = vkcv::image( + core, + colorFormat, + voxelSamplesConfig + ); + + vkcv::SamplerHandle voxelSampler = vkcv::samplerLinear(core, true); + + vkcv::ShaderProgram voxelClearShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/clear.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelClearShader.addShader(shaderStage, path); + }); + + const auto& voxelBindings = voxelClearShader.getReflectedDescriptors().at(0); + auto voxelDescriptorSetLayout = core.createDescriptorSetLayout(voxelBindings); + + vkcv::ComputePipelineHandle voxelClearPipeline = core.createComputePipeline({ + voxelClearShader, + { voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelParticleShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel_particle.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelParticleShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle voxelParticlePipeline = core.createComputePipeline({ + voxelParticleShader, + { descriptorSetLayout, voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelSmokeShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel_smoke.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelSmokeShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle voxelSmokePipeline = core.createComputePipeline({ + voxelSmokeShader, + { smokeDescriptorLayout, voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelTrailShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel_trail.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelTrailShader.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle voxelTrailPipeline = core.createComputePipeline({ + voxelTrailShader, + { trailDescriptorLayout, voxelDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/voxel.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelShader.addShader(shaderStage, path); + }); + + const auto& voxelOutBindings = voxelShader.getReflectedDescriptors().at(1); + auto voxelOutDescriptorSetLayout = core.createDescriptorSetLayout(voxelOutBindings); + + vkcv::ComputePipelineHandle voxelPipeline = core.createComputePipeline({ + voxelShader, + { voxelDescriptorSetLayout, voxelOutDescriptorSetLayout } + }); + + vkcv::ShaderProgram fluidShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/fluid.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + fluidShader.addShader(shaderStage, path); + }); + + const auto& fluidBindings = fluidShader.getReflectedDescriptors().at(0); + auto fluidDescriptorSetLayout = core.createDescriptorSetLayout(fluidBindings); + + vkcv::ComputePipelineHandle fluidPipeline = core.createComputePipeline({ + fluidShader, + { fluidDescriptorSetLayout } + }); + + vkcv::ShaderProgram voxelSampleShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/sample.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + voxelSampleShader.addShader(shaderStage, path); + }); + + const auto& sampleBindings = voxelSampleShader.getReflectedDescriptors().at(1); + auto samplesDescriptorSetLayout = core.createDescriptorSetLayout(sampleBindings); + + vkcv::ComputePipelineHandle voxelSamplePipeline = core.createComputePipeline({ + voxelSampleShader, + { voxelOutDescriptorSetLayout, samplesDescriptorSetLayout } + }); + + auto voxelDescriptorSet = core.createDescriptorSet(voxelDescriptorSetLayout); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, voxelRed.getHandle()); + writes.writeStorageImage(1, voxelGreen.getHandle()); + writes.writeStorageImage(2, voxelBlue.getHandle()); + writes.writeStorageImage(3, voxelDensity.getHandle()); + core.writeDescriptorSet(voxelDescriptorSet, writes); + } + + auto voxelOutDescriptorSet = core.createDescriptorSet(voxelOutDescriptorSetLayout); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, voxelData[0]); + core.writeDescriptorSet(voxelOutDescriptorSet, writes); + } + + std::array<vkcv::DescriptorSetHandle, 2> fluidDescriptorSet { + core.createDescriptorSet(fluidDescriptorSetLayout), + core.createDescriptorSet(fluidDescriptorSetLayout) + }; + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, voxelData[0]); + writes.writeSampler(1, voxelSampler); + writes.writeStorageImage(2, voxelData[1]); + core.writeDescriptorSet(fluidDescriptorSet[0], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, voxelData[1]); + writes.writeSampler(1, voxelSampler); + writes.writeStorageImage(2, voxelData[0]); + core.writeDescriptorSet(fluidDescriptorSet[1], writes); + } + + auto samplesDescriptorSet = core.createDescriptorSet(samplesDescriptorSetLayout); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, voxelSamples.getHandle()); + core.writeDescriptorSet(samplesDescriptorSet, writes); + } + + vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core); + bloomAndFlares.setUpsamplingLimit(3); + + vkcv::ShaderProgram addShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/add.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + addShader.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle addDescriptorLayout = core.createDescriptorSetLayout(addShader.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle addDescriptor = core.createDescriptorSet(addDescriptorLayout); + + vkcv::ComputePipelineHandle addPipe = core.createComputePipeline({ + addShader, + { addDescriptorLayout, generationDescriptorLayout } + }); + + vkcv::ShaderProgram tonemappingShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + tonemappingShader.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout); + vkcv::ComputePipelineHandle tonemappingPipe = core.createComputePipeline({ + tonemappingShader, + { tonemappingDescriptorLayout } + }); + + vkcv::ImageHandle swapchainImage = vkcv::ImageHandle::createSwapchainImageHandle(); + + auto start = std::chrono::system_clock::now(); + auto current = start; + + while (vkcv::Window::hasOpenWindow()) { + vkcv::Window::pollEvents(); + + uint32_t swapchainWidth, swapchainHeight; + if (!core.beginFrame(swapchainWidth, swapchainHeight, windowHandle)) { + continue; + } + + for (auto& colorBuffer : colorBuffers) { + if ((core.getImageWidth(colorBuffer) != swapchainWidth) || + (core.getImageHeight(colorBuffer) != swapchainHeight)) { + vkcv::ImageConfig colorBufferConfig ( + swapchainWidth, + swapchainHeight + ); + + colorBufferConfig.setSupportingStorage(true); + colorBufferConfig.setSupportingColorAttachment(true); + + colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + } + } + + auto next = std::chrono::system_clock::now(); + + auto time = std::chrono::duration_cast<std::chrono::microseconds>(next - start); + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(next - current); + + current = next; + + float time_values [2]; + time_values[0] = 0.000001f * static_cast<float>(time.count()); + time_values[1] = 0.000001f * static_cast<float>(deltatime.count()); + + std::cout << time_values[0] << " " << time_values[1] << std::endl; + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + auto voxelDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(voxelWidth, voxelHeight, voxelDepth), + vkcv::DispatchSize(4, 4, 4) + ); + + core.recordBeginDebugLabel(cmdStream, "Voxel clear", { 0.5f, 0.25f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelClearPipeline, + voxelDispatchCount, + { vkcv::useDescriptorSet(0, voxelDescriptorSet) }, + vkcv::PushConstants(0) + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, eventBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, smokeIndexBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle()); + + auto particleDispatchCount = vkcv::dispatchInvocations(particleBuffer.getCount(), 256); + + vkcv::PushConstants pushConstantsTime (2 * sizeof(float)); + pushConstantsTime.appendDrawcall(time_values); + + core.recordBeginDebugLabel(cmdStream, "Generation", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + generationPipeline, + particleDispatchCount, + { + vkcv::useDescriptorSet(0, descriptorSet), + vkcv::useDescriptorSet(1, generationDescriptorSet), + vkcv::useDescriptorSet(2, smokeDescriptorSet), + vkcv::useDescriptorSet(3, trailDescriptorSet) + }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle()); + + auto smokeDispatchCount = vkcv::dispatchInvocations(smokeBuffer.getCount(), 256); + + core.recordBeginDebugLabel(cmdStream, "Smoke scaling", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + scalePipeline, + smokeDispatchCount, + { vkcv::useDescriptorSet(0, smokeDescriptorSet) }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + + core.recordBeginDebugLabel(cmdStream, "Particle motion", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + motionPipeline, + particleDispatchCount, + { vkcv::useDescriptorSet(0, descriptorSet) }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle()); + + auto trailDispatchCount = vkcv::dispatchInvocations(trailBuffer.getCount(), 256); + + core.recordBeginDebugLabel(cmdStream, "Trail update", { 0.0f, 0.0f, 1.0f, 1.0f }); + core.recordComputeDispatchToCmdStream( + cmdStream, + trailComputePipeline, + trailDispatchCount, + { + vkcv::useDescriptorSet(0, descriptorSet), + vkcv::useDescriptorSet(1, trailDescriptorSet) + }, + pushConstantsTime + ); + core.recordEndDebugLabel(cmdStream); + + cameraManager.update(time_values[1]); + + const auto& camera = cameraManager.getActiveCamera(); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + + draw_particles_t draw_particles { + camera.getMVP(), + swapchainWidth, + swapchainHeight + }; + + vkcv::PushConstants pushConstantsDraw0 (sizeof(draw_particles_t)); + pushConstantsDraw0.appendDrawcall(draw_particles); + + core.recordBeginDebugLabel(cmdStream, "Draw particles", { 1.0f, 0.0f, 1.0f, 1.0f }); + core.recordDrawcallsToCmdStream( + cmdStream, + particlePipeline, + pushConstantsDraw0, + { drawcallParticle }, + { colorBuffers[0] }, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + vkcv::PushConstants pushConstantsVoxel (sizeof(glm::mat4)); + pushConstantsVoxel.appendDrawcall(camera.getMVP()); + + core.recordBeginDebugLabel(cmdStream, "Particle voxel update", { 1.0f, 0.5f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelParticlePipeline, + particleDispatchCount, + { + vkcv::useDescriptorSet(0, descriptorSet), + vkcv::useDescriptorSet(1, voxelDescriptorSet) + }, + pushConstantsVoxel + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, smokeBuffer.getHandle()); + + draw_smoke_t draw_smoke { + camera.getMVP(), + camera.getPosition() + }; + + core.recordBeginDebugLabel(cmdStream, "Draw smoke", { 1.0f, 0.5f, 1.0f, 1.0f }); + vkcv::PushConstants pushConstantsDraw1 (sizeof(draw_smoke_t)); + pushConstantsDraw1.appendDrawcall(draw_smoke); + + core.recordDrawcallsToCmdStream( + cmdStream, + smokePipeline, + pushConstantsDraw1, + { drawcallSmoke }, + { colorBuffers[1] }, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Smoke voxel update", { 1.0f, 0.7f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelSmokePipeline, + smokeDispatchCount, + { + vkcv::useDescriptorSet(0, smokeDescriptorSet), + vkcv::useDescriptorSet(1, voxelDescriptorSet) + }, + pushConstantsVoxel + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBufferMemoryBarrier(cmdStream, trailBuffer.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, pointBuffer.getHandle()); + + core.recordBeginDebugLabel(cmdStream, "Draw trails", { 0.75f, 0.5f, 1.0f, 1.0f }); + core.recordDrawcallsToCmdStream( + cmdStream, + trailPipeline, + pushConstantsDraw1, + { drawcallTrail }, + { colorBuffers[2] }, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Trail voxel update", { 1.0f, 0.9f, 0.8f, 1.0f }); + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelTrailPipeline, + trailDispatchCount, + { + vkcv::useDescriptorSet(0, trailDescriptorSet), + vkcv::useDescriptorSet(1, voxelDescriptorSet) + }, + pushConstantsVoxel + ); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Combine voxel data", { 0.5f, 0.5f, 0.5f, 1.0f }); + + core.prepareImageForStorage(cmdStream, voxelRed.getHandle()); + core.prepareImageForStorage(cmdStream, voxelGreen.getHandle()); + core.prepareImageForStorage(cmdStream, voxelBlue.getHandle()); + core.prepareImageForStorage(cmdStream, voxelDensity.getHandle()); + + core.prepareImageForStorage(cmdStream, voxelData[0]); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelPipeline, + voxelDispatchCount, + { + vkcv::useDescriptorSet(0, voxelDescriptorSet), + vkcv::useDescriptorSet(1, voxelOutDescriptorSet) + }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Fluid voxel data", { 0.2f, 0.2f, 0.9f, 1.0f }); + + for (size_t i = 0; i < 8; i++) { + core.prepareImageForSampling(cmdStream, voxelData[i % 2]); + core.prepareImageForStorage(cmdStream, voxelData[(i + 1) % 2]); + + core.recordComputeDispatchToCmdStream( + cmdStream, + fluidPipeline, + voxelDispatchCount, + { vkcv::useDescriptorSet(0, fluidDescriptorSet[i % 2]) }, + vkcv::PushConstants(0) + ); + } + + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Sample voxels", { 0.5f, 0.5f, 1.0f, 1.0f }); + + core.prepareImageForStorage(cmdStream, voxelData[0]); + core.prepareImageForStorage(cmdStream, voxelSamples.getHandle()); + + auto sampleDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(voxelWidth, voxelHeight), + vkcv::DispatchSize(8, 8) + ); + + core.recordComputeDispatchToCmdStream( + cmdStream, + voxelSamplePipeline, + sampleDispatchCount, + { + vkcv::useDescriptorSet(0, voxelOutDescriptorSet), + vkcv::useDescriptorSet(1, samplesDescriptorSet) + }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "Add rendered images", { 0.5f, 0.5f, 1.0f, 1.0f }); + + vkcv::DescriptorWrites addDescriptorWrites; + addDescriptorWrites.writeSampledImage(0, voxelSamples.getHandle()); + addDescriptorWrites.writeSampler(1, voxelSampler); + + for (size_t i = 0; i < colorBuffers.size(); i++) { + addDescriptorWrites.writeStorageImage(2 + i, colorBuffers[i]); + core.prepareImageForStorage(cmdStream, colorBuffers[i]); + } + + core.writeDescriptorSet(addDescriptor, addDescriptorWrites); + core.prepareImageForSampling(cmdStream, voxelSamples.getHandle()); + + auto colorDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(8, 8) + ); + + core.recordComputeDispatchToCmdStream( + cmdStream, + addPipe, + colorDispatchCount, + { + vkcv::useDescriptorSet(0, addDescriptor), + vkcv::useDescriptorSet(1, generationDescriptorSet) + }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + bloomAndFlares.recordEffect(cmdStream, colorBuffers.back(), colorBuffers.back()); + + core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 0.0f, 1.0f, 0.0f, 1.0f }); + core.prepareImageForStorage(cmdStream, colorBuffers.back()); + core.prepareImageForStorage(cmdStream, swapchainImage); + + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.writeStorageImage( + 0, colorBuffers.back() + ).writeStorageImage( + 1, swapchainImage + ); + + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipe, + colorDispatchCount, + { vkcv::useDescriptorSet(0, tonemappingDescriptor) }, + vkcv::PushConstants(0) + ); + + core.recordEndDebugLabel(cmdStream); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + ImGui::Begin("Settings"); + + bool firework, sparkler, nested; + if (ImGui::BeginListBox(" ")) { + firework = ImGui::Selectable("Firework"); + sparkler = ImGui::Selectable("Sparkler"); + nested = ImGui::Selectable("Nested Firework"); + + ImGui::EndListBox(); + } + + bool resetTime = ImGui::Button("Reset"); + auto color = glm::vec3(0.0f); + + if (!events.empty()) { + color = events[0].color; + } + + bool colorChanged = ImGui::ColorPicker3("Color", (float*) & color); + + ImGui::End(); + gui.endGUI(); + + core.endFrame(windowHandle); + + particleBuffer.read(particles); + sort(particles.begin(), particles.end(), + [](const particle_t p1, const particle_t p2) { + return p1.eventId < p2.eventId; + }); + + std::vector<uint32_t> startingIndex; + startingIndex.resize(events.size()); + uint32_t eventIdCheck = std::numeric_limits<uint32_t>::max(); + + for (size_t i = 0; i < particles.size(); i++) { + if (particles[i].eventId != eventIdCheck) { + eventIdCheck = particles [i].eventId; + if (eventIdCheck < startingIndex.size()) { + startingIndex [eventIdCheck] = i; + } + } + } + + startIndexBuffer.fill(startingIndex); + + if (firework) { + events.clear(); + InitializeFireworkEvents(events); + resetTime = true; + } else if (sparkler) { + events.clear(); + InitializeSparklerEvents(events); + resetTime = true; + } else if (nested) { + events.clear(); + InitializeNestedFireworkEvents(events); + resetTime = true; + } + + if (colorChanged) { + ChangeColor(events, color); + resetTime = true; + } + + if (resetTime) { + start = std::chrono::system_clock::now(); + InitializeParticles(particles); + particleBuffer.fill(particles); + eventBuffer.fill(events); + smokeBuffer.fill(smokes); + trailBuffer.fill(trails); + pointBuffer.fill(points); + + memset(smokeIndices, 0, smokeIndexBuffer.getSize()); + } + + particleBufferCopy.fill(particles); + } + + smokeIndexBuffer.unmap(); + return 0; +} diff --git a/projects/first_mesh/CMakeLists.txt b/projects/first_mesh/CMakeLists.txt index eb0f028db38707272f9fbcf61662633f2868eedc..1846c5276210ed6541bdaa9833f072ccc9836b8d 100644 --- a/projects/first_mesh/CMakeLists.txt +++ b/projects/first_mesh/CMakeLists.txt @@ -2,27 +2,28 @@ cmake_minimum_required(VERSION 3.16) project(first_mesh) # setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# this should fix the execution path to load local files from the project -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - # adding source files to the project -add_executable(first_mesh src/main.cpp) - -# this should fix the execution path to load local files from the project (for MSVC) -if(MSVC) - set_target_properties(first_mesh PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - set_target_properties(first_mesh PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - - # in addition to setting the output directory, the working directory has to be set - # by default visual studio sets the working directory to the build directory, when using the debugger - set_target_properties(first_mesh PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -endif() +add_project(first_mesh src/main.cpp) # including headers of dependencies and the VkCV framework -target_include_directories(first_mesh SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include}) +target_include_directories(first_mesh SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_asset_loader_include} + ${vkcv_geometry_include} + ${vkcv_camera_include} + ${vkcv_shader_compiler_include} +) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(first_mesh vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera) +target_link_libraries(first_mesh + vkcv + ${vkcv_libraries} + vkcv_asset_loader + vkcv_geometry + vkcv_camera + vkcv_shader_compiler +) diff --git a/projects/first_mesh/README.md b/projects/first_mesh/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5aec0c2931d7f960df1ff8ea157f12b0c549503f --- /dev/null +++ b/projects/first_mesh/README.md @@ -0,0 +1,10 @@ +# First mesh +An example project to show a simple mesh can be rendered with the VkCV framework + + + +## Details + +This project is rather a proof of concept to show that rendering a more complex mesh than a single +triangle was indeed possible with the given API. It was used as a benchmark for further API design +changes. diff --git a/projects/first_mesh/resources/cube/boards2_vcyc_jpg.jpg b/projects/first_mesh/assets/cube/boards2_vcyc_jpg.jpg similarity index 100% rename from projects/first_mesh/resources/cube/boards2_vcyc_jpg.jpg rename to projects/first_mesh/assets/cube/boards2_vcyc_jpg.jpg diff --git a/projects/first_mesh/resources/shaders/shader.frag b/projects/first_mesh/assets/shaders/shader.frag similarity index 100% rename from projects/first_mesh/resources/shaders/shader.frag rename to projects/first_mesh/assets/shaders/shader.frag diff --git a/projects/first_mesh/resources/shaders/shader.vert b/projects/first_mesh/assets/shaders/shader.vert similarity index 100% rename from projects/first_mesh/resources/shaders/shader.vert rename to projects/first_mesh/assets/shaders/shader.vert diff --git a/projects/first_mesh/resources/Szene/Szene.bin b/projects/first_mesh/resources/Szene/Szene.bin deleted file mode 100644 index c87d27637516b0bbf864251dd162773f5cc53e06..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/Szene/Szene.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d -size 70328 diff --git a/projects/first_mesh/resources/Szene/Szene.gltf b/projects/first_mesh/resources/Szene/Szene.gltf deleted file mode 100644 index e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/Szene/Szene.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a -size 7421 diff --git a/projects/first_mesh/resources/Szene/boards2_vcyc.jpg b/projects/first_mesh/resources/Szene/boards2_vcyc.jpg deleted file mode 100644 index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/Szene/boards2_vcyc.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 -size 1192476 diff --git a/projects/first_mesh/resources/shaders/compile.bat b/projects/first_mesh/resources/shaders/compile.bat deleted file mode 100644 index b4521235c40fe5fb163bab874560c2f219b7517f..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/shaders/compile.bat +++ /dev/null @@ -1,3 +0,0 @@ -%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv -%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv -pause \ No newline at end of file diff --git a/projects/first_mesh/resources/shaders/frag.spv b/projects/first_mesh/resources/shaders/frag.spv deleted file mode 100644 index 087e4e22fb2fcec27d99b3ff2aa1a705fe755796..0000000000000000000000000000000000000000 Binary files a/projects/first_mesh/resources/shaders/frag.spv and /dev/null differ diff --git a/projects/first_mesh/resources/shaders/vert.spv b/projects/first_mesh/resources/shaders/vert.spv deleted file mode 100644 index 374c023e14b351eb43cbcda5951cbb8b3d6f96a1..0000000000000000000000000000000000000000 Binary files a/projects/first_mesh/resources/shaders/vert.spv and /dev/null differ diff --git a/projects/first_mesh/resources/triangle/Triangle.bin b/projects/first_mesh/resources/triangle/Triangle.bin deleted file mode 100644 index 57f26ad96592b64377e6aa93823d96a94e6c5022..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/triangle/Triangle.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959 -size 104 diff --git a/projects/first_mesh/resources/triangle/Triangle.blend b/projects/first_mesh/resources/triangle/Triangle.blend deleted file mode 100644 index 2421dc5e1bb029d73a9ec09cc4530c5196851fd7..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/triangle/Triangle.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477 -size 758144 diff --git a/projects/first_mesh/resources/triangle/Triangle.glb b/projects/first_mesh/resources/triangle/Triangle.glb deleted file mode 100644 index 4148620cd6af0dadbc791aa1c52bb5431a40884b..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/triangle/Triangle.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227 -size 980 diff --git a/projects/first_mesh/resources/triangle/Triangle.gltf b/projects/first_mesh/resources/triangle/Triangle.gltf deleted file mode 100644 index a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6..0000000000000000000000000000000000000000 --- a/projects/first_mesh/resources/triangle/Triangle.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec -size 2202 diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index c559955aebfae863890782d43e61819387817e5d..cc6d93a2efa37f3b3f1d6a45fe03a28ace686226 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -1,217 +1,168 @@ #include <iostream> #include <vkcv/Core.hpp> -#include <GLFW/glfw3.h> +#include <vkcv/Image.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Sampler.hpp> #include <vkcv/camera/CameraManager.hpp> #include <vkcv/camera/InterpolationLinear.hpp> #include <chrono> -#include <vkcv/asset/asset_loader.hpp> -int main(int argc, const char** argv) { - const char* applicationName = "First Mesh"; +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> - uint32_t windowWidth = 800; - uint32_t windowHeight = 600; +#include <vkcv/geometry/Cuboid.hpp> - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true - ); +int main(int argc, const char** argv) { + const std::string applicationName = "First Mesh"; vkcv::Core core = vkcv::Core::create( - window, applicationName, VK_MAKE_VERSION(0, 0, 1), { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, - {}, - { "VK_KHR_swapchain" } + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); - vkcv::asset::Scene mesh; + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true); + vkcv::Window& window = core.getWindow(windowHandle); - const char* path = argc > 1 ? argv[1] : "resources/cube/cube.gltf"; - int result = vkcv::asset::loadScene(path, mesh); + vkcv::PassHandle firstMeshPass = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } + ); - if (result == 1) { - std::cout << "Mesh loading successful!" << std::endl; - } - else { - std::cout << "Mesh loading failed: " << result << std::endl; - return 1; + if (!firstMeshPass) { + std::cerr << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; } - assert(!mesh.vertexGroups.empty()); - auto vertexBuffer = core.createBuffer<uint8_t>( - vkcv::BufferType::VERTEX, - mesh.vertexGroups[0].vertexBuffer.data.size(), - vkcv::BufferMemoryType::DEVICE_LOCAL - ); + vkcv::ShaderProgram firstMeshProgram; + vkcv::shader::GLSLCompiler compiler; - vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data); + compiler.compileProgram(firstMeshProgram, { + { vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" }, + { vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" } + }, nullptr); - auto indexBuffer = core.createBuffer<uint8_t>( - vkcv::BufferType::INDEX, - mesh.vertexGroups[0].indexBuffer.data.size(), - vkcv::BufferMemoryType::DEVICE_LOCAL + std::vector<vkcv::VertexBinding> bindings = vkcv::createVertexBindings( + firstMeshProgram.getVertexAttachments() ); - indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data); + const vkcv::VertexLayout firstMeshLayout { bindings }; - // an example attachment for passes that output to the window - const vkcv::AttachmentDescription present_color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - core.getSwapchain().getFormat() - ); - - const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - vk::Format::eD32Sfloat - ); + // since we only use one descriptor set (namely, desc set 0), directly address it + // recreate copies of the bindings and the handles (to check whether they are properly reused instead of actually recreated) + const vkcv::DescriptorBindings& set0Bindings = firstMeshProgram.getReflectedDescriptors().at(0); - vkcv::PassConfig firstMeshPassDefinition({ present_color_attachment, depth_attachment }); - vkcv::PassHandle firstMeshPass = core.createPass(firstMeshPassDefinition); + vkcv::DescriptorSetLayoutHandle setLayoutHandle = core.createDescriptorSetLayout(set0Bindings); + vkcv::DescriptorSetLayoutHandle setLayoutHandleCopy = core.createDescriptorSetLayout(set0Bindings); - if (!firstMeshPass) { - std::cout << "Error. Could not create renderpass. Exiting." << std::endl; - return EXIT_FAILURE; - } - - vkcv::ShaderProgram firstMeshProgram{}; - firstMeshProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); - firstMeshProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(setLayoutHandle); - auto& attributes = mesh.vertexGroups[0].vertexBuffer.attributes; + vkcv::GraphicsPipelineHandle firstMeshPipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + firstMeshProgram, + firstMeshPass, + { firstMeshLayout }, + { setLayoutHandle } + ) + ); - std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { - return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); - }); - - const std::vector<vkcv::VertexAttachment> vertexAttachments = firstMeshProgram.getVertexAttachments(); - std::vector<vkcv::VertexBinding> bindings; - for (size_t i = 0; i < vertexAttachments.size(); i++) { - bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + if (!firstMeshPipeline) { + std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; } - const vkcv::VertexLayout firstMeshLayout (bindings); - - uint32_t setID = 0; - std::vector<vkcv::DescriptorBinding> descriptorBindings = { firstMeshProgram.getReflectedDescriptors()[setID] }; - vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorBindings); - - const vkcv::PipelineConfig firstMeshPipelineConfig { - firstMeshProgram, - UINT32_MAX, - UINT32_MAX, - firstMeshPass, - {firstMeshLayout}, - { core.getDescriptorSet(descriptorSet).layout }, - true - }; - vkcv::PipelineHandle firstMeshPipeline = core.createGraphicsPipeline(firstMeshPipelineConfig); + vkcv::asset::Texture tex = vkcv::asset::loadTexture("assets/cube/boards2_vcyc_jpg.jpg"); - if (!firstMeshPipeline) { - std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + if (tex.data.empty()) { + std::cerr << "Error. No texture found. Exiting." << std::endl; return EXIT_FAILURE; } - // FIXME There should be a test here to make sure there is at least 1 - // texture in the mesh. - vkcv::asset::Texture &tex = mesh.textures[0]; - vkcv::Image texture = core.createImage(vk::Format::eR8G8B8A8Srgb, tex.w, tex.h); + vkcv::Image texture = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, tex.w, tex.h); texture.fill(tex.data.data()); - texture.generateMipChainImmediate(); - texture.switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); - - vkcv::SamplerHandle sampler = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); - - const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { - vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[0].offset), vertexBuffer.getVulkanHandle()), - vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[1].offset), vertexBuffer.getVulkanHandle()), - vkcv::VertexBufferBinding(static_cast<vk::DeviceSize>(attributes[2].offset), vertexBuffer.getVulkanHandle()) }; + + { + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + texture.recordMipChainGeneration(cmdStream, core.getDownsampler()); + core.submitCommandStream(cmdStream, false); + } + vkcv::SamplerHandle sampler = vkcv::samplerLinear(core); + vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, texture.getHandle()) }; - setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; + setWrites.writeSampledImage(0, texture.getHandle()); + setWrites.writeSampler(1, sampler); core.writeDescriptorSet(descriptorSet, setWrites); - - vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight, 1, false).getHandle(); + + vkcv::ImageHandle depthBuffer; const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - - const vkcv::Mesh renderMesh(vertexBufferBindings, indexBuffer.getVulkanHandle(), mesh.vertexGroups[0].numIndices); - - vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSet).vulkanHandle); - vkcv::DrawcallInfo drawcall(renderMesh, { descriptorUsage }); + + vkcv::geometry::Cuboid cube (glm::vec3(0), 1.0f); + + vkcv::InstanceDrawcall drawcall (cube.generateVertexData(core)); + drawcall.useDescriptorSet(0, descriptorSet); vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::NONE); - uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); - cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3)); - - vkcv::camera::InterpolationLinear interp(cameraManager.getCamera(camIndex0)); - + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::NONE); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0, 0, -3)); + cameraManager.getCamera(camHandle2).setPosition(glm::vec3(0, 0, -3)); + + vkcv::camera::InterpolationLinear interp (cameraManager.getCamera(camHandle0)); + interp.addPosition(glm::vec3(5,5,-5)); interp.addPosition(glm::vec3(0,5,-5)); interp.addPosition(glm::vec3(0,-3,-3)); interp.addPosition(glm::vec3(3,0,-6)); interp.addPosition(glm::vec3(5,5,5)); interp.addPosition(glm::vec3(5,5,-5)); - - auto start = std::chrono::system_clock::now(); - - while (window.isWindowOpen()) { - window.pollEvents(); - - if(window.getHeight() == 0 || window.getWidth() == 0) - continue; - - uint32_t swapchainWidth, swapchainHeight; - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } - - if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle(); - - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + (swapchainHeight != core.getImageHeight(depthBuffer))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); } - - auto end = std::chrono::system_clock::now(); - auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - start = end; - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); + cameraManager.update(dt); interp.updateCamera(); + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); - vkcv::PushConstantData pushConstantData((void*)&mvp, sizeof(glm::mat4)); + vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>(); + pushConstants.appendDrawcall(mvp); const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); core.recordDrawcallsToCmdStream( cmdStream, - firstMeshPass, firstMeshPipeline, - pushConstantData, + pushConstants, { drawcall }, - renderTargets); + renderTargets, + windowHandle + ); + core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); - core.endFrame(); - } + }); + + core.getContext().getDevice().waitIdle(); return 0; } diff --git a/projects/first_scene/CMakeLists.txt b/projects/first_scene/CMakeLists.txt index 8b90739750011a36b4c1d9e0bff7cba986074228..266630fae0de1d4d9696d454c101bf00085b7599 100644 --- a/projects/first_scene/CMakeLists.txt +++ b/projects/first_scene/CMakeLists.txt @@ -2,27 +2,14 @@ cmake_minimum_required(VERSION 3.16) project(first_scene) # setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# this should fix the execution path to load local files from the project -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - # adding source files to the project -add_executable(first_scene src/main.cpp) - -# this should fix the execution path to load local files from the project (for MSVC) -if(MSVC) - set_target_properties(first_scene PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - set_target_properties(first_scene PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - - # in addition to setting the output directory, the working directory has to be set - # by default visual studio sets the working directory to the build directory, when using the debugger - set_target_properties(first_scene PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -endif() +add_project(first_scene src/main.cpp) # including headers of dependencies and the VkCV framework -target_include_directories(first_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include}) +target_include_directories(first_scene SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_scene_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(first_scene vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera) +target_link_libraries(first_scene vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_scene vkcv_shader_compiler vkcv_gui) diff --git a/projects/first_scene/README.md b/projects/first_scene/README.md new file mode 100644 index 0000000000000000000000000000000000000000..be333a773eae88ff3e96fe2b9b004fc3575f386f --- /dev/null +++ b/projects/first_scene/README.md @@ -0,0 +1,10 @@ +# First scene +An example project to show a whole scene can be rendered with the VkCV framework + + + +## Details + +Similar to the projects to show rendering a single triangle or a simple mesh was possible. This +project shows that a whole scene can easily be loaded from any GLTF file and be rendered using the +modules from the VkCV framework. diff --git a/projects/bloom/resources/Sponza/Sponza.bin b/projects/first_scene/assets/Sponza/Sponza.bin similarity index 100% rename from projects/bloom/resources/Sponza/Sponza.bin rename to projects/first_scene/assets/Sponza/Sponza.bin diff --git a/projects/bloom/resources/Sponza/Sponza.gltf b/projects/first_scene/assets/Sponza/Sponza.gltf similarity index 100% rename from projects/bloom/resources/Sponza/Sponza.gltf rename to projects/first_scene/assets/Sponza/Sponza.gltf diff --git a/projects/first_scene/assets/Sponza/SponzaFloor.bin b/projects/first_scene/assets/Sponza/SponzaFloor.bin new file mode 100644 index 0000000000000000000000000000000000000000..684251288f35070d2e7d244877fd844cc00ca632 --- /dev/null +++ b/projects/first_scene/assets/Sponza/SponzaFloor.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:678455aca641cb1f449aa1a5054a7cae132be81c2b333aac283053967da66df0 +size 512 diff --git a/projects/first_scene/assets/Sponza/SponzaFloor.gltf b/projects/first_scene/assets/Sponza/SponzaFloor.gltf new file mode 100644 index 0000000000000000000000000000000000000000..b45f1c55ef85f2aa1d4bff01df3d9625aa38c809 --- /dev/null +++ b/projects/first_scene/assets/Sponza/SponzaFloor.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6deb75441b1138b50a6b0eec05e60df276fe8fb6d58118fdfce2090b6fbe734 +size 3139 diff --git a/projects/bloom/resources/Sponza/background.png b/projects/first_scene/assets/Sponza/background.png similarity index 100% rename from projects/bloom/resources/Sponza/background.png rename to projects/first_scene/assets/Sponza/background.png diff --git a/projects/bloom/resources/Sponza/chain_texture.png b/projects/first_scene/assets/Sponza/chain_texture.png similarity index 100% rename from projects/bloom/resources/Sponza/chain_texture.png rename to projects/first_scene/assets/Sponza/chain_texture.png diff --git a/projects/bloom/resources/Sponza/lion.png b/projects/first_scene/assets/Sponza/lion.png similarity index 100% rename from projects/bloom/resources/Sponza/lion.png rename to projects/first_scene/assets/Sponza/lion.png diff --git a/projects/bloom/resources/Sponza/spnza_bricks_a_diff.png b/projects/first_scene/assets/Sponza/spnza_bricks_a_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/spnza_bricks_a_diff.png rename to projects/first_scene/assets/Sponza/spnza_bricks_a_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_arch_diff.png b/projects/first_scene/assets/Sponza/sponza_arch_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_arch_diff.png rename to projects/first_scene/assets/Sponza/sponza_arch_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png b/projects/first_scene/assets/Sponza/sponza_ceiling_a_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_ceiling_a_diff.png rename to projects/first_scene/assets/Sponza/sponza_ceiling_a_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_column_a_diff.png b/projects/first_scene/assets/Sponza/sponza_column_a_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_column_a_diff.png rename to projects/first_scene/assets/Sponza/sponza_column_a_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_column_b_diff.png b/projects/first_scene/assets/Sponza/sponza_column_b_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_column_b_diff.png rename to projects/first_scene/assets/Sponza/sponza_column_b_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_column_c_diff.png b/projects/first_scene/assets/Sponza/sponza_column_c_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_column_c_diff.png rename to projects/first_scene/assets/Sponza/sponza_column_c_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png b/projects/first_scene/assets/Sponza/sponza_curtain_blue_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_curtain_blue_diff.png rename to projects/first_scene/assets/Sponza/sponza_curtain_blue_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_curtain_diff.png b/projects/first_scene/assets/Sponza/sponza_curtain_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_curtain_diff.png rename to projects/first_scene/assets/Sponza/sponza_curtain_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_curtain_green_diff.png b/projects/first_scene/assets/Sponza/sponza_curtain_green_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_curtain_green_diff.png rename to projects/first_scene/assets/Sponza/sponza_curtain_green_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_details_diff.png b/projects/first_scene/assets/Sponza/sponza_details_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_details_diff.png rename to projects/first_scene/assets/Sponza/sponza_details_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png b/projects/first_scene/assets/Sponza/sponza_fabric_blue_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_fabric_blue_diff.png rename to projects/first_scene/assets/Sponza/sponza_fabric_blue_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_fabric_diff.png b/projects/first_scene/assets/Sponza/sponza_fabric_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_fabric_diff.png rename to projects/first_scene/assets/Sponza/sponza_fabric_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_fabric_green_diff.png b/projects/first_scene/assets/Sponza/sponza_fabric_green_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_fabric_green_diff.png rename to projects/first_scene/assets/Sponza/sponza_fabric_green_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_flagpole_diff.png b/projects/first_scene/assets/Sponza/sponza_flagpole_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_flagpole_diff.png rename to projects/first_scene/assets/Sponza/sponza_flagpole_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_floor_a_diff.png b/projects/first_scene/assets/Sponza/sponza_floor_a_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_floor_a_diff.png rename to projects/first_scene/assets/Sponza/sponza_floor_a_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_roof_diff.png b/projects/first_scene/assets/Sponza/sponza_roof_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_roof_diff.png rename to projects/first_scene/assets/Sponza/sponza_roof_diff.png diff --git a/projects/bloom/resources/Sponza/sponza_thorn_diff.png b/projects/first_scene/assets/Sponza/sponza_thorn_diff.png similarity index 100% rename from projects/bloom/resources/Sponza/sponza_thorn_diff.png rename to projects/first_scene/assets/Sponza/sponza_thorn_diff.png diff --git a/projects/bloom/resources/Sponza/vase_dif.png b/projects/first_scene/assets/Sponza/vase_dif.png similarity index 100% rename from projects/bloom/resources/Sponza/vase_dif.png rename to projects/first_scene/assets/Sponza/vase_dif.png diff --git a/projects/bloom/resources/Sponza/vase_hanging.png b/projects/first_scene/assets/Sponza/vase_hanging.png similarity index 100% rename from projects/bloom/resources/Sponza/vase_hanging.png rename to projects/first_scene/assets/Sponza/vase_hanging.png diff --git a/projects/bloom/resources/Sponza/vase_plant.png b/projects/first_scene/assets/Sponza/vase_plant.png similarity index 100% rename from projects/bloom/resources/Sponza/vase_plant.png rename to projects/first_scene/assets/Sponza/vase_plant.png diff --git a/projects/bloom/resources/Sponza/vase_round.png b/projects/first_scene/assets/Sponza/vase_round.png similarity index 100% rename from projects/bloom/resources/Sponza/vase_round.png rename to projects/first_scene/assets/Sponza/vase_round.png diff --git a/projects/first_scene/assets/shaders/shader.frag b/projects/first_scene/assets/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..cf96b4c20327fa3f1b1545cba7cebfdfaaf1ba7c --- /dev/null +++ b/projects/first_scene/assets/shaders/shader.frag @@ -0,0 +1,31 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; + +layout(location = 0) out vec4 outColor; + +layout(set=0, binding=0) uniform texture2D meshTexture; +layout(set=0, binding=1) uniform sampler textureSampler; + +void main() { + vec3 lightDirection = normalize(vec3(0.1f, -0.9f, 0.1f)); + + float ambient = 0.35f; + float diffuse = max(0.0f, -dot(passNormal, lightDirection)); + float specular = pow(diffuse, 6.0f); + + float brightness = sqrt( + (ambient + diffuse + specular) / + (2.0f + ambient) + ); + + vec4 color = texture(sampler2D(meshTexture, textureSampler), passUV); + + if (color.a <= 0.0f) { + discard; + } + + outColor = vec4(color.rgb * brightness, color.a); +} \ No newline at end of file diff --git a/projects/first_scene/resources/shaders/shader.vert b/projects/first_scene/assets/shaders/shader.vert similarity index 100% rename from projects/first_scene/resources/shaders/shader.vert rename to projects/first_scene/assets/shaders/shader.vert diff --git a/projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png b/projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png deleted file mode 100644 index 8258525f22097f2382ec5c26ad0df7eb50718642..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Cutlery_chrome_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ce87f6407ee40ffa60983587aeb52333d59b4b1c01a53e11f4bb227ba1099d9 -size 109 diff --git a/projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png b/projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png deleted file mode 100644 index 620fe7621cf35409ae8c04099dc8bc4bbc04343b..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Cutlery_chrome_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:68a0064d457a6f7994814b07d943deda778754128935689874334300ede6161d -size 2332064 diff --git a/projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png b/projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png deleted file mode 100644 index 5570e88c569036b9d00155ef6113013aa23f2503..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Cutlery_details_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42c2715635081eb29c4489ce631798b0e9c881460efc0aa63d0e81641a0dcfe9 -size 108 diff --git a/projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png b/projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png deleted file mode 100644 index d07681f5bfa25c279321c3074e21e4900c5eb0fa..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Cutlery_details_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:15b0133e140899c47ccf35b0f99a7e337e3110ae089f45d27faf9983f3e0a1f7 -size 770758 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png deleted file mode 100644 index 1845e8a7d586a0d23300ad9d04a82f1d5048fcf5..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea7c82c0f9e25afa401470df1fb6903f508fa138d21ad30f57a9153b0395b198 -size 521315 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png deleted file mode 100644 index 1c800c0489693b66d6163bdc2156d453b68ca19b..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Caps_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:119efbbc020244ff9b7ff16ac9795a6d4b1808d1b90d81d20d2c874d0dc8a924 -size 1693468 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png deleted file mode 100644 index 36f46ebf25158c78bc26d83860e1c08b2c9e96cf..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99896468c7d47dd5391d585eecf149f420eca3bfec31923c21fa86c45fe02d0f -size 108 diff --git a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png b/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png deleted file mode 100644 index 28c205d4e70867ec58448ca8ba2c2117c611a367..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Paris_LiquorBottle_01_Glass_Wine_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b31618aa5adce4ad476bec2c03718c5ae097250e784344f2d298b8a74c3bfd46 -size 90 diff --git a/projects/first_scene/resources/Cutlery/Plates_Ceramic_BaseColor.png b/projects/first_scene/resources/Cutlery/Plates_Ceramic_BaseColor.png deleted file mode 100644 index e0104189a1190c160152101e9e328e81ed89121b..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Plates_Ceramic_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6eff6ccd12d8b39d60ae5ee91edd73d4d7838fcb5d9bc6ff0e671bdf009134e9 -size 109 diff --git a/projects/first_scene/resources/Cutlery/Plates_Ceramic_Normal.png b/projects/first_scene/resources/Cutlery/Plates_Ceramic_Normal.png deleted file mode 100644 index fa13483d25595ee6b3a00e7fe6ce48aed7b6aaca..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Plates_Ceramic_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fe92c40ff4032fdaf10eeafd943657a0c6e0bfb3f38770f5654aa943a660f421 -size 59419 diff --git a/projects/first_scene/resources/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png b/projects/first_scene/resources/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png deleted file mode 100644 index b91d0ac62fbeabbef1ed78f1343f919836cbca40..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Plates_Details_BaseColor-Plates_Details_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4ca7d436a68a2a1237aee6e763b2954f01666b21f1dbd46929a322ea277483d2 -size 779227 diff --git a/projects/first_scene/resources/Cutlery/Plates_Details_Normal.png b/projects/first_scene/resources/Cutlery/Plates_Details_Normal.png deleted file mode 100644 index 6efd967984ee2e68b89de9e471b93396c13ca69a..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/Plates_Details_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b01fc6482054c64d7407b283731e57fce0601a8db28b6781c14fae3c6b30b0fe -size 504362 diff --git a/projects/first_scene/resources/Cutlery/ToffeeJar_Label_BaseColor.png b/projects/first_scene/resources/Cutlery/ToffeeJar_Label_BaseColor.png deleted file mode 100644 index d0e0c4f4134cb99d3765d6f078f71efab8861bf1..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/ToffeeJar_Label_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:df138ee68c1d455652d1b9ae3dd03e93fcd2f6a0d8a1f12e3710f39143088674 -size 1593466 diff --git a/projects/first_scene/resources/Cutlery/ToffeeJar_Label_Normal.png b/projects/first_scene/resources/Cutlery/ToffeeJar_Label_Normal.png deleted file mode 100644 index 9f310653b5211575da3cab2f6deb47ec6826a936..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/ToffeeJar_Label_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6af5da97cbb25d79aea2dde8dd71ecbd495334fe34e99497ba17821be93fd7fd -size 2696676 diff --git a/projects/first_scene/resources/Cutlery/TransparentGlass_BaseColor.png b/projects/first_scene/resources/Cutlery/TransparentGlass_BaseColor.png deleted file mode 100644 index 4e4f0fcb312b8f8bd0df965bfe6b8ac62ec5df4d..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/TransparentGlass_BaseColor.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2796affdfdcf6bc805176d9f85505680b5ee52eeec625e9eaeea4f0ff3854883 -size 108 diff --git a/projects/first_scene/resources/Cutlery/TransparentGlass_Normal.png b/projects/first_scene/resources/Cutlery/TransparentGlass_Normal.png deleted file mode 100644 index 28c205d4e70867ec58448ca8ba2c2117c611a367..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/TransparentGlass_Normal.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b31618aa5adce4ad476bec2c03718c5ae097250e784344f2d298b8a74c3bfd46 -size 90 diff --git a/projects/first_scene/resources/Cutlery/cutlerySzene.bin b/projects/first_scene/resources/Cutlery/cutlerySzene.bin deleted file mode 100644 index ab9a0aa47205e8f3064d2f16a950d4733fb4f472..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/cutlerySzene.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f545b986e0a1ac5bff5d49693a52042aa37878425818f72c69c243da20d1f99d -size 183324 diff --git a/projects/first_scene/resources/Cutlery/cutlerySzene.glb b/projects/first_scene/resources/Cutlery/cutlerySzene.glb deleted file mode 100644 index b0c5f345aaaa3f96d7158a0992ee124aae99a69a..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/cutlerySzene.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb1bad604192ca36222c0ca485ba87b846ecbd11ee8254327e04e3c993b00116 -size 11150396 diff --git a/projects/first_scene/resources/Cutlery/cutlerySzene.gltf b/projects/first_scene/resources/Cutlery/cutlerySzene.gltf deleted file mode 100644 index 53e339cda4511f3f1a8670b36469e184aac530e2..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Cutlery/cutlerySzene.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c77cd60e2327daca1a01044e45f2c38655f7b781bd07985fc0135328a8a96b57 -size 34312 diff --git a/projects/first_scene/resources/Szene/Szene.bin b/projects/first_scene/resources/Szene/Szene.bin deleted file mode 100644 index c87d27637516b0bbf864251dd162773f5cc53e06..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Szene/Szene.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ee4742718f720d589a2a03f5d879f8c50ba9057718d191a43b046eaa9071080d -size 70328 diff --git a/projects/first_scene/resources/Szene/Szene.gltf b/projects/first_scene/resources/Szene/Szene.gltf deleted file mode 100644 index e5a32b29af5d0a2ac5f497e60b4b92c1873e1df9..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Szene/Szene.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:75ba834118792ebbacf528a1690c7d04df4b4c8122b9f99a9aa9a9d075d2c86a -size 7421 diff --git a/projects/first_scene/resources/Szene/boards2_vcyc.jpg b/projects/first_scene/resources/Szene/boards2_vcyc.jpg deleted file mode 100644 index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Szene/boards2_vcyc.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 -size 1192476 diff --git a/projects/first_scene/resources/Szene/boards2_vcyc_jpg.jpg b/projects/first_scene/resources/Szene/boards2_vcyc_jpg.jpg deleted file mode 100644 index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/Szene/boards2_vcyc_jpg.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 -size 1192476 diff --git a/projects/first_scene/resources/shaders/compile.bat b/projects/first_scene/resources/shaders/compile.bat deleted file mode 100644 index b4521235c40fe5fb163bab874560c2f219b7517f..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/shaders/compile.bat +++ /dev/null @@ -1,3 +0,0 @@ -%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv -%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv -pause \ No newline at end of file diff --git a/projects/first_scene/resources/shaders/frag.spv b/projects/first_scene/resources/shaders/frag.spv deleted file mode 100644 index 087e4e22fb2fcec27d99b3ff2aa1a705fe755796..0000000000000000000000000000000000000000 Binary files a/projects/first_scene/resources/shaders/frag.spv and /dev/null differ diff --git a/projects/first_scene/resources/shaders/shader.frag b/projects/first_scene/resources/shaders/shader.frag deleted file mode 100644 index b5494bea7d6497e2e3dcd8559606864a71adb74e..0000000000000000000000000000000000000000 --- a/projects/first_scene/resources/shaders/shader.frag +++ /dev/null @@ -1,15 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable - -layout(location = 0) in vec3 passNormal; -layout(location = 1) in vec2 passUV; - -layout(location = 0) out vec3 outColor; - -layout(set=0, binding=0) uniform texture2D meshTexture; -layout(set=0, binding=1) uniform sampler textureSampler; - -void main() { - outColor = texture(sampler2D(meshTexture, textureSampler), passUV).rgb; - //outColor = passNormal * 0.5 + 0.5; -} \ No newline at end of file diff --git a/projects/first_scene/resources/shaders/vert.spv b/projects/first_scene/resources/shaders/vert.spv deleted file mode 100644 index 374c023e14b351eb43cbcda5951cbb8b3d6f96a1..0000000000000000000000000000000000000000 Binary files a/projects/first_scene/resources/shaders/vert.spv and /dev/null differ diff --git a/projects/first_scene/src/main.cpp b/projects/first_scene/src/main.cpp index 420400cdd04865ddd48eec7cf6000e36417d8095..964b048edfc02c8e173fca1931dc5ff804ad65bc 100644 --- a/projects/first_scene/src/main.cpp +++ b/projects/first_scene/src/main.cpp @@ -1,264 +1,149 @@ #include <iostream> #include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> #include <GLFW/glfw3.h> #include <vkcv/camera/CameraManager.hpp> -#include <chrono> +#include <vkcv/gui/GUI.hpp> #include <vkcv/asset/asset_loader.hpp> -#include <vkcv/Logger.hpp> - -glm::mat4 arrayTo4x4Matrix(std::array<float,16> array){ - glm::mat4 matrix; - for (int i = 0; i < 4; i++){ - for (int j = 0; j < 4; j++){ - matrix[i][j] = array[j * 4 + i]; - } - } - return matrix; -} +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/scene/Scene.hpp> +#include <vkcv/Features.hpp> int main(int argc, const char** argv) { - const char* applicationName = "First Scene"; + const std::string applicationName = "First Scene"; uint32_t windowWidth = 800; uint32_t windowHeight = 600; - - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true - ); - - vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); - - cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -3)); - cameraManager.getCamera(camIndex0).setNearFar(0.1f, 30.0f); - cameraManager.getCamera(camIndex1).setNearFar(0.1f, 30.0f); + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); vkcv::Core core = vkcv::Core::create( - window, - applicationName, - VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, - {}, - { "VK_KHR_swapchain" } + applicationName, + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + features ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + vkcv::camera::CameraManager cameraManager(window); - vkcv::asset::Scene scene; - - const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; - int result = vkcv::asset::loadScene(path, scene); - - if (result == 1) { - std::cout << "Mesh loading successful!" << std::endl; - } - else { - std::cout << "Mesh loading failed: " << result << std::endl; - return 1; - } - - assert(!scene.vertexGroups.empty()); - std::vector<std::vector<uint8_t>> vBuffers; - std::vector<std::vector<uint8_t>> iBuffers; - - std::vector<vkcv::VertexBufferBinding> vBufferBindings; - std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; - std::vector<vkcv::asset::VertexAttribute> vAttributes; - - for (int i = 0; i < scene.vertexGroups.size(); i++) { - - vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); - iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); - - auto& attributes = scene.vertexGroups[i].vertexBuffer.attributes; - - std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { - return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); - }); - } - - std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; - for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { - vertexBuffers.push_back(core.createBuffer<uint8_t>( - vkcv::BufferType::VERTEX, - group.vertexBuffer.data.size())); - vertexBuffers.back().fill(group.vertexBuffer.data); - } - - std::vector<vkcv::Buffer<uint8_t>> indexBuffers; - for (const auto& dataBuffer : iBuffers) { - indexBuffers.push_back(core.createBuffer<uint8_t>( - vkcv::BufferType::INDEX, - dataBuffer.size())); - indexBuffers.back().fill(dataBuffer); - } - - int vertexBufferIndex = 0; - for (const auto& vertexGroup : scene.vertexGroups) { - for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { - vAttributes.push_back(attribute); - vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); - } - vertexBufferBindings.push_back(vBufferBindings); - vBufferBindings.clear(); - vertexBufferIndex++; - } + vkcv::gui::GUI gui (core, windowHandle); - const vkcv::AttachmentDescription present_color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - core.getSwapchain().getFormat() + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(-8, 1, -0.5)); + cameraManager.getCamera(camHandle0).setNearFar(0.1f, 30.0f); + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(-8, 1, -0.5)); + cameraManager.getCamera(camHandle1).setNearFar(0.1f, 30.0f); + + vkcv::scene::Scene scene = vkcv::scene::Scene::load( + core, + std::filesystem::path(argc > 1 ? argv[1] : "assets/Sponza/Sponza.gltf"), + { + vkcv::asset::PrimitiveType::POSITION, + vkcv::asset::PrimitiveType::NORMAL, + vkcv::asset::PrimitiveType::TEXCOORD_0 + } ); - - const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - vk::Format::eD32Sfloat + + vkcv::PassHandle scenePass = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } ); - vkcv::PassConfig scenePassDefinition({ present_color_attachment, depth_attachment }); - vkcv::PassHandle scenePass = core.createPass(scenePassDefinition); - if (!scenePass) { std::cout << "Error. Could not create renderpass. Exiting." << std::endl; return EXIT_FAILURE; } - vkcv::ShaderProgram sceneShaderProgram{}; - sceneShaderProgram.addShader(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/vert.spv")); - sceneShaderProgram.addShader(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/frag.spv")); + vkcv::ShaderProgram sceneShaderProgram; + vkcv::shader::GLSLCompiler compiler; + + compiler.compileProgram(sceneShaderProgram, { + { vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" }, + { vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" } + }, nullptr); const std::vector<vkcv::VertexAttachment> vertexAttachments = sceneShaderProgram.getVertexAttachments(); std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { - bindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] })); } - const vkcv::VertexLayout sceneLayout(bindings); - - uint32_t setID = 0; - - std::vector<vkcv::DescriptorBinding> descriptorBindings = { sceneShaderProgram.getReflectedDescriptors()[setID] }; - - vkcv::SamplerHandle sampler = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT + const vkcv::VertexLayout sceneLayout { bindings }; + const auto& material0 = scene.getMaterial(0); + + vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + sceneShaderProgram, + scenePass, + { sceneLayout }, + { material0.getDescriptorSetLayout() } + ) ); - - std::vector<vkcv::Image> sceneImages; - std::vector<vkcv::DescriptorSetHandle> descriptorSets; - for (const auto& vertexGroup : scene.vertexGroups) { - descriptorSets.push_back(core.createDescriptorSet(descriptorBindings)); - - const auto& material = scene.materials[vertexGroup.materialIndex]; - - int baseColorIndex = material.baseColor; - if (baseColorIndex < 0) { - vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); - baseColorIndex = 0; - } - - vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; - - sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h)); - sceneImages.back().fill(sceneTexture.data.data()); - - vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) }; - setWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(1, sampler) }; - core.writeDescriptorSet(descriptorSets.back(), setWrites); - } - - const vkcv::PipelineConfig scenePipelineDefsinition{ - sceneShaderProgram, - UINT32_MAX, - UINT32_MAX, - scenePass, - {sceneLayout}, - { core.getDescriptorSet(descriptorSets[0]).layout }, - true }; - vkcv::PipelineHandle scenePipeline = core.createGraphicsPipeline(scenePipelineDefsinition); if (!scenePipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } - - vkcv::ImageHandle depthBuffer = core.createImage(vk::Format::eD32Sfloat, windowWidth, windowHeight).getHandle(); + + vkcv::ImageHandle depthBuffer; const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); - - std::vector<vkcv::DrawcallInfo> drawcalls; - for(int i = 0; i < scene.vertexGroups.size(); i++){ - vkcv::Mesh renderMesh(vertexBufferBindings[i], indexBuffers[i].getVulkanHandle(), scene.vertexGroups[i].numIndices); - - vkcv::DescriptorSetUsage descriptorUsage(0, core.getDescriptorSet(descriptorSets[i]).vulkanHandle); - - drawcalls.push_back(vkcv::DrawcallInfo(renderMesh, {descriptorUsage})); - } - - std::vector<glm::mat4> modelMatrices; - modelMatrices.resize(scene.vertexGroups.size(), glm::mat4(1.f)); - for (const auto &mesh : scene.meshes) { - const glm::mat4 m = arrayTo4x4Matrix(mesh.modelMatrix); - for (const auto &vertexGroupIndex : mesh.vertexGroups) { - modelMatrices[vertexGroupIndex] = m; - } - } - std::vector<glm::mat4> mvp; - - auto start = std::chrono::system_clock::now(); - while (window.isWindowOpen()) { - vkcv::Window::pollEvents(); - - if(window.getHeight() == 0 || window.getWidth() == 0) - continue; - - uint32_t swapchainWidth, swapchainHeight; - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } - - if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(vk::Format::eD32Sfloat, swapchainWidth, swapchainHeight).getHandle(); - - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + (swapchainHeight != core.getImageHeight(depthBuffer))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); } - - auto end = std::chrono::system_clock::now(); - auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - start = end; - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - glm::mat4 vp = cameraManager.getActiveCamera().getMVP(); - - mvp.clear(); - for (const auto& m : modelMatrices) { - mvp.push_back(vp * m); - } - - vkcv::PushConstantData pushConstantData((void*)mvp.data(), sizeof(glm::mat4)); + cameraManager.update(dt); const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - core.recordDrawcallsToCmdStream( - cmdStream, - scenePass, - scenePipeline, - pushConstantData, - drawcalls, - renderTargets); + auto recordMesh = [](const glm::mat4& MVP, const glm::mat4& M, + vkcv::PushConstants &pushConstants, + vkcv::Drawcall& drawcall) { + pushConstants.appendDrawcall(MVP); + }; + + scene.recordDrawcalls( + cmdStream, + cameraManager.getActiveCamera(), + scenePass, + scenePipeline, + sizeof(glm::mat4), + recordMesh, + renderTargets, + windowHandle + ); + core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); - core.endFrame(); - } + + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::Text("Deltatime %fms, %f", dt * 1000, 1/dt); + ImGui::End(); + + gui.endGUI(); + }); return 0; } diff --git a/projects/first_triangle/CMakeLists.txt b/projects/first_triangle/CMakeLists.txt index ba8c83c06fc804082e6a0a14c3c0414899ef3057..660979099b0fe0096478443a86ddf48c14bd61b0 100644 --- a/projects/first_triangle/CMakeLists.txt +++ b/projects/first_triangle/CMakeLists.txt @@ -2,24 +2,11 @@ cmake_minimum_required(VERSION 3.16) project(first_triangle) # setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# this should fix the execution path to load local files from the project -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - # adding source files to the project -add_executable(first_triangle src/main.cpp) - -# this should fix the execution path to load local files from the project (for MSVC) -if(MSVC) - set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - set_target_properties(first_triangle PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - - # in addition to setting the output directory, the working directory has to be set - # by default visual studio sets the working directory to the build directory, when using the debugger - set_target_properties(first_triangle PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -endif() +add_project(first_triangle src/main.cpp) # including headers of dependencies and the VkCV framework target_include_directories(first_triangle SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) diff --git a/projects/first_triangle/README.md b/projects/first_triangle/README.md new file mode 100644 index 0000000000000000000000000000000000000000..da8cde4e5ae9c1a1b3dfcd2c43204c6dfd26920a --- /dev/null +++ b/projects/first_triangle/README.md @@ -0,0 +1,9 @@ +# First triangle +An example project to show a triangle can be rendered with the VkCV framework + + + +## Details + +This project was the first step of the development to this framework. It was the proof of concept +the designed API can work to reduce the required overhead code for applications using Vulkan. diff --git a/projects/first_triangle/shaders/comp.spv b/projects/first_triangle/shaders/comp.spv deleted file mode 100644 index b414e36b2bea66dab00746298e536d029091e0fd..0000000000000000000000000000000000000000 Binary files a/projects/first_triangle/shaders/comp.spv and /dev/null differ diff --git a/projects/first_triangle/shaders/compile.bat b/projects/first_triangle/shaders/compile.bat deleted file mode 100644 index 17743a7c49cdfc6e091c43a42a0adb755a731682..0000000000000000000000000000000000000000 --- a/projects/first_triangle/shaders/compile.bat +++ /dev/null @@ -1,4 +0,0 @@ -%VULKAN_SDK%\Bin32\glslc.exe shader.vert -o vert.spv -%VULKAN_SDK%\Bin32\glslc.exe shader.frag -o frag.spv -%VULKAN_SDK%\Bin32\glslc.exe shader.comp -o comp.spv -pause \ No newline at end of file diff --git a/projects/first_triangle/shaders/frag.spv b/projects/first_triangle/shaders/frag.spv deleted file mode 100644 index cb13e606fc0041e24ff6a63c0ec7dcca466732aa..0000000000000000000000000000000000000000 Binary files a/projects/first_triangle/shaders/frag.spv and /dev/null differ diff --git a/projects/first_triangle/shaders/shader.comp b/projects/first_triangle/shaders/shader.comp deleted file mode 100644 index fad6cd0815f2f09bf92dcc3171e2e3723f5466df..0000000000000000000000000000000000000000 --- a/projects/first_triangle/shaders/shader.comp +++ /dev/null @@ -1,25 +0,0 @@ -#version 440 - -layout(std430, binding = 0) buffer testBuffer -{ - float test1[10]; - float test2[10]; - float test3[10]; -}; - -layout( push_constant ) uniform constants{ - float pushConstant; -}; - -layout(local_size_x = 5) in; - -void main(){ - - if(gl_GlobalInvocationID.x >= 10){ - return; - } - - test1[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x; - test2[gl_GlobalInvocationID.x] = 69; // nice! - test3[gl_GlobalInvocationID.x] = pushConstant; -} \ No newline at end of file diff --git a/projects/first_triangle/shaders/vert.spv b/projects/first_triangle/shaders/vert.spv deleted file mode 100644 index 03af5758ffff1b5b6505fe98b02044849026832d..0000000000000000000000000000000000000000 Binary files a/projects/first_triangle/shaders/vert.spv and /dev/null differ diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp index 20cfdddf5c1baa9e8727312daa36de94bd56672f..aae046da7f84bcf26017f5fe609732aa2e0e4df7 100644 --- a/projects/first_triangle/src/main.cpp +++ b/projects/first_triangle/src/main.cpp @@ -1,233 +1,96 @@ #include <iostream> #include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> #include <GLFW/glfw3.h> #include <vkcv/camera/CameraManager.hpp> -#include <chrono> - #include <vkcv/shader/GLSLCompiler.hpp> -#include <vkcv/gui/GUI.hpp> int main(int argc, const char** argv) { - const char* applicationName = "First Triangle"; + const std::string applicationName = "First Triangle"; const int windowWidth = 800; const int windowHeight = 600; - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - false - ); - + vkcv::Core core = vkcv::Core::create( - window, applicationName, VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain" } + { vk::QueueFlagBits::eGraphics }, + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } ); - - vkcv::gui::GUI gui (core, window); - - const auto& context = core.getContext(); - const vk::Instance& instance = context.getInstance(); - const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); - const vk::Device& device = context.getDevice(); - - struct vec3 { - float x, y, z; - }; - - const size_t n = 5027; - - auto testBuffer = core.createBuffer<vec3>(vkcv::BufferType::VERTEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL); - vec3 vec_data[n]; - - for (size_t i = 0; i < n; i++) { - vec_data[i] = { 42, static_cast<float>(i), 7 }; - } - - testBuffer.fill(vec_data); - auto triangleIndexBuffer = core.createBuffer<uint16_t>(vkcv::BufferType::INDEX, n, vkcv::BufferMemoryType::DEVICE_LOCAL); - uint16_t indices[3] = { 0, 1, 2 }; - triangleIndexBuffer.fill(&indices[0], sizeof(indices)); - - /*vec3* m = buffer.map(); - m[0] = { 0, 0, 0 }; - m[1] = { 0, 0, 0 }; - m[2] = { 0, 0, 0 }; - buffer.unmap();*/ - - vkcv::SamplerHandle sampler = core.createSampler( - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerMipmapMode::NEAREST, - vkcv::SamplerAddressMode::REPEAT + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + + vkcv::PassHandle trianglePass = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined } ); - std::cout << "Physical device: " << physicalDevice.getProperties().deviceName << std::endl; - - switch (physicalDevice.getProperties().vendorID) { - case 0x1002: std::cout << "Running AMD huh? You like underdogs, are you a Linux user?" << std::endl; break; - case 0x10DE: std::cout << "An NVidia GPU, how predictable..." << std::endl; break; - case 0x8086: std::cout << "Poor child, running on an Intel GPU, probably integrated..." - "or perhaps you are from the future with a dedicated one?" << std::endl; break; - case 0x13B5: std::cout << "ARM? What the hell are you running on, next thing I know you're trying to run Vulkan on a leg..." << std::endl; break; - default: std::cout << "Unknown GPU vendor?! Either you're on an exotic system or your driver is broken..." << std::endl; - } - - // an example attachment for passes that output to the window - const vkcv::AttachmentDescription present_color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - core.getSwapchain().getFormat()); - - vkcv::PassConfig trianglePassDefinition({ present_color_attachment }); - vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition); - - if (!trianglePass) - { + if (!trianglePass) { std::cout << "Error. Could not create renderpass. Exiting." << std::endl; return EXIT_FAILURE; } + + core.setDebugLabel(trianglePass, "Triangle Pass"); - vkcv::ShaderProgram triangleShaderProgram{}; + vkcv::ShaderProgram triangleShaderProgram; vkcv::shader::GLSLCompiler compiler; - compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("shaders/shader.vert"), - [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - triangleShaderProgram.addShader(shaderStage, path); - }); - - compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("shaders/shader.frag"), - [&triangleShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - triangleShaderProgram.addShader(shaderStage, path); - }); - - const vkcv::PipelineConfig trianglePipelineDefinition { - triangleShaderProgram, - (uint32_t)windowWidth, - (uint32_t)windowHeight, - trianglePass, - {}, - {}, - false - }; - - vkcv::PipelineHandle trianglePipeline = core.createGraphicsPipeline(trianglePipelineDefinition); + compiler.compileProgram(triangleShaderProgram, { + {vkcv::ShaderStage::VERTEX, "shaders/shader.vert"}, + { vkcv::ShaderStage::FRAGMENT, "shaders/shader.frag" } + }, nullptr); + + vkcv::GraphicsPipelineHandle trianglePipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + triangleShaderProgram, + trianglePass, + {}, + {} + ) + ); - if (!trianglePipeline) - { + if (!trianglePipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } + + core.setDebugLabel(trianglePipeline, "Triangle Pipeline"); - // Compute Pipeline - vkcv::ShaderProgram computeShaderProgram{}; - computeShaderProgram.addShader(vkcv::ShaderStage::COMPUTE, std::filesystem::path("shaders/comp.spv")); - - // take care, assuming shader has exactly one descriptor set - vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeShaderProgram.getReflectedDescriptors()[0]); - - vkcv::PipelineHandle computePipeline = core.createComputePipeline( - computeShaderProgram, - { core.getDescriptorSet(computeDescriptorSet).layout }); - - struct ComputeTestBuffer { - float test1[10]; - float test2[10]; - float test3[10]; - }; - - vkcv::Buffer computeTestBuffer = core.createBuffer<ComputeTestBuffer>(vkcv::BufferType::STORAGE, 1); - - vkcv::DescriptorWrites computeDescriptorWrites; - computeDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, computeTestBuffer.getHandle()) }; - core.writeDescriptorSet(computeDescriptorSet, computeDescriptorWrites); - - /* - * BufferHandle triangleVertices = core.createBuffer(vertices); - * BufferHandle triangleIndices = core.createBuffer(indices); - * - * // triangle Model creation goes here - * - * - * // attachment creation goes here - * PassHandle trianglePass = core.CreatePass(presentationPass); - * - * // shader creation goes here - * // material creation goes here - * - * PipelineHandle trianglePipeline = core.CreatePipeline(trianglePipeline); - */ - auto start = std::chrono::system_clock::now(); - - vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle(); - - const vkcv::Mesh renderMesh({}, triangleIndexBuffer.getVulkanHandle(), 3); - vkcv::DrawcallInfo drawcall(renderMesh, {}); + vkcv::VertexData vertexData; + vertexData.setCount(3); + + vkcv::InstanceDrawcall drawcall (vertexData); const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - cameraManager.getCamera(camIndex0).setPosition(glm::vec3(0, 0, -2)); - cameraManager.getCamera(camIndex1).setPosition(glm::vec3(0.0f, 0.0f, 0.0f)); - cameraManager.getCamera(camIndex1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f)); - - while (window.isWindowOpen()) - { - window.pollEvents(); + cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -2)); - uint32_t swapchainWidth, swapchainHeight; // No resizing = No problem - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + cameraManager.update(dt); - auto end = std::chrono::system_clock::now(); - auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - start = end; + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); - - vkcv::PushConstantData pushConstantData((void*)&mvp, sizeof(glm::mat4)); auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - + core.setDebugLabel(cmdStream, "Render Commands"); + core.recordDrawcallsToCmdStream( - cmdStream, - trianglePass, - trianglePipeline, - pushConstantData, - { drawcall }, - { swapchainInput }); - - const uint32_t dispatchSize[3] = { 2, 1, 1 }; - const float theMeaningOfLife = 42; - - core.recordComputeDispatchToCmdStream( - cmdStream, - computePipeline, - dispatchSize, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(computeDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData((void*)&theMeaningOfLife, sizeof(theMeaningOfLife))); - + cmdStream, + trianglePipeline, + vkcv::pushConstants<glm::mat4>(mvp), + { drawcall }, + { swapchainInput }, + windowHandle + ); + core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); - - gui.beginGUI(); - - ImGui::Begin("Hello world"); - ImGui::Text("This is a test!"); - ImGui::End(); - - gui.endGUI(); - - core.endFrame(); - } + }); + return 0; } diff --git a/projects/head_demo/.gitignore b/projects/head_demo/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..151f56c5bb7098a3beb7b8e049c0ade02914cd91 --- /dev/null +++ b/projects/head_demo/.gitignore @@ -0,0 +1 @@ +head_demo \ No newline at end of file diff --git a/projects/head_demo/CMakeLists.txt b/projects/head_demo/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..82aebb083054b80f3cf9cbb1bddb94f447e81903 --- /dev/null +++ b/projects/head_demo/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.16) +project(head_demo) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(head_demo src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(head_demo SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_asset_loader_include} + ${vkcv_camera_include} + ${vkcv_scene_include} + ${vkcv_shader_compiler_include} + ${vkcv_gui_include} + ${vkcv_effects_include} + ${vkcv_upscaling_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(head_demo + vkcv + ${vkcv_libraries} + vkcv_asset_loader + ${vkcv_asset_loader_libraries} + vkcv_camera vkcv_scene + vkcv_shader_compiler + vkcv_gui + vkcv_effects + vkcv_upscaling) diff --git a/projects/head_demo/README.md b/projects/head_demo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c9b0bebc7cda14becd29ef1a6cba950e75b9bd79 --- /dev/null +++ b/projects/head_demo/README.md @@ -0,0 +1,11 @@ +# Head demo +An example project to show how the VkCV framework could be used for medical applications + + + +## Details + +This project renders a mesh from a human skull by [HannahNewey](https://sketchfab.com/HannahNewey) +and splits the rendering on three axes into diffusive shading and drawing the vertices as point +cloud. In a similar way you could visualize different layers of a human body to make a specific +part the visual focus without blending the layers above completely away. diff --git a/projects/head_demo/assets/shaders/clip.inc b/projects/head_demo/assets/shaders/clip.inc new file mode 100644 index 0000000000000000000000000000000000000000..edb5ac3ccfe5d7f6ad1e59dd88bee4446b85118f --- /dev/null +++ b/projects/head_demo/assets/shaders/clip.inc @@ -0,0 +1,19 @@ + +#define CLIP_SCALE 10000.0f + +vec4 clipPosition(vec4 pos) { + return vec4( + max(clipX, pos.x), + max(clipY, pos.y), + max(clipZ, pos.z), + 1.0f / CLIP_SCALE + ); +} + +vec4 clipByLimit(vec4 pos) { + if (pos.x / pos.w < clipLimit) { + return vec4(pos.xyz / pos.w, 1.0f); + } else { + return vec4(clipLimit, pos.y / pos.w, pos.z / pos.w, 1.0f); + } +} diff --git a/projects/head_demo/assets/shaders/red.frag b/projects/head_demo/assets/shaders/red.frag new file mode 100644 index 0000000000000000000000000000000000000000..35735be779edd0bf88890638329dc53f6b691f57 --- /dev/null +++ b/projects/head_demo/assets/shaders/red.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec3 passEdge; + +layout(location = 0) out vec3 outColor; + +void main() { + outColor = (0.1f + max(dot(passNormal, vec3(1.0f, -1.0f, 0.5f)), 0.0f) * 0.9f) * (passEdge + 0.5f); +} \ No newline at end of file diff --git a/projects/head_demo/assets/shaders/shader.frag b/projects/head_demo/assets/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..b8756a6b84af4e93f9ac932ec01d28e4f2e9638b --- /dev/null +++ b/projects/head_demo/assets/shaders/shader.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; + +layout(location = 0) out vec3 outColor; + +void main() { + outColor = (vec3(0.3f) + max(dot(passNormal, vec3(1.0f, -1.0f, 0.5f)), 0.0f) * vec3(0.7f)); +} \ No newline at end of file diff --git a/projects/head_demo/assets/shaders/shader.geom b/projects/head_demo/assets/shaders/shader.geom new file mode 100644 index 0000000000000000000000000000000000000000..275b300ee3466e117876aa46a6ea1cde11f6f17d --- /dev/null +++ b/projects/head_demo/assets/shaders/shader.geom @@ -0,0 +1,53 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(location = 0) in vec3 geomNormal[]; + +layout(location = 0) out vec3 passNormal; + +layout(set=1, binding=0) uniform clipBuffer { + float clipLimit; + float clipX; + float clipY; + float clipZ; +}; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +#include "clip.inc" + +void main() { + vec4 v0 = gl_in[0].gl_Position; + vec4 v1 = gl_in[1].gl_Position; + vec4 v2 = gl_in[2].gl_Position; + + v0 = clipPosition(v0 / CLIP_SCALE); + v1 = clipPosition(v1 / CLIP_SCALE); + v2 = clipPosition(v2 / CLIP_SCALE); + + float dx = abs(v0.x - clipX) + abs(v1.x - clipX) + abs(v2.x - clipX); + float dy = abs(v0.y - clipY) + abs(v1.y - clipY) + abs(v2.y - clipY); + float dz = abs(v0.z - clipZ) + abs(v1.z - clipZ) + abs(v2.z - clipZ); + + if (dx * dy * dz > 0.0f) { + gl_Position = mvp * (v0 * CLIP_SCALE); + passNormal = geomNormal[0]; + EmitVertex(); + + gl_Position = mvp * (v1 * CLIP_SCALE); + passNormal = geomNormal[1]; + EmitVertex(); + + gl_Position = mvp * (v2 * CLIP_SCALE); + passNormal = geomNormal[2]; + EmitVertex(); + + EndPrimitive(); + } +} diff --git a/projects/head_demo/assets/shaders/shader.vert b/projects/head_demo/assets/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..26e43e9c89dc2ffb2355d60be8288fa9832f8baa --- /dev/null +++ b/projects/head_demo/assets/shaders/shader.vert @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; + +layout(location = 0) out vec3 geomNormal; + +void main() { + gl_Position = vec4(inPosition, 1.0); + geomNormal = inNormal; +} \ No newline at end of file diff --git a/projects/head_demo/assets/shaders/wired.geom b/projects/head_demo/assets/shaders/wired.geom new file mode 100644 index 0000000000000000000000000000000000000000..689e073dde7fd993d164cfea0b7ca777d79f8508 --- /dev/null +++ b/projects/head_demo/assets/shaders/wired.geom @@ -0,0 +1,53 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +layout(triangles) in; +layout(points, max_vertices = 1) out; + +layout(location = 0) in vec3 geomNormal[]; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec3 passEdge; + +layout(set=1, binding=0) uniform clipBuffer { + float clipLimit; + float clipX; + float clipY; + float clipZ; +}; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +#include "clip.inc" + +void main() { + vec4 v0 = gl_in[0].gl_Position; + vec4 v1 = gl_in[1].gl_Position; + vec4 v2 = gl_in[2].gl_Position; + + v0 = clipPosition(v0 / CLIP_SCALE); + v1 = clipPosition(v1 / CLIP_SCALE); + v2 = clipPosition(v2 / CLIP_SCALE); + + float dx = abs(v0.x - clipX) + abs(v1.x - clipX) + abs(v2.x - clipX); + float dy = abs(v0.y - clipY) + abs(v1.y - clipY) + abs(v2.y - clipY); + float dz = abs(v0.z - clipZ) + abs(v1.z - clipZ) + abs(v2.z - clipZ); + + if (dx * dy * dz <= 0.0f) { + v0 = clipByLimit(mvp * gl_in[0].gl_Position); + v1 = clipByLimit(mvp * gl_in[1].gl_Position); + v2 = clipByLimit(mvp * gl_in[2].gl_Position); + + if ((v0.x < clipLimit) || (v1.x < clipLimit) || (v2.x < clipLimit)) { + gl_Position = (v0 + v1 + v2) / 3; + passNormal = (geomNormal[0] + geomNormal[1] + geomNormal[2]) / 3; + passEdge = vec3(dx, dy, dz); + EmitVertex(); + + EndPrimitive(); + } + } +} diff --git a/projects/head_demo/assets/skull/license.txt b/projects/head_demo/assets/skull/license.txt new file mode 100644 index 0000000000000000000000000000000000000000..6816d6d659087b54dfc6b8e0c2c0e158daa001f0 --- /dev/null +++ b/projects/head_demo/assets/skull/license.txt @@ -0,0 +1,11 @@ +Model Information: +* title: The Anatomy of the Human Skull +* source: https://sketchfab.com/3d-models/the-anatomy-of-the-human-skull-baf6ac7b781a46218dca2b59dee58817 +* author: HannahNewey (https://sketchfab.com/HannahNewey) + +Model License: +* license type: CC-BY-NC-SA-4.0 (http://creativecommons.org/licenses/by-nc-sa/4.0/) +* requirements: Author must be credited. No commercial use. Modified versions must have the same license. + +If you use this 3D model in your project be sure to copy paste this credit wherever you share it: +This work is based on "The Anatomy of the Human Skull" (https://sketchfab.com/3d-models/the-anatomy-of-the-human-skull-baf6ac7b781a46218dca2b59dee58817) by HannahNewey (https://sketchfab.com/HannahNewey) licensed under CC-BY-NC-SA-4.0 (http://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/projects/head_demo/assets/skull/scene.bin b/projects/head_demo/assets/skull/scene.bin new file mode 100644 index 0000000000000000000000000000000000000000..e995091c9d2f10b15959a65d3b0e0de3c9edb0e4 --- /dev/null +++ b/projects/head_demo/assets/skull/scene.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df3cb5e715426a8e7a3c4883eb7c53022e09011ded9ae385cd6bc59824129ed8 +size 78176672 diff --git a/projects/head_demo/assets/skull/scene.gltf b/projects/head_demo/assets/skull/scene.gltf new file mode 100644 index 0000000000000000000000000000000000000000..6a2b75fa5af1e826bf3851db576dd3051e7b77c3 --- /dev/null +++ b/projects/head_demo/assets/skull/scene.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cd4f673e0a555dc138b96e07be60546a799f9ef11837870eae27b16ffd5c1c7 +size 36442 diff --git a/projects/head_demo/assets/skull_scaled/scene.bin b/projects/head_demo/assets/skull_scaled/scene.bin new file mode 100644 index 0000000000000000000000000000000000000000..3edc8865ed5465c33cb8c9d2f2b0eb83b1a5b599 --- /dev/null +++ b/projects/head_demo/assets/skull_scaled/scene.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:685e971ce4e99be286464c56a373e528dc507ce18b5bfdff45f90db27e49176b +size 56596296 diff --git a/projects/head_demo/assets/skull_scaled/scene.gltf b/projects/head_demo/assets/skull_scaled/scene.gltf new file mode 100644 index 0000000000000000000000000000000000000000..72087f182e4cd1ce1a73f7af9db1d36608df2029 --- /dev/null +++ b/projects/head_demo/assets/skull_scaled/scene.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f05a4c8f979dab2793540bafe2b392133d64af028a0ff97f0159dbc52fc20230 +size 4843 diff --git a/projects/head_demo/src/main.cpp b/projects/head_demo/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1e9882852bc56ab9834011e5b27d4b5c9ff081f --- /dev/null +++ b/projects/head_demo/src/main.cpp @@ -0,0 +1,267 @@ +#include <iostream> +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/scene/Scene.hpp> +#include <vkcv/effects/BloomAndFlaresEffect.hpp> +#include <vkcv/upscaling/FSRUpscaling.hpp> + +int main(int argc, const char** argv) { + const std::string applicationName = "Head Demo"; + + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + features.requireFeature([](vk::PhysicalDeviceFeatures& features) { + features.setGeometryShader(true); + }); + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + features + ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + vkcv::camera::CameraManager cameraManager(window); + + vkcv::gui::GUI gui (core, windowHandle); + + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(15.5f, 0, 0)); + cameraManager.getCamera(camHandle0).setNearFar(0.1f, 30.0f); + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(15.5f, 0, 0)); + cameraManager.getCamera(camHandle1).setNearFar(0.1f, 30.0f); + + vkcv::scene::Scene scene = vkcv::scene::Scene::load( + core, + std::filesystem::path(argc > 1 ? argv[1] : "assets/skull_scaled/scene.gltf"), + { + vkcv::asset::PrimitiveType::POSITION, + vkcv::asset::PrimitiveType::NORMAL + } + ); + + vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + + vkcv::PassHandle linePass = vkcv::passFormats(core, { colorFormat, vk::Format::eD32Sfloat }); + vkcv::PassHandle scenePass = vkcv::passFormats(core, { colorFormat, vk::Format::eD32Sfloat }, false); + + if ((!scenePass) || (!linePass)) { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram sceneShaderProgram; + vkcv::ShaderProgram lineShaderProgram; + vkcv::shader::GLSLCompiler compiler; + + compiler.compileProgram(sceneShaderProgram, { + { vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" }, + { vkcv::ShaderStage::GEOMETRY, "assets/shaders/shader.geom" }, + { vkcv::ShaderStage::FRAGMENT, "assets/shaders/shader.frag" } + }, nullptr); + + compiler.compileProgram(lineShaderProgram, { + { vkcv::ShaderStage::VERTEX, "assets/shaders/shader.vert" }, + { vkcv::ShaderStage::GEOMETRY, "assets/shaders/wired.geom" }, + { vkcv::ShaderStage::FRAGMENT, "assets/shaders/red.frag" } + }, nullptr); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = sceneShaderProgram.getVertexAttachments(); + std::vector<vkcv::VertexBinding> bindings; + + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] })); + } + + const auto& clipBindings = sceneShaderProgram.getReflectedDescriptors().at(1); + + auto clipDescriptorSetLayout = core.createDescriptorSetLayout(clipBindings); + auto clipDescriptorSet = core.createDescriptorSet(clipDescriptorSetLayout); + + float clipLimit = 1.0f; + float clipX = 0.0f; + float clipY = 0.0f; + float clipZ = 0.0f; + + auto clipBuffer = vkcv::buffer<float>(core, vkcv::BufferType::UNIFORM, 4); + clipBuffer.fill({ clipLimit, -clipX, -clipY, -clipZ }); + + vkcv::DescriptorWrites clipWrites; + clipWrites.writeUniformBuffer(0, clipBuffer.getHandle()); + + core.writeDescriptorSet(clipDescriptorSet, clipWrites); + + float mouseX = -0.0f; + bool dragLimit = false; + + window.e_mouseMove.add([&](double x, double y) { + double cx = (x - window.getWidth() * 0.5); + double dx = cx / window.getWidth(); + + mouseX = 2.0f * static_cast<float>(dx); + + if (dragLimit) { + clipLimit = mouseX; + } + }); + + window.e_mouseButton.add([&](int button, int action, int mods) { + if ((std::abs(mouseX - clipLimit) < 0.1f) && (action == GLFW_PRESS)) { + dragLimit = true; + } else { + dragLimit = false; + } + }); + + const vkcv::VertexLayout sceneLayout { bindings }; + const auto& material0 = scene.getMaterial(0); + + vkcv::GraphicsPipelineHandle scenePipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + sceneShaderProgram, + scenePass, + { sceneLayout }, + { material0.getDescriptorSetLayout(), clipDescriptorSetLayout } + ) + ); + + vkcv::GraphicsPipelineHandle linePipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + lineShaderProgram, + linePass, + { sceneLayout }, + { material0.getDescriptorSetLayout(), clipDescriptorSetLayout } + ) + ); + + if ((!scenePipeline) || (!linePipeline)) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::ImageHandle depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainExtent.width, + swapchainExtent.height + ) + ); + + vkcv::ImageConfig colorBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + colorBufferConfig.setSupportingStorage(true); + colorBufferConfig.setSupportingColorAttachment(true); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core); + vkcv::upscaling::FSRUpscaling upscaling (core); + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((swapchainWidth != swapchainExtent.width) || + ((swapchainHeight != swapchainExtent.height))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); + + colorBufferConfig.setWidth(swapchainWidth); + colorBufferConfig.setHeight(swapchainHeight); + + colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + + swapchainExtent.width = swapchainWidth; + swapchainExtent.height = swapchainHeight; + } + + cameraManager.update(dt); + + clipBuffer.fill({ clipLimit, -clipX, -clipY, -clipZ }); + + const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + auto recordMesh = [&](const glm::mat4& MVP, const glm::mat4& M, + vkcv::PushConstants &pushConstants, + vkcv::Drawcall& drawcall) { + pushConstants.appendDrawcall(MVP); + drawcall.useDescriptorSet(1, clipDescriptorSet); + }; + + scene.recordDrawcalls( + cmdStream, + cameraManager.getActiveCamera(), + linePass, + linePipeline, + sizeof(glm::mat4), + recordMesh, + renderTargets, + windowHandle + ); + + bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer); + + scene.recordDrawcalls( + cmdStream, + cameraManager.getActiveCamera(), + scenePass, + scenePipeline, + sizeof(glm::mat4), + recordMesh, + renderTargets, + windowHandle + ); + + core.prepareImageForSampling(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, swapchainInput); + upscaling.recordUpscaling(cmdStream, colorBuffer, swapchainInput); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::SliderFloat("Clip X", &clipX, -1.0f, 1.0f); + ImGui::SliderFloat("Clip Y", &clipY, -1.0f, 1.0f); + ImGui::SliderFloat("Clip Z", &clipZ, -1.0f, 1.0f); + ImGui::Text("Mesh by HannahNewey (https://sketchfab.com/HannahNewey)"); + ImGui::End(); + + gui.endGUI(); + }); + + return 0; +} diff --git a/projects/indirect_dispatch/.gitignore b/projects/indirect_dispatch/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5f18d9c205e538dabeb0282640bede0359edc33d --- /dev/null +++ b/projects/indirect_dispatch/.gitignore @@ -0,0 +1 @@ +indirect_dispatch diff --git a/projects/indirect_dispatch/CMakeLists.txt b/projects/indirect_dispatch/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ad59d5b24efa1af1b387253b4c007fd25c50500f --- /dev/null +++ b/projects/indirect_dispatch/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 3.16) +project(indirect_dispatch) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(indirect_dispatch src/main.cpp) + +target_sources(indirect_dispatch PRIVATE + src/App.hpp + src/App.cpp + + src/AppConfig.hpp + src/MotionBlurConfig.hpp + + src/AppSetup.hpp + src/AppSetup.cpp + + src/MotionBlur.hpp + src/MotionBlur.cpp + + src/MotionBlurSetup.hpp + src/MotionBlurSetup.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(indirect_dispatch SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_testing_include} + ${vkcv_camera_include} + ${vkcv_shader_compiler_include} + ${vkcv_gui_include} + ${vkcv_upscaling_include} +) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(indirect_dispatch + vkcv + ${vkcv_libraries} + vkcv_asset_loader + ${vkcv_asset_loader_libraries} + vkcv_testing + vkcv_camera + vkcv_shader_compiler + vkcv_gui + vkcv_upscaling +) \ No newline at end of file diff --git a/projects/indirect_dispatch/README.md b/projects/indirect_dispatch/README.md new file mode 100644 index 0000000000000000000000000000000000000000..675e61d402b4d45c362363da25c1514695cd213d --- /dev/null +++ b/projects/indirect_dispatch/README.md @@ -0,0 +1,10 @@ +# Indirect dispatch +An example project to show usage of indirect compute shader dispatching + + + +## Details + +The project shows off indirect dispatching of compute shaders for an implementation of motion +blur. Additionally, the calculated motion vectors for the motion blur can be used for temporal +upscaling techniques such as FSR2. diff --git a/projects/indirect_dispatch/assets/models/cube.bin b/projects/indirect_dispatch/assets/models/cube.bin new file mode 100644 index 0000000000000000000000000000000000000000..728d38cd39cd10c30a93c15eef021cb0cf7dda74 --- /dev/null +++ b/projects/indirect_dispatch/assets/models/cube.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccc59e0be3552b4347457bc935d8e548b52f12ca91716a0f0dc37d5bac65f123 +size 840 diff --git a/projects/indirect_dispatch/assets/models/cube.gltf b/projects/indirect_dispatch/assets/models/cube.gltf new file mode 100644 index 0000000000000000000000000000000000000000..ef975c326c71ec1a2fa650a422989534f1c32191 --- /dev/null +++ b/projects/indirect_dispatch/assets/models/cube.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0072448af64bdebffe8eec5a7f32f110579b1a256cd97438bf227e4cc4a87328 +size 2571 diff --git a/projects/indirect_dispatch/assets/models/grid.png b/projects/indirect_dispatch/assets/models/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..5f40eee62f7f9dba3dc156ff6a3653ea2e7f5391 --- /dev/null +++ b/projects/indirect_dispatch/assets/models/grid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a11c33e4935d93723ab11f597f2aca1ca1ff84af66f2e2d10a01580eb0b7831a +size 40135 diff --git a/projects/indirect_dispatch/assets/models/ground.bin b/projects/indirect_dispatch/assets/models/ground.bin new file mode 100644 index 0000000000000000000000000000000000000000..e29e4f18552def1ac64c167d994be959f82e35c7 --- /dev/null +++ b/projects/indirect_dispatch/assets/models/ground.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8e20cd1c62da3111536283517b63a149f258ea82b1dff8ddafdb79020065b7c +size 140 diff --git a/projects/indirect_dispatch/assets/models/ground.gltf b/projects/indirect_dispatch/assets/models/ground.gltf new file mode 100644 index 0000000000000000000000000000000000000000..6935d3e21a06da1629087c9b0b7f957c57feaf6e --- /dev/null +++ b/projects/indirect_dispatch/assets/models/ground.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a12b8d7cca8110d4ffa9bc4a2223286d1ccfd9c087739a75294e0a3fbfb65c5 +size 2840 diff --git a/projects/indirect_dispatch/assets/shaders/gammaCorrection.comp b/projects/indirect_dispatch/assets/shaders/gammaCorrection.comp new file mode 100644 index 0000000000000000000000000000000000000000..7a6e129d7f8658d3ea424d35b809a3384d12bccc --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/gammaCorrection.comp @@ -0,0 +1,24 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inTexture; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rgba8) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + ivec2 outImageRes = imageSize(outImage); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(coord, outImageRes))) + return; + + vec2 uv = vec2(coord) / outImageRes; + vec3 linearColor = texture(sampler2D(inTexture, textureSampler), uv).rgb; + + vec3 gammaCorrected = pow(linearColor, vec3(1 / 2.2)); + + imageStore(outImage, coord, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/mesh.frag b/projects/indirect_dispatch/assets/shaders/mesh.frag new file mode 100644 index 0000000000000000000000000000000000000000..531c9cbf8b5e097af618d2ca639821a62a30611d --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/mesh.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform texture2D albedoTexture; +layout(set=0, binding=1) uniform sampler textureSampler; + +void main() { + vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + vec3 N = normalize(passNormal); + float light = max(N.y * 0.5 + 0.5, 0); + outColor = light * albedo; +} \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shader.vert b/projects/indirect_dispatch/assets/shaders/mesh.vert similarity index 68% rename from projects/bloom/resources/shaders/shader.vert rename to projects/indirect_dispatch/assets/shaders/mesh.vert index 926f86af2860cb57c44d2d5ee78712b6ae155e5c..734fd63cdee66e5fbf61cc427ca21fae18a31d82 100644 --- a/projects/bloom/resources/shaders/shader.vert +++ b/projects/indirect_dispatch/assets/shaders/mesh.vert @@ -7,7 +7,6 @@ layout(location = 2) in vec2 inUV; layout(location = 0) out vec3 passNormal; layout(location = 1) out vec2 passUV; -layout(location = 2) out vec3 passPos; layout( push_constant ) uniform constants{ mat4 mvp; @@ -16,7 +15,6 @@ layout( push_constant ) uniform constants{ void main() { gl_Position = mvp * vec4(inPosition, 1.0); - passNormal = mat3(model) * inNormal; // assuming no weird stuff like shearing or non-uniform scaling + passNormal = (model * vec4(inNormal, 0)).xyz; passUV = inUV; - passPos = (model * vec4(inPosition, 1)).xyz; } \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlur.comp b/projects/indirect_dispatch/assets/shaders/motionBlur.comp new file mode 100644 index 0000000000000000000000000000000000000000..091c21aa7ddfe9db1780aa64adc77fd5457a3843 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlur.comp @@ -0,0 +1,194 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlur.inc" +#include "motionBlurConfig.inc" +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) uniform texture2D inColor; +layout(set=0, binding=1) uniform texture2D inDepth; +layout(set=0, binding=2) uniform texture2D inMotionFullRes; +layout(set=0, binding=3) uniform texture2D inMotionNeighbourhoodMax; +layout(set=0, binding=4) uniform sampler nearestSampler; +layout(set=0, binding=5, r11f_g11f_b10f) uniform image2D outImage; + +layout(set=0, binding=6) buffer WorkTileBuffer { + WorkTiles workTiles; +}; + +layout(local_size_x = motionTileSize, local_size_y = motionTileSize, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + // computed from delta time and shutter speed + float motionScaleFactor; + // camera planes are needed to linearize depth + float cameraNearPlane; + float cameraFarPlane; + float motionTileOffsetLength; +}; + +float linearizeDepth(float depth, float near, float far){ + return near * far / (far + depth * (near - far)); +} + +struct SampleData{ + vec3 color; + float depthLinear; + vec2 coordinate; + vec2 motion; + float velocityPixels; +}; + +struct PointSpreadCompare{ + float foreground; + float background; +}; + +// results in range [0, 1] +// computes if the sample pixel in the foreground would blur over the main pixel and if the sample pixel in the background would be part of the main pixel background +// contribution depends on if the distance between pixels is smaller than it's velocity +// note that compared to the constant falloff used in McGuire's papers this function from Jimenez is constant until the last pixel +// this is important for the later gradient computation +PointSpreadCompare samplePointSpreadCompare(SampleData mainPixel, SampleData samplePixel){ + + float sampleOffset = distance(mainPixel.coordinate, samplePixel.coordinate); + + PointSpreadCompare pointSpread; + pointSpread.foreground = clamp(1 - sampleOffset + samplePixel.velocityPixels, 0, 1); + pointSpread.background = clamp(1 - sampleOffset + mainPixel.velocityPixels, 0, 1); + + return pointSpread; +} + +struct DepthClassification{ + float foreground; + float background; +}; + +// classifies depthSample compared to depthMain in regards to being in the fore- or background +// the range is [0, 1] and sums to 1 +DepthClassification sampleDepthClassification(SampleData mainPixel, SampleData samplePixel){ + + const float softDepthExtent = 0.1; + + DepthClassification classification; + // only the sign is different, so the latter term will cancel out on addition, so only two times 0.5 remains which sums to one + classification.foreground = clamp(0.5 + (mainPixel.depthLinear - samplePixel.depthLinear) / softDepthExtent, 0, 1); + classification.background = clamp(0.5 - (mainPixel.depthLinear - samplePixel.depthLinear) / softDepthExtent, 0, 1); + return classification; +} + +// reconstruction filter and helper functions from "Next Generation Post Processing in Call of Duty Advanced Warfare", Jimenez +// returns value in range [0, 1] +float computeSampleWeigth(SampleData mainPixel, SampleData samplePixel){ + + PointSpreadCompare pointSpread = samplePointSpreadCompare( mainPixel, samplePixel); + DepthClassification depthClassification = sampleDepthClassification(mainPixel, samplePixel); + + return + depthClassification.foreground * pointSpread.foreground + + depthClassification.background * pointSpread.background; +} + +SampleData loadSampleData(vec2 uv){ + + SampleData data; + data.color = texture(sampler2D(inColor, nearestSampler), uv).rgb; + data.coordinate = ivec2(uv * imageSize(outImage)); + data.motion = processMotionVector(texture(sampler2D(inMotionFullRes, nearestSampler), uv).rg, motionScaleFactor, imageSize(outImage)); + data.velocityPixels = length(data.motion * imageSize(outImage)); + data.depthLinear = texture(sampler2D(inDepth, nearestSampler), uv).r; + data.depthLinear = linearizeDepth(data.depthLinear, cameraNearPlane, cameraFarPlane); + + return data; +} + +void main(){ + + uint tileIndex = gl_WorkGroupID.x; + ivec2 tileCoordinates = workTiles.tileXY[tileIndex]; + ivec2 coord = ivec2(tileCoordinates * motionTileSize + gl_LocalInvocationID.xy); + + if(any(greaterThanEqual(coord, imageSize(outImage)))) + return; + + ivec2 textureRes = textureSize(sampler2D(inColor, nearestSampler), 0); + vec2 uv = vec2(coord + 0.5) / textureRes; // + 0.5 to shift uv into pixel center + + // the motion tile lookup is jittered, so the hard edges in the blur are replaced by noise + // dither is shifted, so it does not line up with motion tiles + float motionOffset = motionTileOffsetLength * (dither(coord + ivec2(ditherSize / 2)) * 2 - 1); + vec2 motionNeighbourhoodMax = processMotionVector(texelFetch(sampler2D(inMotionNeighbourhoodMax, nearestSampler), ivec2(coord + motionOffset) / motionTileSize, 0).rg, motionScaleFactor, imageSize(outImage)); + + SampleData mainPixel = loadSampleData(uv); + + // early out on movement less than half a pixel + if(length(motionNeighbourhoodMax * imageSize(outImage)) <= 0.5){ + imageStore(outImage, coord, vec4(mainPixel.color, 0.f)); + return; + } + + vec3 color = vec3(0); + float weightSum = 0; + + // clamping start and end points avoids artifacts at image borders + // the sampler clamps the sample uvs anyways, but without clamping here, many samples can be stuck at the border + vec2 uvStart = clamp(uv - motionNeighbourhoodMax, 0, 1); + vec2 uvEnd = clamp(uv + motionNeighbourhoodMax, 0, 1); + + // samples are placed evenly, but the entire filter is jittered + // dither returns either 0 or 1 + // the sampleUV code expects an offset in range [-0.5, 0.5], so the dither is rescaled to a binary -0.25/0.25 + float random = dither(coord) * 0.5 - 0.25; + + const int sampleCountHalf = 8; + + // two samples are processed at a time to allow for mirrored background reconstruction + for(int i = 0; i < sampleCountHalf; i++){ + + vec2 sampleUV1 = mix(uv, uvEnd, (i + random + 1) / float(sampleCountHalf + 1)); + vec2 sampleUV2 = mix(uv, uvStart, (i + random + 1) / float(sampleCountHalf + 1)); + + SampleData sample1 = loadSampleData(sampleUV1); + SampleData sample2 = loadSampleData(sampleUV2); + + float weight1 = computeSampleWeigth(mainPixel, sample1); + float weight2 = computeSampleWeigth(mainPixel, sample2); + + bool mirroredBackgroundReconstruction = true; + if(mirroredBackgroundReconstruction){ + // see Jimenez paper for details and comparison + // problem is that in the foreground the background is reconstructed, which is blurry + // in the background the background is obviously known, so it is sharper + // at the border between fore- and background this causes a discontinuity + // to fix this the weights are mirrored on this border, effectively reconstructing the background, even though it is known + + // these bools check if sample1 is an affected background pixel (further away and slower moving than sample2) + bool inBackground = sample1.depthLinear > sample2.depthLinear; + bool blurredOver = sample1.velocityPixels < sample2.velocityPixels; + + // this mirrors the weights depending on the results: + // if both conditions are true, then weight2 is mirrored to weight1 + // if both conditions are false, then weight1 is mirrored to weight2, as sample2 is an affected background pixel + // if only one condition is true, then the weights are kept as is + weight1 = inBackground && blurredOver ? weight2 : weight1; + weight2 = inBackground || blurredOver ? weight2 : weight1; + } + + weightSum += weight1; + weightSum += weight2; + + color += sample1.color * weight1; + color += sample2.color * weight2; + } + + // normalize color and weight + weightSum /= sampleCountHalf * 2; + color /= sampleCountHalf * 2; + + // the main color is considered the background + // the weight sum can be interpreted as the alpha of the combined samples, see Jimenez paper + color += (1 - weightSum) * mainPixel.color; + + imageStore(outImage, coord, vec4(color, 0.f)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlur.inc b/projects/indirect_dispatch/assets/shaders/motionBlur.inc new file mode 100644 index 0000000000000000000000000000000000000000..6fdaf4c5f5e4b07a3111946b0732137f42f295ef --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlur.inc @@ -0,0 +1,35 @@ +#ifndef MOTION_BLUR +#define MOTION_BLUR + +#include "motionBlurConfig.inc" + +// see "A Reconstruction Filter for Plausible Motion Blur", section 2.2 +vec2 processMotionVector(vec2 motion, float motionScaleFactor, ivec2 imageResolution){ + // every frame a pixel should blur over the distance it moves + // as we blur in two directions (where it was and where it will be) we must half the motion + vec2 motionHalf = motion * 0.5; + vec2 motionScaled = motionHalf * motionScaleFactor; // scale factor contains shutter speed and delta time + + // pixels are anisotropic, so the ratio for clamping the velocity is computed in pixels instead of uv coordinates + vec2 motionPixel = motionScaled * imageResolution; + float velocityPixels = length(motionPixel); + + float epsilon = 0.0001; + + // this clamps the motion to not exceed the radius given by the motion tile size + return motionScaled * max(0.5, min(velocityPixels, motionTileSize)) / (velocityPixels + epsilon); +} + +const int ditherSize = 4; + +// simple binary dither pattern +// could be optimized to avoid modulo and branch +float dither(ivec2 coord){ + + bool x = coord.x % ditherSize < (ditherSize / 2); + bool y = coord.y % ditherSize < (ditherSize / 2); + + return x ^^ y ? 1 : 0; +} + +#endif // #ifndef MOTION_BLUR \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurColorCopy.comp b/projects/indirect_dispatch/assets/shaders/motionBlurColorCopy.comp new file mode 100644 index 0000000000000000000000000000000000000000..1d8f210c86c2670241fa1d011835b120a39eddc0 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurColorCopy.comp @@ -0,0 +1,29 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlurConfig.inc" +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) uniform texture2D inColor; +layout(set=0, binding=1) uniform sampler nearestSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outImage; + +layout(set=0, binding=3) buffer WorkTileBuffer { + WorkTiles workTiles; +}; + +layout(local_size_x = motionTileSize, local_size_y = motionTileSize, local_size_z = 1) in; + +void main(){ + + uint tileIndex = gl_WorkGroupID.x; + ivec2 tileCoordinates = workTiles.tileXY[tileIndex]; + ivec2 coordinate = ivec2(tileCoordinates * motionTileSize + gl_LocalInvocationID.xy); + + if(any(greaterThanEqual(coordinate, imageSize(outImage)))) + return; + + vec3 color = texelFetch(sampler2D(inColor, nearestSampler), coordinate, 0).rgb; + + imageStore(outImage, coordinate, vec4(color, 0.f)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurConfig.inc b/projects/indirect_dispatch/assets/shaders/motionBlurConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..5b8679da119d84242c55d7d89de80ed8b64e5cc9 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurConfig.inc @@ -0,0 +1,8 @@ +#ifndef MOTION_BLUR_CONFIG +#define MOTION_BLUR_CONFIG + +const int motionTileSize = 16; +const int maxMotionBlurWidth = 3840; +const int maxMotionBlurHeight = 2160; + +#endif // #ifndef MOTION_BLUR_CONFIG \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurFastPath.comp b/projects/indirect_dispatch/assets/shaders/motionBlurFastPath.comp new file mode 100644 index 0000000000000000000000000000000000000000..2e27ebedcc4be1da93ff89a187fe1d3e992e8d22 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurFastPath.comp @@ -0,0 +1,68 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlur.inc" +#include "motionBlurConfig.inc" +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) uniform texture2D inColor; +layout(set=0, binding=1) uniform texture2D inMotionNeighbourhoodMax; +layout(set=0, binding=2) uniform sampler nearestSampler; +layout(set=0, binding=3, r11f_g11f_b10f) uniform image2D outImage; + +layout(set=0, binding=4) buffer WorkTileBuffer { + WorkTiles workTiles; +}; + +layout(local_size_x = motionTileSize, local_size_y = motionTileSize, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + // computed from delta time and shutter speed + float motionScaleFactor; +}; + +void main(){ + + uint tileIndex = gl_WorkGroupID.x; + ivec2 tileCoordinates = workTiles.tileXY[tileIndex]; + ivec2 coord = ivec2(tileCoordinates * motionTileSize + gl_LocalInvocationID.xy); + + if(any(greaterThanEqual(coord, imageSize(outImage)))) + return; + + ivec2 textureRes = textureSize(sampler2D(inColor, nearestSampler), 0); + vec2 uv = vec2(coord + 0.5) / textureRes; // + 0.5 to shift uv into pixel center + + vec2 motionNeighbourhoodMax = processMotionVector(texelFetch(sampler2D(inMotionNeighbourhoodMax, nearestSampler), coord / motionTileSize, 0).rg, motionScaleFactor, imageSize(outImage)); + + // early out on movement less than half a pixel + if(length(motionNeighbourhoodMax * imageSize(outImage)) <= 0.5){ + vec3 color = texture(sampler2D(inColor, nearestSampler), uv).rgb; + imageStore(outImage, coord, vec4(color, 0.f)); + return; + } + + vec3 color = vec3(0); + + // clamping start and end points avoids artifacts at image borders + // the sampler clamps the sample uvs anyways, but without clamping here, many samples can be stuck at the border + vec2 uvStart = clamp(uv - motionNeighbourhoodMax, 0, 1); + vec2 uvEnd = clamp(uv + motionNeighbourhoodMax, 0, 1); + + // samples are placed evenly, but the entire filter is jittered + // dither returns either 0 or 1 + // the sampleUV code expects an offset in range [-0.5, 0.5], so the dither is rescaled to a binary -0.25/0.25 + float random = dither(coord) * 0.5 - 0.25; + + const int sampleCount = 16; + + for(int i = 0; i < sampleCount; i++){ + + vec2 sampleUV = mix(uvStart, uvEnd, (i + random + 1) / float(sampleCount + 1)); + color += texture(sampler2D(inColor, nearestSampler), sampleUV).rgb; + } + + color /= sampleCount; + + imageStore(outImage, coord, vec4(color, 0.f)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurTileClassification.comp b/projects/indirect_dispatch/assets/shaders/motionBlurTileClassification.comp new file mode 100644 index 0000000000000000000000000000000000000000..3c6f9e3715951ac4fe6770725c3314590cbbff47 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurTileClassification.comp @@ -0,0 +1,58 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) uniform texture2D inMotionMax; +layout(set=0, binding=1) uniform texture2D inMotionMin; +layout(set=0, binding=2) uniform sampler nearestSampler; + +layout(set=0, binding=3) buffer FullPathTileBuffer { + WorkTiles fullPathTiles; +}; + +layout(set=0, binding=4) buffer CopyPathTileBuffer { + WorkTiles copyPathTiles; +}; + +layout(set=0, binding=5) buffer FastPathTileBuffer { + WorkTiles fastPathTiles; +}; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + uint width; + uint height; + float fastPathThreshold; +}; + +void main(){ + + ivec2 tileCoord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, textureSize(sampler2D(inMotionMax, nearestSampler), 0)))) + return; + + vec2 motionMax = texelFetch(sampler2D(inMotionMax, nearestSampler), tileCoord, 0).rg; + vec2 motionMin = texelFetch(sampler2D(inMotionMin, nearestSampler), tileCoord, 0).rg; + + vec2 motionPixelMax = motionMax * vec2(width, height); + vec2 motionPixelMin = motionMin * vec2(width, height); + + float velocityPixelMax = length(motionPixelMax); + float minMaxDistance = distance(motionPixelMin, motionPixelMax); + + if(velocityPixelMax <= 0.5){ + uint index = atomicAdd(copyPathTiles.tileCount, 1); + copyPathTiles.tileXY[index] = tileCoord; + } + else if(minMaxDistance <= fastPathThreshold){ + uint index = atomicAdd(fastPathTiles.tileCount, 1); + fastPathTiles.tileXY[index] = tileCoord; + } + else{ + uint index = atomicAdd(fullPathTiles.tileCount, 1); + fullPathTiles.tileXY[index] = tileCoord; + } +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurTileClassificationVis.comp b/projects/indirect_dispatch/assets/shaders/motionBlurTileClassificationVis.comp new file mode 100644 index 0000000000000000000000000000000000000000..3382ff5ef0b407b9a3a7785eda0d19efe5a8f96e --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurTileClassificationVis.comp @@ -0,0 +1,56 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlurConfig.inc" +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) uniform texture2D inColor; +layout(set=0, binding=1) uniform sampler nearestSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outImage; + +layout(set=0, binding=3) buffer FullPathTileBuffer { + WorkTiles fullPathTiles; +}; + +layout(set=0, binding=4) buffer CopyPathTileBuffer { + WorkTiles copyPathTiles; +}; + +layout(set=0, binding=5) buffer FastPathTileBuffer { + WorkTiles fastPathTiles; +}; + +layout(local_size_x = motionTileSize, local_size_y = motionTileSize, local_size_z = 1) in; + +void main(){ + + uint tileIndexFullPath = gl_WorkGroupID.x; + uint tileIndexCopyPath = gl_WorkGroupID.x - fullPathTiles.tileCount; + uint tileIndexFastPath = gl_WorkGroupID.x - fullPathTiles.tileCount - copyPathTiles.tileCount; + + vec3 debugColor; + ivec2 tileCoordinates; + + if(tileIndexFullPath < fullPathTiles.tileCount){ + debugColor = vec3(1, 0, 0); + tileCoordinates = fullPathTiles.tileXY[tileIndexFullPath]; + } + else if(tileIndexCopyPath < copyPathTiles.tileCount){ + debugColor = vec3(0, 1, 0); + tileCoordinates = copyPathTiles.tileXY[tileIndexCopyPath]; + } + else if(tileIndexFastPath < fastPathTiles.tileCount){ + debugColor = vec3(0, 0, 1); + tileCoordinates = fastPathTiles.tileXY[tileIndexFastPath]; + } + else{ + return; + } + + ivec2 coordinate = ivec2(tileCoordinates * motionTileSize + gl_LocalInvocationID.xy); + vec3 color = texelFetch(sampler2D(inColor, nearestSampler), coordinate, 0).rgb; + + color = mix(color, debugColor, 0.5); + + imageStore(outImage, coordinate, vec4(color, 0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurWorkTile.inc b/projects/indirect_dispatch/assets/shaders/motionBlurWorkTile.inc new file mode 100644 index 0000000000000000000000000000000000000000..8577f100aac524b93eecac406606a962bc52d222 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurWorkTile.inc @@ -0,0 +1,19 @@ +#ifndef MOTION_BLUR_WORK_TILE +#define MOTION_BLUR_WORK_TILE + +#include "motionBlurConfig.inc" + +const int maxTileCount = + (maxMotionBlurWidth + motionTileSize - 1) / motionTileSize * + (maxMotionBlurHeight + motionTileSize - 1) / motionTileSize; + +struct WorkTiles{ + uint tileCount; + // dispatch Y/Z are here so the buffer can be used directly as an indirect dispatch argument buffer + uint dispatchY; + uint dispatchZ; + + ivec2 tileXY[maxTileCount]; +}; + +#endif // #ifndef MOTION_BLUR_WORK_TILE \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionBlurWorkTileReset.comp b/projects/indirect_dispatch/assets/shaders/motionBlurWorkTileReset.comp new file mode 100644 index 0000000000000000000000000000000000000000..d4b55582a0a18c0c6a3fecf1dd6ce69ed49ca2c1 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionBlurWorkTileReset.comp @@ -0,0 +1,32 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlurWorkTile.inc" + +layout(set=0, binding=0) buffer FullPathTileBuffer { + WorkTiles fullPathTiles; +}; + +layout(set=0, binding=1) buffer CopyPathTileBuffer { + WorkTiles copyPathTiles; +}; + +layout(set=0, binding=2) buffer FastPathTileBuffer { + WorkTiles fastPathTiles; +}; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main(){ + fullPathTiles.tileCount = 0; + fullPathTiles.dispatchY = 1; + fullPathTiles.dispatchZ = 1; + + copyPathTiles.tileCount = 0; + copyPathTiles.dispatchY = 1; + copyPathTiles.dispatchZ = 1; + + fastPathTiles.tileCount = 0; + fastPathTiles.dispatchY = 1; + fastPathTiles.dispatchZ = 1; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionVector.inc b/projects/indirect_dispatch/assets/shaders/motionVector.inc new file mode 100644 index 0000000000000000000000000000000000000000..498478cbc38b9666366eaa3d3e1a715dfc30236b --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionVector.inc @@ -0,0 +1,9 @@ +vec2 computeMotionVector(vec4 NDC, vec4 NDCPrevious){ + vec2 ndc = NDC.xy / NDC.w; + vec2 ndcPrevious = NDCPrevious.xy / NDCPrevious.w; + + vec2 uv = ndc * 0.5 + 0.5; + vec2 uvPrevious = ndcPrevious * 0.5 + 0.5; + + return uvPrevious - uv; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionVectorMinMax.comp b/projects/indirect_dispatch/assets/shaders/motionVectorMinMax.comp new file mode 100644 index 0000000000000000000000000000000000000000..06b1b98f37579ae33406691bf19999d42ab7eb83 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionVectorMinMax.comp @@ -0,0 +1,57 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable +#include "motionBlurConfig.inc" + +layout(set=0, binding=0) uniform texture2D inMotion; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rg16) uniform image2D outMotionMax; +layout(set=0, binding=3, rg16) uniform image2D outMotionMin; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + ivec2 outImageRes = imageSize(outMotionMax); + ivec2 inImageRes = textureSize(sampler2D(inMotion, textureSampler), 0); + ivec2 motionTileCoord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(motionTileCoord, outImageRes))) + return; + + float velocityMax = 0; + vec2 motionMax = vec2(0); + + float velocityMin = 100000; + vec2 motionMin = vec2(0); + + ivec2 motionBufferBaseCoord = motionTileCoord * motionTileSize; + + for(int x = 0; x < motionTileSize; x++){ + for(int y = 0; y < motionTileSize; y++){ + ivec2 sampleCoord = motionBufferBaseCoord + ivec2(x, y); + + bool sampleIsOutsideImage = false; + sampleIsOutsideImage = sampleIsOutsideImage || any(greaterThanEqual(sampleCoord, inImageRes)); + sampleIsOutsideImage = sampleIsOutsideImage || any(lessThan(sampleCoord, ivec2(0))); + + if(sampleIsOutsideImage) + continue; + + vec2 motionSample = texelFetch(sampler2D(inMotion, textureSampler), sampleCoord, 0).rg; + float velocitySample = length(motionSample); + + if(velocitySample > velocityMax){ + velocityMax = velocitySample; + motionMax = motionSample; + } + + if(velocitySample < velocityMin){ + velocityMin = velocitySample; + motionMin = motionSample; + } + } + } + + imageStore(outMotionMax, motionTileCoord, vec4(motionMax, 0, 0)); + imageStore(outMotionMin, motionTileCoord, vec4(motionMin, 0, 0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionVectorMinMaxNeighbourhood.comp b/projects/indirect_dispatch/assets/shaders/motionVectorMinMaxNeighbourhood.comp new file mode 100644 index 0000000000000000000000000000000000000000..3f836341a97a683efe88f41416d541624be03a0e --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionVectorMinMaxNeighbourhood.comp @@ -0,0 +1,59 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inMotionMax; +layout(set=0, binding=1) uniform texture2D inMotionMin; +layout(set=0, binding=2) uniform sampler textureSampler; +layout(set=0, binding=3, rg16) uniform image2D outMotionMaxNeighbourhood; +layout(set=0, binding=4, rg16) uniform image2D outMotionMinNeighbourhood; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + ivec2 outImageRes = imageSize(outMotionMaxNeighbourhood); + ivec2 inImageRes = textureSize(sampler2D(inMotionMax, textureSampler), 0); + ivec2 motionTileCoord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(motionTileCoord, outImageRes))) + return; + + float velocityMax = 0; + vec2 motionMax = vec2(0); + + float velocityMin = 10000; + vec2 motionMin = vec2(0); + + for(int x = -1; x <= 1; x++){ + for(int y = -1; y <= 1; y++){ + ivec2 sampleCoord = motionTileCoord + ivec2(x, y); + + bool sampleIsOutsideImage = false; + sampleIsOutsideImage = sampleIsOutsideImage || any(greaterThanEqual(sampleCoord, inImageRes)); + sampleIsOutsideImage = sampleIsOutsideImage || any(lessThan(sampleCoord, ivec2(0))); + + if(sampleIsOutsideImage) + continue; + + vec2 motionSampleMax = texelFetch(sampler2D(inMotionMax, textureSampler), sampleCoord, 0).rg; + float velocitySampleMax = length(motionSampleMax); + + if(velocitySampleMax > velocityMax){ + velocityMax = velocitySampleMax; + motionMax = motionSampleMax; + } + + + vec2 motionSampleMin = texelFetch(sampler2D(inMotionMin, textureSampler), sampleCoord, 0).rg; + float velocitySampleMin = length(motionSampleMin); + + if(velocitySampleMin < velocityMin){ + velocityMin = velocitySampleMin; + motionMin = motionSampleMin; + } + } + } + + imageStore(outMotionMaxNeighbourhood, motionTileCoord, vec4(motionMax, 0, 0)); + imageStore(outMotionMinNeighbourhood, motionTileCoord, vec4(motionMin, 0, 0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/motionVectorVisualisation.comp b/projects/indirect_dispatch/assets/shaders/motionVectorVisualisation.comp new file mode 100644 index 0000000000000000000000000000000000000000..fdceb575feaf24e7114bbcf223585a28955f45b8 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/motionVectorVisualisation.comp @@ -0,0 +1,33 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "motionBlurConfig.inc" + +layout(set=0, binding=0) uniform texture2D inMotion; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + float range; +}; + +void main(){ + + ivec2 outImageRes = imageSize(outImage); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(coord, outImageRes))) + return; + + vec2 uv = (coord + 0.5) / vec2(outImageRes); + ivec2 inTextureRes = textureSize(sampler2D(inMotion, textureSampler), 0); + + vec2 motionVector = texelFetch(sampler2D(inMotion, textureSampler), ivec2(uv * inTextureRes), 0).rg; + vec2 motionVectorNormalized = clamp(motionVector / range, -1, 1); + + vec2 color = motionVectorNormalized * 0.5 + 0.5; + + imageStore(outImage, coord, vec4(color, 0.5, 0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/prepass.frag b/projects/indirect_dispatch/assets/shaders/prepass.frag new file mode 100644 index 0000000000000000000000000000000000000000..ccfc84d982253f7b89551c099a92b5686a811163 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/prepass.frag @@ -0,0 +1,14 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "motionVector.inc" + +layout(location = 0) in vec4 passNDC; +layout(location = 1) in vec4 passNDCPrevious; + +layout(location = 0) out vec2 outMotion; + +void main() { + outMotion = computeMotionVector(passNDC, passNDCPrevious); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/prepass.vert b/projects/indirect_dispatch/assets/shaders/prepass.vert new file mode 100644 index 0000000000000000000000000000000000000000..230346208007fae0bb7724b5b6d05f62726c4ded --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/prepass.vert @@ -0,0 +1,18 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec4 passNDC; +layout(location = 1) out vec4 passNDCPrevious; + +layout( push_constant ) uniform constants{ + mat4 mvp; + mat4 mvpPrevious; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + passNDC = gl_Position; + passNDCPrevious = mvpPrevious * vec4(inPosition, 1.0); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shadow.frag b/projects/indirect_dispatch/assets/shaders/sky.frag similarity index 53% rename from projects/voxelization/resources/shaders/shadow.frag rename to projects/indirect_dispatch/assets/shaders/sky.frag index 848f853f556660b4900b5db7fb6fc98d57c1cd5b..efc0e03b2d6ee1c71930c866293da66857bd56c7 100644 --- a/projects/voxelization/resources/shaders/shadow.frag +++ b/projects/indirect_dispatch/assets/shaders/sky.frag @@ -1,6 +1,8 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable -void main() { +layout(location = 0) out vec3 outColor; +void main() { + outColor = vec3(0, 0.2, 0.9); } \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/sky.vert b/projects/indirect_dispatch/assets/shaders/sky.vert new file mode 100644 index 0000000000000000000000000000000000000000..44b48cd7f3bfc44e2e43edef0d474581d50608de --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/sky.vert @@ -0,0 +1,13 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; + +layout( push_constant ) uniform constants{ + mat4 viewProjection; +}; + +void main() { + gl_Position = viewProjection * vec4(inPosition, 0.0); + gl_Position.w = gl_Position.z; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/skyPrepass.frag b/projects/indirect_dispatch/assets/shaders/skyPrepass.frag new file mode 100644 index 0000000000000000000000000000000000000000..64ec4f18bbcf89153d70019ace570da53d44a505 --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/skyPrepass.frag @@ -0,0 +1,14 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "motionVector.inc" + +layout(location = 0) out vec2 outMotion; + +layout(location = 0) in vec4 passNDC; +layout(location = 1) in vec4 passNDCPrevious; + +void main() { + outMotion = computeMotionVector(passNDC, passNDCPrevious); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/assets/shaders/skyPrepass.vert b/projects/indirect_dispatch/assets/shaders/skyPrepass.vert new file mode 100644 index 0000000000000000000000000000000000000000..31b9016a592d097825a09e1daa888cb7b72b2cbc --- /dev/null +++ b/projects/indirect_dispatch/assets/shaders/skyPrepass.vert @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 inPosition; + +layout( push_constant ) uniform constants{ + mat4 viewProjection; + mat4 viewProjectionPrevious; +}; + +layout(location = 0) out vec4 passNDC; +layout(location = 1) out vec4 passNDCPrevious; + +void main() { + gl_Position = viewProjection * vec4(inPosition, 0.0); + gl_Position.w = gl_Position.z; + + passNDC = gl_Position; + + passNDCPrevious = viewProjectionPrevious * vec4(inPosition, 0.0); + passNDCPrevious.w = passNDCPrevious.z; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/App.cpp b/projects/indirect_dispatch/src/App.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b441c3dda9f51f3cb3c51c15ea29d8069d4ec6ac --- /dev/null +++ b/projects/indirect_dispatch/src/App.cpp @@ -0,0 +1,562 @@ +#include "App.hpp" +#include "AppConfig.hpp" + +#include <vkcv/Sampler.hpp> +#include <vkcv/gui/GUI.hpp> + +#include <vkcv/upscaling/FSR2Upscaling.hpp> +#include <vkcv/upscaling/FSRUpscaling.hpp> +#include <vkcv/upscaling/NISUpscaling.hpp> +#include <vkcv/upscaling/BilinearUpscaling.hpp> + +#include <chrono> +#include <functional> + +const char* MotionVectorVisualisationModeLabels[6] = { + "None", + "Full resolution", + "Max tile", + "Tile neighbourhood max", + "Min Tile", + "Tile neighbourhood min" +}; + +const char* MotionBlurModeLabels[3] = { + "Default", + "Disabled", + "Tile visualisation" +}; + +static vkcv::Features getAppFeatures() { + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + features.requireFeature([](vk::PhysicalDeviceFeatures& features) { + features.setShaderInt16(true); + }); + + features.requireExtensionFeature<vk::PhysicalDeviceSubgroupSizeControlFeatures>( + VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, + [](vk::PhysicalDeviceSubgroupSizeControlFeatures &features) { + features.setSubgroupSizeControl(true); + } + ); + + features.requireExtensionFeature<vk::PhysicalDeviceShaderFloat16Int8Features>( + VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, + [](vk::PhysicalDeviceShaderFloat16Int8Features &features) { + features.setShaderFloat16(true); + } + ); + + features.tryExtensionFeature<vk::PhysicalDeviceCoherentMemoryFeaturesAMD>( + VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME, + [](vk::PhysicalDeviceCoherentMemoryFeaturesAMD &features) { + features.setDeviceCoherentMemory(true); + } + ); + + return features; +} + +App::App() : + m_applicationName("Indirect Dispatch"), + m_windowWidth(AppConfig::defaultWindowWidth), + m_windowHeight(AppConfig::defaultWindowHeight), + m_core(vkcv::Core::create( + m_applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, + getAppFeatures())), + m_windowHandle(m_core.createWindow(m_applicationName, m_windowWidth, m_windowHeight, true)), + m_cameraManager(m_core.getWindow(m_windowHandle)){} + +bool App::initialize() { + if (!loadMeshPass(m_core, &m_meshPass)) + return false; + + if (!loadSkyPass(m_core, &m_skyPass)) + return false; + + if (!loadPrePass(m_core, &m_prePass)) + return false; + + if (!loadSkyPrePass(m_core, &m_skyPrePass)) + return false; + + if (!loadComputePass(m_core, "assets/shaders/gammaCorrection.comp", &m_gammaCorrectionPass)) + return false; + + if (!loadMesh(m_core, "assets/models/cube.gltf", &m_cubeMesh)) + return false; + + if (!loadMesh(m_core, "assets/models/ground.gltf", &m_groundMesh)) + return false; + + if(!loadImage(m_core, "assets/models/grid.png", &m_gridTexture)) + return false; + + if (!m_motionBlur.initialize(&m_core, m_windowWidth, m_windowHeight)) + return false; + + m_linearSampler = vkcv::samplerLinear(m_core, true); + m_renderTargets = createRenderTargets( + m_core, + m_windowWidth, + m_windowHeight, + vkcv::upscaling::FSR2QualityMode::NONE + ); + + auto cameraHandle = m_cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + m_cameraManager.getCamera(cameraHandle).setPosition(glm::vec3(0, 1, -3)); + m_cameraManager.getCamera(cameraHandle).setNearFar(0.1f, 30.f); + + vkcv::DescriptorWrites meshPassDescriptorWrites; + meshPassDescriptorWrites.writeSampledImage(0, m_gridTexture); + meshPassDescriptorWrites.writeSampler(1, m_linearSampler); + m_core.writeDescriptorSet(m_meshPass.descriptorSet, meshPassDescriptorWrites); + + return true; +} + +void App::run() { + + auto frameStartTime = std::chrono::system_clock::now(); + const auto appStartTime = std::chrono::system_clock::now(); + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + const vkcv::InstanceDrawcall skyDrawcall(m_cubeMesh.mesh); + + vkcv::gui::GUI gui(m_core, m_windowHandle); + + eMotionVectorVisualisationMode motionVectorVisualisationMode = eMotionVectorVisualisationMode::None; + eMotionBlurMode motionBlurMode = eMotionBlurMode::Default; + + bool freezeFrame = false; + float motionBlurTileOffsetLength = 3; + float objectVerticalSpeed = 5; + float objectAmplitude = 0; + float objectMeanHeight = 1; + float objectRotationSpeedX = 5; + float objectRotationSpeedY = 5; + int cameraShutterSpeedInverse = 24; + float motionVectorVisualisationRange = 0.008; + float motionBlurFastPathThreshold = 1; + + glm::mat4 viewProjection = m_cameraManager.getActiveCamera().getMVP(); + glm::mat4 viewProjectionPrevious = m_cameraManager.getActiveCamera().getMVP(); + + struct Object { + MeshResources meshResources; + glm::mat4 modelMatrix = glm::mat4(1.f); + glm::mat4 mvp = glm::mat4(1.f); + glm::mat4 mvpPrevious = glm::mat4(1.f); + std::function<void(float, Object&)> modelMatrixUpdate; + }; + std::vector<Object> sceneObjects; + + Object ground; + ground.meshResources = m_groundMesh; + sceneObjects.push_back(ground); + + Object sphere; + sphere.meshResources = m_cubeMesh; + sphere.modelMatrixUpdate = [&](float time, Object& obj) { + const float currentHeight = objectMeanHeight + objectAmplitude * glm::sin(time * objectVerticalSpeed); + const glm::mat4 translation = glm::translate(glm::mat4(1), glm::vec3(0, currentHeight, 0)); + const glm::mat4 rotationX = glm::rotate(glm::mat4(1), objectRotationSpeedX * time, glm::vec3(1, 0, 0)); + const glm::mat4 rotationY = glm::rotate(glm::mat4(1), objectRotationSpeedY * time, glm::vec3(0, 1, 0)); + obj.modelMatrix = translation * rotationX * rotationY; + }; + sceneObjects.push_back(sphere); + + bool spaceWasPressed = false; + + m_core.getWindow(m_windowHandle).e_key.add([&](int key, int scancode, int action, int mods) { + if (key == GLFW_KEY_SPACE) { + if (action == GLFW_PRESS) { + if (!spaceWasPressed) { + freezeFrame = !freezeFrame; + } + spaceWasPressed = true; + } + else if (action == GLFW_RELEASE) { + spaceWasPressed = false; + } + } + }); + + vkcv::upscaling::FSR2Upscaling fsr2 (m_core); + + fsr2.bindDepthBuffer(m_renderTargets.depthBuffer); + fsr2.bindVelocityBuffer(m_renderTargets.motionBuffer); + + vkcv::upscaling::FSR2QualityMode fsrMode = vkcv::upscaling::FSR2QualityMode::NONE; + vkcv::upscaling::FSR2QualityMode oldFsrMode = fsrMode; + + int fsrModeIndex = static_cast<int>(fsrMode); + + const std::vector<const char*> fsrModeNames = { + "None", + "Quality", + "Balanced", + "Performance", + "Ultra Performance" + }; + + bool fsrMipLoadBiasFlag = true; + bool fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; + + vkcv::upscaling::FSRUpscaling fsr1 (m_core); + vkcv::upscaling::BilinearUpscaling bilinear (m_core); + vkcv::upscaling::NISUpscaling nis (m_core); + + const std::vector<const char*> modeNames = { + "FSR Upscaling 1.0", + "FSR Upscaling 2.1.1", + "NIS Upscaling", + "Bilinear Upscaling" + }; + + int upscalingMode = 3; + + vkcv::SamplerHandle fsr2Sampler; + + auto frameEndTime = std::chrono::system_clock::now(); + + while (vkcv::Window::hasOpenWindow()) { + vkcv::Window::pollEvents(); + + if (!freezeFrame) { + + frameStartTime = frameEndTime; + viewProjectionPrevious = viewProjection; + + for (Object& obj : sceneObjects) { + obj.mvpPrevious = obj.mvp; + } + } + + if (m_core.getWindow(m_windowHandle).getHeight() == 0 || m_core.getWindow(m_windowHandle).getWidth() == 0) + continue; + + uint32_t swapchainWidth, swapchainHeight; + if (!m_core.beginFrame(swapchainWidth, swapchainHeight,m_windowHandle)) + continue; + + const bool hasResolutionChanged = ( + (swapchainWidth != m_windowWidth) || + (swapchainHeight != m_windowHeight) || + (oldFsrMode != fsrMode) || + (fsrMipLoadBiasFlagBackup != fsrMipLoadBiasFlag) + ); + + if (hasResolutionChanged) { + m_windowWidth = swapchainWidth; + m_windowHeight = swapchainHeight; + oldFsrMode = fsrMode; + fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; + + fsr2Sampler = m_core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT, + fsrMipLoadBiasFlag? vkcv::upscaling::getFSR2LodBias(fsrMode) : 0.0f + ); + + vkcv::DescriptorWrites meshPassDescriptorWrites; + meshPassDescriptorWrites.writeSampler(1, fsr2Sampler); + m_core.writeDescriptorSet(m_meshPass.descriptorSet, meshPassDescriptorWrites); + + m_renderTargets = createRenderTargets( + m_core, + m_windowWidth, + m_windowHeight, + fsrMode + ); + + m_motionBlur.setResolution(m_windowWidth, m_windowHeight); + + fsr2.bindDepthBuffer(m_renderTargets.depthBuffer); + fsr2.bindVelocityBuffer(m_renderTargets.motionBuffer); + } + + if(!freezeFrame) + frameEndTime = std::chrono::system_clock::now(); + + const float microsecondToSecond = 0.000001; + const float fDeltaTimeSeconds = microsecondToSecond * std::chrono::duration_cast<std::chrono::microseconds>(frameEndTime - frameStartTime).count(); + + m_cameraManager.update(fDeltaTimeSeconds); + fsr2.update(fDeltaTimeSeconds, false); + + const auto& camera = m_cameraManager.getActiveCamera(); + float near, far; + + camera.getNearFar(near, far); + fsr2.setCamera(near, far, camera.getFov()); + + const auto time = frameEndTime - appStartTime; + const float fCurrentTime = std::chrono::duration_cast<std::chrono::milliseconds>(time).count() * 0.001f; + + float jitterX, jitterY; + + fsr2.calcJitterOffset( + m_core.getImageWidth(m_renderTargets.colorBuffer), + m_core.getImageHeight(m_renderTargets.colorBuffer), + jitterX, + jitterY + ); + + const glm::mat4 jitterMatrix = glm::translate( + glm::identity<glm::mat4>(), + glm::vec3(jitterX, jitterY, 0.0f) + ); + + // update matrices + if (!freezeFrame) { + viewProjection = camera.getMVP(); + + for (Object& obj : sceneObjects) { + if (obj.modelMatrixUpdate) { + obj.modelMatrixUpdate(fCurrentTime, obj); + } + + obj.mvp = jitterMatrix * viewProjection * obj.modelMatrix; + } + } + + const vkcv::CommandStreamHandle cmdStream = m_core.createCommandStream(vkcv::QueueType::Graphics); + + // prepass + vkcv::PushConstants prepassPushConstants(sizeof(glm::mat4) * 2); + + for (const Object& obj : sceneObjects) { + glm::mat4 prepassMatrices[2] = { obj.mvp, obj.mvpPrevious }; + prepassPushConstants.appendDrawcall(prepassMatrices); + } + + const std::vector<vkcv::ImageHandle> prepassRenderTargets = { + m_renderTargets.motionBuffer, + m_renderTargets.depthBuffer + }; + + std::vector<vkcv::InstanceDrawcall> prepassSceneDrawcalls; + for (const Object& obj : sceneObjects) { + prepassSceneDrawcalls.push_back(vkcv::InstanceDrawcall(obj.meshResources.mesh)); + } + + m_core.recordDrawcallsToCmdStream( + cmdStream, + m_prePass.pipeline, + prepassPushConstants, + prepassSceneDrawcalls, + prepassRenderTargets, + m_windowHandle + ); + + // sky prepass + glm::mat4 skyPrepassMatrices[2] = { + viewProjection, + viewProjectionPrevious + }; + + vkcv::PushConstants skyPrepassPushConstants(sizeof(glm::mat4) * 2); + skyPrepassPushConstants.appendDrawcall(skyPrepassMatrices); + + m_core.recordDrawcallsToCmdStream( + cmdStream, + m_skyPrePass.pipeline, + skyPrepassPushConstants, + { skyDrawcall }, + prepassRenderTargets, + m_windowHandle + ); + + // main pass + const std::vector<vkcv::ImageHandle> renderTargets = { + m_renderTargets.colorBuffer, + m_renderTargets.depthBuffer + }; + + vkcv::PushConstants meshPushConstants(2 * sizeof(glm::mat4)); + for (const Object& obj : sceneObjects) { + glm::mat4 matrices[2] = { obj.mvp, obj.modelMatrix }; + meshPushConstants.appendDrawcall(matrices); + } + + std::vector<vkcv::InstanceDrawcall> forwardSceneDrawcalls; + for (const Object& obj : sceneObjects) { + vkcv::InstanceDrawcall drawcall (obj.meshResources.mesh); + drawcall.useDescriptorSet(0, m_meshPass.descriptorSet); + forwardSceneDrawcalls.push_back(drawcall); + } + + m_core.recordDrawcallsToCmdStream( + cmdStream, + m_meshPass.pipeline, + meshPushConstants, + forwardSceneDrawcalls, + renderTargets, + m_windowHandle + ); + + // sky + vkcv::PushConstants skyPushConstants = vkcv::pushConstants<glm::mat4>(); + skyPushConstants.appendDrawcall(jitterMatrix * viewProjection); + + m_core.recordDrawcallsToCmdStream( + cmdStream, + m_skyPass.pipeline, + skyPushConstants, + { skyDrawcall }, + renderTargets, + m_windowHandle + ); + + // upscaling + m_core.prepareImageForSampling(cmdStream, m_renderTargets.colorBuffer); + m_core.prepareImageForStorage(cmdStream, m_renderTargets.finalBuffer); + + switch (upscalingMode) { + case 0: + fsr1.recordUpscaling( + cmdStream, + m_renderTargets.colorBuffer, + m_renderTargets.finalBuffer + ); + break; + case 1: + m_core.prepareImageForSampling(cmdStream, m_renderTargets.depthBuffer); + m_core.prepareImageForSampling(cmdStream, m_renderTargets.motionBuffer); + + fsr2.recordUpscaling( + cmdStream, + m_renderTargets.colorBuffer, + m_renderTargets.finalBuffer + ); + break; + case 2: + nis.recordUpscaling( + cmdStream, + m_renderTargets.colorBuffer, + m_renderTargets.finalBuffer + ); + break; + case 3: + bilinear.recordUpscaling( + cmdStream, + m_renderTargets.colorBuffer, + m_renderTargets.finalBuffer + ); + break; + default: + break; + } + + m_core.prepareImageForSampling(cmdStream, m_renderTargets.finalBuffer); + + // motion blur + vkcv::ImageHandle motionBlurOutput; + + if (motionVectorVisualisationMode == eMotionVectorVisualisationMode::None) { + motionBlurOutput = m_motionBlur.render( + cmdStream, + m_renderTargets.motionBuffer, + m_renderTargets.finalBuffer, + m_renderTargets.depthBuffer, + motionBlurMode, + near, + far, + fDeltaTimeSeconds, + static_cast<float>(cameraShutterSpeedInverse), + motionBlurTileOffsetLength, + motionBlurFastPathThreshold + ); + } else { + motionBlurOutput = m_motionBlur.renderMotionVectorVisualisation( + cmdStream, + m_renderTargets.motionBuffer, + motionVectorVisualisationMode, + motionVectorVisualisationRange + ); + } + + // gamma correction + vkcv::DescriptorWrites gammaCorrectionDescriptorWrites; + gammaCorrectionDescriptorWrites.writeSampledImage(0, motionBlurOutput); + gammaCorrectionDescriptorWrites.writeSampler(1, m_linearSampler); + gammaCorrectionDescriptorWrites.writeStorageImage(2, swapchainInput); + + m_core.writeDescriptorSet(m_gammaCorrectionPass.descriptorSet, gammaCorrectionDescriptorWrites); + + m_core.prepareImageForSampling(cmdStream, motionBlurOutput); + m_core.prepareImageForStorage (cmdStream, swapchainInput); + + const auto fullScreenImageDispatch = vkcv::dispatchInvocations( + vkcv::DispatchSize(m_windowWidth, m_windowHeight), + vkcv::DispatchSize(8, 8) + ); + + m_core.recordComputeDispatchToCmdStream( + cmdStream, + m_gammaCorrectionPass.pipeline, + fullScreenImageDispatch, + { vkcv::useDescriptorSet(0, m_gammaCorrectionPass.descriptorSet) }, + vkcv::PushConstants(0) + ); + + m_core.prepareSwapchainImageForPresent(cmdStream); + m_core.submitCommandStream(cmdStream); + + gui.beginGUI(); + ImGui::Begin("Settings"); + + ImGui::Checkbox("Freeze frame", &freezeFrame); + ImGui::InputFloat("Motion tile offset length", &motionBlurTileOffsetLength); + ImGui::InputFloat("Motion blur fast path threshold", &motionBlurFastPathThreshold); + + ImGui::Combo( + "Motion blur mode", + reinterpret_cast<int*>(&motionBlurMode), + MotionBlurModeLabels, + static_cast<int>(eMotionBlurMode::OptionCount)); + + ImGui::Combo( + "Debug view", + reinterpret_cast<int*>(&motionVectorVisualisationMode), + MotionVectorVisualisationModeLabels, + static_cast<int>(eMotionVectorVisualisationMode::OptionCount)); + + if (motionVectorVisualisationMode != eMotionVectorVisualisationMode::None) + ImGui::InputFloat("Motion vector visualisation range", &motionVectorVisualisationRange); + + ImGui::InputInt("Camera shutter speed inverse", &cameraShutterSpeedInverse); + + ImGui::InputFloat("Object movement speed", &objectVerticalSpeed); + ImGui::InputFloat("Object movement amplitude", &objectAmplitude); + ImGui::InputFloat("Object mean height", &objectMeanHeight); + ImGui::InputFloat("Object rotation speed X", &objectRotationSpeedX); + ImGui::InputFloat("Object rotation speed Y", &objectRotationSpeedY); + + float sharpness = fsr2.getSharpness(); + + ImGui::Combo("FSR Quality Mode", &fsrModeIndex, fsrModeNames.data(), fsrModeNames.size()); + ImGui::DragFloat("FSR Sharpness", &sharpness, 0.001, 0.0f, 1.0f); + ImGui::Checkbox("FSR Mip Lod Bias", &fsrMipLoadBiasFlag); + ImGui::Combo("Upscaling Mode", &upscalingMode, modeNames.data(), modeNames.size()); + + if ((fsrModeIndex >= 0) && (fsrModeIndex <= 4)) { + fsrMode = static_cast<vkcv::upscaling::FSR2QualityMode>(fsrModeIndex); + } + + fsr1.setSharpness(sharpness); + fsr2.setSharpness(sharpness); + nis.setSharpness(sharpness); + + ImGui::End(); + gui.endGUI(); + + m_core.endFrame(m_windowHandle); + } +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/App.hpp b/projects/indirect_dispatch/src/App.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a35c2342c4c90e39b089a7c33a73c3aa7ce8a83f --- /dev/null +++ b/projects/indirect_dispatch/src/App.hpp @@ -0,0 +1,38 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include "AppSetup.hpp" +#include "MotionBlur.hpp" + +class App { +public: + App(); + bool initialize(); + void run(); +private: + const char* m_applicationName; + + uint32_t m_windowWidth; + uint32_t m_windowHeight; + + vkcv::Core m_core; + vkcv::WindowHandle m_windowHandle; + vkcv::camera::CameraManager m_cameraManager; + + MotionBlur m_motionBlur; + + vkcv::ImageHandle m_gridTexture; + + MeshResources m_cubeMesh; + MeshResources m_groundMesh; + + GraphicPassHandles m_meshPass; + GraphicPassHandles m_skyPass; + GraphicPassHandles m_prePass; + GraphicPassHandles m_skyPrePass; + + ComputePassHandles m_gammaCorrectionPass; + + AppRenderTargets m_renderTargets; + vkcv::SamplerHandle m_linearSampler; +}; \ No newline at end of file diff --git a/projects/indirect_dispatch/src/AppConfig.hpp b/projects/indirect_dispatch/src/AppConfig.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c89c34ea8e3c0c45708ca998a642faffb31403d3 --- /dev/null +++ b/projects/indirect_dispatch/src/AppConfig.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "vulkan/vulkan.hpp" + +namespace AppConfig{ + const int defaultWindowWidth = 1280; + const int defaultWindowHeight = 720; + const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; + const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32; + const vk::Format motionBufferFormat = vk::Format::eR16G16Sfloat; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/AppSetup.cpp b/projects/indirect_dispatch/src/AppSetup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5dc6c81e0e9599a561c3de1e359563c1f25334ea --- /dev/null +++ b/projects/indirect_dispatch/src/AppSetup.cpp @@ -0,0 +1,352 @@ +#include "AppSetup.hpp" +#include "AppConfig.hpp" + +#include <vkcv/Buffer.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +bool loadMesh(vkcv::Core& core, const std::filesystem::path& path, MeshResources* outMesh) { + assert(outMesh); + + vkcv::asset::Scene scene; + const int meshLoadResult = vkcv::asset::loadScene(path.string(), scene); + + if (meshLoadResult != 1) { + vkcv_log(vkcv::LogLevel::ERROR, "Mesh loading failed"); + return false; + } + + if (scene.meshes.size() < 1) { + vkcv_log(vkcv::LogLevel::ERROR, "Cube mesh scene does not contain any vertex groups"); + return false; + } + assert(!scene.vertexGroups.empty()); + + auto& vertexData = scene.vertexGroups[0].vertexBuffer; + auto& indexData = scene.vertexGroups[0].indexBuffer; + + vkcv::Buffer<uint8_t> vertexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::VERTEX, + vertexData.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL); + + vkcv::Buffer<uint8_t> indexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::INDEX, + indexData.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL); + + vertexBuffer.fill(vertexData.data); + indexBuffer.fill(indexData.data); + + outMesh->vertexBuffer = vertexBuffer.getHandle(); + outMesh->indexBuffer = indexBuffer.getHandle(); + + const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings( + vertexData.attributes, + vertexBuffer.getHandle(), + { + vkcv::asset::PrimitiveType::POSITION, + vkcv::asset::PrimitiveType::NORMAL, + vkcv::asset::PrimitiveType::TEXCOORD_0 + } + ); + + outMesh->mesh = vkcv::VertexData(vertexBufferBindings); + outMesh->mesh.setIndexBuffer(indexBuffer.getHandle()); + outMesh->mesh.setCount(scene.vertexGroups[0].numIndices); + + return true; +} + +bool loadImage(vkcv::Core& core, const std::filesystem::path& path, vkcv::ImageHandle* outImage) { + + assert(outImage); + + const vkcv::asset::Texture textureData = vkcv::asset::loadTexture(path); + + if (textureData.channels != 4) { + vkcv_log(vkcv::LogLevel::ERROR, "Expecting image with four components"); + return false; + } + + vkcv::Image image = vkcv::image( + core, + vk::Format::eR8G8B8A8Srgb, + textureData.width, + textureData.height, + 1, + true + ); + + image.fill(textureData.data.data(), textureData.data.size()); + + { + auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics); + image.recordMipChainGeneration(mipStream, core.getDownsampler()); + core.submitCommandStream(mipStream, false); + } + + *outImage = image.getHandle(); + return true; +} + +bool loadGraphicPass( + vkcv::Core& core, + const std::filesystem::path vertexPath, + const std::filesystem::path fragmentPath, + const vkcv::PassConfig& passConfig, + const vkcv::DepthTest depthTest, + GraphicPassHandles* outPassHandles) { + + assert(outPassHandles); + + outPassHandles->renderPass = core.createPass(passConfig); + + if (!outPassHandles->renderPass) { + vkcv_log(vkcv::LogLevel::ERROR, "Error: Could not create renderpass"); + return false; + } + + vkcv::ShaderProgram shaderProgram; + vkcv::shader::GLSLCompiler compiler; + + compiler.compile(vkcv::ShaderStage::VERTEX, vertexPath, + [&shaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, fragmentPath, + [&shaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shaderProgram.addShader(shaderStage, path); + }); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = shaderProgram.getVertexAttachments(); + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] })); + } + + const vkcv::VertexLayout vertexLayout { bindings }; + + const auto descriptorBindings = shaderProgram.getReflectedDescriptors(); + const bool hasDescriptor = descriptorBindings.size() > 0; + std::vector<vkcv::DescriptorSetLayoutHandle> descriptorSetLayouts = {}; + if (hasDescriptor) + { + outPassHandles->descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings.at(0)); + outPassHandles->descriptorSet = core.createDescriptorSet(outPassHandles->descriptorSetLayout); + descriptorSetLayouts.push_back(outPassHandles->descriptorSetLayout); + } + + vkcv::GraphicsPipelineConfig pipelineConfig( + shaderProgram, + outPassHandles->renderPass, + { vertexLayout }, + descriptorSetLayouts + ); + + pipelineConfig.setDepthTest(depthTest); + outPassHandles->pipeline = core.createGraphicsPipeline(pipelineConfig); + + if (!outPassHandles->pipeline) { + vkcv_log(vkcv::LogLevel::ERROR, "Error: Could not create graphics pipeline"); + return false; + } + + return true; +} + +bool loadMeshPass(vkcv::Core& core, GraphicPassHandles* outHandles) { + + assert(outHandles); + + vkcv::AttachmentDescription colorAttachment( + AppConfig::colorBufferFormat, + vkcv::AttachmentOperation::DONT_CARE, + vkcv::AttachmentOperation::STORE + ); + + vkcv::AttachmentDescription depthAttachment( + AppConfig::depthBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE + ); + + return loadGraphicPass( + core, + "assets/shaders/mesh.vert", + "assets/shaders/mesh.frag", + vkcv::PassConfig( + { colorAttachment, depthAttachment }, + vkcv::Multisampling::None + ), + vkcv::DepthTest::Equal, + outHandles); +} + +bool loadSkyPass(vkcv::Core& core, GraphicPassHandles* outHandles) { + + assert(outHandles); + + vkcv::AttachmentDescription colorAttachment( + AppConfig::colorBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE + ); + + vkcv::AttachmentDescription depthAttachment( + AppConfig::depthBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE + ); + + return loadGraphicPass( + core, + "assets/shaders/sky.vert", + "assets/shaders/sky.frag", + vkcv::PassConfig( + { colorAttachment, depthAttachment }, + vkcv::Multisampling::None + ), + vkcv::DepthTest::Equal, + outHandles); +} + +bool loadPrePass(vkcv::Core& core, GraphicPassHandles* outHandles) { + assert(outHandles); + + vkcv::AttachmentDescription motionAttachment( + AppConfig::motionBufferFormat, + vkcv::AttachmentOperation::CLEAR, + vkcv::AttachmentOperation::STORE + ); + + vkcv::AttachmentDescription depthAttachment( + AppConfig::depthBufferFormat, + vkcv::AttachmentOperation::CLEAR, + vkcv::AttachmentOperation::STORE + ); + + return loadGraphicPass( + core, + "assets/shaders/prepass.vert", + "assets/shaders/prepass.frag", + vkcv::PassConfig( + { motionAttachment, depthAttachment }, + vkcv::Multisampling::None + ), + vkcv::DepthTest::LessEqual, + outHandles); +} + +bool loadSkyPrePass(vkcv::Core& core, GraphicPassHandles* outHandles) { + assert(outHandles); + + vkcv::AttachmentDescription motionAttachment( + AppConfig::motionBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE + ); + + vkcv::AttachmentDescription depthAttachment( + AppConfig::depthBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE + ); + + return loadGraphicPass( + core, + "assets/shaders/skyPrepass.vert", + "assets/shaders/skyPrepass.frag", + vkcv::PassConfig( + { motionAttachment, depthAttachment }, + vkcv::Multisampling::None + ), + vkcv::DepthTest::LessEqual, + outHandles); +} + +bool loadComputePass(vkcv::Core& core, const std::filesystem::path& path, ComputePassHandles* outComputePass) { + + assert(outComputePass); + vkcv::ShaderProgram shaderProgram; + vkcv::shader::GLSLCompiler compiler; + + compiler.compile(vkcv::ShaderStage::COMPUTE, path, + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shaderProgram.addShader(shaderStage, path); + }); + + if (shaderProgram.getReflectedDescriptors().size() < 1) { + vkcv_log(vkcv::LogLevel::ERROR, "Compute shader has no descriptor set"); + return false; + } + + outComputePass->descriptorSetLayout = core.createDescriptorSetLayout(shaderProgram.getReflectedDescriptors().at(0)); + outComputePass->descriptorSet = core.createDescriptorSet(outComputePass->descriptorSetLayout); + outComputePass->pipeline = core.createComputePipeline({ + shaderProgram, + { outComputePass->descriptorSetLayout }}); + + if (!outComputePass->pipeline) { + vkcv_log(vkcv::LogLevel::ERROR, "Compute shader pipeline creation failed"); + return false; + } + + return true; +} + +AppRenderTargets createRenderTargets(vkcv::Core& core, + uint32_t width, + uint32_t height, + vkcv::upscaling::FSR2QualityMode mode) { + AppRenderTargets targets; + uint32_t renderWidth, renderHeight; + + vkcv::upscaling::getFSR2Resolution( + mode, + width, + height, + renderWidth, + renderHeight + ); + + vkcv::ImageConfig depthBufferConfig (renderWidth, renderHeight); + + targets.depthBuffer = core.createImage( + AppConfig::depthBufferFormat, + depthBufferConfig + ); + + vkcv::ImageConfig bufferConfig (renderWidth, renderHeight); + bufferConfig.setSupportingColorAttachment(true); + + targets.colorBuffer = core.createImage( + AppConfig::colorBufferFormat, + bufferConfig + ); + + targets.motionBuffer = core.createImage( + AppConfig::motionBufferFormat, + bufferConfig + ); + + vkcv::ImageConfig finalConfig (width, height); + finalConfig.setSupportingColorAttachment(true); + finalConfig.setSupportingStorage(true); + + targets.finalBuffer = core.createImage( + AppConfig::colorBufferFormat, + finalConfig + ); + + core.setDebugLabel(targets.depthBuffer, "Depth buffer"); + core.setDebugLabel(targets.colorBuffer, "Color buffer"); + core.setDebugLabel(targets.motionBuffer, "Motion buffer"); + core.setDebugLabel(targets.finalBuffer, "Final buffer"); + + return targets; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/AppSetup.hpp b/projects/indirect_dispatch/src/AppSetup.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d06910b5f02325d27ee7f2a0e0fbfe4cf7560dab --- /dev/null +++ b/projects/indirect_dispatch/src/AppSetup.hpp @@ -0,0 +1,54 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <vkcv/upscaling/FSR2Upscaling.hpp> + +struct AppRenderTargets { + vkcv::ImageHandle depthBuffer; + vkcv::ImageHandle colorBuffer; + vkcv::ImageHandle motionBuffer; + vkcv::ImageHandle finalBuffer; +}; + +struct GraphicPassHandles { + vkcv::GraphicsPipelineHandle pipeline; + vkcv::PassHandle renderPass; + vkcv::DescriptorSetLayoutHandle descriptorSetLayout; + vkcv::DescriptorSetHandle descriptorSet; +}; + +struct ComputePassHandles { + vkcv::ComputePipelineHandle pipeline; + vkcv::DescriptorSetLayoutHandle descriptorSetLayout; + vkcv::DescriptorSetHandle descriptorSet; +}; + +struct MeshResources { + vkcv::VertexData mesh; + vkcv::BufferHandle vertexBuffer; + vkcv::BufferHandle indexBuffer; +}; + +// loads position, uv and normal of the first mesh in a scene +bool loadMesh(vkcv::Core& core, const std::filesystem::path& path, MeshResources* outMesh); + +bool loadImage(vkcv::Core& core, const std::filesystem::path& path, vkcv::ImageHandle* outImage); + +bool loadGraphicPass( + vkcv::Core& core, + std::filesystem::path vertexPath, + std::filesystem::path fragmentPath, + const vkcv::PassConfig& passConfig, + vkcv::DepthTest depthTest, + GraphicPassHandles* outPassHandles); + +bool loadMeshPass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadSkyPass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadPrePass (vkcv::Core& core, GraphicPassHandles* outHandles); +bool loadSkyPrePass(vkcv::Core& core, GraphicPassHandles* outHandles); + +bool loadComputePass(vkcv::Core& core, const std::filesystem::path& path, ComputePassHandles* outComputePass); + +AppRenderTargets createRenderTargets(vkcv::Core& core, + uint32_t width, + uint32_t height, + vkcv::upscaling::FSR2QualityMode mode); \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlur.cpp b/projects/indirect_dispatch/src/MotionBlur.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe55a5821b042feb709deffee806513b18ed06ba --- /dev/null +++ b/projects/indirect_dispatch/src/MotionBlur.cpp @@ -0,0 +1,433 @@ +#include "MotionBlur.hpp" +#include "MotionBlurConfig.hpp" +#include "MotionBlurSetup.hpp" + +#include <vkcv/Buffer.hpp> +#include <vkcv/Sampler.hpp> + +#include <array> + +bool MotionBlur::initialize(vkcv::Core* corePtr, const uint32_t targetWidth, const uint32_t targetHeight) { + + if (!corePtr) { + vkcv_log(vkcv::LogLevel::ERROR, "MotionBlur got invalid corePtr") + return false; + } + + m_core = corePtr; + + if (!loadComputePass(*m_core, "assets/shaders/motionBlur.comp", &m_motionBlurPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionVectorMinMax.comp", &m_motionVectorMinMaxPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionVectorMinMaxNeighbourhood.comp", &m_motionVectorMinMaxNeighbourhoodPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionVectorVisualisation.comp", &m_motionVectorVisualisationPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionBlurColorCopy.comp", &m_colorCopyPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionBlurTileClassification.comp", &m_tileClassificationPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionBlurWorkTileReset.comp", &m_tileResetPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionBlurTileClassificationVis.comp", &m_tileVisualisationPass)) + return false; + + if (!loadComputePass(*m_core, "assets/shaders/motionBlurFastPath.comp", &m_motionBlurFastPathPass)) + return false; + + // work tile buffers and descriptors + const uint32_t workTileBufferSize = static_cast<uint32_t>(2 * sizeof(uint32_t)) * (3 + + ((MotionBlurConfig::maxWidth + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize) * + ((MotionBlurConfig::maxHeight + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize)); + + m_copyPathWorkTileBuffer = vkcv::buffer<uint32_t>( + *m_core, + vkcv::BufferType::INDIRECT, + workTileBufferSize, + vkcv::BufferMemoryType::DEVICE_LOCAL).getHandle(); + + m_fullPathWorkTileBuffer = vkcv::buffer<uint32_t>( + *m_core, + vkcv::BufferType::INDIRECT, + workTileBufferSize, + vkcv::BufferMemoryType::DEVICE_LOCAL).getHandle(); + + m_fastPathWorkTileBuffer = vkcv::buffer<uint32_t>( + *m_core, + vkcv::BufferType::INDIRECT, + workTileBufferSize, + vkcv::BufferMemoryType::DEVICE_LOCAL).getHandle(); + + vkcv::DescriptorWrites tileResetDescriptorWrites; + tileResetDescriptorWrites.writeStorageBuffer( + 0, m_fullPathWorkTileBuffer + ).writeStorageBuffer( + 1, m_copyPathWorkTileBuffer + ).writeStorageBuffer( + 2, m_fastPathWorkTileBuffer + ); + + m_core->writeDescriptorSet(m_tileResetPass.descriptorSet, tileResetDescriptorWrites); + + m_renderTargets = MotionBlurSetup::createRenderTargets(targetWidth, targetHeight, *m_core); + + m_nearestSampler = vkcv::samplerNearest(*m_core, true); + + return true; +} + +void MotionBlur::setResolution(const uint32_t targetWidth, const uint32_t targetHeight) { + m_renderTargets = MotionBlurSetup::createRenderTargets(targetWidth, targetHeight, *m_core); +} + +vkcv::ImageHandle MotionBlur::render( + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBufferFullRes, + const vkcv::ImageHandle colorBuffer, + const vkcv::ImageHandle depthBuffer, + const eMotionBlurMode mode, + const float cameraNear, + const float cameraFar, + const float deltaTimeSeconds, + const float cameraShutterSpeedInverse, + const float motionTileOffsetLength, + const float fastPathThreshold) { + + computeMotionTiles(cmdStream, motionBufferFullRes); + + m_core->recordComputeDispatchToCmdStream( + cmdStream, + m_tileResetPass.pipeline, + 1, + { vkcv::useDescriptorSet(0, m_tileResetPass.descriptorSet) }, + vkcv::PushConstants(0) + ); + + m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer); + m_core->recordBufferMemoryBarrier(cmdStream, m_copyPathWorkTileBuffer); + m_core->recordBufferMemoryBarrier(cmdStream, m_fastPathWorkTileBuffer); + + // work tile classification + vkcv::DescriptorWrites tileClassificationDescriptorWrites; + tileClassificationDescriptorWrites.writeSampledImage( + 0, m_renderTargets.motionMaxNeighbourhood + ).writeSampledImage( + 1, m_renderTargets.motionMinNeighbourhood + ); + + tileClassificationDescriptorWrites.writeSampler(2, m_nearestSampler); + tileClassificationDescriptorWrites.writeStorageBuffer( + 3, m_fullPathWorkTileBuffer + ).writeStorageBuffer( + 4, m_copyPathWorkTileBuffer + ).writeStorageBuffer( + 5, m_fastPathWorkTileBuffer + ); + + m_core->writeDescriptorSet(m_tileClassificationPass.descriptorSet, tileClassificationDescriptorWrites); + + const auto tileClassificationDispatch = vkcv::dispatchInvocations( + vkcv::DispatchSize( + m_core->getImageWidth(m_renderTargets.motionMaxNeighbourhood), + m_core->getImageHeight(m_renderTargets.motionMaxNeighbourhood) + ), + vkcv::DispatchSize(8, 8) + ); + + struct ClassificationConstants { + uint32_t width; + uint32_t height; + float fastPathThreshold; + }; + ClassificationConstants classificationConstants; + classificationConstants.width = m_core->getImageWidth(m_renderTargets.outputColor); + classificationConstants.height = m_core->getImageHeight(m_renderTargets.outputColor); + classificationConstants.fastPathThreshold = fastPathThreshold; + + vkcv::PushConstants classificationPushConstants = vkcv::pushConstants<ClassificationConstants>(); + classificationPushConstants.appendDrawcall(classificationConstants); + + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMaxNeighbourhood); + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMinNeighbourhood); + + m_core->recordComputeDispatchToCmdStream( + cmdStream, + m_tileClassificationPass.pipeline, + tileClassificationDispatch, + { vkcv::useDescriptorSet(0, m_tileClassificationPass.descriptorSet) }, + classificationPushConstants); + + m_core->recordBufferMemoryBarrier(cmdStream, m_fullPathWorkTileBuffer); + m_core->recordBufferMemoryBarrier(cmdStream, m_copyPathWorkTileBuffer); + m_core->recordBufferMemoryBarrier(cmdStream, m_fastPathWorkTileBuffer); + + vkcv::DescriptorWrites motionBlurDescriptorWrites; + motionBlurDescriptorWrites.writeSampledImage( + 0, colorBuffer + ).writeSampledImage( + 1, depthBuffer + ).writeSampledImage( + 2, motionBufferFullRes + ).writeSampledImage( + 3, m_renderTargets.motionMaxNeighbourhood + ); + + motionBlurDescriptorWrites.writeSampler(4, m_nearestSampler); + motionBlurDescriptorWrites.writeStorageImage(5, m_renderTargets.outputColor); + motionBlurDescriptorWrites.writeStorageBuffer(6, m_fullPathWorkTileBuffer); + + m_core->writeDescriptorSet(m_motionBlurPass.descriptorSet, motionBlurDescriptorWrites); + + vkcv::DescriptorWrites colorCopyDescriptorWrites; + colorCopyDescriptorWrites.writeSampledImage(0, colorBuffer); + colorCopyDescriptorWrites.writeSampler(1, m_nearestSampler); + colorCopyDescriptorWrites.writeStorageImage(2, m_renderTargets.outputColor); + colorCopyDescriptorWrites.writeStorageBuffer(3, m_copyPathWorkTileBuffer); + + m_core->writeDescriptorSet(m_colorCopyPass.descriptorSet, colorCopyDescriptorWrites); + + + vkcv::DescriptorWrites fastPathDescriptorWrites; + fastPathDescriptorWrites.writeSampledImage( + 0, colorBuffer + ).writeSampledImage( + 1, m_renderTargets.motionMaxNeighbourhood + ); + + fastPathDescriptorWrites.writeSampler(2, m_nearestSampler); + fastPathDescriptorWrites.writeStorageImage(3, m_renderTargets.outputColor); + fastPathDescriptorWrites.writeStorageBuffer(4, m_fastPathWorkTileBuffer); + + m_core->writeDescriptorSet(m_motionBlurFastPathPass.descriptorSet, fastPathDescriptorWrites); + + // must match layout in "motionBlur.comp" + struct MotionBlurConstantData { + float motionFactor; + float cameraNearPlane; + float cameraFarPlane; + float motionTileOffsetLength; + }; + MotionBlurConstantData motionBlurConstantData; + + const float deltaTimeMotionBlur = deltaTimeSeconds; + + motionBlurConstantData.motionFactor = 1 / (deltaTimeMotionBlur * cameraShutterSpeedInverse); + motionBlurConstantData.cameraNearPlane = cameraNear; + motionBlurConstantData.cameraFarPlane = cameraFar; + motionBlurConstantData.motionTileOffsetLength = motionTileOffsetLength; + + vkcv::PushConstants motionBlurPushConstants = vkcv::pushConstants<MotionBlurConstantData>(); + motionBlurPushConstants.appendDrawcall(motionBlurConstantData); + + struct FastPathConstants { + float motionFactor; + }; + FastPathConstants fastPathConstants; + fastPathConstants.motionFactor = motionBlurConstantData.motionFactor; + + vkcv::PushConstants fastPathPushConstants = vkcv::pushConstants<FastPathConstants>(); + fastPathPushConstants.appendDrawcall(fastPathConstants); + + m_core->prepareImageForStorage(cmdStream, m_renderTargets.outputColor); + m_core->prepareImageForSampling(cmdStream, colorBuffer); + m_core->prepareImageForSampling(cmdStream, depthBuffer); + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMaxNeighbourhood); + + if (mode == eMotionBlurMode::Default) { + m_core->recordComputeIndirectDispatchToCmdStream( + cmdStream, + m_motionBlurPass.pipeline, + m_fullPathWorkTileBuffer, + 0, + { vkcv::useDescriptorSet(0, m_motionBlurPass.descriptorSet) }, + motionBlurPushConstants); + + m_core->recordComputeIndirectDispatchToCmdStream( + cmdStream, + m_colorCopyPass.pipeline, + m_copyPathWorkTileBuffer, + 0, + { vkcv::useDescriptorSet(0, m_colorCopyPass.descriptorSet) }, + vkcv::PushConstants(0)); + + m_core->recordComputeIndirectDispatchToCmdStream( + cmdStream, + m_motionBlurFastPathPass.pipeline, + m_fastPathWorkTileBuffer, + 0, + { vkcv::useDescriptorSet(0, m_motionBlurFastPathPass.descriptorSet) }, + fastPathPushConstants); + } + else if(mode == eMotionBlurMode::Disabled) { + return colorBuffer; + } + else if (mode == eMotionBlurMode::TileVisualisation) { + + vkcv::DescriptorWrites visualisationDescriptorWrites; + visualisationDescriptorWrites.writeSampledImage(0, colorBuffer); + visualisationDescriptorWrites.writeSampler(1, m_nearestSampler); + visualisationDescriptorWrites.writeStorageImage(2, m_renderTargets.outputColor); + + visualisationDescriptorWrites.writeStorageBuffer( + 3, m_fullPathWorkTileBuffer + ).writeStorageBuffer( + 4, m_copyPathWorkTileBuffer + ).writeStorageBuffer( + 5, m_fastPathWorkTileBuffer + ); + + m_core->writeDescriptorSet(m_tileVisualisationPass.descriptorSet, visualisationDescriptorWrites); + + const uint32_t tileCount = + (m_core->getImageWidth(m_renderTargets.outputColor) + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize * + (m_core->getImageHeight(m_renderTargets.outputColor) + MotionBlurConfig::maxMotionTileSize - 1) / MotionBlurConfig::maxMotionTileSize; + + m_core->recordComputeDispatchToCmdStream( + cmdStream, + m_tileVisualisationPass.pipeline, + tileCount, + { vkcv::useDescriptorSet(0, m_tileVisualisationPass.descriptorSet) }, + vkcv::PushConstants(0)); + } + else { + vkcv_log(vkcv::LogLevel::ERROR, "Unknown eMotionBlurMode enum option"); + return colorBuffer; + } + + return m_renderTargets.outputColor; +} + +vkcv::ImageHandle MotionBlur::renderMotionVectorVisualisation( + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBuffer, + const eMotionVectorVisualisationMode mode, + const float velocityRange) { + + computeMotionTiles(cmdStream, motionBuffer); + + vkcv::ImageHandle visualisationInput; + if ( mode == eMotionVectorVisualisationMode::FullResolution) + visualisationInput = motionBuffer; + else if (mode == eMotionVectorVisualisationMode::MaxTile) + visualisationInput = m_renderTargets.motionMax; + else if (mode == eMotionVectorVisualisationMode::MaxTileNeighbourhood) + visualisationInput = m_renderTargets.motionMaxNeighbourhood; + else if (mode == eMotionVectorVisualisationMode::MinTile) + visualisationInput = m_renderTargets.motionMin; + else if (mode == eMotionVectorVisualisationMode::MinTileNeighbourhood) + visualisationInput = m_renderTargets.motionMinNeighbourhood; + else if (mode == eMotionVectorVisualisationMode::None) { + vkcv_log(vkcv::LogLevel::ERROR, "renderMotionVectorVisualisation called with visualisation mode 'None'"); + return motionBuffer; + } + else { + vkcv_log(vkcv::LogLevel::ERROR, "Unknown eDebugView enum value"); + return motionBuffer; + } + + vkcv::DescriptorWrites motionVectorVisualisationDescriptorWrites; + motionVectorVisualisationDescriptorWrites.writeSampledImage(0, visualisationInput); + motionVectorVisualisationDescriptorWrites.writeSampler(1, m_nearestSampler); + motionVectorVisualisationDescriptorWrites.writeStorageImage(2, m_renderTargets.outputColor); + + m_core->writeDescriptorSet( + m_motionVectorVisualisationPass.descriptorSet, + motionVectorVisualisationDescriptorWrites); + + m_core->prepareImageForSampling(cmdStream, visualisationInput); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.outputColor); + + vkcv::PushConstants motionVectorVisualisationPushConstants = vkcv::pushConstants<float>(); + motionVectorVisualisationPushConstants.appendDrawcall(velocityRange); + + const auto dispatchSizes = vkcv::dispatchInvocations( + vkcv::DispatchSize( + m_core->getImageWidth(m_renderTargets.outputColor), + m_core->getImageHeight(m_renderTargets.outputColor) + ), + vkcv::DispatchSize(8, 8) + ); + + m_core->recordComputeDispatchToCmdStream( + cmdStream, + m_motionVectorVisualisationPass.pipeline, + dispatchSizes, + { vkcv::useDescriptorSet(0, m_motionVectorVisualisationPass.descriptorSet) }, + motionVectorVisualisationPushConstants); + + return m_renderTargets.outputColor; +} + +void MotionBlur::computeMotionTiles( + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBufferFullRes) { + + // motion vector min max tiles + vkcv::DescriptorWrites motionVectorMaxTilesDescriptorWrites; + motionVectorMaxTilesDescriptorWrites.writeSampledImage(0, motionBufferFullRes); + motionVectorMaxTilesDescriptorWrites.writeSampler(1, m_nearestSampler); + motionVectorMaxTilesDescriptorWrites.writeStorageImage( + 2, m_renderTargets.motionMax + ).writeStorageImage( + 3, m_renderTargets.motionMin + ); + + m_core->writeDescriptorSet(m_motionVectorMinMaxPass.descriptorSet, motionVectorMaxTilesDescriptorWrites); + + m_core->prepareImageForSampling(cmdStream, motionBufferFullRes); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMax); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMin); + + const auto motionTileDispatchCounts = vkcv::dispatchInvocations( + vkcv::DispatchSize( + m_core->getImageWidth( m_renderTargets.motionMax), + m_core->getImageHeight(m_renderTargets.motionMax) + ), + vkcv::DispatchSize(8, 8) + ); + + m_core->recordComputeDispatchToCmdStream( + cmdStream, + m_motionVectorMinMaxPass.pipeline, + motionTileDispatchCounts, + { vkcv::useDescriptorSet(0, m_motionVectorMinMaxPass.descriptorSet) }, + vkcv::PushConstants(0)); + + // motion vector min max neighbourhood + vkcv::DescriptorWrites motionVectorMaxNeighbourhoodDescriptorWrites; + motionVectorMaxNeighbourhoodDescriptorWrites.writeSampledImage( + 0, m_renderTargets.motionMax + ).writeSampledImage( + 1, m_renderTargets.motionMin + ); + + motionVectorMaxNeighbourhoodDescriptorWrites.writeSampler(2, m_nearestSampler); + motionVectorMaxNeighbourhoodDescriptorWrites.writeStorageImage( + 3, m_renderTargets.motionMaxNeighbourhood + ).writeStorageImage( + 4, m_renderTargets.motionMinNeighbourhood + ); + + m_core->writeDescriptorSet(m_motionVectorMinMaxNeighbourhoodPass.descriptorSet, motionVectorMaxNeighbourhoodDescriptorWrites); + + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMax); + m_core->prepareImageForSampling(cmdStream, m_renderTargets.motionMin); + + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMaxNeighbourhood); + m_core->prepareImageForStorage(cmdStream, m_renderTargets.motionMinNeighbourhood); + + m_core->recordComputeDispatchToCmdStream( + cmdStream, + m_motionVectorMinMaxNeighbourhoodPass.pipeline, + motionTileDispatchCounts, + { vkcv::useDescriptorSet(0, m_motionVectorMinMaxNeighbourhoodPass.descriptorSet) }, + vkcv::PushConstants(0)); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlur.hpp b/projects/indirect_dispatch/src/MotionBlur.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4e6003799237f69f4a422dd9c20f99255fe711fa --- /dev/null +++ b/projects/indirect_dispatch/src/MotionBlur.hpp @@ -0,0 +1,71 @@ +#pragma once +#include <vkcv/Core.hpp> +#include "AppSetup.hpp" +#include "MotionBlurSetup.hpp" + +// selection for motion blur input and visualisation +enum class eMotionVectorVisualisationMode : int { + None = 0, + FullResolution = 1, + MaxTile = 2, + MaxTileNeighbourhood = 3, + MinTile = 4, + MinTileNeighbourhood = 5, + OptionCount = 6 }; + +enum class eMotionBlurMode : int { + Default = 0, + Disabled = 1, + TileVisualisation = 2, + OptionCount = 3 }; + +class MotionBlur { +public: + + bool initialize(vkcv::Core* corePtr, const uint32_t targetWidth, const uint32_t targetHeight); + void setResolution(const uint32_t targetWidth, const uint32_t targetHeight); + + vkcv::ImageHandle render( + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBufferFullRes, + const vkcv::ImageHandle colorBuffer, + const vkcv::ImageHandle depthBuffer, + const eMotionBlurMode mode, + const float cameraNear, + const float cameraFar, + const float deltaTimeSeconds, + const float cameraShutterSpeedInverse, + const float motionTileOffsetLength, + const float fastPathThreshold); + + vkcv::ImageHandle renderMotionVectorVisualisation( + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBuffer, + const eMotionVectorVisualisationMode mode, + const float velocityRange); + +private: + // computes max per tile and neighbourhood tile max + void computeMotionTiles( + const vkcv::CommandStreamHandle cmdStream, + const vkcv::ImageHandle motionBufferFullRes); + + vkcv::Core* m_core; + + MotionBlurRenderTargets m_renderTargets; + vkcv::SamplerHandle m_nearestSampler; + + ComputePassHandles m_motionBlurPass; + ComputePassHandles m_motionVectorMinMaxPass; + ComputePassHandles m_motionVectorMinMaxNeighbourhoodPass; + ComputePassHandles m_motionVectorVisualisationPass; + ComputePassHandles m_colorCopyPass; + ComputePassHandles m_tileClassificationPass; + ComputePassHandles m_tileResetPass; + ComputePassHandles m_tileVisualisationPass; + ComputePassHandles m_motionBlurFastPathPass; + + vkcv::BufferHandle m_fullPathWorkTileBuffer; + vkcv::BufferHandle m_copyPathWorkTileBuffer; + vkcv::BufferHandle m_fastPathWorkTileBuffer; +}; \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlurConfig.hpp b/projects/indirect_dispatch/src/MotionBlurConfig.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7552abd246ca8d2e7489c5065f43ef8b48af7cd2 --- /dev/null +++ b/projects/indirect_dispatch/src/MotionBlurConfig.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "vulkan/vulkan.hpp" + +namespace MotionBlurConfig { + const vk::Format motionVectorTileFormat = vk::Format::eR16G16Sfloat; + const vk::Format outputColorFormat = vk::Format::eB10G11R11UfloatPack32; + const uint32_t maxMotionTileSize = 16; // must match "motionTileSize" in motionBlurConfig.inc + const uint32_t maxWidth = 3840; + const uint32_t maxHeight = 2160; +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlurSetup.cpp b/projects/indirect_dispatch/src/MotionBlurSetup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7589f77b80398e0bc1d7f1aec9b9570cc20acf3f --- /dev/null +++ b/projects/indirect_dispatch/src/MotionBlurSetup.cpp @@ -0,0 +1,48 @@ +#include "MotionBlurSetup.hpp" +#include "MotionBlurConfig.hpp" + +namespace MotionBlurSetup { + +MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t height, vkcv::Core& core) { + + MotionBlurRenderTargets targets; + + // divide and ceil to int + const uint32_t motionMaxWidth = (width + (MotionBlurConfig::maxMotionTileSize - 1)) / MotionBlurConfig::maxMotionTileSize; + const uint32_t motionMaxHeight = (height + (MotionBlurConfig::maxMotionTileSize - 1)) / MotionBlurConfig::maxMotionTileSize; + + vkcv::ImageConfig targetConfig (motionMaxWidth, motionMaxHeight); + targetConfig.setSupportingStorage(true); + + targets.motionMax = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + targetConfig + ); + + targets.motionMaxNeighbourhood = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + targetConfig + ); + + targets.motionMin = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + targetConfig + ); + + targets.motionMinNeighbourhood = core.createImage( + MotionBlurConfig::motionVectorTileFormat, + targetConfig + ); + + vkcv::ImageConfig outputConfig (width, height); + outputConfig.setSupportingStorage(true); + + targets.outputColor = core.createImage( + MotionBlurConfig::outputColorFormat, + outputConfig + ); + + return targets; +} + +} // namespace MotionBlurSetup \ No newline at end of file diff --git a/projects/indirect_dispatch/src/MotionBlurSetup.hpp b/projects/indirect_dispatch/src/MotionBlurSetup.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ca169d7c6b04aa152d42ba36c3d2e02e563bbd91 --- /dev/null +++ b/projects/indirect_dispatch/src/MotionBlurSetup.hpp @@ -0,0 +1,14 @@ +#pragma once +#include <vkcv/Core.hpp> + +struct MotionBlurRenderTargets { + vkcv::ImageHandle outputColor; + vkcv::ImageHandle motionMax; + vkcv::ImageHandle motionMaxNeighbourhood; + vkcv::ImageHandle motionMin; + vkcv::ImageHandle motionMinNeighbourhood; +}; + +namespace MotionBlurSetup { + MotionBlurRenderTargets createRenderTargets(const uint32_t width, const uint32_t height, vkcv::Core& core); +} \ No newline at end of file diff --git a/projects/indirect_dispatch/src/main.cpp b/projects/indirect_dispatch/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b27e0bcb8f1991d76b570b79da9cc4734cf52950 --- /dev/null +++ b/projects/indirect_dispatch/src/main.cpp @@ -0,0 +1,13 @@ +#include "App.hpp" + +int main(int argc, const char** argv) { + + App app; + if (!app.initialize()) { + std::cerr << "Application initialization failed, exiting" << std::endl; + return 1; + } + app.run(); + + return 0; +} diff --git a/projects/indirect_draw/.gitignore b/projects/indirect_draw/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..92714f6a3381225d29daff0d99efe51e12b40970 --- /dev/null +++ b/projects/indirect_draw/.gitignore @@ -0,0 +1 @@ +indirect_draw \ No newline at end of file diff --git a/projects/indirect_draw/CMakeLists.txt b/projects/indirect_draw/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2802210db6111d833172933452bb25abb90a5c5b --- /dev/null +++ b/projects/indirect_draw/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(indirect_draw) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(indirect_draw src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(indirect_draw SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(indirect_draw vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui) diff --git a/projects/indirect_draw/README.md b/projects/indirect_draw/README.md new file mode 100644 index 0000000000000000000000000000000000000000..06c74837d579ea03a6a7061f90ea97dd6c0560c5 --- /dev/null +++ b/projects/indirect_draw/README.md @@ -0,0 +1,17 @@ +# Indirect draw +An example project to show usage of indirect draw calls + + + +## Details + +The project utilizes multiple Vulkan extensions to access to make indirect draw calls which can be +modified on the GPU from within compute shaders. This will be used to cull the rendered scene on +the GPU instead of (potentially) more expensive frustum culling on the CPU. + +## Extensions + +Here is a list of the used extensions: + + - [VK_KHR_shader_draw_parameters](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_draw_parameters.html) + - [VK_EXT_descriptor_indexing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html) diff --git a/projects/first_scene/resources/Sponza/Sponza.bin b/projects/indirect_draw/resources/Sponza/Sponza.bin similarity index 100% rename from projects/first_scene/resources/Sponza/Sponza.bin rename to projects/indirect_draw/resources/Sponza/Sponza.bin diff --git a/projects/first_scene/resources/Sponza/Sponza.gltf b/projects/indirect_draw/resources/Sponza/Sponza.gltf similarity index 100% rename from projects/first_scene/resources/Sponza/Sponza.gltf rename to projects/indirect_draw/resources/Sponza/Sponza.gltf diff --git a/projects/indirect_draw/resources/Sponza/SponzaFloor.bin b/projects/indirect_draw/resources/Sponza/SponzaFloor.bin new file mode 100644 index 0000000000000000000000000000000000000000..684251288f35070d2e7d244877fd844cc00ca632 --- /dev/null +++ b/projects/indirect_draw/resources/Sponza/SponzaFloor.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:678455aca641cb1f449aa1a5054a7cae132be81c2b333aac283053967da66df0 +size 512 diff --git a/projects/indirect_draw/resources/Sponza/SponzaFloor.gltf b/projects/indirect_draw/resources/Sponza/SponzaFloor.gltf new file mode 100644 index 0000000000000000000000000000000000000000..b45f1c55ef85f2aa1d4bff01df3d9625aa38c809 --- /dev/null +++ b/projects/indirect_draw/resources/Sponza/SponzaFloor.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a6deb75441b1138b50a6b0eec05e60df276fe8fb6d58118fdfce2090b6fbe734 +size 3139 diff --git a/projects/first_scene/resources/Sponza/background.png b/projects/indirect_draw/resources/Sponza/background.png similarity index 100% rename from projects/first_scene/resources/Sponza/background.png rename to projects/indirect_draw/resources/Sponza/background.png diff --git a/projects/first_scene/resources/Sponza/chain_texture.png b/projects/indirect_draw/resources/Sponza/chain_texture.png similarity index 100% rename from projects/first_scene/resources/Sponza/chain_texture.png rename to projects/indirect_draw/resources/Sponza/chain_texture.png diff --git a/projects/first_scene/resources/Sponza/lion.png b/projects/indirect_draw/resources/Sponza/lion.png similarity index 100% rename from projects/first_scene/resources/Sponza/lion.png rename to projects/indirect_draw/resources/Sponza/lion.png diff --git a/projects/first_scene/resources/Sponza/spnza_bricks_a_diff.png b/projects/indirect_draw/resources/Sponza/spnza_bricks_a_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/spnza_bricks_a_diff.png rename to projects/indirect_draw/resources/Sponza/spnza_bricks_a_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_arch_diff.png b/projects/indirect_draw/resources/Sponza/sponza_arch_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_arch_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_arch_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_ceiling_a_diff.png b/projects/indirect_draw/resources/Sponza/sponza_ceiling_a_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_ceiling_a_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_ceiling_a_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_column_a_diff.png b/projects/indirect_draw/resources/Sponza/sponza_column_a_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_column_a_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_column_a_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_column_b_diff.png b/projects/indirect_draw/resources/Sponza/sponza_column_b_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_column_b_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_column_b_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_column_c_diff.png b/projects/indirect_draw/resources/Sponza/sponza_column_c_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_column_c_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_column_c_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_curtain_blue_diff.png b/projects/indirect_draw/resources/Sponza/sponza_curtain_blue_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_curtain_blue_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_curtain_blue_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_curtain_diff.png b/projects/indirect_draw/resources/Sponza/sponza_curtain_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_curtain_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_curtain_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_curtain_green_diff.png b/projects/indirect_draw/resources/Sponza/sponza_curtain_green_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_curtain_green_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_curtain_green_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_details_diff.png b/projects/indirect_draw/resources/Sponza/sponza_details_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_details_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_details_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_fabric_blue_diff.png b/projects/indirect_draw/resources/Sponza/sponza_fabric_blue_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_fabric_blue_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_fabric_blue_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_fabric_diff.png b/projects/indirect_draw/resources/Sponza/sponza_fabric_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_fabric_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_fabric_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_fabric_green_diff.png b/projects/indirect_draw/resources/Sponza/sponza_fabric_green_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_fabric_green_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_fabric_green_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_flagpole_diff.png b/projects/indirect_draw/resources/Sponza/sponza_flagpole_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_flagpole_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_flagpole_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_floor_a_diff.png b/projects/indirect_draw/resources/Sponza/sponza_floor_a_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_floor_a_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_floor_a_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_roof_diff.png b/projects/indirect_draw/resources/Sponza/sponza_roof_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_roof_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_roof_diff.png diff --git a/projects/first_scene/resources/Sponza/sponza_thorn_diff.png b/projects/indirect_draw/resources/Sponza/sponza_thorn_diff.png similarity index 100% rename from projects/first_scene/resources/Sponza/sponza_thorn_diff.png rename to projects/indirect_draw/resources/Sponza/sponza_thorn_diff.png diff --git a/projects/first_scene/resources/Sponza/vase_dif.png b/projects/indirect_draw/resources/Sponza/vase_dif.png similarity index 100% rename from projects/first_scene/resources/Sponza/vase_dif.png rename to projects/indirect_draw/resources/Sponza/vase_dif.png diff --git a/projects/first_scene/resources/Sponza/vase_hanging.png b/projects/indirect_draw/resources/Sponza/vase_hanging.png similarity index 100% rename from projects/first_scene/resources/Sponza/vase_hanging.png rename to projects/indirect_draw/resources/Sponza/vase_hanging.png diff --git a/projects/first_scene/resources/Sponza/vase_plant.png b/projects/indirect_draw/resources/Sponza/vase_plant.png similarity index 100% rename from projects/first_scene/resources/Sponza/vase_plant.png rename to projects/indirect_draw/resources/Sponza/vase_plant.png diff --git a/projects/first_scene/resources/Sponza/vase_round.png b/projects/indirect_draw/resources/Sponza/vase_round.png similarity index 100% rename from projects/first_scene/resources/Sponza/vase_round.png rename to projects/indirect_draw/resources/Sponza/vase_round.png diff --git a/projects/indirect_draw/resources/shaders/culling.comp b/projects/indirect_draw/resources/shaders/culling.comp new file mode 100644 index 0000000000000000000000000000000000000000..5d5884b9e494baf56ab32576f094f34584b163b3 --- /dev/null +++ b/projects/indirect_draw/resources/shaders/culling.comp @@ -0,0 +1,58 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_EXT_nonuniform_qualifier : enable + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +struct Plane{ + vec3 pointOnPlane; + float padding0; + vec3 normal; + float padding1; +}; + +struct DrawCommand{ + uint indexCount; + uint instanceCount; + uint firstIndex; + int vertexOffset; + uint firstInstance; +}; + +layout(set=0, binding=0, std140) readonly uniform cameraPlaneBuffer{ + Plane cameraPlanes[6]; +}; + +layout(set=0, binding=1, std430) buffer DrawCommandsBuffer{ + DrawCommand commands[]; +}; + +layout(set=0, binding=2, std430) readonly buffer boundingBoxBuffer{ + // x,y,z for meanPosition; + // w for boundingSphereRadius + vec4 boundingBoxes[]; +}; + +bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius){ + bool isInside = true; + for(int i = 0; i < 6; i++){ + Plane p = cameraPlanes[i]; + isInside = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0; + } + return isInside; +} + +void main(){ + if(gl_GlobalInvocationID.x > commands.length()){ + return; + } + + vec4 bbox = boundingBoxes[gl_GlobalInvocationID.x]; + if (isSphereInsideFrustum(bbox.xyz, bbox.w)){ + commands[gl_GlobalInvocationID.x].instanceCount = 1; + } + else{ + commands[gl_GlobalInvocationID.x].instanceCount = 0; + } +} + diff --git a/projects/indirect_draw/resources/shaders/shader.frag b/projects/indirect_draw/resources/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..f0a0b49314926ef9195d9a145b805b55d17b8807 --- /dev/null +++ b/projects/indirect_draw/resources/shaders/shader.frag @@ -0,0 +1,18 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_EXT_nonuniform_qualifier : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; +layout(location = 2) in flat uint passDrawIndex; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform sampler standardSampler; +layout(set=0, binding=2) uniform texture2D baseColorTex[]; + + +void main() +{ + outColor = texture(sampler2D(baseColorTex[nonuniformEXT(passDrawIndex)], standardSampler), passUV).rgb; +} \ No newline at end of file diff --git a/projects/indirect_draw/resources/shaders/shader.vert b/projects/indirect_draw/resources/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..10b250761437dce6eea01dd195e1248fc9c0bd90 --- /dev/null +++ b/projects/indirect_draw/resources/shaders/shader.vert @@ -0,0 +1,27 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shader_draw_parameters : enable + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in vec2 inUV; + +layout(std430, binding=1) readonly buffer uModel { + mat4 modelMatrix[]; +}; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out vec2 passUV; +layout(location = 2) out flat uint passDrawIndex; + +layout( push_constant ) uniform constants{ + mat4 vp; +}; + +void main() +{ + gl_Position = vp * modelMatrix[gl_DrawID] * vec4(inPosition, 1.0); + passNormal = inNormal; + passUV = inUV; + passDrawIndex = gl_DrawID; +} \ No newline at end of file diff --git a/projects/indirect_draw/src/main.cpp b/projects/indirect_draw/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f90fa0892982f68fa959671b5eb6f207aac72b65 --- /dev/null +++ b/projects/indirect_draw/src/main.cpp @@ -0,0 +1,596 @@ +#include <iostream> +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Sampler.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +struct Plane { + glm::vec3 pointOnPlane; + float padding0; + glm::vec3 normal; + float padding1; +}; + +struct CameraPlanes { + Plane planes[6]; +}; + +CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) { + const float fov = camera.getFov(); + const glm::vec3 pos = camera.getPosition(); + const float ratio = camera.getRatio(); + const glm::vec3 forward = glm::normalize(camera.getFront()); + float near; + float far; + camera.getNearFar(near, far); + + glm::vec3 up = glm::vec3(0, -1, 0); + glm::vec3 right = glm::normalize(glm::cross(forward, up)); + up = glm::cross(forward, right); + + const glm::vec3 nearCenter = pos + forward * near; + const glm::vec3 farCenter = pos + forward * far; + + const float tanFovHalf = glm::tan(fov / 2); + + const glm::vec3 nearUpCenter = nearCenter + up * tanFovHalf * near; + const glm::vec3 nearDownCenter = nearCenter - up * tanFovHalf * near; + const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio; + const glm::vec3 nearLeftCenter = nearCenter - right * tanFovHalf * near * ratio; + + const glm::vec3 farUpCenter = farCenter + up * tanFovHalf * far; + const glm::vec3 farDownCenter = farCenter - up * tanFovHalf * far; + const glm::vec3 farRightCenter = farCenter + right * tanFovHalf * far * ratio; + const glm::vec3 farLeftCenter = farCenter - right * tanFovHalf * far * ratio; + + CameraPlanes cameraPlanes; + // near + cameraPlanes.planes[0].pointOnPlane = nearCenter; + cameraPlanes.planes[0].normal = -forward; + // far + cameraPlanes.planes[1].pointOnPlane = farCenter; + cameraPlanes.planes[1].normal = forward; + + // top + cameraPlanes.planes[2].pointOnPlane = nearUpCenter; + cameraPlanes.planes[2].normal = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right)); + // bot + cameraPlanes.planes[3].pointOnPlane = nearDownCenter; + cameraPlanes.planes[3].normal = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter)); + + // right + cameraPlanes.planes[4].pointOnPlane = nearRightCenter; + cameraPlanes.planes[4].normal = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter)); + // left + cameraPlanes.planes[5].pointOnPlane = nearLeftCenter; + cameraPlanes.planes[5].normal = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up)); + + return cameraPlanes; +} + +struct Vertex +{ + glm::vec3 position; + glm::vec3 normal; + glm::vec2 uv; +}; + +struct CompiledMaterial +{ + std::vector<vkcv::Image> baseColor; + std::vector<vkcv::Image> metalRough; + std::vector<vkcv::Image> normal; + std::vector<vkcv::Image> occlusion; +}; + +void interleaveScene(vkcv::asset::Scene scene, + std::vector<std::vector<Vertex>> &interleavedVertexBuffers, + std::vector<glm::vec4> &boundingBoxBuffers) { + + for(const auto &mesh : scene.meshes) { + for(auto vertexGroupIndex : mesh.vertexGroups) { + const auto &vertexGroup = scene.vertexGroups[vertexGroupIndex]; + + const vkcv::asset::VertexAttribute* positionAttribute = nullptr; + const vkcv::asset::VertexAttribute* normalAttribute = nullptr; + const vkcv::asset::VertexAttribute* uvAttribute = nullptr; + + for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { + switch (attribute.type) { + case vkcv::asset::PrimitiveType::POSITION: + positionAttribute = &attribute; + break; + case vkcv::asset::PrimitiveType::NORMAL: + normalAttribute = &attribute; + break; + case vkcv::asset::PrimitiveType::TEXCOORD_0: + uvAttribute = &attribute; + break; + default: + break; + } + } + + assert(positionAttribute && normalAttribute && uvAttribute); + + const uint64_t &verticesCount = vertexGroup.numVertices; + const std::vector<uint8_t> &vertexData = vertexGroup.vertexBuffer.data; + + std::vector<Vertex> vertices; + vertices.reserve(verticesCount); + + const size_t positionStride = positionAttribute->stride == 0 ? sizeof(glm::vec3) : positionAttribute->stride; + const size_t normalStride = normalAttribute->stride == 0 ? sizeof(glm::vec3) : normalAttribute->stride; + const size_t uvStride = uvAttribute->stride == 0 ? sizeof(glm::vec2) : uvAttribute->stride; + + glm::vec3 max_pos(-std::numeric_limits<float>::max()); + glm::vec3 min_pos(std::numeric_limits<float>::max()); + + for(size_t i = 0; i < verticesCount; i++) + { + const size_t positionOffset = positionAttribute->offset + positionStride * i; + const size_t normalOffset = normalAttribute->offset + normalStride * i; + const size_t uvOffset = uvAttribute->offset + uvStride * i; + + Vertex v; + + v.position = *reinterpret_cast<const glm::vec3*>(&(vertexData[positionOffset])); + v.normal = *reinterpret_cast<const glm::vec3*>(&(vertexData[normalOffset])); + v.uv = *reinterpret_cast<const glm::vec3*>(&(vertexData[uvOffset])); + + glm::vec3 posWorld = glm::make_mat4(mesh.modelMatrix.data()) * glm::vec4(v.position, 1); + + max_pos.x = glm::max(max_pos.x, posWorld.x); + max_pos.y = glm::max(max_pos.y, posWorld.y); + max_pos.z = glm::max(max_pos.z, posWorld.z); + + min_pos.x = glm::min(min_pos.x, posWorld.x); + min_pos.y = glm::min(min_pos.y, posWorld.y); + min_pos.z = glm::min(min_pos.z, posWorld.z); + + vertices.push_back(v); + } + + const glm::vec3 boundingPosition = (max_pos + min_pos) / 2.0f; + const float radius = glm::distance(max_pos, min_pos) / 2.0f; + + boundingBoxBuffers.emplace_back(boundingPosition.x, + boundingPosition.y, + boundingPosition.z, + radius); + + interleavedVertexBuffers.push_back(vertices); + } + } +} + +// Assumes the meshes use index buffers +void compileMeshForIndirectDraw(vkcv::Core &core, + const vkcv::asset::Scene &scene, + std::vector<uint8_t> &compiledVertexBuffer, + std::vector<uint8_t> &compiledIndexBuffer, + CompiledMaterial &compiledMat, + std::vector<vk::DrawIndexedIndirectCommand> &indexedIndirectCommands) +{ + vkcv::Image pseudoImg = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, 2, 2); + std::vector<uint8_t> pseudoData = {0, 0, 0, 0}; + pseudoImg.fill(pseudoData.data()); + + auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics); + pseudoImg.recordMipChainGeneration(mipStream, core.getDownsampler()); + + uint32_t vertexOffset = 0; + for (const auto &mesh : scene.meshes) + { + for(auto &vertexGroupIndex : mesh.vertexGroups) + { + auto &vertexGroup = scene.vertexGroups[vertexGroupIndex]; + + auto &material = scene.materials[vertexGroup.materialIndex]; + + if(material.baseColor == -1) + { + std::cout << "baseColor is -1! Pushing pseudo-texture!" << std::endl; + compiledMat.baseColor.push_back(pseudoImg); + } + else + { + auto &baseColor = scene.textures[material.baseColor]; + + vkcv::Image baseColorImg = vkcv::image(core, vk::Format::eR8G8B8A8Srgb, baseColor.w, baseColor.h); + baseColorImg.fill(baseColor.data.data()); + baseColorImg.recordMipChainGeneration(mipStream, core.getDownsampler()); + + compiledMat.baseColor.push_back(baseColorImg); + } + + indexedIndirectCommands.emplace_back(static_cast<uint32_t>(vertexGroup.numIndices), + 1, + static_cast<uint32_t>(compiledIndexBuffer.size() / 4), + vertexOffset, + static_cast<uint32_t>(indexedIndirectCommands.size())); + + vertexOffset += vertexGroup.numVertices; + + compiledVertexBuffer.insert(compiledVertexBuffer.end(), + vertexGroup.vertexBuffer.data.begin(), + vertexGroup.vertexBuffer.data.end()); + + if(vertexGroup.indexBuffer.type == vkcv::asset::IndexType::UINT8) + { + for(auto index : vertexGroup.indexBuffer.data) + { + uint32_t index32 = static_cast<uint32_t>(index); + uint8_t firstByte = index32; + uint8_t secondByte = index32 >> 8; + uint8_t thirdByte = index32 >> 16; + uint8_t fourthByte = index32 >> 24; + + compiledIndexBuffer.push_back(firstByte); + compiledIndexBuffer.push_back(secondByte); + compiledIndexBuffer.push_back(thirdByte); + compiledIndexBuffer.push_back(fourthByte); + } + } + else if(vertexGroup.indexBuffer.type == vkcv::asset::IndexType::UINT16) + { + for (size_t i = 0; i < vertexGroup.indexBuffer.data.size(); i += 2) + { + uint16_t index16 = *reinterpret_cast<const uint16_t*>(&vertexGroup.indexBuffer.data[i]); + uint32_t index32 = static_cast<uint32_t>(index16); + + uint8_t firstByte = index32; + uint8_t secondByte = index32 >> 8; + uint8_t thirdByte = index32 >> 16; + uint8_t fourthByte = index32 >> 24; + + compiledIndexBuffer.push_back(firstByte); + compiledIndexBuffer.push_back(secondByte); + compiledIndexBuffer.push_back(thirdByte); + compiledIndexBuffer.push_back(fourthByte); + } + } + else + { + compiledIndexBuffer.insert(compiledIndexBuffer.end(), + vertexGroup.indexBuffer.data.begin(), + vertexGroup.indexBuffer.data.end()); + } + } + } + + core.submitCommandStream(mipStream, false); +} + +int main(int argc, const char** argv) { + const std::string applicationName = "Indirect draw"; + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + features.requireFeature([](vk::PhysicalDeviceFeatures &features){ + features.setMultiDrawIndirect(true); + }); + + features.requireExtension(VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME); + features.requireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>( + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, [](vk::PhysicalDeviceDescriptorIndexingFeatures &features) { + // features.setShaderInputAttachmentArrayDynamicIndexing(true); + features.setShaderUniformTexelBufferArrayDynamicIndexing(true); + features.setShaderStorageTexelBufferArrayDynamicIndexing(true); + features.setShaderUniformBufferArrayNonUniformIndexing(true); + features.setShaderSampledImageArrayNonUniformIndexing(true); + features.setShaderStorageBufferArrayNonUniformIndexing(true); + features.setShaderStorageImageArrayNonUniformIndexing(true); + // features.setShaderInputAttachmentArrayNonUniformIndexing(true); + features.setShaderUniformTexelBufferArrayNonUniformIndexing(true); + features.setShaderStorageTexelBufferArrayNonUniformIndexing(true); + + features.setDescriptorBindingUniformBufferUpdateAfterBind(true); + features.setDescriptorBindingSampledImageUpdateAfterBind(true); + features.setDescriptorBindingStorageImageUpdateAfterBind(true); + features.setDescriptorBindingStorageBufferUpdateAfterBind(true); + features.setDescriptorBindingUniformTexelBufferUpdateAfterBind(true); + features.setDescriptorBindingStorageTexelBufferUpdateAfterBind(true); + + features.setDescriptorBindingUpdateUnusedWhilePending(true); + features.setDescriptorBindingPartiallyBound(true); + features.setDescriptorBindingVariableDescriptorCount(true); + features.setRuntimeDescriptorArray(true); + } + ); + + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, + features + ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName,800,600,true); + vkcv::Window& window = core.getWindow(windowHandle); + + vkcv::gui::GUI gui (core, windowHandle); + + vkcv::asset::Scene asset_scene; + const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; + int result = vkcv::asset::loadScene(path, asset_scene); + + if (result == 1) { + std::cout << "Loading Sponza successful!" << std::endl; + } else { + std::cerr << "Loading Sponza failed: " << result << std::endl; + return 1; + } + assert(!asset_scene.vertexGroups.empty()); + if (asset_scene.textures.empty()) + { + std::cerr << "Error. No textures found. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::PassHandle passHandle = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } + ); + + if (!passHandle) { + std::cerr << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + + vkcv::ShaderProgram sponzaProgram; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), + [&sponzaProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + sponzaProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + [&sponzaProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + sponzaProgram.addShader(shaderStage, path); + }); + + vkcv::ShaderProgram cullingProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("resources/shaders/culling.comp"), + [&cullingProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + cullingProgram.addShader(shaderStage, path); + }); + + // vertex layout for the pipeline. (assumed to be) used by all sponza meshes. + const std::vector<vkcv::VertexAttachment> vertexAttachments = sponzaProgram.getVertexAttachments(); + const vkcv::VertexLayout sponzaVertexLayout { + { vkcv::createVertexBinding(0, { vertexAttachments }) } + }; + + std::vector<uint8_t> compiledVertexBuffer; // IGNORED, since the vertex buffer is not interleaved! + + std::vector<uint8_t> compiledIndexBuffer; + CompiledMaterial compiledMaterial; + std::vector<vk::DrawIndexedIndirectCommand> indexedIndirectCommands; + + compileMeshForIndirectDraw(core, + asset_scene, + compiledVertexBuffer, + compiledIndexBuffer, + compiledMaterial, + indexedIndirectCommands); + + std::vector<std::vector<Vertex>> interleavedVertices; + std::vector<glm::vec4> compiledBoundingBoxBuffer; + interleaveScene(asset_scene, + interleavedVertices, + compiledBoundingBoxBuffer); + + std::vector<Vertex> compiledInterleavedVertexBuffer; + for(auto& vertexGroup : interleavedVertices ) + { + compiledInterleavedVertexBuffer.insert(compiledInterleavedVertexBuffer.end(),vertexGroup.begin(),vertexGroup.end()); + } + + auto vkCompiledVertexBuffer = vkcv::buffer<Vertex>( + core, + vkcv::BufferType::VERTEX, + compiledInterleavedVertexBuffer.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL); + vkCompiledVertexBuffer.fill(compiledInterleavedVertexBuffer.data()); + + auto vkCompiledIndexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::INDEX, + compiledIndexBuffer.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL); + vkCompiledIndexBuffer.fill(compiledIndexBuffer.data()); + + vkcv::Buffer<vk::DrawIndexedIndirectCommand> indirectBuffer = vkcv::buffer<vk::DrawIndexedIndirectCommand>( + core, + vkcv::BufferType::INDIRECT, + indexedIndirectCommands.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL); + indirectBuffer.fill(indexedIndirectCommands); + + auto boundingBoxBuffer = vkcv::buffer<glm::vec4>( + core, + vkcv::BufferType::STORAGE, + compiledBoundingBoxBuffer.size()); + boundingBoxBuffer.fill(compiledBoundingBoxBuffer); + + std::vector<glm::mat4> modelMatrix; + for( auto& mesh : asset_scene.meshes) + { + modelMatrix.push_back(glm::make_mat4(mesh.modelMatrix.data())); + } + vkcv::Buffer<glm::mat4> modelBuffer = vkcv::buffer<glm::mat4>( + core, + vkcv::BufferType::STORAGE, + modelMatrix.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + modelBuffer.fill(modelMatrix); + + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::vertexBufferBinding(vkCompiledVertexBuffer.getHandle()) + }; + + vkcv::VertexData vertexData (vertexBufferBindings); + vertexData.setIndexBuffer(vkCompiledIndexBuffer.getHandle(), vkcv::IndexBitCount::Bit32); + + //assert(compiledMaterial.baseColor.size() == compiledMaterial.metalRough.size()); + + vkcv::DescriptorBindings descriptorBindings = sponzaProgram.getReflectedDescriptors().at(0); + descriptorBindings[2].descriptorCount = compiledMaterial.baseColor.size(); + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout(descriptorBindings); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::SamplerHandle standardSampler = vkcv::samplerLinear(core); + + vkcv::DescriptorWrites setWrites; + + std::vector<vkcv::SampledImageDescriptorWrite> textureArrayWrites; + for(uint32_t i = 0; i < compiledMaterial.baseColor.size(); i++) + { + setWrites.writeSampledImage(2, compiledMaterial.baseColor[i].getHandle(), 0, false, i); + } + + setWrites.writeSampler(0, standardSampler); + setWrites.writeStorageBuffer(1, modelBuffer.getHandle()); + core.writeDescriptorSet(descriptorSet, setWrites); + + vkcv::GraphicsPipelineHandle sponzaPipelineHandle = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + sponzaProgram, + passHandle, + { sponzaVertexLayout }, + { descriptorSetLayout } + ) + ); + + if (!sponzaPipelineHandle) { + std::cerr << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::DescriptorBindings cullingBindings = cullingProgram.getReflectedDescriptors().at(0); + vkcv::DescriptorSetLayoutHandle cullingSetLayout = core.createDescriptorSetLayout(cullingBindings); + vkcv::DescriptorSetHandle cullingDescSet = core.createDescriptorSet(cullingSetLayout); + + vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = vkcv::buffer<CameraPlanes>( + core, + vkcv::BufferType::UNIFORM, + 1); + + vkcv::DescriptorWrites cullingWrites; + cullingWrites.writeStorageBuffer(1, indirectBuffer.getHandle()); + cullingWrites.writeStorageBuffer(2, boundingBoxBuffer.getHandle()); + cullingWrites.writeUniformBuffer(0, cameraPlaneBuffer.getHandle()); + core.writeDescriptorSet(cullingDescSet, cullingWrites); + + + const vkcv::ComputePipelineConfig computeCullingConfig { + cullingProgram, + {cullingSetLayout} + }; + vkcv::ComputePipelineHandle cullingPipelineHandle = core.createComputePipeline(computeCullingConfig); + if (!cullingPipelineHandle) { + std::cerr << "Error. Could not create culling pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::camera::CameraManager cameraManager (window); + auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -3)); + cameraManager.getCamera(camHandle).setNearFar(0.1f, 20.f); + + vkcv::ImageHandle depthBuffer; + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + float ceiledDispatchCount = static_cast<float>(indexedIndirectCommands.size()) / 64.0f; + ceiledDispatchCount = std::ceil(ceiledDispatchCount); + const vkcv::DispatchSize dispatchCount = static_cast<uint32_t>(ceiledDispatchCount); + + vkcv::DescriptorSetUsage cullingUsage = vkcv::useDescriptorSet(0, cullingDescSet); + vkcv::PushConstants emptyPushConstant(0); + + bool updateFrustumPlanes = true; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + (swapchainHeight != core.getImageHeight(depthBuffer))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); + } + + cameraManager.update(dt); + + vkcv::camera::Camera cam = cameraManager.getActiveCamera(); + vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>(); + pushConstants.appendDrawcall(cam.getProjection() * cam.getView()); + + if (updateFrustumPlanes) { + const CameraPlanes cameraPlanes = computeCameraPlanes(cam); + cameraPlaneBuffer.fill({ cameraPlanes }); + } + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + core.recordComputeDispatchToCmdStream( + cmdStream, + cullingPipelineHandle, + dispatchCount, + {cullingUsage}, + emptyPushConstant + ); + + core.recordBufferMemoryBarrier(cmdStream, indirectBuffer.getHandle()); + + vkcv::IndirectDrawcall drawcall ( + indirectBuffer.getHandle(), + vertexData, + indexedIndirectCommands.size() + ); + + drawcall.useDescriptorSet(0, descriptorSet); + + core.recordIndirectDrawcallsToCmdStream( + cmdStream, + sponzaPipelineHandle, + pushConstants, + { drawcall }, + renderTargets, + windowHandle + ); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes); + ImGui::Text("Deltatime %fms, %f", dt * 1000, 1/dt); + + ImGui::End(); + + gui.endGUI(); + }); + + return 0; +} diff --git a/projects/mesh_shader/.gitignore b/projects/mesh_shader/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fd009a6281f4b2b6716e193d23829907f4bb5f33 --- /dev/null +++ b/projects/mesh_shader/.gitignore @@ -0,0 +1 @@ +mesh_shader \ No newline at end of file diff --git a/projects/mesh_shader/CMakeLists.txt b/projects/mesh_shader/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..04b632effa28101a105100c768da712676378c99 --- /dev/null +++ b/projects/mesh_shader/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(mesh_shader) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(mesh_shader src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(mesh_shader SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_camera_include} ${vkcv_meshlet_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(mesh_shader vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_testing vkcv_camera vkcv_meshlet vkcv_shader_compiler vkcv_gui) \ No newline at end of file diff --git a/projects/mesh_shader/README.md b/projects/mesh_shader/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5d789a631b5316515e07ed29b59243a97191ccf0 --- /dev/null +++ b/projects/mesh_shader/README.md @@ -0,0 +1,24 @@ +# Mesh shader +An example project to show usage of mesh shaders with the VkCV framework + + + +## Details + +The project utilizes a Vulkan extension to use hardware accelerated mesh shaders. Those mesh +shaders can replace the usual vertex-, tessellation- and geometry-shader stages in the graphics +pipeline. They also act more similar to compute shaders. + +The application uses those mesh shaders to cull a rendered mesh into individual groups of triangles +which are called meshlets in this context. + +The project currently uses the Nvidia GPU exclusive extension for mesh shading but it could be +adjusted to use the cross compatible extension +"[VK_EXT_mesh_shader](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_mesh_shader.html)" +instead. + +## Extensions + +Here is a list of the used extensions: + +- [VK_NV_mesh_shader](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_NV_mesh_shader.html) diff --git a/projects/mesh_shader/assets/Bunny/Bunny.glb b/projects/mesh_shader/assets/Bunny/Bunny.glb new file mode 100644 index 0000000000000000000000000000000000000000..181f1f92f1906e1e1ba900768580203efe19e9be --- /dev/null +++ b/projects/mesh_shader/assets/Bunny/Bunny.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8bc6fab11929ca11bdf4e892ffb03b621b10307f705cdea17d82d3dee3b9aae +size 4045836 diff --git a/projects/mesh_shader/assets/monke.glb b/projects/mesh_shader/assets/monke.glb new file mode 100644 index 0000000000000000000000000000000000000000..47d0b9131f15a8f0697318d0a47302c71cad1db8 --- /dev/null +++ b/projects/mesh_shader/assets/monke.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:597584db90a3f51088beea6652d8320e82cb025f9d3d036b89e54ad72c732a06 +size 98612 diff --git a/projects/mesh_shader/assets/shaders/common.inc b/projects/mesh_shader/assets/shaders/common.inc new file mode 100644 index 0000000000000000000000000000000000000000..280ffee215a8b8342b78d1f5558d63a05e16859b --- /dev/null +++ b/projects/mesh_shader/assets/shaders/common.inc @@ -0,0 +1,4 @@ +struct ObjectMatrices{ + mat4 model; + mat4 mvp; +}; \ No newline at end of file diff --git a/projects/mesh_shader/assets/shaders/meshlet.inc b/projects/mesh_shader/assets/shaders/meshlet.inc new file mode 100644 index 0000000000000000000000000000000000000000..0594f62ceead8ffca09b585305075eb6046f3c46 --- /dev/null +++ b/projects/mesh_shader/assets/shaders/meshlet.inc @@ -0,0 +1,8 @@ +struct Meshlet{ + uint vertexOffset; + uint vertexCount; + uint indexOffset; + uint indexCount; + vec3 meanPosition; + float boundingSphereRadius; +}; \ No newline at end of file diff --git a/projects/mesh_shader/assets/shaders/shader.frag b/projects/mesh_shader/assets/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..f4f6982f2089e6c8e102027f3b8763bb38f8e59c --- /dev/null +++ b/projects/mesh_shader/assets/shaders/shader.frag @@ -0,0 +1,32 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in flat uint passTaskIndex; +layout(location = 0) out vec3 outColor; + +uint lowbias32(uint x) +{ + x ^= x >> 16; + x *= 0x7feb352dU; + x ^= x >> 15; + x *= 0x846ca68bU; + x ^= x >> 16; + return x; +} + +float hashToFloat(uint hash){ + return (hash % 255) / 255.f; +} + +vec3 colorFromIndex(uint i){ + return vec3( + hashToFloat(lowbias32(i+0)), + hashToFloat(lowbias32(i+1)), + hashToFloat(lowbias32(i+2))); +} + +void main() { + outColor = normalize(passNormal) * 0.5 + 0.5; + outColor = colorFromIndex(passTaskIndex); +} \ No newline at end of file diff --git a/projects/mesh_shader/assets/shaders/shader.mesh b/projects/mesh_shader/assets/shaders/shader.mesh new file mode 100644 index 0000000000000000000000000000000000000000..30c98610f4776204ff526c57c1f793e371194629 --- /dev/null +++ b/projects/mesh_shader/assets/shaders/shader.mesh @@ -0,0 +1,78 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable +#extension GL_NV_mesh_shader : require + +#include "meshlet.inc" + +layout(local_size_x=32) in; + +layout(triangles) out; +layout(max_vertices=64, max_primitives=126) out; + +layout(location = 0) out vec3 passNormal[]; +layout(location = 1) out uint passTaskIndex[]; + +struct Vertex +{ + vec3 position; float padding0; + vec3 normal; float padding1; +}; + +layout(std430, binding = 0) readonly buffer vertexBuffer +{ + Vertex vertices[]; +}; + +layout(std430, binding = 1) readonly buffer indexBuffer +{ + uint localIndices[]; // breaks for 16 bit indices +}; + +layout(std430, binding = 2) readonly buffer meshletBuffer +{ + Meshlet meshlets[]; +}; + +taskNV in Task { + uint meshletIndices[32]; + mat4 mvp; +} IN; + +void main() { + + uint meshletIndex = IN.meshletIndices[gl_WorkGroupID.x]; + Meshlet meshlet = meshlets[meshletIndex]; + + // set vertices + for(uint i = 0; i < 2; i++){ + + uint workIndex = gl_LocalInvocationID.x + 32 * i; + if(workIndex >= meshlet.vertexCount){ + break; + } + + uint vertexIndex = meshlet.vertexOffset + workIndex; + Vertex vertex = vertices[vertexIndex]; + + gl_MeshVerticesNV[workIndex].gl_Position = IN.mvp * vec4(vertex.position, 1); + passNormal[workIndex] = vertex.normal; + passTaskIndex[workIndex] = meshletIndex; + } + + // set local indices + for(uint i = 0; i < 12; i++){ + + uint workIndex = gl_LocalInvocationID.x + i * 32; + if(workIndex >= meshlet.indexCount){ + break; + } + + uint indexBufferIndex = meshlet.indexOffset + workIndex; + gl_PrimitiveIndicesNV[workIndex] = localIndices[indexBufferIndex]; + } + + if(gl_LocalInvocationID.x == 0){ + gl_PrimitiveCountNV = meshlet.indexCount / 3; + } +} \ No newline at end of file diff --git a/projects/mesh_shader/assets/shaders/shader.task b/projects/mesh_shader/assets/shaders/shader.task new file mode 100644 index 0000000000000000000000000000000000000000..7a692e98e6384767191d76cef940e295ca127d62 --- /dev/null +++ b/projects/mesh_shader/assets/shaders/shader.task @@ -0,0 +1,78 @@ +#version 460 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_NV_mesh_shader : require +#extension GL_GOOGLE_include_directive : enable + +#include "meshlet.inc" +#include "common.inc" + +layout(local_size_x=32) in; + +taskNV out Task { + uint meshletIndices[32]; + mat4 mvp; +} OUT; + +layout( push_constant ) uniform constants{ + uint matrixIndex; + uint meshletCount; +}; + +// TODO: reuse mesh stage binding at location 2 after required fix in framework +layout(std430, binding = 5) readonly buffer meshletBuffer +{ + Meshlet meshlets[]; +}; + +struct Plane{ + vec3 pointOnPlane; + float padding0; + vec3 normal; + float padding1; +}; + +layout(set=0, binding=3, std140) uniform cameraPlaneBuffer{ + Plane cameraPlanes[6]; +}; + +layout(std430, binding = 4) readonly buffer matrixBuffer +{ + ObjectMatrices objectMatrices[]; +}; + +shared uint taskCount; + +bool isSphereInsideFrustum(vec3 spherePos, float sphereRadius, Plane cameraPlanes[6]){ + bool isInside = true; + for(int i = 0; i < 6; i++){ + Plane p = cameraPlanes[i]; + isInside = isInside && dot(p.normal, spherePos - p.pointOnPlane) - sphereRadius < 0; + } + return isInside; +} + +void main() { + + if(gl_LocalInvocationID.x >= meshletCount){ + return; + } + + uint meshletIndex = gl_GlobalInvocationID.x; + Meshlet meshlet = meshlets[meshletIndex]; + + if(gl_LocalInvocationID.x == 0){ + taskCount = 0; + } + + // TODO: scaling support + vec3 meshletPositionWorld = (vec4(meshlet.meanPosition, 1) * objectMatrices[matrixIndex].model).xyz; + if(isSphereInsideFrustum(meshletPositionWorld, meshlet.boundingSphereRadius, cameraPlanes)){ + uint outIndex = atomicAdd(taskCount, 1); + OUT.meshletIndices[outIndex] = gl_GlobalInvocationID.x; + } + + if(gl_LocalInvocationID.x == 0){ + gl_TaskCountNV = taskCount; + OUT.mvp = objectMatrices[matrixIndex].mvp; + } +} \ No newline at end of file diff --git a/projects/mesh_shader/assets/shaders/shader.vert b/projects/mesh_shader/assets/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..fca5057976f995183c040195bdbd592c63f1074e --- /dev/null +++ b/projects/mesh_shader/assets/shaders/shader.vert @@ -0,0 +1,29 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "common.inc" + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inNormal; + +layout(location = 0) out vec3 passNormal; +layout(location = 1) out uint dummyOutput; + +layout(std430, binding = 0) readonly buffer matrixBuffer +{ + ObjectMatrices objectMatrices[]; +}; + +layout( push_constant ) uniform constants{ + uint matrixIndex; + uint padding; // pad to same size as mesh shader constants +}; + + +void main() { + gl_Position = objectMatrices[matrixIndex].mvp * vec4(inPosition, 1.0); + passNormal = inNormal; + + dummyOutput = padding * 0; // padding must be used, else compiler shrinks constant size +} \ No newline at end of file diff --git a/projects/mesh_shader/src/main.cpp b/projects/mesh_shader/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca57d819892949f49553dedbb5cbe6409e64a600 --- /dev/null +++ b/projects/mesh_shader/src/main.cpp @@ -0,0 +1,403 @@ +#include <iostream> +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> + +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/meshlet/Meshlet.hpp> +#include <vkcv/meshlet/Tipsify.hpp> +#include <vkcv/meshlet/Forsyth.hpp> + +struct Plane { + glm::vec3 pointOnPlane; + float padding0; + glm::vec3 normal; + float padding1; +}; + +struct CameraPlanes { + Plane planes[6]; +}; + +CameraPlanes computeCameraPlanes(const vkcv::camera::Camera& camera) { + const float fov = camera.getFov(); + const glm::vec3 pos = camera.getPosition(); + const float ratio = camera.getRatio(); + const glm::vec3 forward = glm::normalize(camera.getFront()); + float near; + float far; + camera.getNearFar(near, far); + + glm::vec3 up = glm::vec3(0, -1, 0); + glm::vec3 right = glm::normalize(glm::cross(forward, up)); + up = glm::cross(forward, right); + + const glm::vec3 nearCenter = pos + forward * near; + const glm::vec3 farCenter = pos + forward * far; + + const float tanFovHalf = glm::tan(fov / 2); + + const glm::vec3 nearUpCenter = nearCenter + up * tanFovHalf * near; + const glm::vec3 nearDownCenter = nearCenter - up * tanFovHalf * near; + const glm::vec3 nearRightCenter = nearCenter + right * tanFovHalf * near * ratio; + const glm::vec3 nearLeftCenter = nearCenter - right * tanFovHalf * near * ratio; + + const glm::vec3 farUpCenter = farCenter + up * tanFovHalf * far; + const glm::vec3 farDownCenter = farCenter - up * tanFovHalf * far; + const glm::vec3 farRightCenter = farCenter + right * tanFovHalf * far * ratio; + const glm::vec3 farLeftCenter = farCenter - right * tanFovHalf * far * ratio; + + CameraPlanes cameraPlanes; + // near + cameraPlanes.planes[0].pointOnPlane = nearCenter; + cameraPlanes.planes[0].normal = -forward; + // far + cameraPlanes.planes[1].pointOnPlane = farCenter; + cameraPlanes.planes[1].normal = forward; + + // top + cameraPlanes.planes[2].pointOnPlane = nearUpCenter; + cameraPlanes.planes[2].normal = glm::normalize(glm::cross(farUpCenter - nearUpCenter, right)); + // bot + cameraPlanes.planes[3].pointOnPlane = nearDownCenter; + cameraPlanes.planes[3].normal = glm::normalize(glm::cross(right, farDownCenter - nearDownCenter)); + + // right + cameraPlanes.planes[4].pointOnPlane = nearRightCenter; + cameraPlanes.planes[4].normal = glm::normalize(glm::cross(up, farRightCenter - nearRightCenter)); + // left + cameraPlanes.planes[5].pointOnPlane = nearLeftCenter; + cameraPlanes.planes[5].normal = glm::normalize(glm::cross(farLeftCenter - nearLeftCenter, up)); + + return cameraPlanes; +} + +int main(int argc, const char** argv) { + const std::string applicationName = "Mesh shader"; + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + features.requireExtensionFeature<vk::PhysicalDeviceMeshShaderFeaturesNV>( + VK_NV_MESH_SHADER_EXTENSION_NAME, [](vk::PhysicalDeviceMeshShaderFeaturesNV& features) { + features.setTaskShader(true); + features.setMeshShader(true); + }); + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + features + ); + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 1280, 720, true); + vkcv::Window &window = core.getWindow(windowHandle); + + vkcv::gui::GUI gui (core, windowHandle); + + vkcv::asset::Scene mesh; + const char* path = argc > 1 ? argv[1] : "assets/Bunny/Bunny.glb"; + vkcv::asset::loadScene(path, mesh); + + assert(!mesh.vertexGroups.empty()); + + auto vertexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::VERTEX, + mesh.vertexGroups[0].vertexBuffer.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + vertexBuffer.fill(mesh.vertexGroups[0].vertexBuffer.data); + + auto indexBuffer = vkcv::buffer<uint8_t>( + core, + vkcv::BufferType::INDEX, + mesh.vertexGroups[0].indexBuffer.data.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + indexBuffer.fill(mesh.vertexGroups[0].indexBuffer.data); + + const auto vertexBufferBindings = vkcv::asset::loadVertexBufferBindings( + mesh.vertexGroups[0].vertexBuffer.attributes, + vertexBuffer.getHandle(), + { + vkcv::asset::PrimitiveType::POSITION, + vkcv::asset::PrimitiveType::NORMAL + } + ); + + const vkcv::asset::VertexAttribute* positionAttribute = nullptr; + const vkcv::asset::VertexAttribute* normalAttribute = nullptr; + + for (const auto& attribute : mesh.vertexGroups[0].vertexBuffer.attributes) { + switch (attribute.type) { + case vkcv::asset::PrimitiveType::POSITION: + positionAttribute = &attribute; + break; + case vkcv::asset::PrimitiveType::NORMAL: + normalAttribute = &attribute; + break; + default: + break; + } + } + + assert(positionAttribute && normalAttribute); + + const auto& bunny = mesh.vertexGroups[0]; + std::vector<vkcv::meshlet::Vertex> interleavedVertices = vkcv::meshlet::convertToVertices( + bunny.vertexBuffer.data, + bunny.numVertices, + *positionAttribute, + *normalAttribute + ); + + // mesh shader buffers + const auto& assetLoaderIndexBuffer = mesh.vertexGroups[0].indexBuffer; + std::vector<uint32_t> indexBuffer32Bit = vkcv::meshlet::assetLoaderIndicesTo32BitIndices(assetLoaderIndexBuffer.data, assetLoaderIndexBuffer.type); + vkcv::meshlet::VertexCacheReorderResult tipsifyResult = vkcv::meshlet::tipsifyMesh(indexBuffer32Bit, interleavedVertices.size()); + vkcv::meshlet::VertexCacheReorderResult forsythResult = vkcv::meshlet::forsythReorder(indexBuffer32Bit, interleavedVertices.size()); + + const auto meshShaderModelData = createMeshShaderModelData(interleavedVertices, forsythResult.indexBuffer, forsythResult.skippedIndices); + + auto meshShaderVertexBuffer = vkcv::buffer<vkcv::meshlet::Vertex>( + core, + vkcv::BufferType::STORAGE, + meshShaderModelData.vertices.size()); + meshShaderVertexBuffer.fill(meshShaderModelData.vertices); + + auto meshShaderIndexBuffer = vkcv::buffer<uint32_t>( + core, + vkcv::BufferType::STORAGE, + meshShaderModelData.localIndices.size()); + meshShaderIndexBuffer.fill(meshShaderModelData.localIndices); + + auto meshletBuffer = vkcv::buffer<vkcv::meshlet::Meshlet>( + core, + vkcv::BufferType::STORAGE, + meshShaderModelData.meshlets.size(), + vkcv::BufferMemoryType::DEVICE_LOCAL + ); + meshletBuffer.fill(meshShaderModelData.meshlets); + + vkcv::PassHandle renderPass = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } + ); + + if (!renderPass) + { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ShaderProgram bunnyShaderProgram{}; + vkcv::shader::GLSLCompiler compiler; + + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"), + [&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + bunnyShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"), + [&bunnyShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + bunnyShaderProgram.addShader(shaderStage, path); + }); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = bunnyShaderProgram.getVertexAttachments(); + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] })); + } + const vkcv::VertexLayout bunnyLayout { bindings }; + + vkcv::DescriptorSetLayoutHandle vertexShaderDescriptorSetLayout = core.createDescriptorSetLayout(bunnyShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle vertexShaderDescriptorSet = core.createDescriptorSet(vertexShaderDescriptorSetLayout); + + struct ObjectMatrices { + glm::mat4 model; + glm::mat4 mvp; + }; + const size_t objectCount = 1; + vkcv::Buffer<ObjectMatrices> matrixBuffer = vkcv::buffer<ObjectMatrices>(core, vkcv::BufferType::STORAGE, objectCount); + + vkcv::DescriptorWrites vertexShaderDescriptorWrites; + vertexShaderDescriptorWrites.writeStorageBuffer(0, matrixBuffer.getHandle()); + core.writeDescriptorSet(vertexShaderDescriptorSet, vertexShaderDescriptorWrites); + + vkcv::GraphicsPipelineHandle bunnyPipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + bunnyShaderProgram, + renderPass, + { bunnyLayout }, + { vertexShaderDescriptorSetLayout } + ) + ); + + if (!bunnyPipeline) + { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + // mesh shader + vkcv::ShaderProgram meshShaderProgram; + compiler.compile(vkcv::ShaderStage::TASK, std::filesystem::path("assets/shaders/shader.task"), + [&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + meshShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::MESH, std::filesystem::path("assets/shaders/shader.mesh"), + [&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + meshShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"), + [&meshShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + meshShaderProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle meshShaderDescriptorSetLayout = core.createDescriptorSetLayout(meshShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle meshShaderDescriptorSet = core.createDescriptorSet(meshShaderDescriptorSetLayout); + const vkcv::VertexLayout meshShaderLayout = vkcv::createVertexLayout(bindings); + + vkcv::GraphicsPipelineHandle meshShaderPipeline = core.createGraphicsPipeline( + vkcv::GraphicsPipelineConfig( + meshShaderProgram, + renderPass, + { meshShaderLayout }, + { meshShaderDescriptorSetLayout } + ) + ); + + if (!meshShaderPipeline) + { + std::cout << "Error. Could not create mesh shader pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::Buffer<CameraPlanes> cameraPlaneBuffer = vkcv::buffer<CameraPlanes>(core, vkcv::BufferType::UNIFORM, 1); + + vkcv::DescriptorWrites meshShaderWrites; + meshShaderWrites.writeStorageBuffer( + 0, meshShaderVertexBuffer.getHandle() + ).writeStorageBuffer( + 1, meshShaderIndexBuffer.getHandle() + ).writeStorageBuffer( + 2, meshletBuffer.getHandle() + ).writeStorageBuffer( + 4, matrixBuffer.getHandle() + ).writeStorageBuffer( + 5, meshletBuffer.getHandle() + ); + + meshShaderWrites.writeUniformBuffer(3, cameraPlaneBuffer.getHandle()); + + core.writeDescriptorSet( meshShaderDescriptorSet, meshShaderWrites); + + vkcv::ImageHandle depthBuffer; + vkcv::ImageHandle swapchainImageHandle = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::VertexData vertexData (vertexBufferBindings); + vertexData.setIndexBuffer(indexBuffer.getHandle(), vkcv::IndexBitCount::Bit32); + vertexData.setCount(mesh.vertexGroups[0].numIndices); + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::camera::CameraManager cameraManager(window); + auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -2)); + + bool useMeshShader = true; + bool updateFrustumPlanes = true; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + (swapchainHeight != core.getImageHeight(depthBuffer))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); + } + + cameraManager.update(dt); + + const vkcv::camera::Camera& camera = cameraManager.getActiveCamera(); + + ObjectMatrices objectMatrices; + objectMatrices.model = *reinterpret_cast<glm::mat4*>(&mesh.meshes.front().modelMatrix); + objectMatrices.mvp = camera.getMVP() * objectMatrices.model; + + matrixBuffer.fill({ objectMatrices }); + + struct PushConstants { + uint32_t matrixIndex; + uint32_t meshletCount; + }; + PushConstants pushConstants{ 0, static_cast<uint32_t>(meshShaderModelData.meshlets.size()) }; + + if (updateFrustumPlanes) { + const CameraPlanes cameraPlanes = computeCameraPlanes(camera); + cameraPlaneBuffer.fill({ cameraPlanes }); + } + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + vkcv::PushConstants pushConstantData = vkcv::pushConstants<PushConstants>(); + pushConstantData.appendDrawcall(pushConstants); + + if (useMeshShader) { + const uint32_t taskCount = (meshShaderModelData.meshlets.size() + 31) / 32; + + vkcv::TaskDrawcall drawcall (taskCount); + drawcall.useDescriptorSet(0, meshShaderDescriptorSet); + + core.recordMeshShaderDrawcalls( + cmdStream, + meshShaderPipeline, + pushConstantData, + { drawcall }, + { renderTargets }, + windowHandle + ); + } else { + vkcv::InstanceDrawcall drawcall (vertexData); + drawcall.useDescriptorSet(0, vertexShaderDescriptorSet); + + core.recordDrawcallsToCmdStream( + cmdStream, + bunnyPipeline, + pushConstantData, + { drawcall }, + { renderTargets }, + windowHandle + ); + } + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + + ImGui::Begin("Settings"); + ImGui::Checkbox("Use mesh shader", &useMeshShader); + ImGui::Checkbox("Update frustum culling", &updateFrustumPlanes); + + ImGui::End(); + gui.endGUI(); + }); + + return 0; +} diff --git a/projects/mpm/.gitignore b/projects/mpm/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..68b4554cbc85d602c1192730d43d1ebfa28c36a6 --- /dev/null +++ b/projects/mpm/.gitignore @@ -0,0 +1 @@ +mpm diff --git a/projects/mpm/CMakeLists.txt b/projects/mpm/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..859a0ac1504e8922300b174fafef66d4d0553a20 --- /dev/null +++ b/projects/mpm/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(mpm) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(mpm src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(mpm SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_camera_include} ${vkcv_gui_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(mpm vkcv vkcv_camera vkcv_gui vkcv_shader_compiler) diff --git a/projects/mpm/README.md b/projects/mpm/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2b3a8f0ce0be51d2d54b16ab039932d83e31d763 --- /dev/null +++ b/projects/mpm/README.md @@ -0,0 +1,11 @@ +# MPM +An example project to show the implementation of an MPM soft-body simulation with the VkCV framework + + + +## Details + +The project shows off an example implementation of an MPM simulation which are widely used +in computer graphics to simulate the behavior of solids, liquids or gases. It utilizes the discrete +representation of objects as combined particles as well as a discrete grid of data. Together both +representations help to stabilize the result while providing as much detail as possible. diff --git a/projects/mpm/shaders/.gitignore b/projects/mpm/shaders/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/projects/mpm/shaders/grid.frag b/projects/mpm/shaders/grid.frag new file mode 100644 index 0000000000000000000000000000000000000000..8eb2fbc2173bbddb5bdc44c52ce81d2e5d52d389 --- /dev/null +++ b/projects/mpm/shaders/grid.frag @@ -0,0 +1,30 @@ +#version 450 + +layout(location = 0) in vec2 passPos; +layout(location = 1) in vec3 passVelocity; +layout(location = 2) in float passMass; + +layout(location = 0) out vec3 outColor; + +void main() { + if (passMass <= 0.0f) { + discard; + } + + const float value = length(passPos); + + float z = sqrt(0.25 - value * value); + + if (value < 0.5f) { + vec3 surface = vec3(passPos.x + 0.5f, passPos.y + 0.5f, z * 2.0f); + vec3 velocity = vec3(0.5f); + + if (length(passVelocity) > 0.0f) { + velocity = vec3(0.5f) + 0.5f * normalize(passVelocity.xyz); + } + + outColor = velocity; + } else { + discard; + } +} \ No newline at end of file diff --git a/projects/mpm/shaders/grid.vert b/projects/mpm/shaders/grid.vert new file mode 100644 index 0000000000000000000000000000000000000000..54de3f3e1da43e3ea3d018e5c54003b83e055c4c --- /dev/null +++ b/projects/mpm/shaders/grid.vert @@ -0,0 +1,58 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "particle.inc" + +layout(set=0, binding=0) uniform texture3D gridImage; +layout(set=0, binding=1) uniform sampler gridSampler; + +layout(set=0, binding=2) uniform simulationBlock { + Simulation simulation; +}; + +layout(location = 0) in vec2 vertexPos; + +layout(location = 0) out vec2 passPos; +layout(location = 1) out vec3 passVelocity; +layout(location = 2) out float passMass; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +ivec3 actual_mod(ivec3 x, ivec3 y) { + return x - y * (x/y); +} + +void main() { + ivec3 gridResolution = textureSize(sampler3D(gridImage, gridSampler), 0); + + ivec3 gridID = ivec3( + gl_InstanceIndex, + gl_InstanceIndex / gridResolution.x, + gl_InstanceIndex / gridResolution.x / gridResolution.y + ); + + gridID = actual_mod(gridID, gridResolution); + + vec3 position = (vec3(gridID) + vec3(0.5f)) / gridResolution; + + vec3 size = vec3(1.0f) / vec3(gridResolution); + float volume = size.x * size.y * size.z; + float radius = cube_radius(volume); + + vec4 gridData = texture(sampler3D(gridImage, gridSampler), position); + + float mass = gridData.w; + float density = mass / volume; + + float alpha = clamp(density / simulation.density, 0.0f, 1.0f); + + passPos = vertexPos; + passVelocity = gridData.xyz; + passMass = mass; + + // align voxel to face camera + gl_Position = mvp * vec4(position, 1); // transform position into projected view space + gl_Position.xy += vertexPos * (radius * 2.0f) * alpha; // move position directly in view space +} \ No newline at end of file diff --git a/projects/mpm/shaders/init_particle_weights.comp b/projects/mpm/shaders/init_particle_weights.comp new file mode 100644 index 0000000000000000000000000000000000000000..9b821e88fc7cc3fcea87b7eb6de0f8f6d629d911 --- /dev/null +++ b/projects/mpm/shaders/init_particle_weights.comp @@ -0,0 +1,43 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +#include "particle.inc" + +layout(set=0, binding=0, std430) restrict buffer particleBuffer { + Particle particles []; +}; + +layout(set=1, binding=0) uniform texture3D gridImage; +layout(set=1, binding=1) uniform sampler gridSampler; + +void main() { + if (gl_GlobalInvocationID.x < particles.length()) { + ParticleMinimal minimal = particles[gl_GlobalInvocationID.x].minimal; + + minimal.weight_sum = 1.0f; + + ivec3 gridResolution = textureSize(sampler3D(gridImage, gridSampler), 0); + ivec3 gridWindow = ivec3(minimal.size * 2.0f * gridResolution); + + float weight_sum = 0.0f; + + int i, j, k; + + for (i = -gridWindow.x; i <= gridWindow.x; i++) { + for (j = -gridWindow.y; j <= gridWindow.y; j++) { + for (k = -gridWindow.z; k <= gridWindow.z; k++) { + vec3 offset = vec3(i, j, k) / gridResolution; + vec3 voxel = minimal.position + offset; + + weight_sum += voxel_particle_weight(voxel, minimal); + } + } + } + + if (weight_sum > 0.0f) { + particles[gl_GlobalInvocationID.x].minimal.weight_sum = weight_sum; + } + } +} \ No newline at end of file diff --git a/projects/mpm/shaders/lines.frag b/projects/mpm/shaders/lines.frag new file mode 100644 index 0000000000000000000000000000000000000000..37d79d30d2ce751a59b1b467764b2fe464aa5c17 --- /dev/null +++ b/projects/mpm/shaders/lines.frag @@ -0,0 +1,7 @@ +#version 450 + +layout(location = 0) out vec3 outColor; + +void main() { + outColor = vec3(1.0f); +} \ No newline at end of file diff --git a/projects/mpm/shaders/lines.vert b/projects/mpm/shaders/lines.vert new file mode 100644 index 0000000000000000000000000000000000000000..b8e3b01c67986156ad980899697ffea05409b752 --- /dev/null +++ b/projects/mpm/shaders/lines.vert @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec3 vertexPos; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + gl_Position = mvp * vec4(vertexPos, 1); +} \ No newline at end of file diff --git a/projects/mpm/shaders/particle.frag b/projects/mpm/shaders/particle.frag new file mode 100644 index 0000000000000000000000000000000000000000..81c0a3594359e816a28b5e3f0301b47ce74a3cd7 --- /dev/null +++ b/projects/mpm/shaders/particle.frag @@ -0,0 +1,18 @@ +#version 450 + +layout(location = 0) in vec2 passPos; +layout(location = 1) in float passMass; + +layout(location = 0) out vec3 outColor; + +void main() { + const float value = length(passPos); + + float z = sqrt(0.25 - value * value); + + if (value < 0.5f) { + outColor = vec3(passPos.x + 0.5f, passPos.y + 0.5f, z * 2.0f); + } else { + discard; + } +} \ No newline at end of file diff --git a/projects/mpm/shaders/particle.inc b/projects/mpm/shaders/particle.inc new file mode 100644 index 0000000000000000000000000000000000000000..a622485eab5bbcafdc51c030281e53b1cc1c5f11 --- /dev/null +++ b/projects/mpm/shaders/particle.inc @@ -0,0 +1,118 @@ +#ifndef PARTICLE_INC +#define PARTICLE_INC + +#define EPSILON 0.00000001f + +struct ParticleMinimal { + vec3 position; + float size; + vec3 velocity; + float mass; + + vec3 pad; + float weight_sum; +}; + +struct Particle { + ParticleMinimal minimal; + mat4 deformation; + mat4 mls; +}; + +#define SIM_FORM_SPHERE 0 +#define SIM_FORM_CUBE 1 + +#define SIM_TYPE_HYPERELASTIC 0 +#define SIM_TYPE_FLUID 1 + +#define SIM_MODE_RANDOM 0 +#define SIM_MODE_ORDERED 1 + +struct Simulation { + float density; + float size; + float lame1; + float lame2; + + int form; + int type; + float K; + float E; + + float gamma; + int mode; + float gravity; + int count; +}; + +const float PI = 3.1415926535897932384626433832795; + +float sphere_volume(float radius) { + return 4.0f * (radius * radius * radius) * PI / 3.0f; +} + +float sphere_radius(float volume) { + return pow(volume * 3.0f / 4.0f / PI, 1.0f / 3.0f); +} + +float cube_volume(float radius) { + return 8.0f * (radius * radius * radius); +} + +float cube_radius(float volume) { + return pow(volume / 8.0f, 1.0f / 3.0f); +} + +float weight_A(float x) { + return max(1.0f - x, 0.0f); +} + +float weight_B(float x) { + if (x < 0.5f) { + return 0.75f - x * x; + } else + if (x < 1.5f) { + float y = (1.5f - x); + return 0.5f * y * y; + } else { + return 0.0f; + } +} + +float weight_C(float x) { + if (x < 1.0f) { + return (0.5f * x - 1.0f) * x*x + 2.0f / 3.0f; + } else + if (x < 2.0f) { + float y = (2.0f - x); + return 0.5f / 3.0f * y * y * y; + } else { + return 0.0f; + } +} + +float voxel_particle_weight(vec3 voxel, ParticleMinimal particle) { + vec3 delta = abs(particle.position - voxel) / particle.size; + + if (any(isnan(delta)) || any(isinf(delta))) { + return 0.0f; + } + + vec3 weight = vec3( + weight_C(delta.x), + weight_C(delta.y), + weight_C(delta.z) + ); + + float result = ( + weight.x * weight.y * weight.z + ) / particle.weight_sum; + + if ((isnan(result)) || (isinf(result))) { + return 0.0f; + } else { + return result; + } +} + +#endif // PARTICLE_INC \ No newline at end of file diff --git a/projects/mpm/shaders/particle.vert b/projects/mpm/shaders/particle.vert new file mode 100644 index 0000000000000000000000000000000000000000..a8f697e79eacba361d80b5858405add675e761bb --- /dev/null +++ b/projects/mpm/shaders/particle.vert @@ -0,0 +1,31 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + Particle particles []; +}; + +layout(location = 0) in vec2 vertexPos; + +layout(location = 0) out vec2 passPos; +layout(location = 1) out float passMass; + +layout( push_constant ) uniform constants{ + mat4 mvp; +}; + +void main() { + vec3 position = particles[gl_InstanceIndex].minimal.position; + float size = particles[gl_InstanceIndex].minimal.size; + + float mass = particles[gl_InstanceIndex].minimal.mass; + + passPos = vertexPos; + passMass = mass; + + // align particle to face camera + gl_Position = mvp * vec4(position, 1); // transform position into projected view space + gl_Position.xy += vertexPos * size * 2.0f; // move position directly in view space +} \ No newline at end of file diff --git a/projects/mpm/shaders/transform_particles_to_grid.comp b/projects/mpm/shaders/transform_particles_to_grid.comp new file mode 100644 index 0000000000000000000000000000000000000000..1be18c41303ab2208c8cb4a8c33f41b350315d63 --- /dev/null +++ b/projects/mpm/shaders/transform_particles_to_grid.comp @@ -0,0 +1,101 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#include "particle.inc" + +layout(set=0, binding=0, std430) readonly buffer particleBuffer { + Particle particles []; +}; + +layout(set=1, binding=0) uniform simulationBlock { + Simulation simulation; +}; + +layout(set=2, binding=0, rgba16f) restrict writeonly uniform image3D gridImage; + +layout( push_constant ) uniform constants { + float t; + float dt; + float speedfactor; +}; + +#define SHARED_PARTICLES_BATCH_SIZE 64 + +shared ParticleMinimal shared_particles [SHARED_PARTICLES_BATCH_SIZE]; + +void main() { + const vec3 position = (vec3(gl_GlobalInvocationID) + vec3(0.5f)) / imageSize(gridImage); + + float dts = dt * speedfactor; + + vec4 gridValue = vec4(0.0f); + + uint offset = 0; + + for (offset = 0; offset < particles.length(); offset += SHARED_PARTICLES_BATCH_SIZE) { + uint localOffset = offset + gl_LocalInvocationIndex; + + if (localOffset < particles.length()) { + shared_particles[gl_LocalInvocationIndex] = particles[localOffset].minimal; + + shared_particles[gl_LocalInvocationIndex].pad = ( + mat3(particles[localOffset].mls) * + (position - shared_particles[gl_LocalInvocationIndex].position) + ) + ( + shared_particles[gl_LocalInvocationIndex].velocity * + shared_particles[gl_LocalInvocationIndex].mass + ); + } else { + shared_particles[gl_LocalInvocationIndex].position = vec3(0.0f); + shared_particles[gl_LocalInvocationIndex].size = 0.0f; + shared_particles[gl_LocalInvocationIndex].velocity = vec3(0.0f); + shared_particles[gl_LocalInvocationIndex].mass = 0.0f; + + shared_particles[gl_LocalInvocationIndex].pad = vec3(0.0f); + shared_particles[gl_LocalInvocationIndex].weight_sum = 1.0f; + } + + barrier(); + memoryBarrierShared(); + + for (uint i = 0; i < SHARED_PARTICLES_BATCH_SIZE; i++) { + float weight = voxel_particle_weight(position, shared_particles[i]); + + gridValue += vec4( + shared_particles[i].pad * weight, + shared_particles[i].mass * weight + ); + } + + barrier(); + memoryBarrierShared(); + } + + if (any(isnan(gridValue.xyz)) || any(isinf(gridValue.xyz))) { + gridValue.xyz = vec3(0.0f); + } + + gridValue.xyz += vec3(0.0f, -simulation.gravity * dts * gridValue.w, 0.0f); + + bvec3 lowerID = lessThanEqual(gl_GlobalInvocationID, ivec3(0)); + bvec3 negativeVelocity = lessThan(gridValue.xyz, vec3(0.0f)); + + bvec3 greaterID = greaterThanEqual(gl_GlobalInvocationID + ivec3(1), imageSize(gridImage)); + bvec3 positiveVelocity = greaterThan(gridValue.xyz, vec3(0.0f)); + + bvec3 collision = bvec3( + (lowerID.x && negativeVelocity.x) || (greaterID.x && positiveVelocity.x), + (lowerID.y && negativeVelocity.y) || (greaterID.y && positiveVelocity.y), + (lowerID.z && negativeVelocity.z) || (greaterID.z && positiveVelocity.z) + ); + + gridValue.xyz = mix(gridValue.xyz, -gridValue.xyz, collision); + + imageStore( + gridImage, + ivec3(gl_GlobalInvocationID), + gridValue + ); +} \ No newline at end of file diff --git a/projects/mpm/shaders/update_particle_velocities.comp b/projects/mpm/shaders/update_particle_velocities.comp new file mode 100644 index 0000000000000000000000000000000000000000..4420bc9575bcde4d04d7d4f5531d0091362285c2 --- /dev/null +++ b/projects/mpm/shaders/update_particle_velocities.comp @@ -0,0 +1,161 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_EXT_control_flow_attributes : enable + +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +#include "particle.inc" + +layout(set=0, binding=0, std430) restrict buffer particleBuffer { + Particle particles []; +}; + +layout(set=1, binding=0) uniform simulationBlock { + Simulation simulation; +}; + +layout(set=2, binding=0) uniform texture3D gridImage; +layout(set=2, binding=1) uniform sampler gridSampler; + +layout( push_constant ) uniform constants { + float t; + float dt; + float speedfactor; +}; + +void main() { + float dts = dt * speedfactor; + + if (gl_GlobalInvocationID.x < particles.length()) { + Particle particle = particles[gl_GlobalInvocationID.x]; + + vec3 position = particle.minimal.position; + float size = particle.minimal.size; + float mass = particle.minimal.mass; + + ivec3 gridResolution = textureSize(sampler3D(gridImage, gridSampler), 0); + ivec3 gridWindow = ivec3(size * 2.0f * gridResolution); + + mat3 affine_D = mat3(0.0f); + mat3 affine_B = mat3(0.0f); + + vec3 velocity_pic = vec3(0.0f); + vec3 velocity_flip = vec3(particle.minimal.velocity); + + int i, j, k; + + for (i = -gridWindow.x; i <= gridWindow.x; i++) { + for (j = -gridWindow.y; j <= gridWindow.y; j++) { + for (k = -gridWindow.z; k <= gridWindow.z; k++) { + vec3 offset = vec3(i, j, k) / gridResolution; + vec3 voxel = position + offset; + + vec4 gridSample = texture(sampler3D(gridImage, gridSampler), voxel); + + float weight = voxel_particle_weight(voxel, particle.minimal); + vec3 velocity = gridSample.xyz * weight / gridSample.w; + + if (any(isnan(velocity)) || any(isinf(velocity))) { + velocity = vec3(0.0f); + } + + affine_D += outerProduct(weight * offset, offset); + affine_B += outerProduct(velocity, offset); + + velocity_pic += velocity; + } + } + } + + mat3 mls_Q = mat3(0.0f); + mat3 affine_C = mat3(0.0f); + + mat3 F = mat3(particle.deformation); + + mat3 D_inv = inverse(affine_D); + float D_det = determinant(D_inv); + + if ((isnan(D_det)) || (isinf(D_det))) { + D_inv = mat3(0.0f); + } else { + D_inv *= min(abs(D_det), 1.0f / EPSILON) / abs(D_det); + } + + float J = max(determinant(F), EPSILON); + float volume = sphere_volume(size); + + mat3 stress = mat3(0.0f); + + switch (simulation.type) { + case SIM_TYPE_HYPERELASTIC: + mat3 F_T = transpose(F); + mat3 F_T_inv = inverse(F_T); + + mat3 P_term_0 = simulation.lame2 * (F - F_T_inv); + mat3 P_term_1 = simulation.lame1 * log(J) * F_T_inv; + + mat3 P = P_term_0 + P_term_1; + + stress = P * F_T; + break; + case SIM_TYPE_FLUID: + float pressure = simulation.K * (1.0f / pow(J, simulation.gamma) - 1.0f); + + stress = mat3(-pressure * J); + break; + default: + break; + } + + mls_Q -= dts * volume * stress * D_inv; + + affine_C = affine_B * D_inv; + mls_Q += affine_C * mass; + + F = (mat3(1.0f) + dts * affine_C) * F; + + position = position + velocity_pic * dts; + + const float gridRange = (1.0f - 2.0f * size); + + for (uint i = 0; i < 3; i++) { + if (position[i] - size < 0.0f) { + float a = (size - position[i]) / gridRange; + int b = int(floor(a)); + + a = (a - b) * gridRange; + + if (b % 2 == 0) { + position[i] = size + a; + } else { + position[i] = 1.0f - size - a; + } + + if ((velocity_pic[i] < 0.0f) == (b % 2 == 0)) { + velocity_pic[i] *= -1.0f; + } + } else + if (position[i] + size > 1.0f) { + float a = (position[i] + size - 1.0f) / gridRange; + int b = int(floor(a)); + + a = (a - b) * gridRange; + + if (b % 2 == 0) { + position[i] = 1.0f - size - a; + } else { + position[i] = size + a; + } + + if ((velocity_pic[i] > 0.0f) == (b % 2 == 0)) { + velocity_pic[i] *= -1.0f; + } + } + } + + particles[gl_GlobalInvocationID.x].minimal.position = position; + particles[gl_GlobalInvocationID.x].minimal.velocity = velocity_pic; + particles[gl_GlobalInvocationID.x].deformation = mat4(F); + particles[gl_GlobalInvocationID.x].mls = mat4(mls_Q); + } +} \ No newline at end of file diff --git a/projects/mpm/src/main.cpp b/projects/mpm/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..61f27daa9b011b1952ce640653483c8de730eb74 --- /dev/null +++ b/projects/mpm/src/main.cpp @@ -0,0 +1,842 @@ + +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Image.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/gui/GUI.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +#include <random> + +struct Particle { + glm::vec3 position; + float size; + glm::vec3 velocity; + float mass; + + glm::vec3 pad; + float weight_sum; + + glm::mat4 deformation; + glm::mat4 mls; +}; + +#define SIM_FORM_SPHERE 0 +#define SIM_FORM_CUBE 1 + +#define SIM_TYPE_HYPERELASTIC 0 +#define SIM_TYPE_FLUID 1 + +#define SIM_MODE_RANDOM 0 +#define SIM_MODE_ORDERED 1 + +struct Simulation { + float density; + float size; + float lame1; + float lame2; + + int form; + int type; + float K; + float E; + + float gamma; + int mode; + float gravity; + int count; +}; + +struct Physics { + float t; + float dt; + float speedfactor; +}; + +float sphere_volume(float radius) { + return 4.0f * (radius * radius * radius) * M_PI / 3.0f; +} + +float sphere_radius(float volume) { + return std::pow(volume * 3.0f / 4.0f / M_PI, 1.0f / 3.0f); +} + +float cube_volume(float radius) { + return 8.0f * (radius * radius * radius); +} + +float cube_radius(float volume) { + return std::pow(volume / 8.0f, 1.0f / 3.0f); +} + +std::random_device random_dev; +std::uniform_int_distribution<int> dist(0, RAND_MAX); + +float randomFloat(float min, float max) { + return min + (max - min) * dist(random_dev) / static_cast<float>(RAND_MAX); +} + +float mod(float x, float y) { + return x - std::floor(x / y) * y; +} + +void distributeParticlesCube(Particle *particles, size_t count, const glm::vec3& center, float radius, + float mass, const glm::vec3& velocity, bool random) { + const float side = cube_radius(static_cast<float>(count)) * 2.0f; + + float volume = 0.0f; + + for (size_t i = 0; i < count; i++) { + glm::vec3 offset; + + if (random) { + offset.x = randomFloat(-1.0f, +1.0f); + offset.y = randomFloat(-1.0f, +1.0f); + offset.z = randomFloat(-1.0f, +1.0f); + } else { + const float s = static_cast<float>(i) + 0.5f; + + offset.x = 2.0f * mod(s, side) / side - 1.0f; + offset.y = 2.0f * mod(s / side, side) / side - 1.0f; + offset.z = 2.0f * mod(s / side / side, side) / side - 1.0f; + } + + offset *= radius; + + float size = 0.0f; + + if (random) { + const float ax = std::abs(offset.x); + const float ay = std::abs(offset.y); + const float az = std::abs(offset.z); + + const float a = std::max(std::max(ax, ay), az); + + size = (radius - a); + } else { + size = 2.0f * radius / side; + } + + particles[i].position = center + offset; + particles[i].size = size; + particles[i].velocity = velocity; + + volume += cube_volume(size); + } + + for (size_t i = 0; i < count; i++) { + particles[i].mass = (mass * cube_volume(particles[i].size) / volume); + particles[i].deformation = glm::mat4(1.0f); + + particles[i].pad = glm::vec3(0.0f); + particles[i].weight_sum = 1.0f; + + particles[i].mls = glm::mat4(0.0f); + } +} + +void distributeParticlesSphere(Particle *particles, size_t count, const glm::vec3& center, float radius, + float mass, const glm::vec3& velocity, bool random) { + const float side = sphere_radius(static_cast<float>(count)) * 2.0f; + + float volume = 0.0f; + + for (size_t i = 0; i < count; i++) { + glm::vec3 offset; + + if (random) { + offset.x = randomFloat(-1.0f, +1.0f); + offset.y = randomFloat(-1.0f, +1.0f); + offset.z = randomFloat(-1.0f, +1.0f); + + if (glm::length(offset) > 0.0f) + offset = glm::normalize(offset); + + offset *= randomFloat(0.0f, 1.0f); + } else { + const float range = 0.5f * side; + const float s = static_cast<float>(i) + 0.5f; + + float a = mod(s, range) / range; + float b = mod(s / range, M_PI * range); + float c = mod(s / range / M_PI / range, M_PI * range * 2.0f); + + offset.x = a * std::sin(c) * std::sin(b); + offset.y = a * std::cos(b); + offset.z = a * std::cos(c) * std::sin(b); + } + + offset *= radius; + + float size = 0.0f; + + if (random) { + size = (radius - glm::length(offset)); + } else { + size = 2.0f * radius / side; + } + + particles[i].position = center + offset; + particles[i].size = size; + particles[i].velocity = velocity; + + volume += sphere_volume(size); + } + + // Keep the same densitiy as planned! + mass *= (volume / sphere_volume(radius)); + + for (size_t i = 0; i < count; i++) { + particles[i].mass = (mass * sphere_volume(particles[i].size) / volume); + particles[i].deformation = glm::mat4(1.0f); + + particles[i].pad = glm::vec3(0.0f); + particles[i].weight_sum = 1.0f; + + particles[i].mls = glm::mat4(0.0f); + } +} + +vkcv::ComputePipelineHandle createComputePipeline(vkcv::Core& core, vkcv::shader::GLSLCompiler& compiler, + const std::string& path, + std::vector<vkcv::DescriptorSetHandle>& descriptorSets) { + vkcv::ShaderProgram shaderProgram; + + compiler.compile( + vkcv::ShaderStage::COMPUTE, + path, + [&shaderProgram](vkcv::ShaderStage stage, const std::filesystem::path& path) { + shaderProgram.addShader(stage, path); + } + ); + + const auto& descriptors = shaderProgram.getReflectedDescriptors(); + + size_t count = 0; + + for (const auto& descriptor : descriptors) { + if (descriptor.first >= count) { + count = (descriptor.first + 1); + } + } + + std::vector<vkcv::DescriptorSetLayoutHandle> descriptorSetLayouts; + + descriptorSetLayouts.resize(count); + descriptorSets.resize(count); + + for (const auto& descriptor : descriptors) { + descriptorSetLayouts[descriptor.first] = core.createDescriptorSetLayout(descriptor.second); + descriptorSets[descriptor.first] = core.createDescriptorSet(descriptorSetLayouts[descriptor.first]); + } + + vkcv::ComputePipelineConfig config { + shaderProgram, + descriptorSetLayouts + }; + + return core.createComputePipeline(config); +} + +vkcv::BufferHandle resetParticles(vkcv::Core& core, size_t count, const glm::vec3& velocity, + float density, float size, int form, int mode) { + vkcv::Buffer<Particle> particles = vkcv::buffer<Particle>( + core, + vkcv::BufferType::STORAGE, + count + ); + + std::vector<Particle> particles_vec (particles.getCount()); + + switch (form) { + case SIM_FORM_SPHERE: + distributeParticlesSphere( + particles_vec.data(), + particles_vec.size(), + glm::vec3(0.5f), + size, + density * sphere_volume(size), + velocity, + (mode == 0) + ); + break; + case SIM_FORM_CUBE: + distributeParticlesCube( + particles_vec.data(), + particles_vec.size(), + glm::vec3(0.5f), + size, + density * sphere_volume(size), + velocity, + (mode == 0) + ); + break; + default: + break; + } + + particles.fill(particles_vec); + return particles.getHandle(); +} + +int main(int argc, const char **argv) { + const std::string applicationName = "MPM"; + + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + features + ); + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + vkcv::camera::CameraManager cameraManager(window); + + vkcv::gui::GUI gui (core, windowHandle); + + auto trackballHandle = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + cameraManager.getCamera(trackballHandle).setCenter(glm::vec3(0.5f, 0.5f, 0.5f)); // set camera to look at the center of the particle volume + cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::ImageHandle depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainExtent.width, + swapchainExtent.height + ) + ); + + vkcv::Image grid = vkcv::image( + core, + vk::Format::eR16G16B16A16Sfloat, + 32, + 32, + 32, + false, + true + ); + + vkcv::SamplerHandle gridSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::NEAREST, + vkcv::SamplerAddressMode::CLAMP_TO_BORDER, + 0.0f, + vkcv::SamplerBorderColor::FLOAT_ZERO_TRANSPARENT + ); + + vkcv::Buffer<Simulation> simulation = vkcv::buffer<Simulation>( + core, vkcv::BufferType::UNIFORM, 1, vkcv::BufferMemoryType::HOST_VISIBLE + ); + + Simulation* sim = simulation.map(); + + glm::vec3 initialVelocity (0.0f, 0.1f, 0.0f); + + sim->density = 2500.0f; + sim->size = 0.1f; + sim->lame1 = 10.0f; + sim->lame2 = 20.0f; + sim->form = SIM_FORM_SPHERE; + sim->type = SIM_TYPE_HYPERELASTIC; + sim->K = 2.2f; + sim->E = 35.0f; + sim->gamma = 1.330f; + sim->mode = SIM_MODE_RANDOM; + sim->gravity = 9.81f; + sim->count = 1024; + + vkcv::BufferHandle particlesHandle = resetParticles( + core, + sim->count, + initialVelocity, + sim->density, + sim->size, + sim->form, + sim->mode + ); + + vkcv::shader::GLSLCompiler compiler; + + std::vector<vkcv::DescriptorSetHandle> initParticleWeightsSets; + vkcv::ComputePipelineHandle initParticleWeightsPipeline = createComputePipeline( + core, compiler, + "shaders/init_particle_weights.comp", + initParticleWeightsSets + ); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particlesHandle); + core.writeDescriptorSet(initParticleWeightsSets[0], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, grid.getHandle()); + writes.writeSampler(1, gridSampler); + core.writeDescriptorSet(initParticleWeightsSets[1], writes); + } + + std::vector<vkcv::DescriptorSetHandle> transformParticlesToGridSets; + vkcv::ComputePipelineHandle transformParticlesToGridPipeline = createComputePipeline( + core, compiler, + "shaders/transform_particles_to_grid.comp", + transformParticlesToGridSets + ); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particlesHandle); + core.writeDescriptorSet(transformParticlesToGridSets[0], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeUniformBuffer(0, simulation.getHandle()); + core.writeDescriptorSet(transformParticlesToGridSets[1], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeStorageImage(0, grid.getHandle()); + core.writeDescriptorSet(transformParticlesToGridSets[2], writes); + } + + std::vector<vkcv::DescriptorSetHandle> updateParticleVelocitiesSets; + vkcv::ComputePipelineHandle updateParticleVelocitiesPipeline = createComputePipeline( + core, compiler, + "shaders/update_particle_velocities.comp", + updateParticleVelocitiesSets + ); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particlesHandle); + core.writeDescriptorSet(updateParticleVelocitiesSets[0], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeUniformBuffer(0, simulation.getHandle()); + core.writeDescriptorSet(updateParticleVelocitiesSets[1], writes); + } + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, grid.getHandle()); + writes.writeSampler(1, gridSampler); + core.writeDescriptorSet(updateParticleVelocitiesSets[2], writes); + } + + vkcv::ShaderProgram gfxProgramGrid; + + compiler.compileProgram(gfxProgramGrid, { + { vkcv::ShaderStage::VERTEX, "shaders/grid.vert" }, + { vkcv::ShaderStage::FRAGMENT, "shaders/grid.frag" } + }, nullptr); + + vkcv::ShaderProgram gfxProgramParticles; + + compiler.compileProgram(gfxProgramParticles, { + { vkcv::ShaderStage::VERTEX, "shaders/particle.vert" }, + { vkcv::ShaderStage::FRAGMENT, "shaders/particle.frag" } + }, nullptr); + + vkcv::ShaderProgram gfxProgramLines; + + compiler.compileProgram(gfxProgramLines, { + { vkcv::ShaderStage::VERTEX, "shaders/lines.vert" }, + { vkcv::ShaderStage::FRAGMENT, "shaders/lines.frag" } + }, nullptr); + + vkcv::DescriptorSetLayoutHandle gfxSetLayoutGrid = core.createDescriptorSetLayout( + gfxProgramGrid.getReflectedDescriptors().at(0) + ); + + vkcv::DescriptorSetHandle gfxSetGrid = core.createDescriptorSet(gfxSetLayoutGrid); + + { + vkcv::DescriptorWrites writes; + writes.writeSampledImage(0, grid.getHandle()); + writes.writeSampler(1, gridSampler); + writes.writeUniformBuffer(2, simulation.getHandle()); + core.writeDescriptorSet(gfxSetGrid, writes); + } + + vkcv::DescriptorSetLayoutHandle gfxSetLayoutParticles = core.createDescriptorSetLayout( + gfxProgramParticles.getReflectedDescriptors().at(0) + ); + + vkcv::DescriptorSetHandle gfxSetParticles = core.createDescriptorSet(gfxSetLayoutParticles); + + { + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particlesHandle); + core.writeDescriptorSet(gfxSetParticles, writes); + } + + vkcv::PassHandle gfxPassGrid = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } + ); + + vkcv::PassHandle gfxPassParticles = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat } + ); + + vkcv::PassHandle gfxPassLines = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined, vk::Format::eD32Sfloat }, + false + ); + + vkcv::VertexLayout vertexLayoutGrid = vkcv::createVertexLayout({ + vkcv::createVertexBinding(0, gfxProgramGrid.getVertexAttachments()) + }); + + vkcv::GraphicsPipelineConfig gfxPipelineConfigGrid ( + gfxProgramGrid, + gfxPassGrid, + vertexLayoutGrid, + { gfxSetLayoutGrid } + ); + + vkcv::VertexLayout vertexLayoutParticles = vkcv::createVertexLayout({ + vkcv::createVertexBinding(0, gfxProgramParticles.getVertexAttachments()) + }); + + vkcv::GraphicsPipelineConfig gfxPipelineConfigParticles ( + gfxProgramParticles, + gfxPassParticles, + vertexLayoutParticles, + { gfxSetLayoutParticles } + ); + + vkcv::VertexLayout vertexLayoutLines = vkcv::createVertexLayout({ + vkcv::createVertexBinding(0, gfxProgramLines.getVertexAttachments()) + }); + + vkcv::GraphicsPipelineConfig gfxPipelineConfigLines ( + gfxProgramLines, + gfxPassLines, + vertexLayoutLines, + {} + ); + + gfxPipelineConfigLines.setPrimitiveTopology(vkcv::PrimitiveTopology::LineList); + + vkcv::GraphicsPipelineHandle gfxPipelineGrid = core.createGraphicsPipeline(gfxPipelineConfigGrid); + vkcv::GraphicsPipelineHandle gfxPipelineParticles = core.createGraphicsPipeline(gfxPipelineConfigParticles); + vkcv::GraphicsPipelineHandle gfxPipelineLines = core.createGraphicsPipeline(gfxPipelineConfigLines); + + vkcv::Buffer<glm::vec2> trianglePositions = vkcv::buffer<glm::vec2>(core, vkcv::BufferType::VERTEX, 3); + trianglePositions.fill({ + glm::vec2(-1.0f, -1.0f), + glm::vec2(+0.0f, +1.5f), + glm::vec2(+1.0f, -1.0f) + }); + + vkcv::VertexData triangleData ({ vkcv::vertexBufferBinding(trianglePositions.getHandle()) }); + triangleData.setCount(trianglePositions.getCount()); + + vkcv::Buffer<glm::vec3> linesPositions = vkcv::buffer<glm::vec3>(core, vkcv::BufferType::VERTEX, 8); + linesPositions.fill({ + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(1.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 1.0f, 0.0f), + glm::vec3(1.0f, 1.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 1.0f), + glm::vec3(1.0f, 0.0f, +1.0f), + glm::vec3(0.0f, 1.0f, 1.0f), + glm::vec3(1.0f, 1.0f, 1.0f) + }); + + vkcv::Buffer<uint16_t> linesIndices = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 24); + linesIndices.fill({ + 0, 1, + 1, 3, + 3, 2, + 2, 0, + + 4, 5, + 5, 7, + 7, 6, + 6, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7 + }); + + vkcv::VertexData linesData ({ vkcv::vertexBufferBinding(linesPositions.getHandle()) }); + linesData.setIndexBuffer(linesIndices.getHandle()); + linesData.setCount(linesIndices.getCount()); + + vkcv::InstanceDrawcall drawcallGrid ( + triangleData, + grid.getWidth() * grid.getHeight() * grid.getDepth() + ); + + drawcallGrid.useDescriptorSet(0, gfxSetGrid); + + vkcv::InstanceDrawcall drawcallParticle (triangleData, sim->count); + drawcallParticle.useDescriptorSet(0, gfxSetParticles); + + vkcv::InstanceDrawcall drawcallLines (linesData); + + bool renderGrid = true; + float speed_factor = 1.0f; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((swapchainWidth != swapchainExtent.width) || ((swapchainHeight != swapchainExtent.height))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); + + swapchainExtent.width = swapchainWidth; + swapchainExtent.height = swapchainHeight; + } + + Physics physics; + physics.t = static_cast<float>(t); + physics.dt = static_cast<float>(dt); + physics.speedfactor = speed_factor; + + vkcv::PushConstants physicsPushConstants = vkcv::pushConstants<Physics>(); + physicsPushConstants.appendDrawcall(physics); + + cameraManager.update(physics.dt); + + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); + vkcv::PushConstants cameraPushConstants = vkcv::pushConstants<glm::mat4>(); + cameraPushConstants.appendDrawcall(mvp); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + const auto dispatchSizeGrid = vkcv::dispatchInvocations( + vkcv::DispatchSize(grid.getWidth(), grid.getHeight(), grid.getDepth()), + vkcv::DispatchSize(4, 4, 4) + ); + + const auto dispatchSizeParticles = vkcv::dispatchInvocations(sim->count, 64); + + for (int step = 0; step < 1; step++) { + core.recordBeginDebugLabel(cmdStream, "INIT PARTICLE WEIGHTS", {0.78f, 0.89f, 0.94f, 1.0f}); + core.recordBufferMemoryBarrier(cmdStream, particlesHandle); + core.prepareImageForSampling(cmdStream, grid.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + initParticleWeightsPipeline, + dispatchSizeParticles, + { + vkcv::useDescriptorSet( + 0, initParticleWeightsSets[0] + ), + vkcv::useDescriptorSet( + 1, initParticleWeightsSets[1] + ) + }, + vkcv::PushConstants(0) + ); + + core.recordBufferMemoryBarrier(cmdStream, particlesHandle); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "TRANSFORM PARTICLES TO GRID", {0.47f, 0.77f, 0.85f, 1.0f}); + core.recordBufferMemoryBarrier(cmdStream, particlesHandle); + core.prepareImageForStorage(cmdStream, grid.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + transformParticlesToGridPipeline, + dispatchSizeGrid, + { + vkcv::useDescriptorSet( + 0, transformParticlesToGridSets[0] + ), + vkcv::useDescriptorSet( + 1, transformParticlesToGridSets[1] + ), + vkcv::useDescriptorSet( + 2, transformParticlesToGridSets[2] + ) + }, + physicsPushConstants + ); + + core.recordImageMemoryBarrier(cmdStream, grid.getHandle()); + core.recordEndDebugLabel(cmdStream); + + core.recordBeginDebugLabel(cmdStream, "UPDATE PARTICLE VELOCITIES", {0.78f, 0.89f, 0.94f, 1.0f}); + core.recordBufferMemoryBarrier(cmdStream, particlesHandle); + core.recordBufferMemoryBarrier(cmdStream, simulation.getHandle()); + core.prepareImageForSampling(cmdStream, grid.getHandle()); + + core.recordComputeDispatchToCmdStream( + cmdStream, + updateParticleVelocitiesPipeline, + dispatchSizeParticles, + { + vkcv::useDescriptorSet( + 0, updateParticleVelocitiesSets[0] + ), + vkcv::useDescriptorSet( + 1, updateParticleVelocitiesSets[1] + ), + vkcv::useDescriptorSet( + 2, updateParticleVelocitiesSets[2] + ) + }, + physicsPushConstants + ); + + core.recordBufferMemoryBarrier(cmdStream, particlesHandle); + core.recordEndDebugLabel(cmdStream); + } + + std::vector<vkcv::ImageHandle> renderTargets { + vkcv::ImageHandle::createSwapchainImageHandle(), + depthBuffer + }; + + if (renderGrid) { + core.recordBeginDebugLabel(cmdStream, "RENDER GRID", { 0.13f, 0.20f, 0.22f, 1.0f }); + core.recordBufferMemoryBarrier(cmdStream, simulation.getHandle()); + core.prepareImageForSampling(cmdStream, grid.getHandle()); + + core.recordDrawcallsToCmdStream( + cmdStream, + gfxPipelineGrid, + cameraPushConstants, + { drawcallGrid }, + renderTargets, + windowHandle + ); + + core.recordEndDebugLabel(cmdStream); + } else { + core.recordBeginDebugLabel(cmdStream, "RENDER PARTICLES", { 0.13f, 0.20f, 0.22f, 1.0f }); + core.recordBufferMemoryBarrier(cmdStream, particlesHandle); + + core.recordDrawcallsToCmdStream( + cmdStream, + gfxPipelineParticles, + cameraPushConstants, + { drawcallParticle }, + renderTargets, + windowHandle + ); + + core.recordEndDebugLabel(cmdStream); + } + + core.recordBeginDebugLabel(cmdStream, "RENDER LINES", { 0.13f, 0.20f, 0.22f, 1.0f }); + + core.recordDrawcallsToCmdStream( + cmdStream, + gfxPipelineLines, + cameraPushConstants, + { drawcallLines }, + renderTargets, + windowHandle + ); + + core.recordEndDebugLabel(cmdStream); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + gui.beginGUI(); + ImGui::Begin("Settings"); + + ImGui::BeginGroup(); + ImGui::Combo("Mode", &(sim->mode), "Random\0Ordered\0", 2); + ImGui::Combo("Form", &(sim->form), "Sphere\0Cube\0", 2); + ImGui::Combo("Type", &(sim->type), "Hyperelastic\0Fluid\0", 2); + ImGui::EndGroup(); + + ImGui::Spacing(); + + ImGui::SliderInt("Particle Count", &(sim->count), 1, 100000); + ImGui::SliderFloat("Density", &(sim->density), std::numeric_limits<float>::epsilon(), 5000.0f); + ImGui::SameLine(0.0f, 10.0f); + if (ImGui::SmallButton("Reset##density")) { + sim->density = 2500.0f; + } + + ImGui::SliderFloat("Radius", &(sim->size), 0.0f, 0.5f); + ImGui::SameLine(0.0f, 10.0f); + if (ImGui::SmallButton("Reset##radius")) { + sim->size = 0.1f; + } + + ImGui::Spacing(); + + ImGui::BeginGroup(); + ImGui::SliderFloat("Bulk Modulus", &(sim->K), 0.0f, 1000.0f); + ImGui::SliderFloat("Young's Modulus", &(sim->E), 0.0f, 1000.0f); + ImGui::SliderFloat("Heat Capacity Ratio", &(sim->gamma), 1.0f, 2.0f); + ImGui::SliderFloat("Lame1", &(sim->lame1), 0.0f, 1000.0f); + ImGui::SliderFloat("Lame2", &(sim->lame2), 0.0f, 1000.0f); + ImGui::EndGroup(); + + ImGui::Spacing(); + + ImGui::SliderFloat("Simulation Speed", &speed_factor, 0.0f, 2.0f); + + ImGui::Spacing(); + ImGui::Checkbox("Render Grid", &renderGrid); + + ImGui::DragFloat3("Initial Velocity", reinterpret_cast<float*>(&initialVelocity), 0.001f); + ImGui::SameLine(0.0f, 10.0f); + if (ImGui::Button("Reset##particle_velocity")) { + particlesHandle = resetParticles( + core, + sim->count, + initialVelocity, + sim->density, + sim->size, + sim->form, + sim->mode + ); + + vkcv::DescriptorWrites writes; + writes.writeStorageBuffer(0, particlesHandle); + + core.writeDescriptorSet(initParticleWeightsSets[0], writes); + core.writeDescriptorSet(transformParticlesToGridSets[0], writes); + core.writeDescriptorSet(updateParticleVelocitiesSets[0], writes); + + core.writeDescriptorSet(gfxSetParticles, writes); + } + + ImGui::SliderFloat("Gravity", &(sim->gravity), 0.0f, 10.0f); + + ImGui::End(); + gui.endGUI(); + }); + + simulation.unmap(); + return 0; +} diff --git a/projects/particle_simulation/.gitignore b/projects/particle_simulation/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4964f89e973f38358aa57f564f56d3d4b0c328a9 --- /dev/null +++ b/projects/particle_simulation/.gitignore @@ -0,0 +1 @@ +particle_simulation \ No newline at end of file diff --git a/projects/particle_simulation/CMakeLists.txt b/projects/particle_simulation/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..860bfa15a4b77b71ac79e3433326feb6aa3d7123 --- /dev/null +++ b/projects/particle_simulation/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.16) +project(particle_simulation) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(particle_simulation + src/main.cpp + src/ParticleSystem.hpp + src/ParticleSystem.cpp + src/Particle.hpp + src/Particle.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(particle_simulation SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_testing_include} + ${vkcv_camera_include} + ${vkcv_shader_compiler_include} + ${vkcv_effects_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(particle_simulation + vkcv + vkcv_testing + vkcv_camera + vkcv_shader_compiler + vkcv_effects) diff --git a/projects/particle_simulation/README.md b/projects/particle_simulation/README.md new file mode 100644 index 0000000000000000000000000000000000000000..74163ca06e0f61fe57e5ffbfe57877c2d203724b --- /dev/null +++ b/projects/particle_simulation/README.md @@ -0,0 +1,20 @@ +# Particle simulation +An example project to show thousands of particles can be simulated with the VkCV framework + + + + + +## Details + +Similar to the projects to show rendering a single triangle or a simple mesh was possible. This +project shows that many particles can be simulated using compute shaders and rendered using the +usual graphics pipeline, all with the VkCV framework. + +The behavior of the particles can easily be adjusted loading a different compute shader. That is +the reason the application can be started with three different pipelines using one of those +parameters: + + - `--space` to simulate particles flying around in a wild way through space + - `--water` to simulate particles dropping down like a waterfall to the ground + - `--gravity` to simulate particles being drawn by points of gravity in space flying around those points diff --git a/projects/particle_simulation/shaders/particleShading.inc b/projects/particle_simulation/shaders/particleShading.inc new file mode 100644 index 0000000000000000000000000000000000000000..b2d1832b9ccd6ba05a585b59bdfdedd4729e80f8 --- /dev/null +++ b/projects/particle_simulation/shaders/particleShading.inc @@ -0,0 +1,6 @@ +float circleFactor(vec2 triangleCoordinates){ + // percentage of distance from center to circle edge + float p = clamp((0.4 - length(triangleCoordinates)) / 0.4, 0, 1); + // remapping for nice falloff + return sqrt(p); +} \ No newline at end of file diff --git a/projects/particle_simulation/shaders/shader.vert b/projects/particle_simulation/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..0a889b35dbb750dc932de57b611f22acaa1ac3f2 --- /dev/null +++ b/projects/particle_simulation/shaders/shader.vert @@ -0,0 +1,45 @@ +#version 460 core +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 particle; + +struct Particle +{ + vec3 position; + float lifeTime; + vec3 velocity; + float padding_2; + vec3 reset_velocity; + float padding_3; +}; + +layout(std430, binding = 2) coherent buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout( push_constant ) uniform constants{ + mat4 view; + mat4 projection; +}; + +layout(location = 0) out vec2 passTriangleCoordinates; +layout(location = 1) out vec3 passVelocity; +layout(location = 2) out float passlifeTime; + +void main() +{ + int id = gl_InstanceIndex; + passVelocity = inParticle[id].velocity; + passlifeTime = inParticle[id].lifeTime; + // particle position in view space + vec4 positionView = view * vec4(inParticle[id].position, 1); + // by adding the triangle position in view space the mesh is always camera facing + positionView.xyz += particle; + // multiply with projection matrix for final position + gl_Position = projection * positionView; + + // 0.01 corresponds to vertex position size in main + float normalizationDivider = 0.012; + passTriangleCoordinates = particle.xy / normalizationDivider; +} \ No newline at end of file diff --git a/projects/particle_simulation/shaders/shader_gravity.comp b/projects/particle_simulation/shaders/shader_gravity.comp new file mode 100644 index 0000000000000000000000000000000000000000..77954958c694a3c6c620818dd3b5d999e51b4a42 --- /dev/null +++ b/projects/particle_simulation/shaders/shader_gravity.comp @@ -0,0 +1,80 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float lifeTime; + vec3 velocity; + float mass; + vec3 reset_velocity; + float _padding; +}; + +layout(std430, binding = 0) coherent buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout( push_constant ) uniform constants{ + float deltaTime; + float rand; +}; + +const int n = 4; +vec4 gravityPoint[n] = vec4[n]( + vec4(-0.8, -0.5, 0.0, 3), + vec4(-0.4, 0.5, 0.8, 2), + vec4( 0.8, 0.8, -0.3, 4), + vec4( 0.5, -0.7, -0.5, 1) +); + +const float G = 6.6743015e-11; +const float sim_d_factor = 10e11; +const float sim_g_factor = 10e30; +const float sim_t_factor = 5; +const float c = 299792458; + +void main() { + uint id = gl_GlobalInvocationID.x; + inParticle[id].lifeTime -= deltaTime; + vec3 pos = inParticle[id].position; + vec3 vel = inParticle[id].velocity; + float mass = inParticle[id].mass; + + if(inParticle[id].lifeTime < 0.f) + { + inParticle[id].lifeTime = 5.f * rand; + inParticle[id].mass *= rand; + + pos = vec3(0); + vel *= rand; + } + + for(int i = 0; i < n; i++) + { + vec3 d = (gravityPoint[i].xyz - pos) * sim_d_factor; + float r = length(d); + float g = G * (gravityPoint[i].w * sim_g_factor) / (r * r); + + if (r > 0) { + vec3 dvel = (deltaTime * sim_t_factor) * g * (d / r); + + vel = (vel + dvel) / (1.0 + dot(vel, dvel) / (c*c)); + } + } + + pos += vel * (deltaTime * sim_t_factor); + + vec3 a_pos = abs(pos); + + if ((a_pos.x > 2.0) || (a_pos.y > 2.0) || (a_pos.z > 2.0)) + { + inParticle[id].lifeTime *= 0.9; + } + + inParticle[id].position = pos; + inParticle[id].velocity = vel; +} diff --git a/projects/particle_simulation/shaders/shader_space.comp b/projects/particle_simulation/shaders/shader_space.comp new file mode 100644 index 0000000000000000000000000000000000000000..6e25fff8aec8ceab7c1ffdd9be65d9b8fa8f0974 --- /dev/null +++ b/projects/particle_simulation/shaders/shader_space.comp @@ -0,0 +1,73 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float lifeTime; + vec3 velocity; + float padding_2; + vec3 reset_velocity; + float padding_3; +}; + +layout(std430, binding = 0) coherent buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout( push_constant ) uniform constants{ + float deltaTime; + float rand; +}; + +vec3 attraction(vec3 pos, vec3 attractPos) +{ + vec3 delta = attractPos - pos; + const float damp = 0.5; + float dDampedDot = dot(delta, delta) + damp; + float invDist = 1.0f / sqrt(dDampedDot); + float invDistCubed = invDist*invDist*invDist; + return delta * invDistCubed * 0.0035; +} + +vec3 repulsion(vec3 pos, vec3 attractPos) +{ + vec3 delta = attractPos - pos; + float targetDistance = sqrt(dot(delta, delta)); + return delta * (1.0 / (targetDistance * targetDistance * targetDistance)) * -0.000035; +} + + +const int n = 4; +vec3 gravity = vec3(0,-9.8,0); +vec3 gravityPoint[n] = vec3[n](vec3(-0.3, .5, -0.6),vec3(-0.2, 0.6, -0.3),vec3(.4, -0.4, 0.6),vec3(-.4, -0.4, -0.6)); +//vec3 gravityPoint[n] = vec3[n](vec3(-0.5, 0.5, 0)); +void main() { + uint id = gl_GlobalInvocationID.x; + inParticle[id].lifeTime -= deltaTime; + vec3 pos = inParticle[id].position; + vec3 vel = inParticle[id].velocity; + if(inParticle[id].lifeTime < 0.f) + { + inParticle[id].lifeTime = 5.f; + pos = vec3(0); + } + // inParticle[id].position += deltaTime * -normalize(max(2 - distance(inParticle[id].position,respawnPos),0.0) * respawnPos - inParticle[id].position); + + for(int i = 0; i < n; i++) + { + vel += deltaTime * deltaTime * normalize(max(2 - distance(pos,gravityPoint[i]),0.1) * gravityPoint[i] - pos); + } + + if((pos.x <= -2.0) || (pos.x > 2.0) || (pos.y <= -2.0) || (pos.y > 2.0)|| (pos.z <= -2.0) || (pos.z > 2.0)){ + vel = (-vel * 0.1); + } + + pos += normalize(vel) * deltaTime; + inParticle[id].position = pos; + float rand1 = rand; + inParticle[id].velocity = vel; +} diff --git a/projects/particle_simulation/shaders/shader_space.frag b/projects/particle_simulation/shaders/shader_space.frag new file mode 100644 index 0000000000000000000000000000000000000000..7f6d22065caa3c4b3ab2b1f697c9545a66d7bd54 --- /dev/null +++ b/projects/particle_simulation/shaders/shader_space.frag @@ -0,0 +1,46 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "particleShading.inc" + +layout(location = 0) in vec2 passTriangleCoordinates; +layout(location = 1) in vec3 passVelocity; +layout(location = 2) in float passlifeTime; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform uColor { + vec4 color; +} Color; + +layout(set=0,binding=1) uniform uPosition{ + vec2 position; +} Position; + + +void main() +{ + vec2 mouse = vec2(Position.position.x, Position.position.y); + + vec3 c0 = vec3(1, 1, 0.05); + vec3 c1 = vec3(1, passlifeTime * 0.5, 0.05); + vec3 c2 = vec3(passlifeTime * 0.5,passlifeTime * 0.5,0.05); + vec3 c3 = vec3(1, 0.05, 0.05); + + if(passlifeTime < 1){ + outColor = mix(c0, c1, passlifeTime ); + } + else if(passlifeTime < 2){ + outColor = mix(c1, c2, passlifeTime - 1); + } + else{ + outColor = mix(c2, c3, clamp((passlifeTime - 2) * 0.5, 0, 1)); + } + + // make the triangle look like a circle + outColor *= circleFactor(passTriangleCoordinates); + + // fade out particle shortly before it dies + outColor *= clamp(passlifeTime * 2, 0, 1); +} \ No newline at end of file diff --git a/projects/particle_simulation/shaders/shader_water.comp b/projects/particle_simulation/shaders/shader_water.comp new file mode 100644 index 0000000000000000000000000000000000000000..d1a0e761038b5fb367a33454c746871f1a6a4553 --- /dev/null +++ b/projects/particle_simulation/shaders/shader_water.comp @@ -0,0 +1,84 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float lifeTime; + vec3 velocity; + float padding_2; + vec3 reset_velocity; + float padding_3; +}; + +layout(std430, binding = 0) coherent buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout( push_constant ) uniform constants{ + float deltaTime; + float rand; +}; + +vec3 attraction(vec3 pos, vec3 attractPos) +{ + vec3 delta = attractPos - pos; + const float damp = 0.5; + float dDampedDot = dot(delta, delta) + damp; + float invDist = 1.0f / sqrt(dDampedDot); + float invDistCubed = invDist*invDist*invDist; + return delta * invDistCubed * 0.0035; +} + +vec3 repulsion(vec3 pos, vec3 attractPos) +{ + vec3 delta = attractPos - pos; + float targetDistance = sqrt(dot(delta, delta)); + return delta * (1.0 / (targetDistance * targetDistance * targetDistance)) * -0.000035; +} + + +const int n = 3; +vec3 gravity = vec3(0,-9.8,0); +vec3 gravityPoint[n] = vec3[n](vec3(-0.5, 0.5, 0),vec3(0.5, 0.5, 0),vec3(0, -0.5, 0)); +//vec3 gravityPoint[n] = vec3[n](vec3(-0.5, 0.5, 0)); +void main() { + uint id = gl_GlobalInvocationID.x; + inParticle[id].lifeTime -= deltaTime; + vec3 pos = inParticle[id].position; + vec3 vel = inParticle[id].velocity; + if(inParticle[id].lifeTime < 0.f) + { + inParticle[id].lifeTime = 7.f; + pos = vec3(0); + vel = inParticle[id].reset_velocity; + inParticle[id].velocity = inParticle[id].reset_velocity; + } + // inParticle[id].position += deltaTime * -normalize(max(2 - distance(inParticle[id].position,respawnPos),0.0) * respawnPos - inParticle[id].position); + + for(int i = 0; i < n; i++) + { + vel += deltaTime * deltaTime * deltaTime * normalize(max(2 - distance(pos,gravityPoint[i]),0.1) * gravityPoint[i] - pos); + } + + //vec3 delta = respawnPos - pos; + //float targetDistane = sqrt(dot(delta,delta)); + //vel += repulsion(pos, respawnPos); + + //if((pos.x <= -1.0) || (pos.x > 1.0) || (pos.y <= -1.0) || (pos.y > 1.0)|| (pos.z <= -1.0) || (pos.z > 1.0)) + vel = (-vel * 0.01); + + if((pos.y <= -1.0) || (pos.y > 1.0)){ + vel = reflect(vel, vec3(0,1,0)); + } + + pos += normalize(vel) * deltaTime; + inParticle[id].position = pos; + + float weight = 1.0; + float rand1 = rand; + inParticle[id].velocity = vel; +} diff --git a/projects/particle_simulation/shaders/shader_water.frag b/projects/particle_simulation/shaders/shader_water.frag new file mode 100644 index 0000000000000000000000000000000000000000..b68f9572a91b05e836c3fead9ae9afd7ce16ba8e --- /dev/null +++ b/projects/particle_simulation/shaders/shader_water.frag @@ -0,0 +1,46 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "particleShading.inc" + +layout(location = 0) in vec2 passTriangleCoordinates; +layout(location = 1) in vec3 passVelocity; +layout(location = 2) in float passlifeTime; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform uColor { + vec4 color; +} Color; + +layout(set=0,binding=1) uniform uPosition{ + vec2 position; +} Position; + +void main() +{ + float normlt = 1-normalize(passlifeTime); + vec2 mouse = vec2(Position.position.x, Position.position.y); + + vec3 c0 = vec3(0.2,0.5,1); + vec3 c1 = vec3(0.3, 0.7,1); + vec3 c2 = vec3(0.5,0.9,1); + vec3 c3 = vec3(0.9,1,1); + + if(passlifeTime < 1){ + outColor = mix(c0, c1, passlifeTime ); + } + else if(passlifeTime < 2){ + outColor = mix(c1, c2, passlifeTime - 1); + } + else{ + outColor = mix(c2, c3, clamp((passlifeTime - 2) * 0.5, 0, 1)); + } + + // make the triangle look like a circle + outColor *= circleFactor(passTriangleCoordinates); + + // fade out particle shortly before it dies + outColor *= clamp(passlifeTime * 2, 0, 1); +} diff --git a/projects/voxelization/resources/shaders/tonemapping.comp b/projects/particle_simulation/shaders/tonemapping.comp similarity index 64% rename from projects/voxelization/resources/shaders/tonemapping.comp rename to projects/particle_simulation/shaders/tonemapping.comp index 2383302fa946e7d92871039daff28232df2eafdd..26f0232d66e3475afdd1266c0cc6288b47ed1c38 100644 --- a/projects/voxelization/resources/shaders/tonemapping.comp +++ b/projects/particle_simulation/shaders/tonemapping.comp @@ -1,7 +1,7 @@ #version 440 -layout(set=0, binding=0, r11f_g11f_b10f) uniform image2D inImage; -layout(set=0, binding=1, rgba8) uniform image2D outImage; +layout(set=0, binding=0, rgba16f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; @@ -13,7 +13,7 @@ void main(){ } ivec2 uv = ivec2(gl_GlobalInvocationID.xy); vec3 linearColor = imageLoad(inImage, uv).rgb; - vec3 tonemapped = linearColor / (linearColor + 1); // reinhard tonemapping + vec3 tonemapped = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 1); // reinhard tonemapping vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); } \ No newline at end of file diff --git a/projects/particle_simulation/src/Particle.cpp b/projects/particle_simulation/src/Particle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b80d063d382c9ae1cb63887388cce065b8289b63 --- /dev/null +++ b/projects/particle_simulation/src/Particle.cpp @@ -0,0 +1,42 @@ + +#include "Particle.hpp" + +Particle::Particle(glm::vec3 position, glm::vec3 velocity, float lifeTime) +: m_position(position), + m_lifeTime(lifeTime), + m_velocity(velocity), + m_mass(1.0f), + m_reset_velocity(velocity) +{} + +const glm::vec3& Particle::getPosition()const{ + return m_position; +} + +bool Particle::isAlive()const{ + return m_lifeTime > 0.f; +} + +void Particle::setPosition( const glm::vec3 pos ){ + m_position = pos; +} + +const glm::vec3& Particle::getVelocity()const{ + return m_velocity; +} + +void Particle::setVelocity( const glm::vec3 vel ){ + m_velocity = vel; +} + +void Particle::update( const float delta ){ + m_position += m_velocity * delta; +} + +void Particle::setLifeTime( const float lifeTime ){ + m_lifeTime = lifeTime; +} + +const float& Particle::getLifeTime()const{ + return m_lifeTime; +} \ No newline at end of file diff --git a/projects/particle_simulation/src/Particle.hpp b/projects/particle_simulation/src/Particle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..73e7cbf517709ee03274cfd199081ade3f756545 --- /dev/null +++ b/projects/particle_simulation/src/Particle.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <glm/glm.hpp> + +class Particle { + +public: + Particle(glm::vec3 position, glm::vec3 velocity, float lifeTime = 1.f); + + const glm::vec3& getPosition()const; + + void setPosition( const glm::vec3 pos ); + + const glm::vec3& getVelocity()const; + + void setVelocity( const glm::vec3 vel ); + + void update( const float delta ); + + bool isAlive()const; + + void setLifeTime( const float lifeTime ); + + const float& getLifeTime()const; + +private: + // all properties of the Particle + glm::vec3 m_position; + float m_lifeTime; + glm::vec3 m_velocity; + float m_mass; + glm::vec3 m_reset_velocity; + float padding_3; +}; diff --git a/projects/particle_simulation/src/ParticleSystem.cpp b/projects/particle_simulation/src/ParticleSystem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b3162d6bea685640d3949577271affc8b2080407 --- /dev/null +++ b/projects/particle_simulation/src/ParticleSystem.cpp @@ -0,0 +1,60 @@ +#include "ParticleSystem.hpp" + +ParticleSystem::ParticleSystem(uint32_t particleCount ,glm::vec3 minVelocity , glm::vec3 maxVelocity , glm::vec2 lifeTime ) +{ + m_rdmVel.resize(3); + m_rdmVel[0] = std::uniform_real_distribution<float>(minVelocity.x, maxVelocity.x); + m_rdmVel[1] = std::uniform_real_distribution<float>(minVelocity.y, maxVelocity.y); + m_rdmVel[2] = std::uniform_real_distribution<float>(minVelocity.z, maxVelocity.z); + m_rdmLifeTime = std::uniform_real_distribution<float>(lifeTime.x, lifeTime.y); + + for(uint32_t i = 0; i < particleCount ;i++ ){ + addParticle(Particle(m_respawnPos, getRandomVelocity(), getRandomLifeTime())); + } +} + +const std::vector<Particle>& ParticleSystem::getParticles() const{ + return m_particles; +} + +void ParticleSystem::addParticle( const Particle particle ){ + m_particles.push_back(particle); +} +void ParticleSystem::addParticles( const std::vector<Particle> particles ){ + m_particles.insert(m_particles.end(), particles.begin(), particles.end()); +} + +void ParticleSystem::updateParticles( const float deltaTime ){ + for(Particle& particle :m_particles){ + bool alive = particle.isAlive(); + particle.setPosition( particle.getPosition() * static_cast<float>(alive) + static_cast<float>(!alive) * m_respawnPos ); + particle.setVelocity( particle.getVelocity() * static_cast<float>(alive) + static_cast<float>(!alive) * getRandomVelocity()); + particle.setLifeTime( (particle.getLifeTime() * alive + !alive * getRandomLifeTime() ) - deltaTime ); + particle.update(deltaTime); + } +} + +glm::vec3 ParticleSystem::getRandomVelocity(){ + return glm::vec3(m_rdmVel[0](m_rdmEngine), m_rdmVel[1](m_rdmEngine),m_rdmVel[2](m_rdmEngine)); +} + +float ParticleSystem::getRandomLifeTime(){ + return m_rdmLifeTime(m_rdmEngine); +} + +void ParticleSystem::setRespawnPos( const glm::vec3 respawnPos){ + m_respawnPos = respawnPos; +} +void ParticleSystem::setRdmLifeTime( const glm::vec2 lifeTime ){ + m_rdmLifeTime = std::uniform_real_distribution<float> (lifeTime.x,lifeTime.y); +} + +void ParticleSystem::setRdmVelocity( glm::vec3 minVelocity, glm::vec3 maxVelocity ){ + m_rdmVel[0] = std::uniform_real_distribution<float> (minVelocity.x,maxVelocity.x); + m_rdmVel[1] = std::uniform_real_distribution<float> (minVelocity.y,maxVelocity.y); + m_rdmVel[2] = std::uniform_real_distribution<float> (minVelocity.z,maxVelocity.z); +} + +const glm::vec3 ParticleSystem::getRespawnPos() const{ + return m_respawnPos; +} diff --git a/projects/particle_simulation/src/ParticleSystem.hpp b/projects/particle_simulation/src/ParticleSystem.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fe5c99f9b407b9dbdfd414e265e7cd91bbe790b9 --- /dev/null +++ b/projects/particle_simulation/src/ParticleSystem.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include <vector> +#include "Particle.hpp" +#include <random> +#include "vkcv/Buffer.hpp" + +class ParticleSystem { + +public: + ParticleSystem(uint32_t particleCount , glm::vec3 minVelocity = glm::vec3(0.f,0.f,0.f), glm::vec3 maxVelocity = glm::vec3(1.f,1.f,0.f), glm::vec2 lifeTime = glm::vec2(2.f,3.f)); + const std::vector<Particle> &getParticles() const; + void updateParticles( const float deltaTime ); + void setRespawnPos( const glm::vec3 respawnPos ); + void setRdmLifeTime( const glm::vec2 lifeTime ); + void setRdmVelocity( glm::vec3 minVelocity, glm::vec3 maxVelocity ); + const glm::vec3 getRespawnPos() const; + +private: + + void addParticle( const Particle particle ); + void addParticles( const std::vector<Particle> particles ); + glm::vec3 getRandomVelocity(); + float getRandomLifeTime(); + + std::vector<Particle> m_particles; + glm::vec3 m_respawnPos = glm::vec3(0.f); + + std::vector<std::uniform_real_distribution<float>> m_rdmVel; + std::uniform_real_distribution<float> m_rdmLifeTime; + std::default_random_engine m_rdmEngine; +}; \ No newline at end of file diff --git a/projects/particle_simulation/src/main.cpp b/projects/particle_simulation/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72249ee3e993b4f903220f496975ff7ae7328af4 --- /dev/null +++ b/projects/particle_simulation/src/main.cpp @@ -0,0 +1,329 @@ +#include <iostream> +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include "ParticleSystem.hpp" +#include <random> +#include <glm/gtc/matrix_access.hpp> +#include <ctime> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/effects/BloomAndFlaresEffect.hpp> + +int main(int argc, const char **argv) { + const std::string applicationName = "Particlesystem"; + + uint32_t windowWidth = 800; + uint32_t windowHeight = 600; + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + {vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute}, + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } + ); + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + vkcv::camera::CameraManager cameraManager(window); + + auto particleIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3, + vkcv::BufferMemoryType::DEVICE_LOCAL); + uint16_t indices[3] = {0, 1, 2}; + particleIndexBuffer.fill(&indices[0], sizeof(indices)); + + vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + vkcv::PassHandle particlePass = vkcv::passFormat(core, colorFormat); + + if (!particlePass) + { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + // use space or use water or gravity + std::string shaderPathCompute = "shaders/shader_space.comp"; + std::string shaderPathFragment = "shaders/shader_space.frag"; + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--space") == 0) { + shaderPathCompute = "shaders/shader_space.comp"; + shaderPathFragment = "shaders/shader_space.frag"; + } else + if (strcmp(argv[i], "--water") == 0) { + shaderPathCompute = "shaders/shader_water.comp"; + shaderPathFragment = "shaders/shader_water.frag"; + } else + if (strcmp(argv[i], "--gravity") == 0) { + shaderPathCompute = "shaders/shader_gravity.comp"; + shaderPathFragment = "shaders/shader_space.frag"; + } + } + + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram computeShaderProgram{}; + compiler.compile(vkcv::ShaderStage::COMPUTE, shaderPathCompute, [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + computeShaderProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle computeDescriptorSetLayout = core.createDescriptorSetLayout(computeShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeDescriptorSetLayout); + + const std::vector<vkcv::VertexAttachment> computeVertexAttachments = computeShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> computeBindings; + for (size_t i = 0; i < computeVertexAttachments.size(); i++) { + computeBindings.push_back(vkcv::createVertexBinding(i, { computeVertexAttachments[i] })); + } + const vkcv::VertexLayout computeLayout { computeBindings }; + + vkcv::ShaderProgram particleShaderProgram{}; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/shader.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, shaderPathFragment, [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout( + particleShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::Buffer<glm::vec3> vertexBuffer = vkcv::buffer<glm::vec3>( + core, + vkcv::BufferType::VERTEX, + 3 + ); + const std::vector<vkcv::VertexAttachment> vertexAttachments = particleShaderProgram.getVertexAttachments(); + + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::vertexBufferBinding(vertexBuffer.getHandle()) + }; + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, {vertexAttachments[i]})); + } + + const vkcv::VertexLayout particleLayout { bindings }; + + vkcv::GraphicsPipelineConfig particlePipelineDefinition ( + particleShaderProgram, + particlePass, + {particleLayout}, + {descriptorSetLayout} + ); + + particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive); + + const std::vector<glm::vec3> vertices = {glm::vec3(-0.012, 0.012, 0), + glm::vec3(0.012, 0.012, 0), + glm::vec3(0, -0.012, 0)}; + + vertexBuffer.fill(vertices); + + vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); + + vkcv::ComputePipelineHandle computePipeline = core.createComputePipeline({ + computeShaderProgram, {computeDescriptorSetLayout} + }); + + vkcv::Buffer<glm::vec4> color = vkcv::buffer<glm::vec4>( + core, + vkcv::BufferType::UNIFORM, + 1 + ); + + vkcv::Buffer<glm::vec2> position = vkcv::buffer<glm::vec2>( + core, + vkcv::BufferType::UNIFORM, + 1 + ); + + glm::vec3 minVelocity = glm::vec3(-0.1f,-0.1f,-0.1f); + glm::vec3 maxVelocity = glm::vec3(0.1f,0.1f,0.1f); + glm::vec2 lifeTime = glm::vec2(-1.f,8.f); + ParticleSystem particleSystem = ParticleSystem( 100000 , minVelocity, maxVelocity, lifeTime); + + vkcv::Buffer<Particle> particleBuffer = vkcv::buffer<Particle>( + core, + vkcv::BufferType::STORAGE, + particleSystem.getParticles().size() + ); + + particleBuffer.fill(particleSystem.getParticles()); + + vkcv::DescriptorWrites setWrites; + setWrites.writeUniformBuffer(0, color.getHandle()).writeUniformBuffer(1, position.getHandle()); + setWrites.writeStorageBuffer(2, particleBuffer.getHandle()); + core.writeDescriptorSet(descriptorSet, setWrites); + + vkcv::DescriptorWrites computeWrites; + computeWrites.writeStorageBuffer(0, particleBuffer.getHandle()); + core.writeDescriptorSet(computeDescriptorSet, computeWrites); + + if (!particlePipeline || !computePipeline) + { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::VertexData vertexData (vertexBufferBindings); + vertexData.setIndexBuffer(particleIndexBuffer.getHandle()); + vertexData.setCount(particleIndexBuffer.getCount()); + + auto descriptorUsage = vkcv::useDescriptorSet(0, descriptorSet); + + auto pos = glm::vec2(0.f); + auto spawnPosition = glm::vec3(0.f); + + window.e_mouseMove.add([&](double offsetX, double offsetY) { + pos = glm::vec2(static_cast<float>(offsetX), static_cast<float>(offsetY)); + pos.x = (-2 * pos.x + static_cast<float>(window.getWidth())) / static_cast<float>(window.getWidth()); + pos.y = (-2 * pos.y + static_cast<float>(window.getHeight())) / static_cast<float>(window.getHeight()); + spawnPosition = glm::vec3(pos.x, pos.y, 0.f); + particleSystem.setRespawnPos(glm::vec3(-spawnPosition.x, spawnPosition.y, spawnPosition.z)); + }); + + std::vector<glm::mat4> modelMatrices; + + vkcv::InstanceDrawcall drawcall (vertexData, particleSystem.getParticles().size()); + drawcall.useDescriptorSet(0, descriptorSet); + + glm::vec4 colorData = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setNearFar(0.1f, 30.0f); + cameraManager.getCamera(camHandle1).setNearFar(0.1f, 30.0f); + + cameraManager.setActiveCamera(camHandle1); + + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(0.0f, 0.0f, -2.0f)); + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0.0f, 0.0f, -2.0f)); + + const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::ImageConfig colorBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + colorBufferConfig.setSupportingStorage(true); + colorBufferConfig.setSupportingColorAttachment(true); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + + vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core); + bloomAndFlares.setUpsamplingLimit(3); + + vkcv::ShaderProgram tonemappingShader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + tonemappingShader.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle tonemappingDescriptorLayout = core.createDescriptorSetLayout(tonemappingShader.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle tonemappingDescriptor = core.createDescriptorSet(tonemappingDescriptorLayout); + vkcv::ComputePipelineHandle tonemappingPipe = core.createComputePipeline({ + tonemappingShader, + { tonemappingDescriptorLayout } + }); + + std::uniform_real_distribution<float> rdm = std::uniform_real_distribution<float>(0.95f, 1.05f); + std::default_random_engine rdmEngine; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((core.getImageWidth(colorBuffer) != swapchainWidth) || + (core.getImageHeight(colorBuffer) != swapchainHeight)) { + colorBufferConfig.setWidth(swapchainWidth); + colorBufferConfig.setHeight(swapchainHeight); + + colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + } + + color.fill(&colorData); + position.fill(&pos); + + cameraManager.update(dt); + + // split view and projection to allow for easy billboarding in shader + struct { + glm::mat4 view; + glm::mat4 projection; + } renderingMatrices; + + renderingMatrices.view = cameraManager.getActiveCamera().getView(); + renderingMatrices.projection = cameraManager.getActiveCamera().getProjection(); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + float random = rdm(rdmEngine); + glm::vec2 pushData = glm::vec2(dt, random); + + vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<glm::vec2>(); + pushConstantsCompute.appendDrawcall(pushData); + + core.recordComputeDispatchToCmdStream( + cmdStream, + computePipeline, + vkcv::dispatchInvocations(particleSystem.getParticles().size(), 256), + {vkcv::useDescriptorSet(0, computeDescriptorSet)}, + pushConstantsCompute + ); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer.getHandle()); + + vkcv::PushConstants pushConstantsDraw (sizeof(renderingMatrices)); + pushConstantsDraw.appendDrawcall(renderingMatrices); + + core.recordDrawcallsToCmdStream( + cmdStream, + particlePipeline, + pushConstantsDraw, + { drawcall }, + { colorBuffer }, + windowHandle + ); + + bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer); + + core.prepareImageForStorage(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, swapchainInput); + + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.writeStorageImage( + 0, colorBuffer + ).writeStorageImage( + 1, swapchainInput + ); + + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); + + const auto tonemappingDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(8, 8) + ); + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipe, + tonemappingDispatchCount, + { vkcv::useDescriptorSet(0, tonemappingDescriptor) }, + vkcv::PushConstants(0) + ); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + }); + + return 0; +} diff --git a/projects/path_tracer/.gitignore b/projects/path_tracer/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..24a57cda822232aa24d513fab3901ff7db36adb1 --- /dev/null +++ b/projects/path_tracer/.gitignore @@ -0,0 +1 @@ +path_tracer \ No newline at end of file diff --git a/projects/path_tracer/CMakeLists.txt b/projects/path_tracer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..56f14090ce68b91d0d7163360faaf413fb9e3de9 --- /dev/null +++ b/projects/path_tracer/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(path_tracer) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(path_tracer src/main.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(path_tracer SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(path_tracer vkcv vkcv_testing vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui) diff --git a/projects/path_tracer/README.md b/projects/path_tracer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0ff2970b686f7b8c8f24d6bb044566dd270b50b9 --- /dev/null +++ b/projects/path_tracer/README.md @@ -0,0 +1,10 @@ +# Path tracer +An example project to show the implementation of a path tracer with the VkCV framework + + + +## Details + +The project shows off an example implementation of a path tracer using compute shaders to render +a physically plausible image given an artificial subset of materials. The path tracer uses a +converging technique to reduce the noise of the image iteratively. diff --git a/projects/path_tracer/shaders/clearImage.comp b/projects/path_tracer/shaders/clearImage.comp new file mode 100644 index 0000000000000000000000000000000000000000..97998e945112d166be7d00df98ee44ea8322a633 --- /dev/null +++ b/projects/path_tracer/shaders/clearImage.comp @@ -0,0 +1,17 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0, rgba32f) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + ivec2 outImageRes = imageSize(outImage); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(coord, outImageRes))) + return; + + imageStore(outImage, coord, vec4(0)); +} \ No newline at end of file diff --git a/projects/path_tracer/shaders/combineImages.comp b/projects/path_tracer/shaders/combineImages.comp new file mode 100644 index 0000000000000000000000000000000000000000..d1a4e85caf175dfc3125afd847d7458ddec2fef1 --- /dev/null +++ b/projects/path_tracer/shaders/combineImages.comp @@ -0,0 +1,21 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0, rgba32f) uniform image2D newImage; +layout(set=0, binding=1, rgba32f) uniform image2D meanImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + ivec2 outImageRes = imageSize(meanImage); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(coord, outImageRes))) + return; + + vec4 colorNew = imageLoad(newImage, coord); + vec4 colorMean = imageLoad(meanImage, coord); + + imageStore(meanImage, coord, colorNew + colorMean); +} \ No newline at end of file diff --git a/projects/path_tracer/shaders/path_tracer.comp b/projects/path_tracer/shaders/path_tracer.comp new file mode 100644 index 0000000000000000000000000000000000000000..f08bdfd123ede964befe5feed4ba9f438dc0a498 --- /dev/null +++ b/projects/path_tracer/shaders/path_tracer.comp @@ -0,0 +1,430 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +const float pi = 3.1415926535897932384626433832795; +const float hitBias = 0.0001; // used to offset hits to avoid self intersection +const float denomMin = 0.001; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +struct Material { + vec3 emission; + float ks; // specular percentage + vec3 albedo; + float r; // roughness + vec3 f0; + float padding; +}; + +struct Sphere{ + vec3 center; + float radius; + int materialIndex; + float padding[3]; +}; + +struct Plane{ + vec3 center; + int materialIndex; + vec3 N; + float padding1; + vec2 extent; + vec2 padding2; +}; + +layout(std430, binding = 0) buffer spheres{ + Sphere inSpheres[]; +}; + +layout(std430, binding = 1) buffer planes{ + Plane inPlanes[]; +}; + +layout(std430, binding = 2) buffer materials{ + Material inMaterials[]; +}; + +layout(set=0, binding = 3, rgba32f) uniform image2D outImage; + +layout( push_constant ) uniform constants{ + mat4 viewToWorld; + vec3 skyColor; + int sphereCount; + int planeCount; + int frameIndex; +}; + +// ---- Intersection functions ---- + +struct Ray{ + vec3 origin; + vec3 direction; +}; + +struct Intersection{ + bool hit; + float distance; + vec3 pos; + vec3 N; + Material material; +}; + +// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection +Intersection raySphereIntersect(Ray ray, Sphere sphere){ + + Intersection intersection; + intersection.hit = false; + + vec3 L = sphere.center - ray.origin; + float tca = dot(L, ray.direction); + float d2 = dot(L, L) - tca * tca; + + if (d2 > sphere.radius * sphere.radius){ + return intersection; + } + float thc = float(sqrt(sphere.radius * sphere.radius - d2)); + float t0 = tca - thc; + float t1 = tca + thc; + + if (t0 < 0) + t0 = t1; + + if (t0 < 0) + return intersection; + + intersection.hit = true; + intersection.distance = t0; + intersection.pos = ray.origin + ray.direction * intersection.distance; + intersection.N = normalize(intersection.pos - sphere.center); + intersection.material = inMaterials[sphere.materialIndex]; + + return intersection; +} + +struct Basis{ + vec3 right; + vec3 up; + vec3 forward; +}; + +Basis buildBasisAroundNormal(vec3 N){ + Basis basis; + basis.up = N; + basis.right = abs(basis.up.x) < 0.99 ? vec3(1, 0, 0) : vec3(0, 0, 1); + basis.forward = normalize(cross(basis.up, basis.right)); + basis.right = cross(basis.up, basis.forward); + return basis; +} + +// see: https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection +Intersection rayPlaneIntersect(Ray ray, Plane plane){ + + Intersection intersection; + intersection.hit = false; + + vec3 toPlane = plane.center - ray.origin; + float denom = dot(ray.direction, plane.N); + if(abs(denom) < 0.001) + return intersection; + + intersection.distance = dot(toPlane, plane.N) / denom; + + if(intersection.distance < 0) + return intersection; + + intersection.pos = ray.origin + ray.direction * intersection.distance; + + vec3 centerToIntersection = intersection.pos - plane.center; + Basis planeBasis = buildBasisAroundNormal(plane.N); + float projectedRight = dot(centerToIntersection, planeBasis.right); + float projectedUp = dot(centerToIntersection, planeBasis.forward); + + intersection.hit = abs(projectedRight) <= plane.extent.x && abs(projectedUp) <= plane.extent.y; + intersection.N = plane.N; + intersection.material = inMaterials[plane.materialIndex]; + + return intersection; +} + +Intersection sceneIntersect(Ray ray) { + float minDistance = 100000; // lets start with something big + + Intersection intersection; + intersection.hit = false; + + for (int i = 0; i < sphereCount; i++) { + Intersection sphereIntersection = raySphereIntersect(ray, inSpheres[i]); + if (sphereIntersection.hit && sphereIntersection.distance < minDistance) { + intersection = sphereIntersection; + minDistance = intersection.distance; + } + } + for (int i = 0; i < planeCount; i++){ + Intersection planeIntersection = rayPlaneIntersect(ray, inPlanes[i]); + if (planeIntersection.hit && planeIntersection.distance < minDistance) { + intersection = planeIntersection; + minDistance = intersection.distance; + } + } + return intersection; +} + +vec3 biasHitPosition(vec3 hitPos, vec3 rayDirection, vec3 N){ + // return hitPos + N * hitBias; // works as long as no refraction/transmission is used and camera is outside sphere + return hitPos + sign(dot(rayDirection, N)) * N * hitBias; +} + +// ---- noise/hash functions for pseudorandom variables ---- + +// extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences +vec2 r2Sequence(uint n){ + n = n % 42000; + const float g = 1.32471795724474602596; + return fract(vec2( + n / g, + n / (g*g))); +} + +// random() and helpers from: https://www.shadertoy.com/view/XlycWh +float g_seed = 0; + +uint base_hash(uvec2 p) { + p = 1103515245U*((p >> 1U)^(p.yx)); + uint h32 = 1103515245U*((p.x)^(p.y>>3U)); + return h32^(h32 >> 16); +} + +vec2 hash2(inout float seed) { + uint n = base_hash(floatBitsToUint(vec2(seed+=.1,seed+=.1))); + uvec2 rz = uvec2(n, n*48271U); + return vec2(rz.xy & uvec2(0x7fffffffU))/float(0x7fffffff); +} + +void initRandom(ivec2 coord){ + g_seed = float(base_hash(coord)/float(0xffffffffU)+frameIndex); +} + +vec2 random(){ + return hash2(g_seed); +} + +// ---- shading ---- + +vec3 lambertBRDF(vec3 albedo){ + return albedo / pi; +} + +vec3 computeDiffuseBRDF(Material material){ + return lambertBRDF(material.albedo); +} + +float distributionGGX(float r, float NoH){ + float r2 = r*r; + float denom = pi * pow(NoH*NoH * (r2-1) + 1, 2); + return r2 / max(denom, denomMin); +} + +float geometryGGXSmith(float r, float NoL){ + float r2 = r*r; + float denom = NoL + sqrt(r2 + (1-r2) * NoL*NoL); + return 2 * NoL / max(denom, denomMin); +} + +float geometryGGX(float r, float NoV, float NoL){ + return geometryGGXSmith(r, NoV) * geometryGGXSmith(r, NoL); +} + +vec3 fresnelSchlick(vec3 f0, float NoH){ + return f0 + (1 - f0) * pow(1 - NoH, 5); +} + +vec3 computeSpecularBRDF(vec3 f0, float r, float NoV, float NoL, float NoH){ + float denom = 4 * NoV * NoL; + float D = distributionGGX(r, NoH); + float G = geometryGGX(r, NoV, NoL); + vec3 F = fresnelSchlick(f0, NoH); + return D * F * G / max(denom, denomMin); +} + +// ---- pathtracing and main ---- + +// distributions: https://link.springer.com/content/pdf/10.1007/978-1-4842-4427-2_16.pdf +float cosineDistributionPDF(float NoL){ + return NoL / pi; +} + +vec3 sampleCosineDistribution(vec2 xi){ + float phi = 2 * pi * xi.y; + return vec3( + sqrt(xi.x) * cos(phi), + sqrt(1 - xi.x), + sqrt(xi.x) * sin(phi)); +} + +float uniformDistributionPDF(){ + return 1.f / (2 * pi); +} + +vec3 sampleUniformDistribution(vec2 xi){ + float phi = 2 * pi * xi.y; + return vec3( + sqrt(xi.x) * cos(phi), + 1 - xi.x, + sqrt(xi.x) * sin(phi)); +} + +float ggxDistributionPDF(float r, float NoH){ + return distributionGGX(r, NoH) * NoH; +} + +float ggxDistributionPDFReflected(float r, float NoH, float NoV){ + float jacobian = 0.25 / max(NoV, denomMin); + return ggxDistributionPDF(r, NoH) * jacobian; +} + +vec3 sampleGGXDistribution(vec2 xi, float r){ + float phi = 2 * pi * xi.y; + float cosTheta = sqrt((1 - xi.x) / ((r*r - 1) * xi.x + 1)); + float sinTheta = sqrt(1 - cosTheta*cosTheta); + return vec3( + cos(phi) * sinTheta, + cosTheta, + sin(phi) * sinTheta); +} + +vec3 sampleTangentToWorldSpace(vec3 tangentSpaceSample, vec3 N){ + Basis tangentBasis = buildBasisAroundNormal(N); + return + tangentBasis.right * tangentSpaceSample.x + + tangentBasis.up * tangentSpaceSample.y + + tangentBasis.forward * tangentSpaceSample.z; +} + +vec3 castRay(Ray ray) { + + vec3 throughput = vec3(1); + vec3 color = vec3(0); + + const int maxDepth = 10; + for(int i = 0; i < maxDepth; i++){ + + Intersection intersection = sceneIntersect(ray); + + vec3 hitLighting = vec3(0); + vec3 brdf = vec3(1); + + // V is where the ray came from and will lead back to the camera (over multiple bounces) + vec3 V = -normalize(ray.direction); + vec3 R = reflect(-V, intersection.N); + float NoV = max(dot(intersection.N, V), 0); + + intersection.material.r *= intersection.material.r; // remapping for perceuptual linearity + intersection.material.r = max(intersection.material.r, 0.01); + + float kd = 1 - intersection.material.ks; + bool sampleDiffuse = random().x < kd; + + vec3 sampleTangentSpace; + float pdf; + if(sampleDiffuse){ + sampleTangentSpace = sampleCosineDistribution(random()); + ray.direction = sampleTangentToWorldSpace(sampleTangentSpace, intersection.N); + + float NoL = max(dot(intersection.N, ray.direction), 0); + pdf = cosineDistributionPDF(NoL); + } + else{ + #define IMPORTANCE + + #ifdef IMPORTANCE + sampleTangentSpace = sampleGGXDistribution(random(), intersection.material.r); + ray.direction = sampleTangentToWorldSpace(sampleTangentSpace, R); + vec3 L = normalize(ray.direction); + pdf = ggxDistributionPDFReflected(intersection.material.r, max(sampleTangentSpace.y, 0.01), max(dot(intersection.N, V), 0.01)); + #else + sampleTangentSpace = sampleUniformDistribution(random()); + ray.direction = sampleTangentToWorldSpace(sampleTangentSpace, intersection.N); + pdf = uniformDistributionPDF(); + #endif + } + + ray.origin = biasHitPosition(intersection.pos, ray.direction, intersection.N); + + // L is where the ray is going, as that is the direction where light will from + vec3 L = normalize(ray.direction); + vec3 H = normalize(L + V); + + float NoL = max(dot(intersection.N, L), 0); + float NoH = max(dot(intersection.N, H), 0); + + if(intersection.hit){ + vec3 diffuseBRDF = computeDiffuseBRDF(intersection.material); + + vec3 specularBRDF = computeSpecularBRDF(intersection.material.f0, intersection.material.r, NoV, NoL, NoH); + brdf = mix(diffuseBRDF, specularBRDF, intersection.material.ks); + + + hitLighting = intersection.material.emission * max(sign(NoV), 0); // objects only emit in direction of normal + } + else{ + hitLighting = skyColor; + } + + color += hitLighting * throughput; + throughput *= brdf * NoL / max(pdf, denomMin); + + if(!intersection.hit) + break; + } + + return color; +} + +// coord must be in pixel coordinates, but already shifted to pixel center +vec3 computeCameraRay(vec2 coord){ + + ivec2 outImageRes = imageSize(outImage); + float fovDegree = 45; + float fov = fovDegree * pi / 180; + + vec2 uv = coord / vec2(outImageRes); + vec2 ndc = 2 * uv - 1; + + float tanFovHalf = tan(fov / 2.f); + float aspectRatio = outImageRes.x / float(outImageRes.y); + float x = ndc.x * tanFovHalf * aspectRatio; + float y = -ndc.y * tanFovHalf; + + // view direction goes through pixel on image plane with z=1 + vec3 directionViewSpace = normalize(vec3(x, y, 1)); + vec3 directionWorldSpace = mat3(viewToWorld) * directionViewSpace; + return directionWorldSpace; +} + +void main(){ + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec2 pixelSize = 1.f / coord; + initRandom(coord); + + Ray cameraRay; + cameraRay.origin = viewToWorld[3].xyz; + vec2 coordCentered = coord + 0.5; + + vec3 color = vec3(0); + + const int samplesPerPixel = 1; + for(int i = 0; i < samplesPerPixel; i++){ + vec2 jitter = r2Sequence(i + frameIndex) - 0.5; + cameraRay.direction = computeCameraRay(coordCentered + jitter); + color += castRay(cameraRay); + } + color /= samplesPerPixel; + + vec4 final = vec4(color, 1); + + // occasional NaNs in reflection, should be fixed properly + if(any(isnan(color))) + final = vec4(0); + + imageStore(outImage, coord, final); +} \ No newline at end of file diff --git a/projects/path_tracer/shaders/presentImage.comp b/projects/path_tracer/shaders/presentImage.comp new file mode 100644 index 0000000000000000000000000000000000000000..a52159c0c6173779b091e5d4153b15b0a6361780 --- /dev/null +++ b/projects/path_tracer/shaders/presentImage.comp @@ -0,0 +1,23 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0, rgba32f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + ivec2 outImageRes = imageSize(outImage); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + + if(any(greaterThanEqual(coord, outImageRes))) + return; + + vec4 colorRaw = imageLoad(inImage, coord); + vec3 colorNormalized = colorRaw.rgb / colorRaw.a; + vec3 colorTonemapped = colorNormalized / (1 + dot(colorNormalized, vec3(0.71, 0.21, 0.08))); // reinhard tonemapping + vec3 colorGammaCorrected = pow(colorTonemapped, vec3(1.f / 2.2)); + + imageStore(outImage, coord, vec4(colorGammaCorrected, 0)); +} \ No newline at end of file diff --git a/projects/path_tracer/src/main.cpp b/projects/path_tracer/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..75ee36b2ac232e20d7baf29db34909dee67cca00 --- /dev/null +++ b/projects/path_tracer/src/main.cpp @@ -0,0 +1,443 @@ +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include "vkcv/gui/GUI.hpp" +#include <chrono> +#include <vector> + +int main(int argc, const char** argv) { + + // structs must match shader version + struct Material { + Material(const glm::vec3& emission, const glm::vec3& albedo, float ks, float roughness, const glm::vec3& f0) + : emission(emission), ks(ks), albedo(albedo), roughness(roughness), f0(f0), padding() {} + + glm::vec3 emission; + float ks; + glm::vec3 albedo; + float roughness; + glm::vec3 f0; + float padding; + }; + + struct Sphere { + Sphere(const glm::vec3& c, const float& r, const int m) + : center(c), radius(r), materialIndex(m), padding() {} + + glm::vec3 center; + float radius; + uint32_t materialIndex; + float padding[3]; + }; + + struct Plane { + Plane(const glm::vec3& c, const glm::vec3& n, const glm::vec2 e, int m) + : center(c), materialIndex(m), normal(n), padding1(), extent(e), padding3() {} + + glm::vec3 center; + uint32_t materialIndex; + glm::vec3 normal; + float padding1; + glm::vec2 extent; + glm::vec2 padding3; + }; + + const std::string applicationName = "Path Tracer"; + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + { "VK_KHR_swapchain" } + ); + + const int initialWidth = 1280; + const int initialHeight = 720; + + vkcv::WindowHandle windowHandle = core.createWindow( + applicationName, + initialWidth, + initialHeight, + true + ); + + vkcv::ImageConfig imageConfig ( + initialWidth, + initialHeight + ); + + imageConfig.setSupportingStorage(true); + + // images + vkcv::ImageHandle outputImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + imageConfig + ); + + vkcv::ImageHandle meanImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + imageConfig + ); + + vkcv::shader::GLSLCompiler compiler; + + // path tracing shader + vkcv::ShaderProgram traceShaderProgram{}; + + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/path_tracer.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + traceShaderProgram.addShader(shaderStage, path); + }); + + const vkcv::DescriptorBindings& traceDescriptorBindings = traceShaderProgram.getReflectedDescriptors().at(0); + vkcv::DescriptorSetLayoutHandle traceDescriptorSetLayout = core.createDescriptorSetLayout(traceDescriptorBindings); + vkcv::DescriptorSetHandle traceDescriptorSet = core.createDescriptorSet(traceDescriptorSetLayout); + + // image combine shader + vkcv::ShaderProgram imageCombineShaderProgram{}; + + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/combineImages.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + imageCombineShaderProgram.addShader(shaderStage, path); + }); + + const vkcv::DescriptorBindings& imageCombineDescriptorBindings = imageCombineShaderProgram.getReflectedDescriptors().at(0); + vkcv::DescriptorSetLayoutHandle imageCombineDescriptorSetLayout = core.createDescriptorSetLayout(imageCombineDescriptorBindings); + vkcv::DescriptorSetHandle imageCombineDescriptorSet = core.createDescriptorSet(imageCombineDescriptorSetLayout); + vkcv::ComputePipelineHandle imageCombinePipeline = core.createComputePipeline({ + imageCombineShaderProgram, + { imageCombineDescriptorSetLayout } + }); + + vkcv::DescriptorWrites imageCombineDescriptorWrites; + imageCombineDescriptorWrites.writeStorageImage(0, outputImage).writeStorageImage(1, meanImage); + core.writeDescriptorSet(imageCombineDescriptorSet, imageCombineDescriptorWrites); + + // image present shader + vkcv::ShaderProgram presentShaderProgram{}; + + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/presentImage.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + presentShaderProgram.addShader(shaderStage, path); + }); + + const vkcv::DescriptorBindings& presentDescriptorBindings = presentShaderProgram.getReflectedDescriptors().at(0); + vkcv::DescriptorSetLayoutHandle presentDescriptorSetLayout = core.createDescriptorSetLayout(presentDescriptorBindings); + vkcv::DescriptorSetHandle presentDescriptorSet = core.createDescriptorSet(presentDescriptorSetLayout); + vkcv::ComputePipelineHandle presentPipeline = core.createComputePipeline({ + presentShaderProgram, + { presentDescriptorSetLayout } + }); + + // clear shader + vkcv::ShaderProgram clearShaderProgram{}; + + compiler.compile(vkcv::ShaderStage::COMPUTE, "shaders/clearImage.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + clearShaderProgram.addShader(shaderStage, path); + }); + + const vkcv::DescriptorBindings& imageClearDescriptorBindings = clearShaderProgram.getReflectedDescriptors().at(0); + vkcv::DescriptorSetLayoutHandle imageClearDescriptorSetLayout = core.createDescriptorSetLayout(imageClearDescriptorBindings); + vkcv::DescriptorSetHandle imageClearDescriptorSet = core.createDescriptorSet(imageClearDescriptorSetLayout); + vkcv::ComputePipelineHandle imageClearPipeline = core.createComputePipeline({ + clearShaderProgram, + { imageClearDescriptorSetLayout } + }); + + vkcv::DescriptorWrites imageClearDescriptorWrites; + imageClearDescriptorWrites.writeStorageImage(0, meanImage); + core.writeDescriptorSet(imageClearDescriptorSet, imageClearDescriptorWrites); + + // buffers + typedef std::pair<std::string, Material> MaterialSetting; + + std::vector<MaterialSetting> materialSettings; + materialSettings.emplace_back(MaterialSetting("white", Material(glm::vec3(0), glm::vec3(0.65), 0, 0.25, glm::vec3(0.04)))); + materialSettings.emplace_back(MaterialSetting("red", Material(glm::vec3(0), glm::vec3(0.5, 0.0, 0.0), 0, 0.25, glm::vec3(0.04)))); + materialSettings.emplace_back(MaterialSetting("green", Material(glm::vec3(0), glm::vec3(0.0, 0.5, 0.0), 0, 0.25, glm::vec3(0.04)))); + materialSettings.emplace_back(MaterialSetting("light", Material(glm::vec3(20), glm::vec3(0), 0, 0.25, glm::vec3(0.04)))); + materialSettings.emplace_back(MaterialSetting("sphere", Material(glm::vec3(0), glm::vec3(0.65), 1, 0.25, glm::vec3(0.04)))); + materialSettings.emplace_back(MaterialSetting("ground", Material(glm::vec3(0), glm::vec3(0.65), 0, 0.25, glm::vec3(0.04)))); + + const uint32_t whiteMaterialIndex = 0; + const uint32_t redMaterialIndex = 1; + const uint32_t greenMaterialIndex = 2; + const uint32_t lightMaterialIndex = 3; + const uint32_t sphereMaterialIndex = 4; + const uint32_t groundMaterialIndex = 5; + + std::vector<Sphere> spheres; + spheres.emplace_back(Sphere(glm::vec3(0, -1.5, 0), 0.5, sphereMaterialIndex)); + + std::vector<Plane> planes; + planes.emplace_back(Plane(glm::vec3( 0, -2, 0), glm::vec3( 0, 1, 0), glm::vec2(2), groundMaterialIndex)); + planes.emplace_back(Plane(glm::vec3( 0, 2, 0), glm::vec3( 0, -1, 0), glm::vec2(2), whiteMaterialIndex)); + planes.emplace_back(Plane(glm::vec3( 2, 0, 0), glm::vec3(-1, 0, 0), glm::vec2(2), redMaterialIndex)); + planes.emplace_back(Plane(glm::vec3(-2, 0, 0), glm::vec3( 1, 0, 0), glm::vec2(2), greenMaterialIndex)); + planes.emplace_back(Plane(glm::vec3( 0, 0, 2), glm::vec3( 0, 0, -1), glm::vec2(2), whiteMaterialIndex)); + planes.emplace_back(Plane(glm::vec3( 0, 1.9, 0), glm::vec3( 0, -1, 0), glm::vec2(1), lightMaterialIndex)); + + vkcv::Buffer<Sphere> sphereBuffer = vkcv::buffer<Sphere>( + core, + vkcv::BufferType::STORAGE, + spheres.size()); + sphereBuffer.fill(spheres); + + vkcv::Buffer<Plane> planeBuffer = vkcv::buffer<Plane>( + core, + vkcv::BufferType::STORAGE, + planes.size()); + planeBuffer.fill(planes); + + vkcv::Buffer<Material> materialBuffer = vkcv::buffer<Material>( + core, + vkcv::BufferType::STORAGE, + materialSettings.size()); + + vkcv::DescriptorWrites traceDescriptorWrites; + traceDescriptorWrites.writeStorageBuffer( + 0, sphereBuffer.getHandle() + ).writeStorageBuffer( + 1, planeBuffer.getHandle() + ).writeStorageBuffer( + 2, materialBuffer.getHandle() + ); + + traceDescriptorWrites.writeStorageImage(3, outputImage); + core.writeDescriptorSet(traceDescriptorSet, traceDescriptorWrites); + + vkcv::ComputePipelineHandle tracePipeline = core.createComputePipeline({ + traceShaderProgram, + { traceDescriptorSetLayout } + }); + + if (!tracePipeline) + { + vkcv_log(vkcv::LogLevel::ERROR, "Could not create graphics pipeline. Exiting."); + return EXIT_FAILURE; + } + + vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle)); + auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + + cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -2)); + + int frameIndex = 0; + bool clearMeanImage = true; + bool updateMaterials = true; + + float cameraPitchPrevious = 0; + float cameraYawPrevious = 0; + glm::vec3 cameraPositionPrevious = glm::vec3(0); + + uint32_t widthPrevious = initialWidth; + uint32_t heightPrevious = initialHeight; + + vkcv::gui::GUI gui(core, windowHandle); + + bool renderUI = true; + core.getWindow(windowHandle).e_key.add([&renderUI](int key, int scancode, int action, int mods) { + if (key == GLFW_KEY_I && action == GLFW_PRESS) { + renderUI = !renderUI; + } + }); + + glm::vec3 skyColor = glm::vec3(0.2, 0.7, 0.8); + float skyColorMultiplier = 1; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((swapchainWidth != widthPrevious) || (swapchainHeight != heightPrevious)) { + imageConfig.setWidth(swapchainWidth); + imageConfig.setHeight(swapchainHeight); + + // resize images + outputImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + imageConfig + ); + + meanImage = core.createImage( + vk::Format::eR32G32B32A32Sfloat, + imageConfig + ); + + // update descriptor sets + traceDescriptorWrites.writeStorageImage(3, outputImage); + core.writeDescriptorSet(traceDescriptorSet, traceDescriptorWrites); + + vkcv::DescriptorWrites imageCombineDescriptorWrites; + imageCombineDescriptorWrites.writeStorageImage( + 0, outputImage + ).writeStorageImage( + 1, meanImage + ); + + core.writeDescriptorSet(imageCombineDescriptorSet, imageCombineDescriptorWrites); + + vkcv::DescriptorWrites imageClearDescriptorWrites; + imageClearDescriptorWrites.writeStorageImage(0, meanImage); + core.writeDescriptorSet(imageClearDescriptorSet, imageClearDescriptorWrites); + + widthPrevious = swapchainWidth; + heightPrevious = swapchainHeight; + + clearMeanImage = true; + } + + cameraManager.update(dt); + + const vkcv::CommandStreamHandle cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + const auto fullscreenDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(8, 8) + ); + + if (updateMaterials) { + std::vector<Material> materials; + for (const auto& settings : materialSettings) { + materials.push_back(settings.second); + } + materialBuffer.fill(materials); + updateMaterials = false; + clearMeanImage = true; + } + + float cameraPitch; + float cameraYaw; + cameraManager.getActiveCamera().getAngles(cameraPitch, cameraYaw); + + if (glm::abs(cameraPitch - cameraPitchPrevious) > 0.01 || glm::abs(cameraYaw - cameraYawPrevious) > 0.01) + clearMeanImage = true; // camera rotated + + cameraPitchPrevious = cameraPitch; + cameraYawPrevious = cameraYaw; + + glm::vec3 cameraPosition = cameraManager.getActiveCamera().getPosition(); + + if(glm::distance(cameraPosition, cameraPositionPrevious) > 0.0001) + clearMeanImage = true; // camera moved + + cameraPositionPrevious = cameraPosition; + + if (clearMeanImage) { + core.prepareImageForStorage(cmdStream, meanImage); + + core.recordComputeDispatchToCmdStream(cmdStream, + imageClearPipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, imageClearDescriptorSet) }, + vkcv::PushConstants(0)); + + clearMeanImage = false; + } + + // path tracing + struct RaytracingPushConstantData { + glm::mat4 viewToWorld; + glm::vec3 skyColor; + int32_t sphereCount; + int32_t planeCount; + int32_t frameIndex; + }; + + RaytracingPushConstantData raytracingPushData; + raytracingPushData.viewToWorld = glm::inverse(cameraManager.getActiveCamera().getView()); + raytracingPushData.skyColor = skyColor * skyColorMultiplier; + raytracingPushData.sphereCount = spheres.size(); + raytracingPushData.planeCount = planes.size(); + raytracingPushData.frameIndex = frameIndex; + + vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<RaytracingPushConstantData>(); + pushConstantsCompute.appendDrawcall(raytracingPushData); + + const auto traceDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(16, 16) + ); + + core.prepareImageForStorage(cmdStream, outputImage); + + core.recordComputeDispatchToCmdStream(cmdStream, + tracePipeline, + traceDispatchCount, + { vkcv::useDescriptorSet(0, traceDescriptorSet) }, + pushConstantsCompute); + + core.prepareImageForStorage(cmdStream, meanImage); + core.recordImageMemoryBarrier(cmdStream, outputImage); + + // combine images + core.recordComputeDispatchToCmdStream(cmdStream, + imageCombinePipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, imageCombineDescriptorSet) }, + vkcv::PushConstants(0)); + + core.recordImageMemoryBarrier(cmdStream, meanImage); + + // present image + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::DescriptorWrites presentDescriptorWrites; + presentDescriptorWrites.writeStorageImage( + 0, meanImage + ).writeStorageImage( + 1, swapchainInput + ); + + core.writeDescriptorSet(presentDescriptorSet, presentDescriptorWrites); + + core.prepareImageForStorage(cmdStream, swapchainInput); + + core.recordComputeDispatchToCmdStream(cmdStream, + presentPipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, presentDescriptorSet) }, + vkcv::PushConstants(0)); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + + if (renderUI) { + gui.beginGUI(); + + ImGui::Begin("Settings"); + + clearMeanImage |= ImGui::ColorEdit3("Sky color", &skyColor.x); + clearMeanImage |= ImGui::InputFloat("Sky color multiplier", &skyColorMultiplier); + + if (ImGui::CollapsingHeader("Materials")) { + + for (auto& setting : materialSettings) { + if (ImGui::CollapsingHeader(setting.first.c_str())) { + + const glm::vec3 emission = setting.second.emission; + float emissionStrength = glm::max(glm::max(glm::max(emission.x, emission.y), emission.z), 1.f); + glm::vec3 emissionColor = emission / emissionStrength; + + updateMaterials |= ImGui::ColorEdit3((std::string("Emission color ") + setting.first).c_str(), &emissionColor.x); + updateMaterials |= ImGui::InputFloat((std::string("Emission strength ") + setting.first).c_str(), &emissionStrength); + + setting.second.emission = emissionStrength * emissionColor; + + updateMaterials |= ImGui::ColorEdit3((std::string("Albedo color ") + setting.first).c_str(), &setting.second.albedo.x); + updateMaterials |= ImGui::ColorEdit3((std::string("F0 ") + setting.first).c_str(), &setting.second.f0.x); + updateMaterials |= ImGui::DragFloat(( std::string("ks ") + setting.first).c_str(), &setting.second.ks, 0.01, 0, 1); + updateMaterials |= ImGui::DragFloat(( std::string("roughness ") + setting.first).c_str(), &setting.second.roughness, 0.01, 0, 1); + + } + } + } + + ImGui::End(); + + gui.endGUI(); + } + + frameIndex++; + }); + + return 0; +} diff --git a/projects/ray_tracer/.gitignore b/projects/ray_tracer/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b9f0cf28bbc12617183c19ebe240a753d741158c --- /dev/null +++ b/projects/ray_tracer/.gitignore @@ -0,0 +1 @@ +ray_tracer \ No newline at end of file diff --git a/projects/ray_tracer/CMakeLists.txt b/projects/ray_tracer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8c8d80b45e2c0f1ec160d509b194b8339da1205f --- /dev/null +++ b/projects/ray_tracer/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.16) +project(ray_tracer) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(ray_tracer src/main.cpp "src/scene.hpp") + +# including headers of dependencies and the VkCV framework +target_include_directories(ray_tracer SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_testing_include} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(ray_tracer vkcv vkcv_testing vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler) diff --git a/projects/ray_tracer/README.md b/projects/ray_tracer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..69094cb98fd8a2c7d95f5b20fdac4966fb8e94a5 --- /dev/null +++ b/projects/ray_tracer/README.md @@ -0,0 +1,11 @@ +# Ray tracer +An example project to show the implementation of a ray tracer with the VkCV framework + + + +## Details + +The project shows off an example implementation of a ray tracer using compute shaders to render +an image containing abstract geometry and materials with different properties. The ray tracer +limits the maximal depth to allow highly detailed reflections without getting too slow for +realtime use. diff --git a/projects/ray_tracer/shaders/raytracing.comp b/projects/ray_tracer/shaders/raytracing.comp new file mode 100644 index 0000000000000000000000000000000000000000..a7c6b92a646e5c2f753946f74fe7ab78aea44fe6 --- /dev/null +++ b/projects/ray_tracer/shaders/raytracing.comp @@ -0,0 +1,292 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable + +// defines constants +const float pi = 3.1415926535897932384626433832795; +const float hitBias = 0.01; // used to offset hits to avoid self intersection + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +//structs of materials, lights, spheres and intersection for use in compute shader +struct Material { + vec3 albedo; + vec3 diffuseColor; + float specularExponent; + float refractiveIndex; +}; + +struct Light{ + vec3 position; + float intensity; +}; + +struct Sphere{ + vec3 center; + float radius; + Material material; +}; + +struct Intersection{ + bool hit; + vec3 pos; + vec3 N; + Material material; +}; + + +//incoming light data +layout(std430, binding = 0) coherent buffer lights{ + Light inLights[]; +}; + +// incoming sphere data +layout(std430, binding = 1) coherent buffer spheres{ + Sphere inSpheres[]; +}; + +// output store image as swapchain input +layout(set=0, binding = 2, rgba8) uniform image2D outImage; + +// incoming constants, because size of dynamic arrays cannot be computed on gpu +layout( push_constant ) uniform constants{ + mat4 viewToWorld; + int lightCount; + int sphereCount; +}; + +/* +* safrReflect computes the new reflected or refracted ray depending on the material +* @param vec3: raydirection vector +* @param vec3: normalvector on which should be reflected or refracted +* @param float: degree of refraction. In case of simple reflection it is 1.0 +* @return vec3: new ray that is the result of the reflection or refraction +*/ +vec3 safrReflect(vec3 V, vec3 N, float refractIndex){ + if(refractIndex != 1.0){ + // Snell's law + float cosi = - max(-1.f, min(1.f, dot(V,N))); + float etai = 1; + float etat = refractIndex; + vec3 n = N; + float swap; + if(cosi < 0){ + cosi = -cosi; + n = -N; + swap = etai; + etai = etat; + etat = swap; + } + float eta = etai / etat; + float k = 1 - eta * eta * (1 - cosi * cosi); + if(k < 0){ + return vec3(0,0,0); + } else { + return V * eta + n * (eta * cosi - sqrt(k)); + } + }else{ + return reflect(V, N); + } +} + +/* +* the rayIntersect function checks, if a ray from the raytracer passes through the sphere, hits the sphere or passes by the the sphere +* @param vec3: origin of ray +* @param vec3: direction of ray +* @param float: distance of the ray to the sphere (out because there are no references in shaders) +* @return bool: if ray interesects sphere or not (out because there are no references in shaders) +*/ + +bool rayIntersect(const vec3 origin, const vec3 dir, out float t0, const int id){ + vec3 L = inSpheres[id].center - origin; + float tca = dot(L, dir); + float d2 = dot(L, L) - tca * tca; + if (d2 > inSpheres[id].radius * inSpheres[id].radius){ + return false; + } + float thc = float(sqrt(inSpheres[id].radius * inSpheres[id].radius - d2)); + t0 = tca - thc; + float t1 = tca + thc; + if (t0 < 0) { + t0 = t1; + } + if (t0 < 0){ + return false; + } + return true; +} + +/* +* sceneIntersect iterates over whole scene (over every single object) to check for intersections +* @param vec3: Origin of the ray +* @param vec3: direction of the ray +* @return: Intersection struct with hit(bool) position, normal and material of sphere +*/ + +Intersection sceneIntersect(const vec3 rayOrigin, const vec3 rayDirection) { + //distance if spheres will be rendered + float min_d = 1.0 / 0.0; // lets start with something big + + Intersection intersection; + intersection.hit = false; + + //go over every sphere, check if sphere is hit by ray, save if hit is near enough into intersection struct + for (int i = 0; i < sphereCount; i++) { + float d; + if (rayIntersect(rayOrigin, rayDirection, d, i)) { + + intersection.hit = true; + + if(d < min_d){ + min_d = d; + intersection.pos = rayOrigin + rayDirection * d; + intersection.N = normalize(intersection.pos - inSpheres[i].center); + intersection.material = inSpheres[i].material; + } + } + } + + float checkerboard_dist = min_d; + if (abs(rayDirection.y)>1e-3) { + float d = -(rayOrigin.y + 4) / rayDirection.y; // the checkerboard plane has equation y = -4 + vec3 pt = rayOrigin + rayDirection * d; + if (d > 0 && abs(pt.x) < 10 && pt.z<-10 && pt.z>-30 && d < min_d) { + checkerboard_dist = d; + intersection.hit = true; + intersection.pos = pt; + intersection.N = vec3(0, 1, 0); + intersection.material = inSpheres[0].material; + } + } + return intersection; +} + +/* +* biasHitPosition computes the new hitposition with respect to the raydirection and a bias +* @param vec3: Hit Position +* @param vec3: direction of ray +* @param vec3: N(ormal) +* @return vec3: new Hit position depending on hitBias (used to offset hits to avoid self intersection) +*/ +vec3 biasHitPosition(vec3 hitPos, vec3 rayDirection, vec3 N){ + return hitPos + sign(dot(rayDirection, N)) * N * hitBias; +} + +/* +* computeHitLighting iterates over all lights to compute the color for every ray +* @param Intersection: struct with all the data of the intersection +* @param vec3: Raydirection +* @param float: material albedo of the intersection +* @return colour/shadows of sphere with illumination +*/ +vec3 computeHitLighting(Intersection intersection, vec3 V, out float outReflectionThroughput){ + + float lightIntensityDiffuse = 0; + float lightIntensitySpecular = 0; + + //iterate over every light source to compute sphere colours/shadows + for (int i = 0; i < lightCount; i++) { + + //compute normal + distance between light and intersection + vec3 L = normalize(inLights[i].position - intersection.pos); + float d = distance(inLights[i].position, intersection.pos); + + //compute shadows + vec3 shadowOrigin = biasHitPosition(intersection.pos, L, intersection.N); + Intersection shadowIntersection = sceneIntersect(shadowOrigin, L); + bool isShadowed = false; + if(shadowIntersection.hit){ + isShadowed = distance(shadowIntersection.pos, shadowOrigin) < d; + } + if(isShadowed){ + continue; + } + + lightIntensityDiffuse += inLights[i].intensity * max(0.f, dot(L, intersection.N)); + lightIntensitySpecular += pow(max(0.f, dot(safrReflect(V, intersection.N, intersection.material.refractiveIndex), L)), intersection.material.specularExponent) * inLights[i].intensity; + } + + outReflectionThroughput = intersection.material.albedo[2]; + return intersection.material.diffuseColor * lightIntensityDiffuse * intersection.material.albedo[0] + lightIntensitySpecular * intersection.material.albedo[1]; +} + +/* +* castRay throws a ray out of the initial origin with respect to the initial direction checks for intersection and refelction +* @param vec3: initial origin of ray +* @param vec3: initial direction of ray +* @param int: max depth o ray reflection +* @return s +*/ + +vec3 castRay(const vec3 initialOrigin, const vec3 initialDirection, int max_depth) { + + vec3 skyColor = vec3(0.2, 0.7, 0.8); + vec3 rayOrigin = initialOrigin; + vec3 rayDirection = initialDirection; + + float reflectionThroughput = 1; + vec3 color = vec3(0); + + //iterate to max depth of reflections + for(int i = 0; i < max_depth; i++){ + + Intersection intersection = sceneIntersect(rayOrigin, rayDirection); + + vec3 hitColor; + float hitReflectionThroughput; + + if(intersection.hit){ + hitColor = computeHitLighting(intersection, rayDirection, hitReflectionThroughput); + }else{ + hitColor = skyColor; + } + + color += hitColor * reflectionThroughput; + reflectionThroughput *= hitReflectionThroughput; + + //if there is no intersection of a ray with a sphere, break out of the loop + if(!intersection.hit){ + break; + } + + //compute new direction and origin of the reflected ray + rayDirection = normalize(safrReflect(rayDirection, intersection.N, intersection.material.refractiveIndex)); + rayOrigin = biasHitPosition(intersection.pos, rayDirection, intersection.N); + } + + return color; +} + +/* +* computeDirection transforms the pixel coords to worldspace coords +* @param ivec2: pixel coordinates +* @return vec3: world coordinates +*/ +vec3 computeDirection(ivec2 coord){ + + ivec2 outImageRes = imageSize(outImage); + float fovDegree = 45; + float fov = fovDegree * pi / 180; + + vec2 uv = coord / vec2(outImageRes); + vec2 ndc = 2 * uv - 1; + + float tanFovHalf = tan(fov / 2.f); + float aspectRatio = outImageRes.x / float(outImageRes.y); + float x = ndc.x * tanFovHalf * aspectRatio; + float y = -ndc.y * tanFovHalf; + + vec3 directionViewSpace = normalize(vec3(x, y, 1)); + vec3 directionWorldSpace = mat3(viewToWorld) * directionViewSpace; + return directionWorldSpace; +} + +// the main function +void main(){ + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + int max_depth = 4; + vec3 direction = computeDirection(coord); + vec3 cameraPos = viewToWorld[3].xyz; + vec3 color = castRay(cameraPos, direction, max_depth); + + imageStore(outImage, coord, vec4(color, 0.f)); +} \ No newline at end of file diff --git a/projects/ray_tracer/src/main.cpp b/projects/ray_tracer/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a906d5d8141148acbbf767163fb173f8053d466 --- /dev/null +++ b/projects/ray_tracer/src/main.cpp @@ -0,0 +1,235 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <vkcv/Buffer.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Sampler.hpp> + +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +#include <cmath> +#include <vector> +#include <cstring> +#include <GLFW/glfw3.h> + +#include "scene.hpp" + +void createQuadraticLightCluster(std::vector<scene::Light>& lights, int countPerDimension, float dimension, float height, float intensity) { + float distance = dimension/countPerDimension; + + for(int x = 0; x <= countPerDimension; x++) { + for (int z = 0; z <= countPerDimension; z++) { + lights.push_back(scene::Light(glm::vec3(x * distance, height, z * distance), + float (intensity/countPerDimension) / 10.f) // Divide by 10, because intensity is busting O.o + ); + } + } + +} + +int main(int argc, const char** argv) { + const std::string applicationName = "Ray tracer"; + + //window creation + const int windowWidth = 800; + const int windowHeight = 600; + + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + { "VK_KHR_swapchain" } + ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + + std::string shaderPathCompute = "shaders/raytracing.comp"; + + //creating the shader programs + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram computeShaderProgram; + + compiler.compile(vkcv::ShaderStage::COMPUTE, shaderPathCompute, [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + computeShaderProgram.addShader(shaderStage, path); + }); + + vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + const vkcv::DescriptorBindings& computeDescriptorBindings = computeShaderProgram.getReflectedDescriptors().at(0); + + vkcv::DescriptorSetLayoutHandle computeDescriptorSetLayout = core.createDescriptorSetLayout(computeDescriptorBindings); + vkcv::DescriptorSetHandle computeDescriptorSet = core.createDescriptorSet(computeDescriptorSetLayout); + + const std::vector<vkcv::VertexAttachment> computeVertexAttachments = computeShaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> computeBindings; + for (size_t i = 0; i < computeVertexAttachments.size(); i++) { + computeBindings.push_back(vkcv::createVertexBinding(i, { computeVertexAttachments[i] })); + } + const vkcv::VertexLayout computeLayout { computeBindings }; + + /* + * create the scene + */ + + //materials for the spheres + std::vector<scene::Material> materials; + scene::Material ivory(glm::vec4(0.6, 0.3, 0.1, 0.0), glm::vec3(0.4, 0.4, 0.3), 50., 1.0); + scene::Material red_rubber(glm::vec4(0.9, 0.1, 0.0, 0.0), glm::vec3(0.3, 0.1, 0.1), 10., 1.0); + scene::Material mirror(glm::vec4(0.0, 10.0, 0.8, 0.0), glm::vec3(1.0, 1.0, 1.0), 1425., 1.0); + scene::Material glass(glm::vec4(0.0, 10.0, 0.8, 0.0), glm::vec3(1.0, 1.0, 1.0), 1425., 1.5); + + materials.push_back(ivory); + materials.push_back(red_rubber); + materials.push_back(mirror); + + //spheres for the scene + std::vector<scene::Sphere> spheres; + spheres.push_back(scene::Sphere(glm::vec3(-3, 0, -16), 2, ivory)); + //spheres.push_back(safrScene::Sphere(glm::vec3(-1.0, -1.5, 12), 2, mirror)); + spheres.push_back(scene::Sphere(glm::vec3(-1.0, -1.5, -12), 2, glass)); + spheres.push_back(scene::Sphere(glm::vec3(1.5, -0.5, -18), 3, red_rubber)); + spheres.push_back(scene::Sphere(glm::vec3(7, 5, -18), 4, mirror)); + + //lights for the scene + std::vector<scene::Light> lights; + /* + lights.push_back(safrScene::Light(glm::vec3(-20, 20, 20), 1.5)); + lights.push_back(safrScene::Light(glm::vec3(30, 50, -25), 1.8)); + lights.push_back(safrScene::Light(glm::vec3(30, 20, 30), 1.7)); + */ + createQuadraticLightCluster(lights, 10, 2.5f, 20, 1.5f); + + vkcv::SamplerHandle sampler = vkcv::samplerLinear(core); + + //create Buffer for compute shader + vkcv::Buffer<scene::Light> lightsBuffer = vkcv::buffer<scene::Light>( + core, + vkcv::BufferType::STORAGE, + lights.size() + ); + lightsBuffer.fill(lights); + + vkcv::Buffer<scene::Sphere> sphereBuffer = vkcv::buffer<scene::Sphere>( + core, + vkcv::BufferType::STORAGE, + spheres.size() + ); + sphereBuffer.fill(spheres); + + vkcv::DescriptorWrites computeWrites; + computeWrites.writeStorageBuffer( + 0, lightsBuffer.getHandle() + ).writeStorageBuffer( + 1, sphereBuffer.getHandle() + ); + + core.writeDescriptorSet(computeDescriptorSet, computeWrites); + + auto safrIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3); + uint16_t indices[3] = { 0, 1, 2 }; + safrIndexBuffer.fill(&indices[0], sizeof(indices)); + + vkcv::PassHandle safrPass = vkcv::passSwapchain( + core, + window.getSwapchain(), + { vk::Format::eUndefined } + ); + + if (!safrPass) { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + + vkcv::ComputePipelineHandle computePipeline = core.createComputePipeline( + vkcv::ComputePipelineConfig( + computeShaderProgram, + { computeDescriptorSetLayout } + ) + ); + + if (!computePipeline) { + std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + //create the camera + vkcv::camera::CameraManager cameraManager(window); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(0, 0, 2)); + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0, 0, -4)); + cameraManager.getCamera(camHandle1).setCenter(glm::vec3(0.0f, 0.0f, -1.0f)); + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + //adjust light position + /* + 639a53157e7d3936caf7c3e40379159cbcf4c89e + lights[0].position.x += std::cos(time * 3.0f) * 2.5f; + lights[1].position.z += std::cos(time * 2.5f) * 3.0f; + lights[2].position.y += std::cos(time * 1.5f) * 4.0f; + lightsBuffer.fill(lights); + */ + + spheres[0].center.y += std::cos(t * 0.5f * 3.141f) * 0.25f; + spheres[1].center.x += std::cos(t * 2.f) * 0.25f; + spheres[1].center.z += std::cos(t * 2.f + 0.5f * 3.141f) * 0.25f; + sphereBuffer.fill(spheres); + + //update camera + cameraManager.update(dt); + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); + glm::mat4 proj = cameraManager.getActiveCamera().getProjection(); + + //create pushconstants for render + vkcv::PushConstants pushConstants(sizeof(glm::mat4) * 2); + pushConstants.appendDrawcall(std::array<glm::mat4, 2>{ mvp, proj }); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + //configure the outImage for compute shader (render into the swapchain image) + computeWrites.writeStorageImage(2, swapchainInput); + core.writeDescriptorSet(computeDescriptorSet, computeWrites); + core.prepareImageForStorage (cmdStream, swapchainInput); + + //fill pushconstants for compute shader + struct RaytracingPushConstantData { + glm::mat4 viewToWorld; + int32_t lightCount; + int32_t sphereCount; + }; + + RaytracingPushConstantData raytracingPushData; + raytracingPushData.lightCount = lights.size(); + raytracingPushData.sphereCount = spheres.size(); + raytracingPushData.viewToWorld = glm::inverse(cameraManager.getActiveCamera().getView()); + + vkcv::PushConstants pushConstantsCompute = vkcv::pushConstants<RaytracingPushConstantData>(); + pushConstantsCompute.appendDrawcall(raytracingPushData); + + //dispatch compute shader + const auto computeDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(16, 16) + ); + + core.recordComputeDispatchToCmdStream(cmdStream, + computePipeline, + computeDispatchCount, + { vkcv::useDescriptorSet(0, computeDescriptorSet) }, + pushConstantsCompute + ); + + core.recordBufferMemoryBarrier(cmdStream, lightsBuffer.getHandle()); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + }); + + return 0; +} diff --git a/projects/ray_tracer/src/scene.hpp b/projects/ray_tracer/src/scene.hpp new file mode 100644 index 0000000000000000000000000000000000000000..be1cee22d21fc4009c390f629f6118071502fe47 --- /dev/null +++ b/projects/ray_tracer/src/scene.hpp @@ -0,0 +1,51 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <GLFW/glfw3.h> +#include <vkcv/camera/CameraManager.hpp> +#include <vkcv/asset/asset_loader.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <chrono> +#include <limits> +#include <cmath> +#include <vector> +#include <string.h> // memcpy(3) + +class scene { + +public: + + /* + * Light struct with a position and intensity of the light source + */ + struct Light { + Light(const glm::vec3& p, const float& i) : position(p), intensity(i) {} + glm::vec3 position; + float intensity; + }; + + /* + * Material struct with defuse color, albedo and specular component + */ + struct Material { + Material(const glm::vec4& a, const glm::vec3& color, const float& spec, const float& r) : albedo(a), diffuse_color(color), specular_exponent(spec), refractive_index(r) {} + Material() : albedo(1, 0, 0, 0), diffuse_color(), specular_exponent(), refractive_index(1) {} + glm::vec4 albedo; + alignas(16) glm::vec3 diffuse_color; + float specular_exponent; + float refractive_index; + }; + + /* + * the sphere is defined by it's center, the radius and the material + */ + struct Sphere { + glm::vec3 center; + float radius; + Material material; + + Sphere(const glm::vec3& c, const float& r, const Material& m) : center(c), radius(r), material(m) {} + + }; + + +}; \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/.gitignore b/projects/rtx_ambient_occlusion/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..61b2ff94710af817f199d848e4253062c024a6bf --- /dev/null +++ b/projects/rtx_ambient_occlusion/.gitignore @@ -0,0 +1 @@ +rtx_ambient_occlusion \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/CMakeLists.txt b/projects/rtx_ambient_occlusion/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2c784a4f173ea5d6e0de1df1a2cfe514a5787153 --- /dev/null +++ b/projects/rtx_ambient_occlusion/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.16) +project(rtx_ambient_occlusion) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(rtx_source ${PROJECT_SOURCE_DIR}/src/RTX) + +set(rtx_sources + ${rtx_source}/RTX.hpp + ${rtx_source}/RTX.cpp + + ${rtx_source}/ASManager.hpp + ${rtx_source}/ASManager.cpp + + ${rtx_source}/RTXExtensions.hpp + ${rtx_source}/RTXExtensions.cpp +) + +# adding source files to the project +add_project(rtx_ambient_occlusion src/main.cpp src/teapot.hpp ${rtx_sources}) + +# including headers of dependencies and the VkCV framework +target_include_directories(rtx_ambient_occlusion SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_scene_include} ${vkcv_shader_compiler_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(rtx_ambient_occlusion vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_scene vkcv_shader_compiler) diff --git a/projects/rtx_ambient_occlusion/README.md b/projects/rtx_ambient_occlusion/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d9fd98cf6787038e7e3f9c1b21f61a71541f09e4 --- /dev/null +++ b/projects/rtx_ambient_occlusion/README.md @@ -0,0 +1,25 @@ +# RTX ambient occlusion +An example project to show usage of hardware accelerated ray tracing with the VkCV framework + + + +## Details + +The project utilizes multiple Vulkan extensions to make use of hardware accelerated ray tracing. +Newer GPU architectures provide special hardware to allow the use of acceleration structures and +ray tracing pipelines. This application uses those features to render an implementation of +ambient occlusion in realtime. + +## Extensions + +Here is a list of the used extensions: + +- [VK_KHR_get_physical_device_properties2](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_get_physical_device_properties2.html) +- [VK_KHR_maintenance3](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_maintenance3.html) +- [VK_KHR_deferred_host_operations](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_deferred_host_operations.html) +- [VK_KHR_spirv_1_4](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_spirv_1_4.html) +- [VK_KHR_pipeline_library](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_pipeline_library.html) +- [VK_EXT_descriptor_indexing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html) +- [VK_KHR_buffer_device_address](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_buffer_device_address.html) +- [VK_KHR_acceleration_structure](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_acceleration_structure.html) +- [VK_KHR_ray_tracing_pipeline](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_ray_tracing_pipeline.html) diff --git a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rchit b/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rchit new file mode 100644 index 0000000000000000000000000000000000000000..b073916e4eaf992d5e6ae4186013478634263fbd --- /dev/null +++ b/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rchit @@ -0,0 +1,48 @@ +#version 460 +#extension GL_EXT_ray_tracing : require +#extension GL_EXT_scalar_block_layout : require + +hitAttributeEXT vec2 attributes; + +layout(location = 0) rayPayloadInEXT Payload { + float hitSky; + vec3 worldPosition; + vec3 worldNormal; +} payload; + +layout(binding = 2, set = 0) uniform accelerationStructureEXT tlas; // top level acceleration structure + +layout(binding = 3, set = 0, scalar) buffer rtxVertices +{ + float vertices[]; +}; + +layout(binding = 4, set = 0, scalar) buffer rtxIndices +{ + uint indices[]; +}; + +void main() { + payload.worldPosition = vec3(1.0, 0.0, 0.5); + + ivec3 indicesVec = ivec3(indices[3 * gl_PrimitiveID + 0], indices[3 * gl_PrimitiveID + 1], indices[3 * gl_PrimitiveID + 2]); + + // current triangle + const vec3 v0 = vec3(vertices[3 * indicesVec.x + 0],vertices[3 * indicesVec.x + 1],vertices[3 * indicesVec.x + 2]); + const vec3 v1 = vec3(vertices[3 * indicesVec.y + 0],vertices[3 * indicesVec.y + 1],vertices[3 * indicesVec.y + 2]); + const vec3 v2 = vec3(vertices[3 * indicesVec.z + 0],vertices[3 * indicesVec.z + 1],vertices[3 * indicesVec.z + 2]); + + // use barycentric coordinates to compute intersection + const vec3 barycentrics = vec3(1.0 - attributes.x - attributes.y, attributes.xy); + const vec3 objectPosition = v0 * barycentrics.x + v1 * barycentrics.y + v2 * barycentrics.z; + + payload.worldPosition = gl_ObjectToWorldEXT * vec4(objectPosition, 1.0); + + const vec3 objectNormal = cross(v1 - v0, v2 - v0); + + payload.worldNormal = normalize((objectNormal * gl_WorldToObjectEXT).xyz); + + payload.worldNormal = faceforward(payload.worldNormal, gl_WorldRayDirectionEXT, payload.worldNormal); + + payload.hitSky = 0.0f; +} diff --git a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rgen b/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rgen new file mode 100644 index 0000000000000000000000000000000000000000..711070fcf1eec18253f331cbd133330791fa6be6 --- /dev/null +++ b/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rgen @@ -0,0 +1,158 @@ +#version 460 +#extension GL_EXT_ray_tracing : require + +#define M_PI 3.1415926535897932384626433832795 + +// A location for a ray payload (we can have multiple of these) +layout(location = 0) rayPayloadEXT Payload { + float hitSky; + vec3 worldPosition; + vec3 worldNormal; +} payload; + +layout(binding = 0, set = 0, rgba16) uniform image2D outImg; // the output image -> maybe use 16 bit values? +layout(binding = 1, set = 0) uniform accelerationStructureEXT tlas; // top level acceleration structure + +layout( push_constant ) uniform constants { + vec4 camera_position; // as origin for ray generation + vec4 camera_right; // for computing ray direction + vec4 camera_up; // for computing ray direction + vec4 camera_forward; // for computing ray direction +} camera; + +// random() and helpers from: https://www.shadertoy.com/view/XlycWh +float g_seed = 0; + +uint base_hash(uvec2 p) { + p = 1103515245U*((p >> 1U)^(p.yx)); + uint h32 = 1103515245U*((p.x)^(p.y>>3U)); + return h32^(h32 >> 16); +} + +vec2 hash2(inout float seed) { + uint n = base_hash(floatBitsToUint(vec2(seed+=.1,seed+=.1))); + uvec2 rz = uvec2(n, n*48271U); + return vec2(rz.xy & uvec2(0x7fffffffU))/float(0x7fffffff); +} + +void initRandom(uvec2 coord){ + g_seed = float(base_hash(coord)/float(0xffffffffU)); +} + +vec2 random(){ + return hash2(g_seed); +} + +/** + * Traces the ray from the camera and provides the intersection information. + * @param[in,out] hitSky Defines if the ray has hit the sky + * @param[in,out] pos The position of intersection + * @param[in,out] norm The normal at the position of intersection + */ +void TraceCameraRay(out bool hitSky, out vec3 pos, out vec3 norm){ + // Use a camera model to generate a ray for this pixel. + vec2 uv = gl_LaunchIDEXT.xy + vec2(random()); // random breaks up aliasing + uv /= vec2(gl_LaunchSizeEXT.xy); + uv = (uv * 2.0 - 1.0) // normalize uv coordinates into Vulkan viewport space + * vec2(1.0, -1.0); // flips y-axis + const vec3 orig = camera.camera_position.xyz; + const vec3 dir = normalize(uv.x * camera.camera_right + uv.y * camera.camera_up + camera.camera_forward).xyz; + + // Trace a ray into the scene; get back data in the payload. + traceRayEXT(tlas, // Acceleration structure + gl_RayFlagsOpaqueEXT, // Ray flags, here saying "ignore intersection shaders" + 0xFF, // 8-bit instance mask, here saying "trace against all instances" + 0, // SBT record offset + 0, // SBT record stride for offset + 0, // Miss index + orig, // Ray origin + 0.0, // Minimum t-value + dir, // Ray direction + 1000.0, // Maximum t-value + 0); // Location of payload + + // Read the values from the payload: + hitSky = (payload.hitSky > 0.0); + pos = payload.worldPosition; + norm = payload.worldNormal; +} + +/** + * @brief Casts a shadow ray. Returns @p true, if the shadow ray hit the sky. + * @param[in] orig The point of origin of the shadow ray. + * @param[in] dir The direction of the shadow ray. + */ +float CastShadowRay(vec3 orig, vec3 dir){ + payload.hitSky = 0.0f; // Assume ray is occluded + traceRayEXT(tlas, // Acceleration structure + gl_RayFlagsOpaqueEXT | gl_RayFlagsSkipClosestHitShaderEXT | gl_RayFlagsTerminateOnFirstHitEXT, // Ray flags, here saying "ignore any hit shaders and closest hit shaders, and terminate the ray on the first found intersection" + 0xFF, // 8-bit instance mask, here saying "trace against all instances" + 0, // SBT record offset + 0, // SBT record stride for offset + 0, // Miss index + orig, // Ray origin + 0.0001, // Minimum t-value - avoid self intersection + dir, // Ray direction + 1000.0, // Maximum t-value + 0); // Location of payload + return payload.hitSky; +} + +vec3 sampleCosineDistribution(vec2 xi){ + float phi = 2 * M_PI * xi.y; + return vec3( + sqrt(xi.x) * cos(phi), + sqrt(1 - xi.x), + sqrt(xi.x) * sin(phi)); +} + +struct Basis{ + vec3 right; + vec3 up; + vec3 forward; +}; + +Basis buildBasisAroundNormal(vec3 N){ + Basis basis; + basis.up = N; + basis.right = abs(basis.up.x) < 0.99 ? vec3(1, 0, 0) : vec3(0, 0, 1); + basis.forward = normalize(cross(basis.up, basis.right)); + basis.right = cross(basis.up, basis.forward); + return basis; +} + +vec3 sampleTangentToWorldSpace(vec3 tangentSpaceSample, vec3 N){ + Basis tangentBasis = buildBasisAroundNormal(N); + return + tangentBasis.right * tangentSpaceSample.x + + tangentBasis.up * tangentSpaceSample.y + + tangentBasis.forward * tangentSpaceSample.z; +} + +void main(){ + uint rayCount = 64; // the amount of rays to be casted + + initRandom(gl_LaunchIDEXT.xy); + + uvec2 pixel = gl_LaunchIDEXT.xy; + bool pixelIsSky; // Does the pixel show the sky (not an object)? + vec3 pos, norm; // AO rays from where? + TraceCameraRay(pixelIsSky, pos, norm); + + if(pixelIsSky){ + // Don't compute ambient occlusion for the sky + imageStore(outImg, ivec2(pixel), vec4(0.8,0.8,0.8,1.0)); + return; + } + + // Compute ambient occlusion + float aoValue = 0.0; + for(uint i = 0; i < rayCount; i++){ + vec3 sampleTangentSpace = sampleCosineDistribution(random()); + vec3 sampleWorldSpace = sampleTangentToWorldSpace(sampleTangentSpace, norm); + aoValue += CastShadowRay(pos, sampleWorldSpace); + } + aoValue /= rayCount; + + imageStore(outImg, ivec2(pixel), vec4(vec3(aoValue), 1)); +} diff --git a/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss b/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss new file mode 100644 index 0000000000000000000000000000000000000000..c107dbd03e6a8fdfdd84d2b8d280cb6264307c14 --- /dev/null +++ b/projects/rtx_ambient_occlusion/resources/shaders/ambientOcclusion.rmiss @@ -0,0 +1,12 @@ +#version 460 +#extension GL_EXT_ray_tracing : require + +layout(location = 0) rayPayloadInEXT Payload { + float hitSky; + vec3 worldPosition; + vec3 worldNormal; +} payload; + +void main() { + payload.hitSky = 1.0f; +} diff --git a/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp b/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fefd468067658cde88eefe02834b2e7328672af6 --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/RTX/ASManager.cpp @@ -0,0 +1,460 @@ +#include "ASManager.hpp" +#include <array> + +namespace vkcv::rtx { + + + ASManager::ASManager(vkcv::Core *core) : + m_core(core), + m_device(&(core->getContext().getDevice())){ + // INFO: Using RTX extensions implies that we cannot use the standard dispatcher from Vulkan because using RTX + // specific functions via vk::Device will result in validation errors. Instead we need to use a + // vk::DispatchLoaderDynamic which is used as dispatcher parameter of the device functions. + m_rtxDispatcher = vk::DispatchLoaderDynamic( (PFN_vkGetInstanceProcAddr) m_core->getContext().getInstance().getProcAddr("vkGetInstanceProcAddr") ); + m_rtxDispatcher.init(m_core->getContext().getInstance()); + + // TODO: Recursive call of buildBLAS for bigger scenes. Currently, the RTX module only supports one mesh. + } + + ASManager::~ASManager() noexcept { + m_rtxDispatcher = vk::DispatchLoaderDynamic( (PFN_vkGetInstanceProcAddr) m_core->getContext().getInstance().getProcAddr("vkGetInstanceProcAddr") ); + m_rtxDispatcher.init(m_core->getContext().getInstance()); + + // destroy every BLAS, its data containers and free used memory blocks + for (size_t i=0; i < m_bottomLevelAccelerationStructures.size(); i++) { + BottomLevelAccelerationStructure blas = m_bottomLevelAccelerationStructures[i]; + m_core->getContext().getDevice().destroyAccelerationStructureKHR(blas.vulkanHandle, nullptr, m_rtxDispatcher); + m_core->getContext().getDevice().destroy(blas.accelerationBuffer.vulkanHandle); + m_core->getContext().getDevice().destroy(blas.indexBuffer.vulkanHandle); + m_core->getContext().getDevice().destroy(blas.vertexBuffer.vulkanHandle); + + m_core->getContext().getDevice().freeMemory(blas.accelerationBuffer.deviceMemory); + m_core->getContext().getDevice().freeMemory(blas.indexBuffer.deviceMemory); + m_core->getContext().getDevice().freeMemory(blas.vertexBuffer.deviceMemory); + } + + // destroy the TLAS, its data containers and free used memory blocks + TopLevelAccelerationStructure tlas = m_topLevelAccelerationStructure; + m_core->getContext().getDevice().destroyAccelerationStructureKHR(tlas.vulkanHandle, nullptr, m_rtxDispatcher); + m_core->getContext().getDevice().destroy(tlas.tlasBuffer.vulkanHandle); + m_core->getContext().getDevice().destroy(tlas.gpuBufferInstances.vulkanHandle); + + m_core->getContext().getDevice().freeMemory(tlas.tlasBuffer.deviceMemory); + m_core->getContext().getDevice().freeMemory(tlas.gpuBufferInstances.deviceMemory); + } + + + + vk::CommandPool ASManager::createCommandPool() { + vk::CommandPool commandPool; + vk::CommandPoolCreateInfo commandPoolCreateInfo; + commandPoolCreateInfo.setQueueFamilyIndex(m_core->getContext().getQueueManager().getComputeQueues()[0].familyIndex); + vk::Result res = m_device->createCommandPool(&commandPoolCreateInfo, nullptr, &commandPool); + if (res != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "ASManager: command pool could not be created! (%s)", vk::to_string(res).c_str()); + } + return commandPool; + }; + + vk::CommandBuffer ASManager::createAndBeginCommandBuffer(vk::CommandPool commandPool) + { + vk::CommandBufferAllocateInfo commandBufferAllocateInfo{}; + commandBufferAllocateInfo.setLevel(vk::CommandBufferLevel::ePrimary); + commandBufferAllocateInfo.setCommandPool(commandPool); + commandBufferAllocateInfo.setCommandBufferCount(1); + vk::CommandBuffer commandBuffer; + vk::Result result = m_device->allocateCommandBuffers(&commandBufferAllocateInfo, &commandBuffer); + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "ASManager: command buffer for Acceleration Strucutre Build could not be allocated! (%s)", vk::to_string(result).c_str()); + } + + const vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + commandBuffer.begin(beginInfo); + return commandBuffer; + } + + void ASManager::submitCommandBuffer(vk::CommandPool commandPool, vk::CommandBuffer& commandBuffer) + { + commandBuffer.end(); + + vk::SubmitInfo submitInfo; + submitInfo.setCommandBufferCount(1); + submitInfo.setPCommandBuffers(&commandBuffer); + + vkcv::Queue queue = m_core->getContext().getQueueManager().getComputeQueues()[0]; + + queue.handle.submit(submitInfo); + queue.handle.waitIdle(); + + m_device->freeCommandBuffers(commandPool, 1, &commandBuffer); + m_device->destroyCommandPool(commandPool); + } + + vk::DeviceAddress ASManager::getBufferDeviceAddress(vk::Buffer buffer) + { + vk::BufferDeviceAddressInfo bufferDeviceAddressInfo(buffer); + return m_device->getBufferAddress(bufferDeviceAddressInfo); + } + + void ASManager::createBuffer(RTXBuffer& buffer) { + + vk::BufferCreateInfo bufferCreateInfo; + bufferCreateInfo.setFlags(vk::BufferCreateFlags()); + bufferCreateInfo.setUsage(buffer.bufferUsageFlagBits); + bufferCreateInfo.setSize(buffer.deviceSize); + + buffer.vulkanHandle = m_device->createBuffer(bufferCreateInfo); + vk::MemoryRequirements2 memoryRequirements2; + vk::MemoryDedicatedRequirements dedicatedRequirements; + vk::BufferMemoryRequirementsInfo2 bufferRequirements; + + bufferRequirements.setBuffer(buffer.vulkanHandle); + memoryRequirements2.pNext = &dedicatedRequirements; + m_device->getBufferMemoryRequirements2(&bufferRequirements, &memoryRequirements2); + + vk::PhysicalDeviceMemoryProperties physicalDeviceMemoryProperties = m_core->getContext().getPhysicalDevice().getMemoryProperties(); + + uint32_t memoryTypeIndex = -1; + for (size_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) { + if ((memoryRequirements2.memoryRequirements.memoryTypeBits & (1 << i)) + && (physicalDeviceMemoryProperties.memoryTypes[i].propertyFlags & buffer.memoryPropertyFlagBits) == buffer.memoryPropertyFlagBits) { + memoryTypeIndex = i; + break; + } + } + + vk::MemoryAllocateInfo memoryAllocateInfo( + memoryRequirements2.memoryRequirements.size, // size of allocation in bytes + memoryTypeIndex // index identifying a memory type from the memoryTypes array of the vk::PhysicalDeviceMemoryProperties structure. + ); + vk::MemoryAllocateFlagsInfo allocateFlagsInfo( + vk::MemoryAllocateFlagBits::eDeviceAddress // vk::MemoryAllocateFlags + ); + memoryAllocateInfo.setPNext(&allocateFlagsInfo); // extend memory allocate info with allocate flag info + buffer.deviceMemory = m_device->allocateMemory(memoryAllocateInfo); + + uint32_t memoryOffset = 0; + m_device->bindBufferMemory(buffer.vulkanHandle, buffer.deviceMemory, memoryOffset); + + // only fill data in case of CPU buffer + if (buffer.bufferType == RTXBufferType::STAGING) { + void* mapped = m_device->mapMemory(buffer.deviceMemory, memoryOffset, buffer.deviceSize); + std::memcpy(mapped, buffer.data, buffer.deviceSize); + m_device->unmapMemory(buffer.deviceMemory); + } + } + + void ASManager::copyFromCPUToGPU(RTXBuffer& cpuBuffer, RTXBuffer& gpuBuffer) { + vk::CommandPool commandPool= createCommandPool(); + vk::CommandBuffer commandBuffer= createAndBeginCommandBuffer(commandPool); + vk::BufferCopy bufferCopy; + bufferCopy.size = cpuBuffer.deviceSize; + commandBuffer.copyBuffer(cpuBuffer.vulkanHandle, gpuBuffer.vulkanHandle, 1, &bufferCopy); + + submitCommandBuffer(commandPool,commandBuffer); + + m_device->destroyBuffer(cpuBuffer.vulkanHandle); + m_device->freeMemory(cpuBuffer.deviceMemory); + } + + void ASManager::buildBLAS(RTXBuffer vertexBuffer, uint32_t vertexCount, RTXBuffer indexBuffer, uint32_t indexCount) { + // TODO: organize hierarchical structure of multiple BLAS + + vk::DeviceAddress vertexBufferAddress = getBufferDeviceAddress(vertexBuffer.vulkanHandle); + vk::DeviceAddress indexBufferAddress = getBufferDeviceAddress(indexBuffer.vulkanHandle); + + // triangle mesh data + vk::AccelerationStructureGeometryTrianglesDataKHR asTriangles( + vk::Format::eR32G32B32Sfloat, // vertex format + vertexBufferAddress, // vertex buffer address (vk::DeviceOrHostAddressConstKHR) + 3 * sizeof(float), // vertex stride (vk::DeviceSize) + uint32_t(vertexCount - 1), // maxVertex (uint32_t) + vk::IndexType::eUint32, // indexType (vk::IndexType) --> INFO: UINT16 oder UINT32! + indexBufferAddress, // indexData (vk::DeviceOrHostAddressConstKHR) + {} // transformData (vk::DeviceOrHostAddressConstKHR) + ); + + // Geometry data + vk::AccelerationStructureGeometryKHR asGeometry( + vk::GeometryTypeKHR::eTriangles, // The geometry type, e.g. triangles, AABBs, instances + asTriangles, // the geometry data + vk::GeometryFlagBitsKHR::eOpaque // This flag disables any-hit shaders to increase ray tracing performance + ); + + // Ranges for data lists + vk::AccelerationStructureBuildRangeInfoKHR asRangeInfo( + uint32_t(indexCount / 3), // the primitiveCount (uint32_t) + 0, // primitiveOffset (uint32_t) + 0, // firstVertex (uint32_t) + 0 // transformOffset (uint32_t) + ); + + // Settings and array of geometries to build into BLAS + vk::AccelerationStructureBuildGeometryInfoKHR asBuildInfo( + vk::AccelerationStructureTypeKHR::eBottomLevel, // type of the AS: bottom vs. top + vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, // some flags for different purposes, e.g. efficiency + vk::BuildAccelerationStructureModeKHR::eBuild, // AS mode: build vs. update + {}, // src AS (this seems to be for copying AS) + {}, // dst AS (this seems to be for copying AS) + 1, // the geometryCount. TODO: how many do we need? + &asGeometry // the next input entry would be a pointer to a pointer to geometries. Maybe geometryCount depends on the next entry? + ); + + // Calculate memory needed for AS + vk::AccelerationStructureBuildSizesInfoKHR asBuildSizesInfo; + m_device->getAccelerationStructureBuildSizesKHR( + vk::AccelerationStructureBuildTypeKHR::eDevice, // build on device instead of host + &asBuildInfo, // pointer to build info + &asRangeInfo.primitiveCount, // array of number of primitives per geometry + &asBuildSizesInfo, // output pointer to store sizes + m_rtxDispatcher + ); + + // create buffer for acceleration structure + RTXBuffer blasBuffer; + blasBuffer.bufferType = RTXBufferType::ACCELERATION; + blasBuffer.deviceSize = asBuildSizesInfo.accelerationStructureSize; + blasBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR + | vk::BufferUsageFlagBits::eShaderDeviceAddress + | vk::BufferUsageFlagBits::eStorageBuffer; + blasBuffer.memoryPropertyFlagBits = { vk::MemoryPropertyFlagBits::eDeviceLocal }; + + createBuffer(blasBuffer); + + // Create an empty AS object + vk::AccelerationStructureCreateInfoKHR asCreateInfo( + vk::AccelerationStructureCreateFlagsKHR(), // creation flags + blasBuffer.vulkanHandle, // allocated AS buffer. + 0, + asBuildSizesInfo.accelerationStructureSize, // size of the AS + asBuildInfo.type // type of the AS + ); + + // Create the intended AS object + vk::AccelerationStructureKHR blasKHR; + vk::Result res = m_device->createAccelerationStructureKHR( + &asCreateInfo, // AS create info + nullptr, // allocator callbacks + &blasKHR, // the AS + m_rtxDispatcher + ); + if (res != vk::Result::eSuccess) { + vkcv_log(vkcv::LogLevel::ERROR, "The Bottom Level Acceleration Structure could not be build! (%s)", vk::to_string(res).c_str()); + } + asBuildInfo.setDstAccelerationStructure(blasKHR); + + // Create temporary scratch buffer used for building the AS + RTXBuffer scratchBuffer; + scratchBuffer.bufferType = RTXBufferType::SCRATCH; + scratchBuffer.deviceSize = asBuildSizesInfo.buildScratchSize; + scratchBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddress + | vk::BufferUsageFlagBits::eStorageBuffer; + scratchBuffer.memoryPropertyFlagBits = { vk::MemoryPropertyFlagBits::eDeviceLocal }; + + createBuffer(scratchBuffer); + + asBuildInfo.setScratchData(getBufferDeviceAddress(scratchBuffer.vulkanHandle)); + + // Pointer to rangeInfo, used later for build + vk::AccelerationStructureBuildRangeInfoKHR* pointerToRangeInfo = &asRangeInfo; + + vk::CommandPool commandPool = createCommandPool(); + vk::CommandBuffer commandBuffer = createAndBeginCommandBuffer(commandPool); + + commandBuffer.buildAccelerationStructuresKHR(1, &asBuildInfo, &pointerToRangeInfo, m_rtxDispatcher); + + submitCommandBuffer(commandPool, commandBuffer); + + m_core->getContext().getDevice().destroyBuffer(scratchBuffer.vulkanHandle, nullptr, m_rtxDispatcher); + m_core->getContext().getDevice().freeMemory(scratchBuffer.deviceMemory, nullptr, m_rtxDispatcher); + + BottomLevelAccelerationStructure blas = { + vertexBuffer, + indexBuffer, + blasBuffer, + blasKHR + }; + m_bottomLevelAccelerationStructures.push_back(blas); + } + + void ASManager::buildTLAS() { + // TODO: organize hierarchical structure of multiple BLAS + + // We need an the device address of each BLAS --> TODO: for loop for bigger scenes + vk::AccelerationStructureDeviceAddressInfoKHR addressInfo( + m_bottomLevelAccelerationStructures[0].vulkanHandle + ); + vk::DeviceAddress blasAddress = m_device->getAccelerationStructureAddressKHR(&addressInfo, m_rtxDispatcher); + + std::array<std::array<float, 4>, 3> transformMatrix = { + std::array<float, 4>{1.f, 0.f, 0.f, 0.f}, + std::array<float, 4>{0.f, 1.f, 0.f, 0.f}, + std::array<float, 4>{0.f, 0.f, 1.f, 0.f}, + }; + + vk::TransformMatrixKHR transformMatrixKhr( + transformMatrix // std::array<std::array<float,4>,3> const& + ); + + vk::AccelerationStructureInstanceKHR accelerationStructureInstanceKhr( + transformMatrixKhr, // vk::TransformMatrixKHR transform_ = {}, + 0, // uint32_t instanceCustomIndex, + 0xFF, //uint32_t mask_ = {}, + 0, // uint32_t instanceShaderBindingTableRecordOffset, + vk::GeometryInstanceFlagBitsKHR::eTriangleFacingCullDisable, // vk::GeometryInstanceFlagsKHR + blasAddress // uint64_t accelerationStructureReference (the device address of the BLAS) + ); + + // create a buffer of instances on the device and upload the array of instances to it + RTXBuffer stagingBufferInstances; + stagingBufferInstances.bufferType = RTXBufferType::STAGING; + stagingBufferInstances.deviceSize = sizeof(accelerationStructureInstanceKhr); + stagingBufferInstances.data = &accelerationStructureInstanceKhr; + stagingBufferInstances.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddress + | vk::BufferUsageFlagBits::eTransferSrc; + stagingBufferInstances.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eHostCoherent + | vk::MemoryPropertyFlagBits::eHostVisible; + + createBuffer(stagingBufferInstances); + + RTXBuffer bufferInstances; + bufferInstances.bufferType = RTXBufferType::GPU; + bufferInstances.deviceSize = sizeof(accelerationStructureInstanceKhr); + bufferInstances.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddress + | vk::BufferUsageFlagBits::eTransferDst + | vk::BufferUsageFlagBits::eTransferSrc + | vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR; + bufferInstances.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eDeviceLocal; + + createBuffer(bufferInstances); + copyFromCPUToGPU(stagingBufferInstances, bufferInstances); // automatically deletes and frees memory of stagingBufferInstances + + vk::MemoryBarrier barrier; + barrier.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite); + barrier.setDstAccessMask(vk::AccessFlagBits::eAccelerationStructureWriteKHR); + vk::CommandPool commandPool = createCommandPool(); + vk::CommandBuffer commandBuffer = createAndBeginCommandBuffer(commandPool); + commandBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR, + {},1,&barrier,0, nullptr,0,nullptr); + submitCommandBuffer(commandPool, commandBuffer); + + + // ranging information for TLAS build + vk::AccelerationStructureBuildRangeInfoKHR asRangeInfo( + 1, // primitiveCount -> number of instances + 0, // primitiveOffset + 0, // firstVertex + 0 //transformOffset + ); + + vk::DeviceAddress bufferInstancesAddress = getBufferDeviceAddress(bufferInstances.vulkanHandle); + + vk::AccelerationStructureGeometryInstancesDataKHR asInstances( + false, // vk::Bool32 arrayOfPointers + bufferInstancesAddress // vk::DeviceOrHostAddressConstKHR data_ = {} + ); + + // Geometry, in this case instances of BLAS + vk::AccelerationStructureGeometryKHR asGeometry( + vk::GeometryTypeKHR::eInstances, // vk::GeometryTypeKHR geometryType_ = vk::GeometryTypeKHR::eTriangles + asInstances, // vk::AccelerationStructureGeometryDataKHR geometry_ = {} + {} // vk::GeometryFlagsKHR flags_ = {} + ); + + // Finally, create the TLAS + vk::AccelerationStructureBuildGeometryInfoKHR asBuildInfo( + vk::AccelerationStructureTypeKHR::eTopLevel, // type of the AS: bottom vs. top + vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace, // some flags for different purposes, e.g. efficiency + vk::BuildAccelerationStructureModeKHR::eBuild, // AS mode: build vs. update + {}, // src AS (this seems to be for copying AS) + {}, // dst AS (this seems to be for copying AS) + 1, // the geometryCount. + &asGeometry // the next input entry would be a pointer to a pointer to geometries. Maybe geometryCount depends on the next entry? + ); + + // AS size and scratch space size, given by count of instances (1) + vk::AccelerationStructureBuildSizesInfoKHR asSizeInfo; + m_core->getContext().getDevice().getAccelerationStructureBuildSizesKHR( + vk::AccelerationStructureBuildTypeKHR::eDevice, + &asBuildInfo, + &asRangeInfo.primitiveCount, + &asSizeInfo, + m_rtxDispatcher + ); + + // Create buffer for the TLAS + RTXBuffer tlasBuffer; + tlasBuffer.bufferType = RTXBufferType::ACCELERATION; + tlasBuffer.deviceSize = asSizeInfo.accelerationStructureSize; + tlasBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eAccelerationStructureStorageKHR + | vk::BufferUsageFlagBits::eShaderDeviceAddress + | vk::BufferUsageFlagBits::eStorageBuffer; + + createBuffer(tlasBuffer); + + // Create empty TLAS object + vk::AccelerationStructureCreateInfoKHR asCreateInfo( + {}, // creation flags + tlasBuffer.vulkanHandle, // allocated AS buffer. + 0, + asSizeInfo.accelerationStructureSize, // size of the AS + asBuildInfo.type // type of the AS + ); + + vk::AccelerationStructureKHR tlas; + vk::Result res = m_device->createAccelerationStructureKHR(&asCreateInfo, nullptr, &tlas, m_rtxDispatcher); + if (res != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "Top Level Acceleration Structure could not be created! (%s)", vk::to_string(res).c_str()); + } + asBuildInfo.setDstAccelerationStructure(tlas); + + // Create temporary scratch buffer used for building the AS + RTXBuffer tlasScratchBuffer; // scratch buffer + tlasScratchBuffer.bufferType = RTXBufferType::ACCELERATION; + tlasScratchBuffer.deviceSize = asSizeInfo.buildScratchSize; + tlasScratchBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderDeviceAddressKHR + | vk::BufferUsageFlagBits::eStorageBuffer; + + createBuffer(tlasScratchBuffer); + + vk::BufferDeviceAddressInfo tempBuildDataBufferDeviceAddressInfo(tlasScratchBuffer.vulkanHandle); + vk::DeviceAddress tempBuildBufferDataAddress = m_core->getContext().getDevice().getBufferAddressKHR(tempBuildDataBufferDeviceAddressInfo, m_rtxDispatcher); + asBuildInfo.setScratchData(tempBuildBufferDataAddress); + + // Pointer to rangeInfo, used later for build + vk::AccelerationStructureBuildRangeInfoKHR* pointerToRangeInfo = &asRangeInfo; + + // Build the TLAS. + + vk::CommandPool commandPool2 = createCommandPool(); + vk::CommandBuffer commandBuffer2 = createAndBeginCommandBuffer(commandPool2); + commandBuffer2.buildAccelerationStructuresKHR(1, &asBuildInfo, &pointerToRangeInfo, m_rtxDispatcher); + submitCommandBuffer(commandPool2, commandBuffer2); + + m_device->destroyBuffer(tlasScratchBuffer.vulkanHandle, nullptr, m_rtxDispatcher); + m_device->freeMemory(tlasScratchBuffer.deviceMemory, nullptr, m_rtxDispatcher); + + m_topLevelAccelerationStructure = { + bufferInstances, + tlasBuffer, + tlasScratchBuffer, + tlas + }; + } + + TopLevelAccelerationStructure ASManager::getTLAS() + { + return m_topLevelAccelerationStructure; + } + + BottomLevelAccelerationStructure ASManager::getBLAS(uint32_t id) + { + return m_bottomLevelAccelerationStructures[id]; + } + + const vk::DispatchLoaderDynamic& ASManager::getDispatcher() { + return m_rtxDispatcher; + } +} \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp b/projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9a1374356cf4afdb04b0231f3c65e823ebe26dac --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/RTX/ASManager.hpp @@ -0,0 +1,188 @@ +#pragma once + +#include <vkcv/Core.hpp> + +namespace vkcv::rtx { + + /** + * @brief Used for @#RTXBuffer creation depending on the @#RTXBufferType. + */ + enum class RTXBufferType { + STAGING, + GPU, + ACCELERATION, + SHADER_BINDING, + SCRATCH + }; + + /** + * @brief Used as a container to handle buffer creation and destruction in RTX-specific use cases. + */ + struct RTXBuffer { + RTXBufferType bufferType; + void* data; + vk::DeviceSize deviceSize; + vk::DeviceMemory deviceMemory; + vk::BufferUsageFlags bufferUsageFlagBits; + vk::MemoryPropertyFlags memoryPropertyFlagBits; + vk::Buffer vulkanHandle; + }; + + /** + * @brief Used as a container to handle bottom-level acceleration structure (BLAS) construction and destruction. + */ + struct BottomLevelAccelerationStructure { + RTXBuffer vertexBuffer; + RTXBuffer indexBuffer; + RTXBuffer accelerationBuffer; + vk::AccelerationStructureKHR vulkanHandle; + }; + + /** + * @brief Used as a container to handle top-level acceleration structure (TLAS) construction and destruction. + */ + struct TopLevelAccelerationStructure { + RTXBuffer gpuBufferInstances; + RTXBuffer tlasBuffer; + RTXBuffer tempBuildDataBuffer; // scratch buffer + vk::AccelerationStructureKHR vulkanHandle; + }; + + /** + * @brief A class for managing acceleration structures (bottom, top). + */ + class ASManager { + private: + Core* m_core; + const vk::Device* m_device; + std::vector<BottomLevelAccelerationStructure> m_bottomLevelAccelerationStructures; + TopLevelAccelerationStructure m_topLevelAccelerationStructure; + vk::DispatchLoaderDynamic m_rtxDispatcher; + + /** + * Creates a command pool. + */ + vk::CommandPool createCommandPool(); + + /** + * @brief Takes a @p cmdPool, allocates a command buffer and starts recording it. + * @param cmdPool The command pool. + * @return The allocated command buffer. + */ + vk::CommandBuffer createAndBeginCommandBuffer( vk::CommandPool cmdPool); + + /** + * @brief Ends the @p commandBuffer,submits it and waits. Afterwards frees the @p commandBuffer. + * @param commandPool The command pool. + * @param commandBuffer The command buffer. + */ + void submitCommandBuffer(vk::CommandPool commandPool, vk::CommandBuffer& commandBuffer); + + /** + * @brief Gets the device address of a @p buffer. + * @param buffer The buffer. + * @return The device address of the @p buffer. + */ + vk::DeviceAddress getBufferDeviceAddress(vk::Buffer buffer); + + /** + * @brief Copies @p cpuBuffer data into a @p gpuBuffer. Typical use case is a staging buffer (namely, + * @p cpuBuffer) used to fill a @p gpuBuffer with @p vk::MemoryPropertyFlagBits::eDeviceLocal flag set. + * @p cpuBuffer is destroyed and freed after copying. + * @param cpuBuffer + * @param gpuBuffer + */ + void copyFromCPUToGPU(RTXBuffer &cpuBuffer, RTXBuffer &gpuBuffer); + + public: + + /** + * @brief Constructor of @#ASManager . + * @param core + */ + ASManager(vkcv::Core *core); + + /** + * @brief Default destructor of @#ASManager. + */ + ~ASManager(); + + /** + * @brief Returns a @#RTXBuffer object holding data of type @p T. + * @param data The input data of type @p T. + * @return A @#RTXBuffer object holding @p data of type @p T. + */ + template<class T> + RTXBuffer makeBufferFromData(std::vector<T>& data) { + + // first: Staging Buffer creation + RTXBuffer stagingBuffer; + stagingBuffer.bufferType = RTXBufferType::STAGING; + stagingBuffer.deviceSize = sizeof(T) * data.size(); + stagingBuffer.data = data.data(); + stagingBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eTransferSrc; + stagingBuffer.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible; + + createBuffer(stagingBuffer); + + // second: create AS Buffer + RTXBuffer targetBuffer; + targetBuffer.bufferType = RTXBufferType::GPU; + targetBuffer.deviceSize = sizeof(T) * data.size(); + targetBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eAccelerationStructureBuildInputReadOnlyKHR + | vk::BufferUsageFlagBits::eTransferDst + | vk::BufferUsageFlagBits::eStorageBuffer + | vk::BufferUsageFlagBits::eShaderDeviceAddress; + targetBuffer.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eDeviceLocal; + + createBuffer(targetBuffer); + + // copy from CPU to GPU + copyFromCPUToGPU(stagingBuffer, targetBuffer); + + return targetBuffer; + } + + /** + * @brief A helper function used by @#ASManager::makeBufferFromData. Creates a fully initialized @#RTXBuffer object + * from partially specified @p buffer. All missing data of @p buffer will be completed by this function. + * @param buffer The partially specified @#RTXBuffer holding that part of information which is required for + * successfully creating a @p vk::Buffer object. + */ + void createBuffer(RTXBuffer& buffer); + + /** + * @brief Build a Bottom Level Acceleration Structure (BLAS) object from given @p vertexBuffer and @p indexBuffer. + * @param[in] vertexBuffer The vertex data. + * @param[in] vertexCount The amount of vertices in @p vertexBuffer. + * @param[in] indexBuffer The index data. + * @param[in] indexCount The amount of indices in @p indexBuffer. + */ + void buildBLAS(RTXBuffer vertexBuffer, uint32_t vertexCount, RTXBuffer indexBuffer, uint32_t indexCount); + + /** + * @brief Build a Top Level Acceleration Structure (TLAS) object from the created + * @#ASManager::m_accelerationStructures objects. + */ + void buildTLAS(); + + /** + * @brief Returns the top-level acceleration structure (TLAS) buffer. + * @return A @#TopLevelAccelerationStructure object holding the TLAS. + */ + TopLevelAccelerationStructure getTLAS(); + + /** + * @brief Returns the bottom-level acceleration structure at @p id. + * @param id The ID used for indexing. + * @return The specified @#BottomLevelAccelerationStructure object. + */ + BottomLevelAccelerationStructure getBLAS(uint32_t id); + + /** + * @brief Returns the dispatcher member variable for access in the @#RTXModule. + * @return The dispatcher member variable. + */ + const vk::DispatchLoaderDynamic& getDispatcher(); + }; +} \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp b/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bd5453abae353eed6739c41dbff084a0c909aaca --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/RTX/RTX.cpp @@ -0,0 +1,330 @@ +#include "RTX.hpp" + +namespace vkcv::rtx { + + RTXModule::RTXModule(Core* core, ASManager* asManager, std::vector<float>& vertices, + std::vector<uint32_t>& indices, std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles){ + m_core = core; + m_asManager = asManager; + // build acceleration structures BLAS then TLAS --> see ASManager + RTXBuffer vertexBuffer = m_asManager->makeBufferFromData(vertices); + RTXBuffer indexBuffer = m_asManager->makeBufferFromData(indices); + m_asManager->buildBLAS(vertexBuffer, vertices.size(), indexBuffer,indices.size()); + m_asManager->buildTLAS(); + RTXDescriptors(descriptorSetHandles); + } + + + RTXModule::~RTXModule() + { + m_core->getContext().getDevice().destroy(m_pipeline); + m_core->getContext().getDevice().destroy(m_pipelineLayout); + m_core->getContext().getDevice().destroy(m_shaderBindingTableBuffer.vulkanHandle); + m_core->getContext().getDevice().freeMemory(m_shaderBindingTableBuffer.deviceMemory); + } + + void RTXModule::createShaderBindingTable(uint32_t shaderCount) { + vk::PhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties; + + vk::PhysicalDeviceProperties2 physicalProperties; + physicalProperties.pNext = &rayTracingProperties; + + m_core->getContext().getPhysicalDevice().getProperties2(&physicalProperties); + + vk::DeviceSize shaderBindingTableSize = rayTracingProperties.shaderGroupHandleSize * shaderCount; + + + m_shaderBindingTableBuffer.bufferType = RTXBufferType::SHADER_BINDING; + m_shaderBindingTableBuffer.bufferUsageFlagBits = vk::BufferUsageFlagBits::eShaderBindingTableKHR | vk::BufferUsageFlagBits::eShaderDeviceAddressKHR; + m_shaderBindingTableBuffer.memoryPropertyFlagBits = vk::MemoryPropertyFlagBits::eHostVisible; + m_shaderBindingTableBuffer.deviceSize = shaderBindingTableSize; + + m_asManager->createBuffer(m_shaderBindingTableBuffer); + + void* shaderHandleStorage = (void*)malloc(sizeof(uint8_t) * shaderBindingTableSize); + + if (m_core->getContext().getDevice().getRayTracingShaderGroupHandlesKHR(m_pipeline, 0, shaderCount, shaderBindingTableSize, + shaderHandleStorage, m_asManager->getDispatcher()) != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "Could not retrieve shader binding table group handles."); + } + + m_shaderGroupBaseAlignment = rayTracingProperties.shaderGroupBaseAlignment; + uint8_t* mapped = (uint8_t*) m_core->getContext().getDevice().mapMemory(m_shaderBindingTableBuffer.deviceMemory, 0, shaderBindingTableSize); + for (size_t i = 0; i < shaderCount; i++) { + memcpy(mapped, (uint8_t*)shaderHandleStorage + (i * rayTracingProperties.shaderGroupHandleSize), rayTracingProperties.shaderGroupHandleSize); + mapped += m_shaderGroupBaseAlignment; + } + + m_core->getContext().getDevice().unmapMemory(m_shaderBindingTableBuffer.deviceMemory); + free(shaderHandleStorage); + } + + ShaderBindingTableRegions RTXModule::createRegions() { + // Define offsets for the RTX shaders. RayGen is the first allocated shader. Each following shader is + // shifted by shaderGroupBaseAlignment. + // Offset Calculation: offset = count of previous shaders * m_shaderGroupBaseAlignment + // Regions are hard coded + vk::DeviceSize rayGenOffset = 0; //First Shader group -> offset 0 * m_shaderGroupBaseAlignment =0 + vk::DeviceSize missOffset = m_shaderGroupBaseAlignment;//Second group, offset = 1 * m_shaderGroupBaseAlignment + vk::DeviceSize closestHitOffset = 2 * m_shaderGroupBaseAlignment; //Third group, offset = 2 * m_shaderGroupBaseAlignment + vk::DeviceSize shaderBindingTableSize = m_shaderGroupBaseAlignment * 3; // 3 hardcoded to rtx-shader count + + auto m_rtxDispatcher = vk::DispatchLoaderDynamic((PFN_vkGetInstanceProcAddr)m_core->getContext().getInstance().getProcAddr("vkGetInstanceProcAddr")); + m_rtxDispatcher.init(m_core->getContext().getInstance()); + + + // Create regions for the shader binding table buffer which are used for vk::CommandBuffer::traceRaysKHR + vk::StridedDeviceAddressRegionKHR rgenRegion; + vk::BufferDeviceAddressInfoKHR shaderBindingTableAddressInfo(m_shaderBindingTableBuffer.vulkanHandle); + rgenRegion.deviceAddress = m_core->getContext().getDevice().getBufferAddressKHR(shaderBindingTableAddressInfo, m_rtxDispatcher) + rayGenOffset; + rgenRegion.setStride(shaderBindingTableSize); + rgenRegion.setSize(shaderBindingTableSize); + vk::StridedDeviceAddressRegionKHR rmissRegion; + rmissRegion.deviceAddress = m_core->getContext().getDevice().getBufferAddressKHR(shaderBindingTableAddressInfo, m_rtxDispatcher) + missOffset; + rmissRegion.setStride(shaderBindingTableSize); + rmissRegion.setSize(shaderBindingTableSize); + vk::StridedDeviceAddressRegionKHR rchitRegion; + rchitRegion.deviceAddress = m_core->getContext().getDevice().getBufferAddressKHR(shaderBindingTableAddressInfo, m_rtxDispatcher) + closestHitOffset; + rchitRegion.setStride(shaderBindingTableSize); + rchitRegion.setSize(shaderBindingTableSize); + vk::StridedDeviceAddressRegionKHR rcallRegion = {}; + + return ShaderBindingTableRegions{ rgenRegion, rmissRegion, rchitRegion, rcallRegion }; + } + + + void RTXModule::RTXDescriptors(std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles) + { + //TLAS-Descriptor-Write + TopLevelAccelerationStructure tlas = m_asManager->getTLAS(); + vk::WriteDescriptorSetAccelerationStructureKHR AccelerationDescriptor = {}; + AccelerationDescriptor.accelerationStructureCount = 1; + const TopLevelAccelerationStructure constTLAS = tlas; + AccelerationDescriptor.pAccelerationStructures = &constTLAS.vulkanHandle; + + vk::WriteDescriptorSet tlasWrite; + tlasWrite.setPNext(&AccelerationDescriptor); + tlasWrite.setDstSet(m_core->getVulkanDescriptorSet(descriptorSetHandles[0])); + tlasWrite.setDstBinding(1); + tlasWrite.setDstArrayElement(0); + tlasWrite.setDescriptorCount(1); + tlasWrite.setDescriptorType(vk::DescriptorType::eAccelerationStructureKHR); + m_core->getContext().getDevice().updateDescriptorSets(tlasWrite, nullptr); + tlasWrite.setDstBinding(2); + m_core->getContext().getDevice().updateDescriptorSets(tlasWrite, nullptr); + + //INDEX & VERTEX BUFFER + BottomLevelAccelerationStructure blas = m_asManager->getBLAS(0);//HARD CODED + + //VERTEX BUFFER + vk::DescriptorBufferInfo vertexInfo = {}; + vertexInfo.setBuffer(blas.vertexBuffer.vulkanHandle); + vertexInfo.setOffset(0); + vertexInfo.setRange(blas.vertexBuffer.deviceSize); //maybe check if size is correct + + vk::WriteDescriptorSet vertexWrite; + vertexWrite.setDstSet(m_core->getVulkanDescriptorSet(descriptorSetHandles[0])); + vertexWrite.setDstBinding(3); + vertexWrite.setDescriptorCount(1); + vertexWrite.setDescriptorType(vk::DescriptorType::eStorageBuffer); + vertexWrite.setPBufferInfo(&vertexInfo); + m_core->getContext().getDevice().updateDescriptorSets(vertexWrite, nullptr); + + //INDEXBUFFER + vk::DescriptorBufferInfo indexInfo = {}; + indexInfo.setBuffer(blas.indexBuffer.vulkanHandle); + indexInfo.setOffset(0); + indexInfo.setRange(blas.indexBuffer.deviceSize); //maybe check if size is correct + + vk::WriteDescriptorSet indexWrite; + indexWrite.setDstSet(m_core->getVulkanDescriptorSet(descriptorSetHandles[0])); + indexWrite.setDstBinding(4); + indexWrite.setDescriptorCount(1); + indexWrite.setDescriptorType(vk::DescriptorType::eStorageBuffer); + indexWrite.setPBufferInfo(&indexInfo); + m_core->getContext().getDevice().updateDescriptorSets(indexWrite, nullptr); + } + + void RTXModule::createRTXPipelineAndLayout(uint32_t pushConstantSize, std::vector<DescriptorSetLayoutHandle> descriptorSetLayouts, ShaderProgram &rtxShader) { + // -- process vkcv::ShaderProgram into vk::ShaderModule + std::vector<uint32_t> rayGenShaderCode = rtxShader.getShaderBinary(ShaderStage::RAY_GEN); + + vk::ShaderModuleCreateInfo rayGenShaderModuleInfo( + vk::ShaderModuleCreateFlags(), // vk::ShaderModuleCreateFlags flags_, + rayGenShaderCode.size() * sizeof(uint32_t), // size_t codeSize + rayGenShaderCode.data() // const uint32_t* pCode + ); + vk::ShaderModule rayGenShaderModule = m_core->getContext().getDevice().createShaderModule(rayGenShaderModuleInfo); + if (!rayGenShaderModule) { + vkcv_log(LogLevel::ERROR, "The Ray Generation Shader Module could not be created!"); + } + + std::vector<uint32_t> rayMissShaderCode = rtxShader.getShaderBinary(ShaderStage::RAY_MISS); + vk::ShaderModuleCreateInfo rayMissShaderModuleInfo( + vk::ShaderModuleCreateFlags(), // vk::ShaderModuleCreateFlags flags_, + rayMissShaderCode.size() * sizeof(uint32_t), //size_t codeSize + rayMissShaderCode.data() // const uint32_t* pCode + ); + + vk::ShaderModule rayMissShaderModule = m_core->getContext().getDevice().createShaderModule(rayMissShaderModuleInfo); + if (!rayMissShaderModule) { + vkcv_log(LogLevel::ERROR, "The Ray Miss Shader Module could not be created!"); + } + + std::vector<uint32_t> rayClosestHitShaderCode = rtxShader.getShaderBinary(ShaderStage::RAY_CLOSEST_HIT); + vk::ShaderModuleCreateInfo rayClosestHitShaderModuleInfo( + vk::ShaderModuleCreateFlags(), // vk::ShaderModuleCreateFlags flags_, + rayClosestHitShaderCode.size() * sizeof(uint32_t), //size_t codeSize + rayClosestHitShaderCode.data() // const uint32_t* pCode_ + ); + vk::ShaderModule rayClosestHitShaderModule = m_core->getContext().getDevice().createShaderModule(rayClosestHitShaderModuleInfo); + if (!rayClosestHitShaderModule) { + vkcv_log(LogLevel::ERROR, "The Ray Closest Hit Shader Module could not be created!"); + } + + // -- PipelineShaderStages + + // ray generation + vk::PipelineShaderStageCreateInfo rayGenShaderStageInfo( + vk::PipelineShaderStageCreateFlags(), // vk::PipelineShaderStageCreateFlags flags_ = {} + vk::ShaderStageFlagBits::eRaygenKHR, // vk::ShaderStageFlagBits stage_ = vk::ShaderStageFlagBits::eVertex, + rayGenShaderModule, // vk::ShaderModule module_ = {}, + "main" // const char* pName_ = {}, + ); + + // ray miss + vk::PipelineShaderStageCreateInfo rayMissShaderStageInfo( + vk::PipelineShaderStageCreateFlags(), // vk::PipelineShaderStageCreateFlags flags_ = {} + vk::ShaderStageFlagBits::eMissKHR, // vk::ShaderStageFlagBits stage_ = vk::ShaderStageFlagBits::eVertex, + rayMissShaderModule, // vk::ShaderModule module_ = {}, + "main" // const char* pName_ = {}, + ); + + // ray closest hit + vk::PipelineShaderStageCreateInfo rayClosestHitShaderStageInfo( + vk::PipelineShaderStageCreateFlags(), // vk::PipelineShaderStageCreateFlags flags_ = {} + vk::ShaderStageFlagBits::eClosestHitKHR, // vk::ShaderStageFlagBits stage_ = vk::ShaderStageFlagBits::eVertex, + rayClosestHitShaderModule, // vk::ShaderModule module_ = {}, + "main" // const char* pName_ = {}, + ); + + std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { // HARD CODED. TODO: Support more shader stages. + rayGenShaderStageInfo, rayMissShaderStageInfo, rayClosestHitShaderStageInfo + }; + + // -- PipelineLayouts + + std::vector<vk::RayTracingShaderGroupCreateInfoKHR> shaderGroups(shaderStages.size()); + // Ray Gen + shaderGroups[0] = vk::RayTracingShaderGroupCreateInfoKHR( + vk::RayTracingShaderGroupTypeKHR::eGeneral, // vk::RayTracingShaderGroupTypeKHR type_ = vk::RayTracingShaderGroupTypeKHR::eGeneral + 0, // uint32_t generalShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t closestHitShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t anyHitShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t intersectionShader_ = {} + nullptr // const void* pShaderGroupCaptureReplayHandle_ = {} + ); + // Ray Miss + shaderGroups[1] = vk::RayTracingShaderGroupCreateInfoKHR( + vk::RayTracingShaderGroupTypeKHR::eGeneral, // vk::RayTracingShaderGroupTypeKHR type_ = vk::RayTracingShaderGroupTypeKHR::eGeneral + 1, // uint32_t generalShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t closestHitShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t anyHitShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t intersectionShader_ = {} + nullptr // const void* pShaderGroupCaptureReplayHandle_ = {} + ); + // Ray Closest Hit + shaderGroups[2] = vk::RayTracingShaderGroupCreateInfoKHR( + vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup, // vk::RayTracingShaderGroupTypeKHR type_ = vk::RayTracingShaderGroupTypeKHR::eGeneral + VK_SHADER_UNUSED_KHR, // uint32_t generalShader_ = {} + 2, // uint32_t closestHitShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t anyHitShader_ = {} + VK_SHADER_UNUSED_KHR, // uint32_t intersectionShader_ = {} + nullptr // const void* pShaderGroupCaptureReplayHandle_ = {} + ); + + std::vector<vk::DescriptorSetLayout> descriptorSetLayoutsVulkan; + for (size_t i=0; i<descriptorSetLayouts.size(); i++) { + descriptorSetLayoutsVulkan.push_back(m_core->getVulkanDescriptorSetLayout(descriptorSetLayouts[i])); + } + + vk::PushConstantRange pushConstant( + vk::ShaderStageFlagBits::eRaygenKHR | vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eMissKHR, // vk::ShaderStageFlags stageFlags_ = {}, + 0, // uint32_t offset_ = {}, + pushConstantSize // uint32_t size_ = {} + ); + + vk::PipelineLayoutCreateInfo rtxPipelineLayoutCreateInfo( + vk::PipelineLayoutCreateFlags(), // vk::PipelineLayoutCreateFlags flags_ = {} + (uint32_t) descriptorSetLayoutsVulkan.size(), // uint32_t setLayoutCount_ = {} HARD CODED (2) + descriptorSetLayoutsVulkan.data(), // const vk::DescriptorSetLayout* pSetLayouts_ = {} + 1, // 0, // uint32_t pushConstantRangeCount_ = {} + &pushConstant // nullptr // const vk::PushConstantRange* pPushConstantRanges_ = {} + ); + + m_pipelineLayout = m_core->getContext().getDevice().createPipelineLayout(rtxPipelineLayoutCreateInfo); + if (!m_pipelineLayout) { + vkcv_log(LogLevel::ERROR, "The RTX Pipeline Layout could not be created!"); + } + + vk::PipelineLibraryCreateInfoKHR rtxPipelineLibraryCreateInfo( + 0, // uint32_t libraryCount_ = {} + nullptr // const vk::Pipeline* pLibraries_ = {} + ); + + // -- RTX Pipeline + + vk::RayTracingPipelineCreateInfoKHR rtxPipelineInfo( + vk::PipelineCreateFlags(), // vk::PipelineCreateFlags flags_ = {} + (uint32_t) shaderStages.size(), // uint32_t stageCount_ = {} + shaderStages.data(), // const vk::PipelineShaderStageCreateInfo* pStages_ = {} + (uint32_t) shaderGroups.size(), // uint32_t groupCount_ = {} + shaderGroups.data(), // const vk::RayTracingShaderGroupCreateInfoKHR* pGroups_ = {} + 16, // uint32_t maxPipelineRayRecursionDepth_ = {} + &rtxPipelineLibraryCreateInfo, // const vk::PipelineLibraryCreateInfoKHR* pLibraryInfo_ = {} + nullptr, // const vk::RayTracingPipelineInterfaceCreateInfoKHR* pLibraryInterface_ = {} + nullptr, // const vk::PipelineDynamicStateCreateInfo* pDynamicState_ = {} + m_pipelineLayout // vk::PipelineLayout layout_ = {} + ); + + auto pipelineResult = m_core->getContext().getDevice().createRayTracingPipelineKHR( + vk::DeferredOperationKHR(), + vk::PipelineCache(), + rtxPipelineInfo, + nullptr, + m_asManager->getDispatcher() + ); + + if (pipelineResult.result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "The RTX Pipeline could not be created!"); + } + + m_pipeline = pipelineResult.value; + + m_core->getContext().getDevice().destroy(rayGenShaderModule); + m_core->getContext().getDevice().destroy(rayMissShaderModule); + m_core->getContext().getDevice().destroy(rayClosestHitShaderModule); + + // TODO: add possibility of more than one shader per stage + createShaderBindingTable(shaderStages.size()); + } + + vk::Pipeline RTXModule::getPipeline() { + return m_pipeline; + } + + vk::Buffer RTXModule::getShaderBindingTableBuffer() + { + return m_shaderBindingTableBuffer.vulkanHandle; + } + + vk::DeviceSize RTXModule::getShaderGroupBaseAlignment() + { + return m_shaderGroupBaseAlignment; + } + + vk::PipelineLayout RTXModule::getPipelineLayout() { + return m_pipelineLayout; + } + +} \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTX.hpp b/projects/rtx_ambient_occlusion/src/RTX/RTX.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ece4ac8e6707039ab3fe166750140a8040aed924 --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/RTX/RTX.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include <vector> +#include "vulkan/vulkan.hpp" +#include <vkcv/Core.hpp> +#include "ASManager.hpp" + +namespace vkcv::rtx { + + //struct that holds all shader binding table regions + struct ShaderBindingTableRegions { + vk::StridedDeviceAddressRegionKHR rgenRegion; + vk::StridedDeviceAddressRegionKHR rmissRegion; + vk::StridedDeviceAddressRegionKHR rchitRegion; + vk::StridedDeviceAddressRegionKHR rcallRegion; + }; + + class RTXModule { + private: + + Core* m_core; + ASManager* m_asManager; + vk::Pipeline m_pipeline; + vk::PipelineLayout m_pipelineLayout; + RTXBuffer m_shaderBindingTableBuffer; + vk::DeviceSize m_shaderGroupBaseAlignment; + + public: + + /** + * @brief Initializes the @#RTXModule with scene data. + * @param core The reference to the @#Core. + * @param asManager The reference to the @#ASManager. + * @param vertices The vertex data of the scene. + * @param indices The index data of the scene. + * @param descriptorSetHandles The descriptor set handles for RTX. + */ + RTXModule(Core* core, ASManager* asManager, std::vector<float>& vertices, + std::vector<uint32_t>& indices, std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles); + + /** + * @brief Default #RTXModule destructor. + */ + ~RTXModule(); + + /** + * @brief Returns the RTX pipeline. + * @return The RTX pipeline. + */ + vk::Pipeline getPipeline(); + + /** + * @brief Returns the shader binding table buffer. + * @return The shader binding table buffer. + */ + vk::Buffer getShaderBindingTableBuffer(); + + /** + * @brief Returns the shader group base alignment for partitioning the shader binding table buffer. + * @return The shader group base alignment. + */ + vk::DeviceSize getShaderGroupBaseAlignment(); + + /** + * @brief Returns the RTX pipeline layout. + * @return The RTX pipeline layout. + */ + vk::PipelineLayout getPipelineLayout(); + + /** + * @brief Sets the shader group base alignment and creates the shader binding table by allocating a shader + * binding table buffer. The allocation depends on @p shaderCount and the shader group base alignment. + * @param shaderCount The amount of shaders to be used for RTX. + */ + void createShaderBindingTable(uint32_t shaderCount); + + /** + * @brief Divides the shader binding table into regions for each shader type + * (ray generation, ray miss, ray closest hit, callable) and returns them as a struct. + * @return The struct holding all four regions of type vk::StridedDeviceAddressRegionKHR. + */ + ShaderBindingTableRegions createRegions(); + + /** + * @brief Creates Descriptor-Writes for RTX + * @param descriptorSetHandles The descriptorSetHandles for RTX. + */ + void RTXDescriptors(std::vector<vkcv::DescriptorSetHandle>& descriptorSetHandles); + + /** + * @brief Creates the RTX pipeline and the RTX pipeline layout. Currently, only RayGen, RayClosestHit and + * RayMiss are supported. + * @param pushConstantSize The size of the push constant used in the RTX shaders. + * @param descriptorSetLayouts The descriptor set layout handles. + * @param rtxShader The RTX shader program. + */ + void createRTXPipelineAndLayout(uint32_t pushConstantSize, std::vector<DescriptorSetLayoutHandle> descriptorSetLayouts, ShaderProgram &rtxShader); + }; + +} diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTXExtensions.cpp b/projects/rtx_ambient_occlusion/src/RTX/RTXExtensions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0bc6a4d3dff358bdeab74d9d5da695f81c3675c7 --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/RTX/RTXExtensions.cpp @@ -0,0 +1,66 @@ +#include "RTXExtensions.hpp" + +namespace vkcv::rtx{ + +RTXExtensions::RTXExtensions() +{ + + // prepare needed raytracing extensions + m_instanceExtensions = { + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME + }; + m_deviceExtensions = { + VK_KHR_MAINTENANCE3_EXTENSION_NAME, + VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, + VK_KHR_SPIRV_1_4_EXTENSION_NAME, + VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME + }; + + // get all features required by the device extensions + for (auto deviceExtension : m_deviceExtensions) { + m_features.requireExtension(deviceExtension); + } + + m_features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>( + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, + [](vk::PhysicalDeviceDescriptorIndexingFeatures& features) {} + ); + + m_features.requireExtensionFeature<vk::PhysicalDeviceBufferDeviceAddressFeatures>( + VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, + [](vk::PhysicalDeviceBufferDeviceAddressFeatures& features) { + features.setBufferDeviceAddress(true); + } + ); + + m_features.requireExtensionFeature<vk::PhysicalDeviceAccelerationStructureFeaturesKHR>( + VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, + [](vk::PhysicalDeviceAccelerationStructureFeaturesKHR& features) { + features.setAccelerationStructure(true); + } + ); + + m_features.requireExtensionFeature<vk::PhysicalDeviceRayTracingPipelineFeaturesKHR>( + VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME, + [](vk::PhysicalDeviceRayTracingPipelineFeaturesKHR& features) { + features.setRayTracingPipeline(true); + } + ); +} + +std::vector<const char*> RTXExtensions::getInstanceExtensions() +{ + return m_instanceExtensions; +} + +std::vector<const char*> RTXExtensions::getDeviceExtensions() +{ + return m_deviceExtensions; +} + +vkcv::Features RTXExtensions::getFeatures() +{ + return m_features; +} + +} \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/src/RTX/RTXExtensions.hpp b/projects/rtx_ambient_occlusion/src/RTX/RTXExtensions.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5ad5ae0769b75aa18672648b5eb3cb598b6f8d47 --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/RTX/RTXExtensions.hpp @@ -0,0 +1,36 @@ +#pragma once +#include <vector> +#include <vkcv/Core.hpp> + +namespace vkcv::rtx{ + +class RTXExtensions { +private: + std::vector<const char*> m_instanceExtensions; // the instance extensions needed for using RTX + std::vector<const char*> m_deviceExtensions; // the device extensions needed for using RTX + vkcv::Features m_features; // the features needed to be enabled for using RTX +public: + + RTXExtensions(); + ~RTXExtensions()=default; + /** + * @brief Returns the raytracing instance extensions. + * @return The raytracing instance extensions. + */ + std::vector<const char*> getInstanceExtensions(); + + /** + * @brief Returns the raytracing device extensions. + * @return The raytracing device extensions. + */ + std::vector<const char*> getDeviceExtensions(); + + /** + * @brief Returns the raytracing features. + * @return The raytracing features. + */ + vkcv::Features getFeatures(); + + +}; +} \ No newline at end of file diff --git a/projects/rtx_ambient_occlusion/src/main.cpp b/projects/rtx_ambient_occlusion/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a9d5d204d9c09a5c007494fadd2b3e2285ba372 --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/main.cpp @@ -0,0 +1,150 @@ +#include <vkcv/Core.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <vkcv/shader/GLSLCompiler.hpp> +#include "RTX/RTX.hpp" +#include "RTX/RTXExtensions.hpp" +#include "teapot.hpp" + +/** + * Note: This project is based on the following tutorial https://github.com/Apress/Ray-Tracing-Gems-II/tree/main/Chapter_16. + */ + +int main(int argc, const char** argv) { + const std::string applicationName = "RTX Ambient Occlusion"; + + // prepare raytracing extensions. IMPORTANT: configure compiler to build in 64 bit mode + vkcv::rtx::RTXExtensions rtxExtensions; + std::vector<const char*> raytracingInstanceExtensions = rtxExtensions.getInstanceExtensions(); + + std::vector<const char*> instanceExtensions = {}; // add some more instance extensions, if needed + instanceExtensions.insert(instanceExtensions.end(), raytracingInstanceExtensions.begin(), raytracingInstanceExtensions.end()); // merge together all instance extensions + + vkcv::Features features = rtxExtensions.getFeatures(); // all features required by the RTX device extensions + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eGraphics ,vk::QueueFlagBits::eCompute , vk::QueueFlagBits::eTransfer }, + features, + instanceExtensions + ); + + vkcv::rtx::ASManager asManager(&core); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 800, 600, true); + + vkcv::camera::CameraManager cameraManager(core.getWindow(windowHandle)); + auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -3)); + cameraManager.getCamera(camHandle).setNearFar(0.1f, 30.0f); + + // get Teapot vertices and indices + Teapot teapot; + std::vector<float> vertices = teapot.getVertices(); + std::vector<uint32_t> indices = teapot.getIndices(); + + vkcv::shader::GLSLCompiler compiler (vkcv::shader::GLSLCompileTarget::RAY_TRACING); + + vkcv::ShaderProgram rtxShaderProgram; + compiler.compile(vkcv::ShaderStage::RAY_GEN, std::filesystem::path("resources/shaders/ambientOcclusion.rgen"), + [&rtxShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + rtxShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::RAY_CLOSEST_HIT, std::filesystem::path("resources/shaders/ambientOcclusion.rchit"), + [&rtxShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + rtxShaderProgram.addShader(shaderStage, path); + }); + + compiler.compile(vkcv::ShaderStage::RAY_MISS, std::filesystem::path("resources/shaders/ambientOcclusion.rmiss"), + [&rtxShaderProgram](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + rtxShaderProgram.addShader(shaderStage, path); + }); + + std::vector<vkcv::DescriptorSetHandle> descriptorSetHandles; + std::vector<vkcv::DescriptorSetLayoutHandle> descriptorSetLayoutHandles; + + vkcv::DescriptorSetLayoutHandle rtxShaderDescriptorSetLayout = core.createDescriptorSetLayout(rtxShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle rtxShaderDescriptorSet = core.createDescriptorSet(rtxShaderDescriptorSetLayout); + descriptorSetHandles.push_back(rtxShaderDescriptorSet); + descriptorSetLayoutHandles.push_back(rtxShaderDescriptorSetLayout); + + // init RTXModule + vkcv::rtx::RTXModule rtxModule(&core, &asManager, vertices, indices,descriptorSetHandles); + + struct RaytracingPushConstantData { + glm::vec4 camera_position; // as origin for ray generation + glm::vec4 camera_right; // for computing ray direction + glm::vec4 camera_up; // for computing ray direction + glm::vec4 camera_forward; // for computing ray direction + }; + + uint32_t pushConstantSize = sizeof(RaytracingPushConstantData); + + rtxModule.createRTXPipelineAndLayout(pushConstantSize, descriptorSetLayoutHandles, rtxShaderProgram); + + vk::Pipeline rtxPipeline = rtxModule.getPipeline(); + vk::PipelineLayout rtxPipelineLayout = rtxModule.getPipelineLayout(); + + vkcv::rtx::ShaderBindingTableRegions rtxRegions = rtxModule.createRegions(); + + vkcv::ImageHandle depthBuffer; + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::DescriptorWrites rtxWrites; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((!depthBuffer) || + (swapchainWidth != core.getImageWidth(depthBuffer)) || + ((swapchainHeight != core.getImageHeight(depthBuffer)))) { + depthBuffer = core.createImage( + vk::Format::eD32Sfloat, + vkcv::ImageConfig( + swapchainWidth, + swapchainHeight + ) + ); + } + + cameraManager.update(dt); + + const std::vector<vkcv::ImageHandle> renderTargets = { swapchainInput, depthBuffer }; + + RaytracingPushConstantData raytracingPushData; + raytracingPushData.camera_position = glm::vec4(cameraManager.getActiveCamera().getPosition(),0); + raytracingPushData.camera_right = glm::vec4(glm::cross(cameraManager.getActiveCamera().getUp(), cameraManager.getActiveCamera().getFront()), 0); + raytracingPushData.camera_up = glm::vec4(cameraManager.getActiveCamera().getUp(),0); + raytracingPushData.camera_forward = glm::vec4(cameraManager.getActiveCamera().getFront(),0); + + vkcv::PushConstants pushConstantsRTX = vkcv::pushConstants<RaytracingPushConstantData>(); + pushConstantsRTX.appendDrawcall(raytracingPushData); + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + rtxWrites.writeStorageImage(0, swapchainInput); + core.writeDescriptorSet(rtxShaderDescriptorSet, rtxWrites); + + core.prepareImageForStorage(cmdStream, swapchainInput); + + core.recordRayGenerationToCmdStream( + cmdStream, + rtxPipeline, + rtxPipelineLayout, + rtxRegions.rgenRegion, + rtxRegions.rmissRegion, + rtxRegions.rchitRegion, + rtxRegions.rcallRegion, + { vkcv::useDescriptorSet(0, rtxShaderDescriptorSet) }, + pushConstantsRTX, + windowHandle); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + }); + + return 0; +} diff --git a/projects/rtx_ambient_occlusion/src/teapot.hpp b/projects/rtx_ambient_occlusion/src/teapot.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1f35400a313b230f92098fa2ef59cc3c8f3a30df --- /dev/null +++ b/projects/rtx_ambient_occlusion/src/teapot.hpp @@ -0,0 +1,7114 @@ +#pragma once +#include <vector> + +class Teapot { +public: + /** + * @brief The default constructor. + */ + Teapot() = default; + + /** + * default destructor + */ + ~Teapot() = default; + + /** + * @brief Returns the vertex data of the teapot. + * @return The vertex data of the teapot. + */ + std::vector<float> getVertices() { + return m_teapotVertices; + } + + /** + * @brief Returns the index data of the teapot. + * @return The index data of the teapot. + */ + std::vector<uint32_t> getIndices() { + return m_teapotIndices; + } + +private: + + std::vector<float> m_teapotVertices = + { + 0.69999999f, 0.45000005f, 0.00000001f, + 0.69098389f, 0.44999996f, 0.11485600f, + 0.66483200f, 0.45000011f, 0.22332802f, + 0.62288797f, 0.45000002f, 0.32407200f, + 0.56649601f, 0.45000008f, 0.41574404f, + 0.49699998f, 0.45000005f, 0.49700001f, + 0.41574395f, 0.45000005f, 0.56649601f, + 0.32407200f, 0.45000008f, 0.62288797f, + 0.22332799f, 0.45000005f, 0.66483200f, + 0.11485596f, 0.45000005f, 0.69098401f, + 0.00000000f, 0.45000005f, 0.69999999f, + 0.69296241f, 0.46771872f, 0.00000001f, + 0.68403697f, 0.46771869f, 0.11370128f, + 0.65814799f, 0.46771878f, 0.22108276f, + 0.61662567f, 0.46771872f, 0.32081389f, + 0.56080067f, 0.46771872f, 0.41156429f, + 0.49200332f, 0.46771872f, 0.49200332f, + 0.41156423f, 0.46771872f, 0.56080067f, + 0.32081389f, 0.46771872f, 0.61662567f, + 0.22108275f, 0.46771872f, 0.65814799f, + 0.11370124f, 0.46771872f, 0.68403703f, + 0.00000000f, 0.46771872f, 0.69296241f, + 0.69020003f, 0.48150003f, 0.00000001f, + 0.68131018f, 0.48149997f, 0.11324803f, + 0.65552443f, 0.48150006f, 0.22020143f, + 0.61416763f, 0.48150000f, 0.31953502f, + 0.55856514f, 0.48150009f, 0.40992361f, + 0.49004203f, 0.48150003f, 0.49004203f, + 0.40992361f, 0.48150006f, 0.55856514f, + 0.31953505f, 0.48150003f, 0.61416763f, + 0.22020143f, 0.48150003f, 0.65552437f, + 0.11324799f, 0.48150003f, 0.68131030f, + 0.00000000f, 0.48150003f, 0.69020003f, + 0.69111252f, 0.49134374f, 0.00000001f, + 0.68221092f, 0.49134374f, 0.11339775f, + 0.65639102f, 0.49134377f, 0.22049254f, + 0.61497957f, 0.49134377f, 0.31995746f, + 0.55930358f, 0.49134377f, 0.41046557f, + 0.49068987f, 0.49134374f, 0.49068987f, + 0.41046554f, 0.49134374f, 0.55930358f, + 0.31995746f, 0.49134374f, 0.61497957f, + 0.22049253f, 0.49134377f, 0.65639102f, + 0.11339770f, 0.49134374f, 0.68221098f, + 0.00000000f, 0.49134374f, 0.69111252f, + 0.69510001f, 0.49725008f, 0.00000001f, + 0.68614709f, 0.49725005f, 0.11405202f, + 0.66017824f, 0.49725014f, 0.22176473f, + 0.61852777f, 0.49725008f, 0.32180351f, + 0.56253058f, 0.49725008f, 0.41283381f, + 0.49352103f, 0.49725008f, 0.49352100f, + 0.41283381f, 0.49725008f, 0.56253058f, + 0.32180351f, 0.49725008f, 0.61852777f, + 0.22176471f, 0.49725008f, 0.66017818f, + 0.11405198f, 0.49725008f, 0.68614715f, + 0.00000000f, 0.49725008f, 0.69510001f, + 0.70156252f, 0.49921876f, 0.00000001f, + 0.69252634f, 0.49921870f, 0.11511238f, + 0.66631603f, 0.49921876f, 0.22382653f, + 0.62427837f, 0.49921873f, 0.32479537f, + 0.56776059f, 0.49921879f, 0.41667202f, + 0.49810940f, 0.49921876f, 0.49810940f, + 0.41667199f, 0.49921879f, 0.56776053f, + 0.32479542f, 0.49921876f, 0.62427843f, + 0.22382650f, 0.49921876f, 0.66631603f, + 0.11511234f, 0.49921876f, 0.69252640f, + 0.00000000f, 0.49921876f, 0.70156252f, + 0.70989996f, 0.49725002f, 0.00000001f, + 0.70075637f, 0.49724996f, 0.11648039f, + 0.67423457f, 0.49725008f, 0.22648652f, + 0.63169742f, 0.49724999f, 0.32865530f, + 0.57450789f, 0.49725002f, 0.42162383f, + 0.50402898f, 0.49725002f, 0.50402898f, + 0.42162377f, 0.49725002f, 0.57450783f, + 0.32865530f, 0.49725002f, 0.63169742f, + 0.22648649f, 0.49725005f, 0.67423463f, + 0.11648035f, 0.49725002f, 0.70075643f, + 0.00000000f, 0.49725002f, 0.70989996f, + 0.71951252f, 0.49134377f, 0.00000001f, + 0.71024513f, 0.49134374f, 0.11805762f, + 0.68336421f, 0.49134380f, 0.22955328f, + 0.64025098f, 0.49134377f, 0.33310553f, + 0.58228713f, 0.49134383f, 0.42733288f, + 0.51085389f, 0.49134377f, 0.51085389f, + 0.42733288f, 0.49134377f, 0.58228713f, + 0.33310553f, 0.49134377f, 0.64025104f, + 0.22955328f, 0.49134380f, 0.68336427f, + 0.11805756f, 0.49134377f, 0.71024519f, + 0.00000000f, 0.49134377f, 0.71951252f, + 0.72979999f, 0.48150003f, 0.00000001f, + 0.72040009f, 0.48149997f, 0.11974559f, + 0.69313490f, 0.48150006f, 0.23283541f, + 0.64940518f, 0.48150000f, 0.33786824f, + 0.59061253f, 0.48150009f, 0.43344283f, + 0.51815796f, 0.48150003f, 0.51815796f, + 0.43344280f, 0.48150006f, 0.59061259f, + 0.33786821f, 0.48150003f, 0.64940524f, + 0.23283540f, 0.48150003f, 0.69313484f, + 0.11974555f, 0.48150003f, 0.72040015f, + 0.00000000f, 0.48150003f, 0.72979999f, + 0.74016249f, 0.46771878f, 0.00000001f, + 0.73062909f, 0.46771872f, 0.12144586f, + 0.70297670f, 0.46771881f, 0.23614147f, + 0.65862614f, 0.46771878f, 0.34266564f, + 0.59899879f, 0.46771878f, 0.43959734f, + 0.52551538f, 0.46771878f, 0.52551538f, + 0.43959731f, 0.46771878f, 0.59899873f, + 0.34266564f, 0.46771878f, 0.65862620f, + 0.23614144f, 0.46771878f, 0.70297670f, + 0.12144582f, 0.46771878f, 0.73062921f, + 0.00000000f, 0.46771878f, 0.74016249f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.74033999f, 0.44999996f, 0.12306000f, + 0.71232003f, 0.45000011f, 0.23928000f, + 0.66737998f, 0.45000002f, 0.34721997f, + 0.60696000f, 0.45000008f, 0.44544002f, + 0.53250003f, 0.45000005f, 0.53250003f, + 0.44543999f, 0.45000005f, 0.60696000f, + 0.34722000f, 0.45000008f, 0.66738003f, + 0.23927999f, 0.45000005f, 0.71231997f, + 0.12305996f, 0.45000005f, 0.74033999f, + 0.00000000f, 0.45000005f, 0.75000000f, + 0.00000000f, 0.45000005f, -0.69999999f, + 0.11485599f, 0.44999996f, -0.69098389f, + 0.22332802f, 0.45000011f, -0.66483200f, + 0.32407200f, 0.45000002f, -0.62288797f, + 0.41574404f, 0.45000008f, -0.56649601f, + 0.49700001f, 0.45000005f, -0.49699998f, + 0.56649601f, 0.45000005f, -0.41574395f, + 0.62288797f, 0.45000008f, -0.32407200f, + 0.66483200f, 0.45000005f, -0.22332799f, + 0.69098401f, 0.45000005f, -0.11485595f, + 0.69999999f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.46771872f, -0.69296241f, + 0.11370128f, 0.46771869f, -0.68403697f, + 0.22108276f, 0.46771878f, -0.65814799f, + 0.32081389f, 0.46771872f, -0.61662567f, + 0.41156429f, 0.46771872f, -0.56080067f, + 0.49200332f, 0.46771872f, -0.49200332f, + 0.56080067f, 0.46771872f, -0.41156423f, + 0.61662567f, 0.46771872f, -0.32081389f, + 0.65814799f, 0.46771872f, -0.22108275f, + 0.68403703f, 0.46771872f, -0.11370123f, + 0.69296241f, 0.46771872f, 0.00000001f, + 0.00000000f, 0.48150003f, -0.69020003f, + 0.11324802f, 0.48149997f, -0.68131018f, + 0.22020143f, 0.48150006f, -0.65552443f, + 0.31953502f, 0.48150000f, -0.61416763f, + 0.40992361f, 0.48150009f, -0.55856514f, + 0.49004203f, 0.48150003f, -0.49004203f, + 0.55856514f, 0.48150006f, -0.40992361f, + 0.61416763f, 0.48150003f, -0.31953505f, + 0.65552437f, 0.48150003f, -0.22020143f, + 0.68131030f, 0.48150003f, -0.11324798f, + 0.69020003f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.49134374f, -0.69111252f, + 0.11339774f, 0.49134374f, -0.68221092f, + 0.22049254f, 0.49134377f, -0.65639102f, + 0.31995746f, 0.49134377f, -0.61497957f, + 0.41046557f, 0.49134377f, -0.55930358f, + 0.49068987f, 0.49134374f, -0.49068987f, + 0.55930358f, 0.49134374f, -0.41046554f, + 0.61497957f, 0.49134374f, -0.31995746f, + 0.65639102f, 0.49134377f, -0.22049253f, + 0.68221098f, 0.49134374f, -0.11339770f, + 0.69111252f, 0.49134374f, 0.00000001f, + 0.00000000f, 0.49725008f, -0.69510001f, + 0.11405201f, 0.49725005f, -0.68614709f, + 0.22176473f, 0.49725014f, -0.66017824f, + 0.32180351f, 0.49725008f, -0.61852777f, + 0.41283381f, 0.49725008f, -0.56253058f, + 0.49352100f, 0.49725008f, -0.49352103f, + 0.56253058f, 0.49725008f, -0.41283381f, + 0.61852777f, 0.49725008f, -0.32180351f, + 0.66017818f, 0.49725008f, -0.22176471f, + 0.68614715f, 0.49725008f, -0.11405197f, + 0.69510001f, 0.49725008f, 0.00000001f, + 0.00000000f, 0.49921876f, -0.70156252f, + 0.11511238f, 0.49921870f, -0.69252634f, + 0.22382653f, 0.49921876f, -0.66631603f, + 0.32479537f, 0.49921873f, -0.62427837f, + 0.41667202f, 0.49921879f, -0.56776059f, + 0.49810940f, 0.49921876f, -0.49810940f, + 0.56776053f, 0.49921879f, -0.41667199f, + 0.62427843f, 0.49921876f, -0.32479542f, + 0.66631603f, 0.49921876f, -0.22382650f, + 0.69252640f, 0.49921876f, -0.11511233f, + 0.70156252f, 0.49921876f, 0.00000001f, + 0.00000000f, 0.49725002f, -0.70989996f, + 0.11648039f, 0.49724996f, -0.70075637f, + 0.22648652f, 0.49725008f, -0.67423457f, + 0.32865530f, 0.49724999f, -0.63169742f, + 0.42162383f, 0.49725002f, -0.57450789f, + 0.50402898f, 0.49725002f, -0.50402898f, + 0.57450783f, 0.49725002f, -0.42162377f, + 0.63169742f, 0.49725002f, -0.32865530f, + 0.67423463f, 0.49725005f, -0.22648649f, + 0.70075643f, 0.49725002f, -0.11648034f, + 0.70989996f, 0.49725002f, 0.00000001f, + 0.00000000f, 0.49134377f, -0.71951252f, + 0.11805761f, 0.49134374f, -0.71024513f, + 0.22955328f, 0.49134380f, -0.68336421f, + 0.33310553f, 0.49134377f, -0.64025098f, + 0.42733288f, 0.49134383f, -0.58228713f, + 0.51085389f, 0.49134377f, -0.51085389f, + 0.58228713f, 0.49134377f, -0.42733288f, + 0.64025104f, 0.49134377f, -0.33310553f, + 0.68336427f, 0.49134380f, -0.22955328f, + 0.71024519f, 0.49134377f, -0.11805756f, + 0.71951252f, 0.49134377f, 0.00000001f, + 0.00000000f, 0.48150003f, -0.72979999f, + 0.11974558f, 0.48149997f, -0.72040009f, + 0.23283541f, 0.48150006f, -0.69313490f, + 0.33786824f, 0.48150000f, -0.64940518f, + 0.43344283f, 0.48150009f, -0.59061253f, + 0.51815796f, 0.48150003f, -0.51815796f, + 0.59061259f, 0.48150006f, -0.43344280f, + 0.64940524f, 0.48150003f, -0.33786821f, + 0.69313484f, 0.48150003f, -0.23283540f, + 0.72040015f, 0.48150003f, -0.11974554f, + 0.72979999f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.46771878f, -0.74016249f, + 0.12144586f, 0.46771872f, -0.73062909f, + 0.23614147f, 0.46771881f, -0.70297670f, + 0.34266564f, 0.46771878f, -0.65862614f, + 0.43959734f, 0.46771878f, -0.59899879f, + 0.52551538f, 0.46771878f, -0.52551538f, + 0.59899873f, 0.46771878f, -0.43959731f, + 0.65862620f, 0.46771878f, -0.34266564f, + 0.70297670f, 0.46771878f, -0.23614144f, + 0.73062921f, 0.46771878f, -0.12144581f, + 0.74016249f, 0.46771878f, 0.00000001f, + 0.00000000f, 0.45000005f, -0.75000000f, + 0.12305999f, 0.44999996f, -0.74033999f, + 0.23928000f, 0.45000011f, -0.71232003f, + 0.34721997f, 0.45000002f, -0.66737998f, + 0.44544002f, 0.45000008f, -0.60696000f, + 0.53250003f, 0.45000005f, -0.53250003f, + 0.60696000f, 0.45000005f, -0.44543999f, + 0.66738003f, 0.45000008f, -0.34722000f, + 0.71231997f, 0.45000005f, -0.23927999f, + 0.74033999f, 0.45000005f, -0.12305995f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.45000005f, 0.69999999f, + -0.11485599f, 0.44999996f, 0.69098389f, + -0.22332802f, 0.45000011f, 0.66483200f, + -0.32407200f, 0.45000002f, 0.62288797f, + -0.41574404f, 0.45000008f, 0.56649601f, + -0.49700001f, 0.45000005f, 0.49699998f, + -0.56649601f, 0.45000005f, 0.41574395f, + -0.62288797f, 0.45000008f, 0.32407200f, + -0.66483200f, 0.45000005f, 0.22332799f, + -0.69098401f, 0.45000005f, 0.11485597f, + -0.69999999f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.46771872f, 0.69296241f, + -0.11370128f, 0.46771869f, 0.68403697f, + -0.22108276f, 0.46771878f, 0.65814799f, + -0.32081389f, 0.46771872f, 0.61662567f, + -0.41156429f, 0.46771872f, 0.56080067f, + -0.49200332f, 0.46771872f, 0.49200332f, + -0.56080067f, 0.46771872f, 0.41156423f, + -0.61662567f, 0.46771872f, 0.32081389f, + -0.65814799f, 0.46771872f, 0.22108275f, + -0.68403703f, 0.46771872f, 0.11370125f, + -0.69296241f, 0.46771872f, 0.00000001f, + 0.00000000f, 0.48150003f, 0.69020003f, + -0.11324802f, 0.48149997f, 0.68131018f, + -0.22020143f, 0.48150006f, 0.65552443f, + -0.31953502f, 0.48150000f, 0.61416763f, + -0.40992361f, 0.48150009f, 0.55856514f, + -0.49004203f, 0.48150003f, 0.49004203f, + -0.55856514f, 0.48150006f, 0.40992361f, + -0.61416763f, 0.48150003f, 0.31953505f, + -0.65552437f, 0.48150003f, 0.22020143f, + -0.68131030f, 0.48150003f, 0.11324800f, + -0.69020003f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.49134374f, 0.69111252f, + -0.11339774f, 0.49134374f, 0.68221092f, + -0.22049254f, 0.49134377f, 0.65639102f, + -0.31995746f, 0.49134377f, 0.61497957f, + -0.41046557f, 0.49134377f, 0.55930358f, + -0.49068987f, 0.49134374f, 0.49068987f, + -0.55930358f, 0.49134374f, 0.41046554f, + -0.61497957f, 0.49134374f, 0.31995746f, + -0.65639102f, 0.49134377f, 0.22049253f, + -0.68221098f, 0.49134374f, 0.11339771f, + -0.69111252f, 0.49134374f, 0.00000001f, + 0.00000000f, 0.49725008f, 0.69510001f, + -0.11405201f, 0.49725005f, 0.68614709f, + -0.22176473f, 0.49725014f, 0.66017824f, + -0.32180351f, 0.49725008f, 0.61852777f, + -0.41283381f, 0.49725008f, 0.56253058f, + -0.49352100f, 0.49725008f, 0.49352103f, + -0.56253058f, 0.49725008f, 0.41283381f, + -0.61852777f, 0.49725008f, 0.32180351f, + -0.66017818f, 0.49725008f, 0.22176471f, + -0.68614715f, 0.49725008f, 0.11405198f, + -0.69510001f, 0.49725008f, 0.00000001f, + 0.00000000f, 0.49921876f, 0.70156252f, + -0.11511238f, 0.49921870f, 0.69252634f, + -0.22382653f, 0.49921876f, 0.66631603f, + -0.32479537f, 0.49921873f, 0.62427837f, + -0.41667202f, 0.49921879f, 0.56776059f, + -0.49810940f, 0.49921876f, 0.49810940f, + -0.56776053f, 0.49921879f, 0.41667199f, + -0.62427843f, 0.49921876f, 0.32479542f, + -0.66631603f, 0.49921876f, 0.22382650f, + -0.69252640f, 0.49921876f, 0.11511235f, + -0.70156252f, 0.49921876f, 0.00000001f, + 0.00000000f, 0.49725002f, 0.70989996f, + -0.11648039f, 0.49724996f, 0.70075637f, + -0.22648652f, 0.49725008f, 0.67423457f, + -0.32865530f, 0.49724999f, 0.63169742f, + -0.42162383f, 0.49725002f, 0.57450789f, + -0.50402898f, 0.49725002f, 0.50402898f, + -0.57450783f, 0.49725002f, 0.42162377f, + -0.63169742f, 0.49725002f, 0.32865530f, + -0.67423463f, 0.49725005f, 0.22648649f, + -0.70075643f, 0.49725002f, 0.11648036f, + -0.70989996f, 0.49725002f, 0.00000001f, + 0.00000000f, 0.49134377f, 0.71951252f, + -0.11805761f, 0.49134374f, 0.71024513f, + -0.22955328f, 0.49134380f, 0.68336421f, + -0.33310553f, 0.49134377f, 0.64025098f, + -0.42733288f, 0.49134383f, 0.58228713f, + -0.51085389f, 0.49134377f, 0.51085389f, + -0.58228713f, 0.49134377f, 0.42733288f, + -0.64025104f, 0.49134377f, 0.33310553f, + -0.68336427f, 0.49134380f, 0.22955328f, + -0.71024519f, 0.49134377f, 0.11805757f, + -0.71951252f, 0.49134377f, 0.00000001f, + 0.00000000f, 0.48150003f, 0.72979999f, + -0.11974558f, 0.48149997f, 0.72040009f, + -0.23283541f, 0.48150006f, 0.69313490f, + -0.33786824f, 0.48150000f, 0.64940518f, + -0.43344283f, 0.48150009f, 0.59061253f, + -0.51815796f, 0.48150003f, 0.51815796f, + -0.59061259f, 0.48150006f, 0.43344280f, + -0.64940524f, 0.48150003f, 0.33786821f, + -0.69313484f, 0.48150003f, 0.23283540f, + -0.72040015f, 0.48150003f, 0.11974555f, + -0.72979999f, 0.48150003f, 0.00000001f, + 0.00000000f, 0.46771878f, 0.74016249f, + -0.12144586f, 0.46771872f, 0.73062909f, + -0.23614147f, 0.46771881f, 0.70297670f, + -0.34266564f, 0.46771878f, 0.65862614f, + -0.43959734f, 0.46771878f, 0.59899879f, + -0.52551538f, 0.46771878f, 0.52551538f, + -0.59899873f, 0.46771878f, 0.43959731f, + -0.65862620f, 0.46771878f, 0.34266564f, + -0.70297670f, 0.46771878f, 0.23614144f, + -0.73062921f, 0.46771878f, 0.12144583f, + -0.74016249f, 0.46771878f, 0.00000001f, + 0.00000000f, 0.45000005f, 0.75000000f, + -0.12305999f, 0.44999996f, 0.74033999f, + -0.23928000f, 0.45000011f, 0.71232003f, + -0.34721997f, 0.45000002f, 0.66737998f, + -0.44544002f, 0.45000008f, 0.60696000f, + -0.53250003f, 0.45000005f, 0.53250003f, + -0.60696000f, 0.45000005f, 0.44543999f, + -0.66738003f, 0.45000008f, 0.34722000f, + -0.71231997f, 0.45000005f, 0.23927999f, + -0.74033999f, 0.45000005f, 0.12305997f, + -0.75000000f, 0.45000005f, 0.00000001f, + -0.69999999f, 0.45000005f, 0.00000001f, + -0.69098389f, 0.44999996f, -0.11485598f, + -0.66483200f, 0.45000011f, -0.22332802f, + -0.62288797f, 0.45000002f, -0.32407200f, + -0.56649601f, 0.45000008f, -0.41574404f, + -0.49699998f, 0.45000005f, -0.49700001f, + -0.41574395f, 0.45000005f, -0.56649601f, + -0.32407200f, 0.45000008f, -0.62288797f, + -0.22332799f, 0.45000005f, -0.66483200f, + -0.11485596f, 0.45000005f, -0.69098401f, + 0.00000000f, 0.45000005f, -0.69999999f, + -0.69296241f, 0.46771872f, 0.00000001f, + -0.68403697f, 0.46771869f, -0.11370127f, + -0.65814799f, 0.46771878f, -0.22108276f, + -0.61662567f, 0.46771872f, -0.32081389f, + -0.56080067f, 0.46771872f, -0.41156429f, + -0.49200332f, 0.46771872f, -0.49200332f, + -0.41156423f, 0.46771872f, -0.56080067f, + -0.32081389f, 0.46771872f, -0.61662567f, + -0.22108275f, 0.46771872f, -0.65814799f, + -0.11370124f, 0.46771872f, -0.68403703f, + 0.00000000f, 0.46771872f, -0.69296241f, + -0.69020003f, 0.48150003f, 0.00000001f, + -0.68131018f, 0.48149997f, -0.11324801f, + -0.65552443f, 0.48150006f, -0.22020143f, + -0.61416763f, 0.48150000f, -0.31953502f, + -0.55856514f, 0.48150009f, -0.40992361f, + -0.49004203f, 0.48150003f, -0.49004203f, + -0.40992361f, 0.48150006f, -0.55856514f, + -0.31953505f, 0.48150003f, -0.61416763f, + -0.22020143f, 0.48150003f, -0.65552437f, + -0.11324799f, 0.48150003f, -0.68131030f, + 0.00000000f, 0.48150003f, -0.69020003f, + -0.69111252f, 0.49134374f, 0.00000001f, + -0.68221092f, 0.49134374f, -0.11339773f, + -0.65639102f, 0.49134377f, -0.22049254f, + -0.61497957f, 0.49134377f, -0.31995746f, + -0.55930358f, 0.49134377f, -0.41046557f, + -0.49068987f, 0.49134374f, -0.49068987f, + -0.41046554f, 0.49134374f, -0.55930358f, + -0.31995746f, 0.49134374f, -0.61497957f, + -0.22049253f, 0.49134377f, -0.65639102f, + -0.11339770f, 0.49134374f, -0.68221098f, + 0.00000000f, 0.49134374f, -0.69111252f, + -0.69510001f, 0.49725008f, 0.00000001f, + -0.68614709f, 0.49725005f, -0.11405201f, + -0.66017824f, 0.49725014f, -0.22176473f, + -0.61852777f, 0.49725008f, -0.32180351f, + -0.56253058f, 0.49725008f, -0.41283381f, + -0.49352103f, 0.49725008f, -0.49352100f, + -0.41283381f, 0.49725008f, -0.56253058f, + -0.32180351f, 0.49725008f, -0.61852777f, + -0.22176471f, 0.49725008f, -0.66017818f, + -0.11405198f, 0.49725008f, -0.68614715f, + 0.00000000f, 0.49725008f, -0.69510001f, + -0.70156252f, 0.49921876f, 0.00000001f, + -0.69252634f, 0.49921870f, -0.11511236f, + -0.66631603f, 0.49921876f, -0.22382653f, + -0.62427837f, 0.49921873f, -0.32479537f, + -0.56776059f, 0.49921879f, -0.41667202f, + -0.49810940f, 0.49921876f, -0.49810940f, + -0.41667199f, 0.49921879f, -0.56776053f, + -0.32479542f, 0.49921876f, -0.62427843f, + -0.22382650f, 0.49921876f, -0.66631603f, + -0.11511234f, 0.49921876f, -0.69252640f, + 0.00000000f, 0.49921876f, -0.70156252f, + -0.70989996f, 0.49725002f, 0.00000001f, + -0.70075637f, 0.49724996f, -0.11648037f, + -0.67423457f, 0.49725008f, -0.22648652f, + -0.63169742f, 0.49724999f, -0.32865530f, + -0.57450789f, 0.49725002f, -0.42162383f, + -0.50402898f, 0.49725002f, -0.50402898f, + -0.42162377f, 0.49725002f, -0.57450783f, + -0.32865530f, 0.49725002f, -0.63169742f, + -0.22648649f, 0.49725005f, -0.67423463f, + -0.11648035f, 0.49725002f, -0.70075643f, + 0.00000000f, 0.49725002f, -0.70989996f, + -0.71951252f, 0.49134377f, 0.00000001f, + -0.71024513f, 0.49134374f, -0.11805760f, + -0.68336421f, 0.49134380f, -0.22955328f, + -0.64025098f, 0.49134377f, -0.33310553f, + -0.58228713f, 0.49134383f, -0.42733288f, + -0.51085389f, 0.49134377f, -0.51085389f, + -0.42733288f, 0.49134377f, -0.58228713f, + -0.33310553f, 0.49134377f, -0.64025104f, + -0.22955328f, 0.49134380f, -0.68336427f, + -0.11805756f, 0.49134377f, -0.71024519f, + 0.00000000f, 0.49134377f, -0.71951252f, + -0.72979999f, 0.48150003f, 0.00000001f, + -0.72040009f, 0.48149997f, -0.11974557f, + -0.69313490f, 0.48150006f, -0.23283541f, + -0.64940518f, 0.48150000f, -0.33786824f, + -0.59061253f, 0.48150009f, -0.43344283f, + -0.51815796f, 0.48150003f, -0.51815796f, + -0.43344280f, 0.48150006f, -0.59061259f, + -0.33786821f, 0.48150003f, -0.64940524f, + -0.23283540f, 0.48150003f, -0.69313484f, + -0.11974555f, 0.48150003f, -0.72040015f, + 0.00000000f, 0.48150003f, -0.72979999f, + -0.74016249f, 0.46771878f, 0.00000001f, + -0.73062909f, 0.46771872f, -0.12144585f, + -0.70297670f, 0.46771881f, -0.23614147f, + -0.65862614f, 0.46771878f, -0.34266564f, + -0.59899879f, 0.46771878f, -0.43959734f, + -0.52551538f, 0.46771878f, -0.52551538f, + -0.43959731f, 0.46771878f, -0.59899873f, + -0.34266564f, 0.46771878f, -0.65862620f, + -0.23614144f, 0.46771878f, -0.70297670f, + -0.12144582f, 0.46771878f, -0.73062921f, + 0.00000000f, 0.46771878f, -0.74016249f, + -0.75000000f, 0.45000005f, 0.00000001f, + -0.74033999f, 0.44999996f, -0.12305998f, + -0.71232003f, 0.45000011f, -0.23928000f, + -0.66737998f, 0.45000002f, -0.34721997f, + -0.60696000f, 0.45000008f, -0.44544002f, + -0.53250003f, 0.45000005f, -0.53250003f, + -0.44543999f, 0.45000005f, -0.60696000f, + -0.34722000f, 0.45000008f, -0.66738003f, + -0.23927999f, 0.45000005f, -0.71231997f, + -0.12305996f, 0.45000005f, -0.74033999f, + 0.00000000f, 0.45000005f, -0.75000000f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.74033999f, 0.44999996f, 0.12306000f, + 0.71232003f, 0.45000011f, 0.23928000f, + 0.66737998f, 0.45000002f, 0.34721997f, + 0.60696000f, 0.45000008f, 0.44544002f, + 0.53250003f, 0.45000005f, 0.53250003f, + 0.44543999f, 0.45000005f, 0.60696000f, + 0.34722000f, 0.45000008f, 0.66738003f, + 0.23927999f, 0.45000005f, 0.71231997f, + 0.12305996f, 0.45000005f, 0.74033999f, + 0.00000000f, 0.45000005f, 0.75000000f, + 0.78737491f, 0.37128749f, 0.00000000f, + 0.77723348f, 0.37128747f, 0.12919247f, + 0.74781722f, 0.37128752f, 0.25120407f, + 0.70063770f, 0.37128749f, 0.36452308f, + 0.63720679f, 0.37128752f, 0.46763775f, + 0.55903620f, 0.37128749f, 0.55903620f, + 0.46763766f, 0.37128749f, 0.63720679f, + 0.36452311f, 0.37128749f, 0.70063770f, + 0.25120407f, 0.37128752f, 0.74781728f, + 0.12919241f, 0.37128749f, 0.77723348f, + 0.00000000f, 0.37128749f, 0.78737491f, + 0.82400006f, 0.29280004f, 0.00000000f, + 0.81338686f, 0.29280004f, 0.13520193f, + 0.78260237f, 0.29280004f, 0.26288900f, + 0.73322815f, 0.29280004f, 0.38147905f, + 0.66684681f, 0.29280004f, 0.48939016f, + 0.58504003f, 0.29280001f, 0.58504003f, + 0.48939011f, 0.29280004f, 0.66684675f, + 0.38147908f, 0.29280007f, 0.73322821f, + 0.26288897f, 0.29280004f, 0.78260231f, + 0.13520187f, 0.29280004f, 0.81338698f, + 0.00000000f, 0.29280004f, 0.82400006f, + 0.85912502f, 0.21476251f, 0.00000000f, + 0.84805942f, 0.21476249f, 0.14096522f, + 0.81596267f, 0.21476252f, 0.27409527f, + 0.76448381f, 0.21476249f, 0.39774051f, + 0.69527274f, 0.21476252f, 0.51025152f, + 0.60997874f, 0.21476251f, 0.60997874f, + 0.51025152f, 0.21476251f, 0.69527274f, + 0.39774054f, 0.21476251f, 0.76448381f, + 0.27409524f, 0.21476251f, 0.81596261f, + 0.14096518f, 0.21476251f, 0.84805954f, + 0.00000000f, 0.21476251f, 0.85912502f, + 0.89200008f, 0.13740003f, 0.00000000f, + 0.88051099f, 0.13740002f, 0.14635937f, + 0.84718603f, 0.13740005f, 0.28458369f, + 0.79373735f, 0.13740002f, 0.41296035f, + 0.72187787f, 0.13740005f, 0.52977669f, + 0.63332003f, 0.13740002f, 0.63332003f, + 0.52977669f, 0.13740002f, 0.72187781f, + 0.41296035f, 0.13740002f, 0.79373741f, + 0.28458369f, 0.13740000f, 0.84718597f, + 0.14635932f, 0.13740000f, 0.88051111f, + 0.00000000f, 0.13740000f, 0.89200008f, + 0.92187500f, 0.06093751f, 0.00000000f, + 0.91000110f, 0.06093750f, 0.15126126f, + 0.87556010f, 0.06093751f, 0.29411504f, + 0.82032120f, 0.06093750f, 0.42679128f, + 0.74605507f, 0.06093750f, 0.54752004f, + 0.65453124f, 0.06093751f, 0.65453124f, + 0.54751998f, 0.06093750f, 0.74605501f, + 0.42679128f, 0.06093750f, 0.82032126f, + 0.29411501f, 0.06093750f, 0.87556005f, + 0.15126120f, 0.06093750f, 0.91000128f, + 0.00000000f, 0.06093750f, 0.92187500f, + 0.94800001f, -0.01440001f, -0.00000000f, + 0.93578976f, -0.01440001f, 0.15554784f, + 0.90037251f, -0.01440001f, 0.30244994f, + 0.84356833f, -0.01440001f, 0.43888608f, + 0.76719749f, -0.01440001f, 0.56303620f, + 0.67308003f, -0.01440001f, 0.67308003f, + 0.56303614f, -0.01440002f, 0.76719743f, + 0.43888611f, -0.01440002f, 0.84356833f, + 0.30244991f, -0.01440002f, 0.90037251f, + 0.15554780f, -0.01440002f, 0.93578976f, + 0.00000000f, -0.01440002f, 0.94800001f, + 0.96962506f, -0.08838750f, -0.00000000f, + 0.95713621f, -0.08838749f, 0.15909605f, + 0.92091113f, -0.08838750f, 0.30934918f, + 0.86281121f, -0.08838750f, 0.44889757f, + 0.78469825f, -0.08838750f, 0.57587969f, + 0.68843377f, -0.08838750f, 0.68843377f, + 0.57587969f, -0.08838750f, 0.78469819f, + 0.44889760f, -0.08838750f, 0.86281121f, + 0.30934915f, -0.08838750f, 0.92091107f, + 0.15909600f, -0.08838750f, 0.95713627f, + 0.00000000f, -0.08838750f, 0.96962506f, + 0.98600000f, -0.16080001f, -0.00000000f, + 0.97330022f, -0.16080000f, 0.16178288f, + 0.93646342f, -0.16080002f, 0.31457347f, + 0.87738222f, -0.16080001f, 0.45647857f, + 0.79795015f, -0.16080002f, 0.58560514f, + 0.70006001f, -0.16080001f, 0.70006001f, + 0.58560514f, -0.16080001f, 0.79795015f, + 0.45647860f, -0.16080001f, 0.87738228f, + 0.31457344f, -0.16080001f, 0.93646336f, + 0.16178282f, -0.16080001f, 0.97330034f, + 0.00000000f, -0.16080001f, 0.98600000f, + 0.99637496f, -0.23141253f, -0.00000000f, + 0.98354161f, -0.23141250f, 0.16348520f, + 0.94631720f, -0.23141254f, 0.31788349f, + 0.88661432f, -0.23141253f, 0.46128178f, + 0.80634636f, -0.23141254f, 0.59176707f, + 0.70742619f, -0.23141253f, 0.70742619f, + 0.59176701f, -0.23141253f, 0.80634630f, + 0.46128178f, -0.23141253f, 0.88661432f, + 0.31788346f, -0.23141253f, 0.94631708f, + 0.16348514f, -0.23141253f, 0.98354173f, + 0.00000000f, -0.23141253f, 0.99637496f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.98711991f, -0.29999998f, 0.16407999f, + 0.94976002f, -0.30000004f, 0.31904000f, + 0.88984001f, -0.30000004f, 0.46296000f, + 0.80928004f, -0.30000001f, 0.59392005f, + 0.71000004f, -0.30000001f, 0.71000004f, + 0.59391999f, -0.30000001f, 0.80928004f, + 0.46296003f, -0.30000001f, 0.88984001f, + 0.31904000f, -0.30000001f, 0.94976002f, + 0.16407993f, -0.30000001f, 0.98712003f, + 0.00000000f, -0.30000001f, 1.00000000f, + 0.00000000f, 0.45000005f, -0.75000000f, + 0.12305999f, 0.44999996f, -0.74033999f, + 0.23928000f, 0.45000011f, -0.71232003f, + 0.34721997f, 0.45000002f, -0.66737998f, + 0.44544002f, 0.45000008f, -0.60696000f, + 0.53250003f, 0.45000005f, -0.53250003f, + 0.60696000f, 0.45000005f, -0.44543999f, + 0.66738003f, 0.45000008f, -0.34722000f, + 0.71231997f, 0.45000005f, -0.23927999f, + 0.74033999f, 0.45000005f, -0.12305995f, + 0.75000000f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.37128749f, -0.78737491f, + 0.12919247f, 0.37128747f, -0.77723348f, + 0.25120407f, 0.37128752f, -0.74781722f, + 0.36452308f, 0.37128749f, -0.70063770f, + 0.46763775f, 0.37128752f, -0.63720679f, + 0.55903620f, 0.37128749f, -0.55903620f, + 0.63720679f, 0.37128749f, -0.46763766f, + 0.70063770f, 0.37128749f, -0.36452311f, + 0.74781728f, 0.37128752f, -0.25120407f, + 0.77723348f, 0.37128749f, -0.12919241f, + 0.78737491f, 0.37128749f, 0.00000000f, + 0.00000000f, 0.29280004f, -0.82400006f, + 0.13520193f, 0.29280004f, -0.81338686f, + 0.26288900f, 0.29280004f, -0.78260237f, + 0.38147905f, 0.29280004f, -0.73322815f, + 0.48939016f, 0.29280004f, -0.66684681f, + 0.58504003f, 0.29280001f, -0.58504003f, + 0.66684675f, 0.29280004f, -0.48939011f, + 0.73322821f, 0.29280007f, -0.38147908f, + 0.78260231f, 0.29280004f, -0.26288897f, + 0.81338698f, 0.29280004f, -0.13520187f, + 0.82400006f, 0.29280004f, 0.00000000f, + 0.00000000f, 0.21476251f, -0.85912502f, + 0.14096522f, 0.21476249f, -0.84805942f, + 0.27409527f, 0.21476252f, -0.81596267f, + 0.39774051f, 0.21476249f, -0.76448381f, + 0.51025152f, 0.21476252f, -0.69527274f, + 0.60997874f, 0.21476251f, -0.60997874f, + 0.69527274f, 0.21476251f, -0.51025152f, + 0.76448381f, 0.21476251f, -0.39774054f, + 0.81596261f, 0.21476251f, -0.27409524f, + 0.84805954f, 0.21476251f, -0.14096518f, + 0.85912502f, 0.21476251f, 0.00000000f, + 0.00000000f, 0.13740005f, -0.89200008f, + 0.14635937f, 0.13740005f, -0.88051099f, + 0.28458369f, 0.13740005f, -0.84718603f, + 0.41296035f, 0.13740005f, -0.79373735f, + 0.52977669f, 0.13740005f, -0.72187787f, + 0.63332003f, 0.13740003f, -0.63332003f, + 0.72187781f, 0.13740003f, -0.52977669f, + 0.79373741f, 0.13740005f, -0.41296035f, + 0.84718597f, 0.13740003f, -0.28458369f, + 0.88051111f, 0.13740003f, -0.14635932f, + 0.89200008f, 0.13740003f, 0.00000000f, + 0.00000000f, 0.06093752f, -0.92187500f, + 0.15126126f, 0.06093751f, -0.91000110f, + 0.29411504f, 0.06093752f, -0.87556010f, + 0.42679128f, 0.06093752f, -0.82032120f, + 0.54752004f, 0.06093752f, -0.74605507f, + 0.65453124f, 0.06093752f, -0.65453124f, + 0.74605501f, 0.06093752f, -0.54751998f, + 0.82032126f, 0.06093751f, -0.42679128f, + 0.87556005f, 0.06093752f, -0.29411501f, + 0.91000128f, 0.06093751f, -0.15126120f, + 0.92187500f, 0.06093751f, 0.00000000f, + 0.00000000f, -0.01440000f, -0.94800001f, + 0.15554784f, -0.01440000f, -0.93578976f, + 0.30244994f, -0.01440000f, -0.90037251f, + 0.43888608f, -0.01440000f, -0.84356833f, + 0.56303620f, -0.01440000f, -0.76719749f, + 0.67308003f, -0.01440000f, -0.67308003f, + 0.76719743f, -0.01440001f, -0.56303614f, + 0.84356833f, -0.01440001f, -0.43888611f, + 0.90037251f, -0.01440001f, -0.30244991f, + 0.93578976f, -0.01440001f, -0.15554780f, + 0.94800001f, -0.01440001f, -0.00000000f, + 0.00000000f, -0.08838749f, -0.96962506f, + 0.15909605f, -0.08838748f, -0.95713621f, + 0.30934918f, -0.08838750f, -0.92091113f, + 0.44889757f, -0.08838750f, -0.86281121f, + 0.57587969f, -0.08838750f, -0.78469825f, + 0.68843377f, -0.08838749f, -0.68843377f, + 0.78469819f, -0.08838750f, -0.57587969f, + 0.86281121f, -0.08838750f, -0.44889760f, + 0.92091107f, -0.08838750f, -0.30934915f, + 0.95713627f, -0.08838749f, -0.15909600f, + 0.96962506f, -0.08838750f, -0.00000000f, + 0.00000000f, -0.16080000f, -0.98600000f, + 0.16178288f, -0.16079998f, -0.97330022f, + 0.31457347f, -0.16080001f, -0.93646342f, + 0.45647857f, -0.16080000f, -0.87738222f, + 0.58560514f, -0.16080001f, -0.79795015f, + 0.70006001f, -0.16080000f, -0.70006001f, + 0.79795015f, -0.16080000f, -0.58560514f, + 0.87738228f, -0.16080001f, -0.45647860f, + 0.93646336f, -0.16080001f, -0.31457344f, + 0.97330034f, -0.16080001f, -0.16178282f, + 0.98600000f, -0.16080001f, -0.00000000f, + 0.00000000f, -0.23141253f, -0.99637496f, + 0.16348520f, -0.23141250f, -0.98354161f, + 0.31788349f, -0.23141254f, -0.94631720f, + 0.46128178f, -0.23141253f, -0.88661432f, + 0.59176707f, -0.23141254f, -0.80634636f, + 0.70742619f, -0.23141253f, -0.70742619f, + 0.80634630f, -0.23141253f, -0.59176701f, + 0.88661432f, -0.23141253f, -0.46128178f, + 0.94631708f, -0.23141253f, -0.31788346f, + 0.98354173f, -0.23141253f, -0.16348514f, + 0.99637496f, -0.23141253f, -0.00000000f, + 0.00000000f, -0.30000001f, -1.00000000f, + 0.16407999f, -0.29999998f, -0.98711991f, + 0.31904000f, -0.30000004f, -0.94976002f, + 0.46296000f, -0.30000004f, -0.88984001f, + 0.59392005f, -0.30000001f, -0.80928004f, + 0.71000004f, -0.30000001f, -0.71000004f, + 0.80928004f, -0.30000001f, -0.59391999f, + 0.88984001f, -0.30000001f, -0.46296003f, + 0.94976002f, -0.30000001f, -0.31904000f, + 0.98712003f, -0.30000001f, -0.16407993f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.00000000f, 0.45000005f, 0.75000000f, + -0.12305999f, 0.44999996f, 0.74033999f, + -0.23928000f, 0.45000011f, 0.71232003f, + -0.34721997f, 0.45000002f, 0.66737998f, + -0.44544002f, 0.45000008f, 0.60696000f, + -0.53250003f, 0.45000005f, 0.53250003f, + -0.60696000f, 0.45000005f, 0.44543999f, + -0.66738003f, 0.45000008f, 0.34722000f, + -0.71231997f, 0.45000005f, 0.23927999f, + -0.74033999f, 0.45000005f, 0.12305997f, + -0.75000000f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.37128749f, 0.78737491f, + -0.12919247f, 0.37128747f, 0.77723348f, + -0.25120407f, 0.37128752f, 0.74781722f, + -0.36452308f, 0.37128749f, 0.70063770f, + -0.46763775f, 0.37128752f, 0.63720679f, + -0.55903620f, 0.37128749f, 0.55903620f, + -0.63720679f, 0.37128749f, 0.46763766f, + -0.70063770f, 0.37128749f, 0.36452311f, + -0.74781728f, 0.37128752f, 0.25120407f, + -0.77723348f, 0.37128749f, 0.12919241f, + -0.78737491f, 0.37128749f, 0.00000000f, + 0.00000000f, 0.29280004f, 0.82400006f, + -0.13520193f, 0.29280004f, 0.81338686f, + -0.26288900f, 0.29280004f, 0.78260237f, + -0.38147905f, 0.29280004f, 0.73322815f, + -0.48939016f, 0.29280004f, 0.66684681f, + -0.58504003f, 0.29280001f, 0.58504003f, + -0.66684675f, 0.29280004f, 0.48939011f, + -0.73322821f, 0.29280007f, 0.38147908f, + -0.78260231f, 0.29280004f, 0.26288897f, + -0.81338698f, 0.29280004f, 0.13520187f, + -0.82400006f, 0.29280004f, 0.00000000f, + 0.00000000f, 0.21476251f, 0.85912502f, + -0.14096522f, 0.21476249f, 0.84805942f, + -0.27409527f, 0.21476252f, 0.81596267f, + -0.39774051f, 0.21476249f, 0.76448381f, + -0.51025152f, 0.21476252f, 0.69527274f, + -0.60997874f, 0.21476251f, 0.60997874f, + -0.69527274f, 0.21476251f, 0.51025152f, + -0.76448381f, 0.21476251f, 0.39774054f, + -0.81596261f, 0.21476251f, 0.27409524f, + -0.84805954f, 0.21476251f, 0.14096518f, + -0.85912502f, 0.21476251f, 0.00000000f, + 0.00000000f, 0.13740000f, 0.89200008f, + -0.14635937f, 0.13740000f, 0.88051099f, + -0.28458369f, 0.13740002f, 0.84718603f, + -0.41296035f, 0.13740002f, 0.79373735f, + -0.52977669f, 0.13740002f, 0.72187787f, + -0.63332003f, 0.13740002f, 0.63332003f, + -0.72187781f, 0.13740002f, 0.52977669f, + -0.79373741f, 0.13740003f, 0.41296035f, + -0.84718597f, 0.13740003f, 0.28458369f, + -0.88051111f, 0.13740003f, 0.14635932f, + -0.89200008f, 0.13740003f, 0.00000000f, + 0.00000000f, 0.06093750f, 0.92187500f, + -0.15126126f, 0.06093750f, 0.91000110f, + -0.29411504f, 0.06093751f, 0.87556010f, + -0.42679128f, 0.06093750f, 0.82032120f, + -0.54752004f, 0.06093750f, 0.74605507f, + -0.65453124f, 0.06093750f, 0.65453124f, + -0.74605501f, 0.06093750f, 0.54751998f, + -0.82032126f, 0.06093751f, 0.42679128f, + -0.87556005f, 0.06093751f, 0.29411501f, + -0.91000128f, 0.06093751f, 0.15126120f, + -0.92187500f, 0.06093751f, 0.00000000f, + 0.00000000f, -0.01440002f, 0.94800001f, + -0.15554784f, -0.01440002f, 0.93578976f, + -0.30244994f, -0.01440002f, 0.90037251f, + -0.43888608f, -0.01440002f, 0.84356833f, + -0.56303620f, -0.01440002f, 0.76719749f, + -0.67308003f, -0.01440001f, 0.67308003f, + -0.76719743f, -0.01440001f, 0.56303614f, + -0.84356833f, -0.01440001f, 0.43888611f, + -0.90037251f, -0.01440001f, 0.30244991f, + -0.93578976f, -0.01440001f, 0.15554780f, + -0.94800001f, -0.01440001f, -0.00000000f, + 0.00000000f, -0.08838750f, 0.96962506f, + -0.15909605f, -0.08838750f, 0.95713621f, + -0.30934918f, -0.08838750f, 0.92091113f, + -0.44889757f, -0.08838750f, 0.86281121f, + -0.57587969f, -0.08838751f, 0.78469825f, + -0.68843377f, -0.08838750f, 0.68843377f, + -0.78469819f, -0.08838750f, 0.57587969f, + -0.86281121f, -0.08838750f, 0.44889760f, + -0.92091107f, -0.08838750f, 0.30934915f, + -0.95713627f, -0.08838750f, 0.15909600f, + -0.96962506f, -0.08838750f, -0.00000000f, + 0.00000000f, -0.16080001f, 0.98600000f, + -0.16178288f, -0.16080000f, 0.97330022f, + -0.31457347f, -0.16080002f, 0.93646342f, + -0.45647857f, -0.16080001f, 0.87738222f, + -0.58560514f, -0.16080002f, 0.79795015f, + -0.70006001f, -0.16080001f, 0.70006001f, + -0.79795015f, -0.16080001f, 0.58560514f, + -0.87738228f, -0.16080001f, 0.45647860f, + -0.93646336f, -0.16080001f, 0.31457344f, + -0.97330034f, -0.16080001f, 0.16178282f, + -0.98600000f, -0.16080001f, -0.00000000f, + 0.00000000f, -0.23141253f, 0.99637496f, + -0.16348520f, -0.23141250f, 0.98354161f, + -0.31788349f, -0.23141254f, 0.94631720f, + -0.46128178f, -0.23141253f, 0.88661432f, + -0.59176707f, -0.23141254f, 0.80634636f, + -0.70742619f, -0.23141253f, 0.70742619f, + -0.80634630f, -0.23141253f, 0.59176701f, + -0.88661432f, -0.23141253f, 0.46128178f, + -0.94631708f, -0.23141253f, 0.31788346f, + -0.98354173f, -0.23141253f, 0.16348514f, + -0.99637496f, -0.23141253f, -0.00000000f, + 0.00000000f, -0.30000001f, 1.00000000f, + -0.16407999f, -0.29999998f, 0.98711991f, + -0.31904000f, -0.30000004f, 0.94976002f, + -0.46296000f, -0.30000004f, 0.88984001f, + -0.59392005f, -0.30000001f, 0.80928004f, + -0.71000004f, -0.30000001f, 0.71000004f, + -0.80928004f, -0.30000001f, 0.59391999f, + -0.88984001f, -0.30000001f, 0.46296003f, + -0.94976002f, -0.30000001f, 0.31904000f, + -0.98712003f, -0.30000001f, 0.16407993f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.75000000f, 0.45000005f, 0.00000001f, + -0.74033999f, 0.44999996f, -0.12305998f, + -0.71232003f, 0.45000011f, -0.23928000f, + -0.66737998f, 0.45000002f, -0.34721997f, + -0.60696000f, 0.45000008f, -0.44544002f, + -0.53250003f, 0.45000005f, -0.53250003f, + -0.44543999f, 0.45000005f, -0.60696000f, + -0.34722000f, 0.45000008f, -0.66738003f, + -0.23927999f, 0.45000005f, -0.71231997f, + -0.12305996f, 0.45000005f, -0.74033999f, + 0.00000000f, 0.45000005f, -0.75000000f, + -0.78737491f, 0.37128749f, 0.00000000f, + -0.77723348f, 0.37128747f, -0.12919247f, + -0.74781722f, 0.37128752f, -0.25120407f, + -0.70063770f, 0.37128749f, -0.36452308f, + -0.63720679f, 0.37128752f, -0.46763775f, + -0.55903620f, 0.37128749f, -0.55903620f, + -0.46763766f, 0.37128749f, -0.63720679f, + -0.36452311f, 0.37128749f, -0.70063770f, + -0.25120407f, 0.37128752f, -0.74781728f, + -0.12919241f, 0.37128749f, -0.77723348f, + 0.00000000f, 0.37128749f, -0.78737491f, + -0.82400006f, 0.29280004f, 0.00000000f, + -0.81338686f, 0.29280004f, -0.13520193f, + -0.78260237f, 0.29280004f, -0.26288900f, + -0.73322815f, 0.29280004f, -0.38147905f, + -0.66684681f, 0.29280004f, -0.48939016f, + -0.58504003f, 0.29280001f, -0.58504003f, + -0.48939011f, 0.29280004f, -0.66684675f, + -0.38147908f, 0.29280007f, -0.73322821f, + -0.26288897f, 0.29280004f, -0.78260231f, + -0.13520187f, 0.29280004f, -0.81338698f, + 0.00000000f, 0.29280004f, -0.82400006f, + -0.85912502f, 0.21476251f, 0.00000000f, + -0.84805942f, 0.21476249f, -0.14096522f, + -0.81596267f, 0.21476252f, -0.27409527f, + -0.76448381f, 0.21476249f, -0.39774051f, + -0.69527274f, 0.21476252f, -0.51025152f, + -0.60997874f, 0.21476251f, -0.60997874f, + -0.51025152f, 0.21476251f, -0.69527274f, + -0.39774054f, 0.21476251f, -0.76448381f, + -0.27409524f, 0.21476251f, -0.81596261f, + -0.14096518f, 0.21476251f, -0.84805954f, + 0.00000000f, 0.21476251f, -0.85912502f, + -0.89200008f, 0.13740003f, 0.00000000f, + -0.88051099f, 0.13740003f, -0.14635937f, + -0.84718603f, 0.13740005f, -0.28458369f, + -0.79373735f, 0.13740005f, -0.41296035f, + -0.72187787f, 0.13740005f, -0.52977669f, + -0.63332003f, 0.13740005f, -0.63332003f, + -0.52977669f, 0.13740005f, -0.72187781f, + -0.41296035f, 0.13740005f, -0.79373741f, + -0.28458369f, 0.13740005f, -0.84718597f, + -0.14635932f, 0.13740005f, -0.88051111f, + 0.00000000f, 0.13740005f, -0.89200008f, + -0.92187500f, 0.06093751f, 0.00000000f, + -0.91000110f, 0.06093751f, -0.15126126f, + -0.87556010f, 0.06093752f, -0.29411504f, + -0.82032120f, 0.06093751f, -0.42679128f, + -0.74605507f, 0.06093752f, -0.54752004f, + -0.65453124f, 0.06093752f, -0.65453124f, + -0.54751998f, 0.06093752f, -0.74605501f, + -0.42679128f, 0.06093752f, -0.82032126f, + -0.29411501f, 0.06093752f, -0.87556005f, + -0.15126120f, 0.06093752f, -0.91000128f, + 0.00000000f, 0.06093752f, -0.92187500f, + -0.94800001f, -0.01440001f, -0.00000000f, + -0.93578976f, -0.01440001f, -0.15554784f, + -0.90037251f, -0.01440001f, -0.30244994f, + -0.84356833f, -0.01440001f, -0.43888608f, + -0.76719749f, -0.01440001f, -0.56303620f, + -0.67308003f, -0.01440000f, -0.67308003f, + -0.56303614f, -0.01440000f, -0.76719743f, + -0.43888611f, -0.01440000f, -0.84356833f, + -0.30244991f, -0.01440000f, -0.90037251f, + -0.15554780f, -0.01440000f, -0.93578976f, + 0.00000000f, -0.01440000f, -0.94800001f, + -0.96962506f, -0.08838750f, -0.00000000f, + -0.95713621f, -0.08838749f, -0.15909605f, + -0.92091113f, -0.08838750f, -0.30934918f, + -0.86281121f, -0.08838750f, -0.44889757f, + -0.78469825f, -0.08838750f, -0.57587969f, + -0.68843377f, -0.08838749f, -0.68843377f, + -0.57587969f, -0.08838749f, -0.78469819f, + -0.44889760f, -0.08838749f, -0.86281121f, + -0.30934915f, -0.08838749f, -0.92091107f, + -0.15909600f, -0.08838749f, -0.95713627f, + 0.00000000f, -0.08838749f, -0.96962506f, + -0.98600000f, -0.16080001f, -0.00000000f, + -0.97330022f, -0.16080000f, -0.16178288f, + -0.93646342f, -0.16080001f, -0.31457347f, + -0.87738222f, -0.16080000f, -0.45647857f, + -0.79795015f, -0.16080001f, -0.58560514f, + -0.70006001f, -0.16080001f, -0.70006001f, + -0.58560514f, -0.16080000f, -0.79795015f, + -0.45647860f, -0.16080001f, -0.87738228f, + -0.31457344f, -0.16080000f, -0.93646336f, + -0.16178282f, -0.16080000f, -0.97330034f, + 0.00000000f, -0.16080000f, -0.98600000f, + -0.99637496f, -0.23141253f, -0.00000000f, + -0.98354161f, -0.23141250f, -0.16348520f, + -0.94631720f, -0.23141254f, -0.31788349f, + -0.88661432f, -0.23141253f, -0.46128178f, + -0.80634636f, -0.23141254f, -0.59176707f, + -0.70742619f, -0.23141253f, -0.70742619f, + -0.59176701f, -0.23141253f, -0.80634630f, + -0.46128178f, -0.23141253f, -0.88661432f, + -0.31788346f, -0.23141253f, -0.94631708f, + -0.16348514f, -0.23141253f, -0.98354173f, + 0.00000000f, -0.23141253f, -0.99637496f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.98711991f, -0.29999998f, -0.16407999f, + -0.94976002f, -0.30000004f, -0.31904000f, + -0.88984001f, -0.30000004f, -0.46296000f, + -0.80928004f, -0.30000001f, -0.59392005f, + -0.71000004f, -0.30000001f, -0.71000004f, + -0.59391999f, -0.30000001f, -0.80928004f, + -0.46296003f, -0.30000001f, -0.88984001f, + -0.31904000f, -0.30000001f, -0.94976002f, + -0.16407993f, -0.30000001f, -0.98712003f, + 0.00000000f, -0.30000001f, -1.00000000f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.98711991f, -0.29999998f, 0.16407999f, + 0.94976002f, -0.30000004f, 0.31904000f, + 0.88984001f, -0.30000004f, 0.46296000f, + 0.80928004f, -0.30000001f, 0.59392005f, + 0.71000004f, -0.30000001f, 0.71000004f, + 0.59391999f, -0.30000001f, 0.80928004f, + 0.46296003f, -0.30000001f, 0.88984001f, + 0.31904000f, -0.30000001f, 0.94976002f, + 0.16407993f, -0.30000001f, 0.98712003f, + 0.00000000f, -0.30000001f, 1.00000000f, + 0.99299991f, -0.36416247f, -0.00000000f, + 0.98021001f, -0.36416242f, 0.16293143f, + 0.94311166f, -0.36416250f, 0.31680670f, + 0.88361102f, -0.36416245f, 0.45971927f, + 0.80361503f, -0.36416247f, 0.58976257f, + 0.70502996f, -0.36416247f, 0.70502996f, + 0.58976251f, -0.36416250f, 0.80361497f, + 0.45971930f, -0.36416247f, 0.88361108f, + 0.31680670f, -0.36416250f, 0.94311166f, + 0.16293137f, -0.36416247f, 0.98021007f, + 0.00000000f, -0.36416247f, 0.99299991f, + 0.97400004f, -0.42180002f, -0.00000001f, + 0.96145481f, -0.42179996f, 0.15981390f, + 0.92506635f, -0.42180005f, 0.31074497f, + 0.86670411f, -0.42180002f, 0.45092306f, + 0.78823876f, -0.42180011f, 0.57847816f, + 0.69154000f, -0.42180008f, 0.69154000f, + 0.57847810f, -0.42180005f, 0.78823876f, + 0.45092303f, -0.42180008f, 0.86670417f, + 0.31074494f, -0.42180008f, 0.92506629f, + 0.15981385f, -0.42180008f, 0.96145493f, + 0.00000000f, -0.42180005f, 0.97400004f, + 0.94600004f, -0.47313750f, -0.00000001f, + 0.93381548f, -0.47313747f, 0.15521967f, + 0.89847302f, -0.47313756f, 0.30181187f, + 0.84178865f, -0.47313750f, 0.43796018f, + 0.76557899f, -0.47313753f, 0.56184840f, + 0.67166001f, -0.47313753f, 0.67166001f, + 0.56184828f, -0.47313753f, 0.76557899f, + 0.43796021f, -0.47313753f, 0.84178865f, + 0.30181184f, -0.47313753f, 0.89847302f, + 0.15521961f, -0.47313753f, 0.93381560f, + 0.00000000f, -0.47313753f, 0.94600004f, + 0.91200000f, -0.51840001f, -0.00000001f, + 0.90025330f, -0.51839995f, 0.14964095f, + 0.86618114f, -0.51840007f, 0.29096451f, + 0.81153411f, -0.51840001f, 0.42221951f, + 0.73806345f, -0.51840007f, 0.54165506f, + 0.64752001f, -0.51840007f, 0.64752001f, + 0.54165506f, -0.51840007f, 0.73806340f, + 0.42221954f, -0.51840007f, 0.81153411f, + 0.29096448f, -0.51840007f, 0.86618114f, + 0.14964090f, -0.51840007f, 0.90025342f, + 0.00000000f, -0.51840007f, 0.91200000f, + 0.87500000f, -0.55781251f, -0.00000001f, + 0.86372989f, -0.55781251f, 0.14356998f, + 0.83104002f, -0.55781251f, 0.27916002f, + 0.77860999f, -0.55781251f, 0.40509003f, + 0.70812005f, -0.55781257f, 0.51968002f, + 0.62125003f, -0.55781251f, 0.62125003f, + 0.51967996f, -0.55781251f, 0.70812005f, + 0.40509003f, -0.55781257f, 0.77860999f, + 0.27915999f, -0.55781251f, 0.83104002f, + 0.14356995f, -0.55781251f, 0.86373001f, + 0.00000000f, -0.55781251f, 0.87500000f, + 0.83800000f, -0.59160000f, -0.00000001f, + 0.82720655f, -0.59159994f, 0.13749902f, + 0.79589891f, -0.59160000f, 0.26735553f, + 0.74568588f, -0.59160000f, 0.38796049f, + 0.67817670f, -0.59160006f, 0.49770495f, + 0.59498000f, -0.59160000f, 0.59497994f, + 0.49770495f, -0.59160000f, 0.67817664f, + 0.38796049f, -0.59160006f, 0.74568594f, + 0.26735550f, -0.59160000f, 0.79589891f, + 0.13749899f, -0.59160000f, 0.82720655f, + 0.00000000f, -0.59160000f, 0.83800000f, + 0.80400008f, -0.61998749f, -0.00000001f, + 0.79364455f, -0.61998743f, 0.13192031f, + 0.76360714f, -0.61998749f, 0.25650817f, + 0.71543139f, -0.61998749f, 0.37221986f, + 0.65066117f, -0.61998749f, 0.47751173f, + 0.57084000f, -0.61998749f, 0.57084000f, + 0.47751170f, -0.61998749f, 0.65066117f, + 0.37221986f, -0.61998749f, 0.71543145f, + 0.25650814f, -0.61998749f, 0.76360714f, + 0.13192026f, -0.61998749f, 0.79364455f, + 0.00000000f, -0.61998749f, 0.80400008f, + 0.77600002f, -0.64320004f, -0.00000001f, + 0.76600504f, -0.64319998f, 0.12732607f, + 0.73701382f, -0.64320010f, 0.24757506f, + 0.69051588f, -0.64320004f, 0.35925698f, + 0.62800133f, -0.64320010f, 0.46088195f, + 0.55096000f, -0.64320004f, 0.55096000f, + 0.46088192f, -0.64320004f, 0.62800133f, + 0.35925698f, -0.64320004f, 0.69051588f, + 0.24757503f, -0.64320004f, 0.73701382f, + 0.12732603f, -0.64320004f, 0.76600516f, + 0.00000000f, -0.64320004f, 0.77600002f, + 0.75699997f, -0.66146249f, -0.00000001f, + 0.74724966f, -0.66146243f, 0.12420854f, + 0.71896833f, -0.66146255f, 0.24151328f, + 0.67360884f, -0.66146243f, 0.35046071f, + 0.61262494f, -0.66146255f, 0.44959745f, + 0.53746998f, -0.66146249f, 0.53746998f, + 0.44959742f, -0.66146249f, 0.61262494f, + 0.35046071f, -0.66146249f, 0.67360890f, + 0.24151327f, -0.66146255f, 0.71896827f, + 0.12420851f, -0.66146249f, 0.74724984f, + 0.00000000f, -0.66146249f, 0.75699997f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.74033999f, -0.67499995f, 0.12305998f, + 0.71232003f, -0.67500007f, 0.23928000f, + 0.66737998f, -0.67500001f, 0.34721997f, + 0.60696000f, -0.67500007f, 0.44544002f, + 0.53250003f, -0.67500007f, 0.53250003f, + 0.44543999f, -0.67500001f, 0.60696000f, + 0.34722000f, -0.67500001f, 0.66738003f, + 0.23927999f, -0.67500001f, 0.71231997f, + 0.12305996f, -0.67500001f, 0.74033999f, + 0.00000000f, -0.67500001f, 0.75000000f, + 0.00000000f, -0.30000001f, -1.00000000f, + 0.16407999f, -0.29999998f, -0.98711991f, + 0.31904000f, -0.30000004f, -0.94976002f, + 0.46296000f, -0.30000004f, -0.88984001f, + 0.59392005f, -0.30000001f, -0.80928004f, + 0.71000004f, -0.30000001f, -0.71000004f, + 0.80928004f, -0.30000001f, -0.59391999f, + 0.88984001f, -0.30000001f, -0.46296003f, + 0.94976002f, -0.30000001f, -0.31904000f, + 0.98712003f, -0.30000001f, -0.16407993f, + 1.00000000f, -0.30000001f, -0.00000000f, + 0.00000000f, -0.36416247f, -0.99299991f, + 0.16293143f, -0.36416242f, -0.98021001f, + 0.31680670f, -0.36416250f, -0.94311166f, + 0.45971927f, -0.36416245f, -0.88361102f, + 0.58976257f, -0.36416247f, -0.80361503f, + 0.70502996f, -0.36416247f, -0.70502996f, + 0.80361497f, -0.36416250f, -0.58976251f, + 0.88361108f, -0.36416247f, -0.45971930f, + 0.94311166f, -0.36416250f, -0.31680670f, + 0.98021007f, -0.36416247f, -0.16293137f, + 0.99299991f, -0.36416247f, -0.00000000f, + 0.00000000f, -0.42180002f, -0.97400004f, + 0.15981390f, -0.42179996f, -0.96145481f, + 0.31074497f, -0.42180005f, -0.92506635f, + 0.45092306f, -0.42180002f, -0.86670411f, + 0.57847816f, -0.42180005f, -0.78823876f, + 0.69154000f, -0.42180002f, -0.69154000f, + 0.78823876f, -0.42180002f, -0.57847810f, + 0.86670417f, -0.42180002f, -0.45092303f, + 0.92506629f, -0.42180002f, -0.31074494f, + 0.96145493f, -0.42180002f, -0.15981385f, + 0.97400004f, -0.42180002f, -0.00000001f, + 0.00000000f, -0.47313750f, -0.94600004f, + 0.15521967f, -0.47313747f, -0.93381548f, + 0.30181187f, -0.47313756f, -0.89847302f, + 0.43796018f, -0.47313750f, -0.84178865f, + 0.56184840f, -0.47313753f, -0.76557899f, + 0.67166001f, -0.47313750f, -0.67166001f, + 0.76557899f, -0.47313750f, -0.56184828f, + 0.84178865f, -0.47313750f, -0.43796021f, + 0.89847302f, -0.47313750f, -0.30181184f, + 0.93381560f, -0.47313750f, -0.15521961f, + 0.94600004f, -0.47313750f, -0.00000001f, + 0.00000000f, -0.51840001f, -0.91200000f, + 0.14964095f, -0.51839995f, -0.90025330f, + 0.29096451f, -0.51840001f, -0.86618114f, + 0.42221951f, -0.51840001f, -0.81153411f, + 0.54165506f, -0.51840007f, -0.73806345f, + 0.64752001f, -0.51840001f, -0.64752001f, + 0.73806340f, -0.51840001f, -0.54165506f, + 0.81153411f, -0.51840007f, -0.42221954f, + 0.86618114f, -0.51840007f, -0.29096448f, + 0.90025342f, -0.51840001f, -0.14964090f, + 0.91200000f, -0.51840001f, -0.00000001f, + 0.00000000f, -0.55781251f, -0.87500000f, + 0.14356999f, -0.55781251f, -0.86372989f, + 0.27916002f, -0.55781251f, -0.83104002f, + 0.40509003f, -0.55781251f, -0.77860999f, + 0.51968002f, -0.55781257f, -0.70812005f, + 0.62125003f, -0.55781251f, -0.62125003f, + 0.70812005f, -0.55781251f, -0.51967996f, + 0.77860999f, -0.55781257f, -0.40509003f, + 0.83104002f, -0.55781251f, -0.27915999f, + 0.86373001f, -0.55781251f, -0.14356995f, + 0.87500000f, -0.55781251f, -0.00000001f, + 0.00000000f, -0.59160000f, -0.83800000f, + 0.13749902f, -0.59159994f, -0.82720655f, + 0.26735553f, -0.59160000f, -0.79589891f, + 0.38796049f, -0.59160000f, -0.74568588f, + 0.49770495f, -0.59160006f, -0.67817670f, + 0.59497994f, -0.59160000f, -0.59498000f, + 0.67817664f, -0.59160000f, -0.49770495f, + 0.74568594f, -0.59160006f, -0.38796049f, + 0.79589891f, -0.59160000f, -0.26735550f, + 0.82720655f, -0.59160000f, -0.13749899f, + 0.83800000f, -0.59160000f, -0.00000001f, + 0.00000000f, -0.61998749f, -0.80400008f, + 0.13192032f, -0.61998743f, -0.79364455f, + 0.25650817f, -0.61998749f, -0.76360714f, + 0.37221986f, -0.61998749f, -0.71543139f, + 0.47751173f, -0.61998749f, -0.65066117f, + 0.57084000f, -0.61998749f, -0.57084000f, + 0.65066117f, -0.61998749f, -0.47751170f, + 0.71543145f, -0.61998749f, -0.37221986f, + 0.76360714f, -0.61998749f, -0.25650814f, + 0.79364455f, -0.61998749f, -0.13192026f, + 0.80400008f, -0.61998749f, -0.00000001f, + 0.00000000f, -0.64320004f, -0.77600002f, + 0.12732607f, -0.64319998f, -0.76600504f, + 0.24757506f, -0.64320010f, -0.73701382f, + 0.35925698f, -0.64320004f, -0.69051588f, + 0.46088195f, -0.64320010f, -0.62800133f, + 0.55096000f, -0.64320004f, -0.55096000f, + 0.62800133f, -0.64320004f, -0.46088192f, + 0.69051588f, -0.64320004f, -0.35925698f, + 0.73701382f, -0.64320004f, -0.24757503f, + 0.76600516f, -0.64320004f, -0.12732603f, + 0.77600002f, -0.64320004f, -0.00000001f, + 0.00000000f, -0.66146249f, -0.75699997f, + 0.12420855f, -0.66146243f, -0.74724966f, + 0.24151328f, -0.66146255f, -0.71896833f, + 0.35046071f, -0.66146243f, -0.67360884f, + 0.44959745f, -0.66146255f, -0.61262494f, + 0.53746998f, -0.66146249f, -0.53746998f, + 0.61262494f, -0.66146249f, -0.44959742f, + 0.67360890f, -0.66146249f, -0.35046071f, + 0.71896827f, -0.66146255f, -0.24151327f, + 0.74724984f, -0.66146249f, -0.12420852f, + 0.75699997f, -0.66146249f, -0.00000001f, + 0.00000000f, -0.67500001f, -0.75000000f, + 0.12305999f, -0.67499995f, -0.74033999f, + 0.23928000f, -0.67500007f, -0.71232003f, + 0.34721997f, -0.67500001f, -0.66737998f, + 0.44544002f, -0.67500007f, -0.60696000f, + 0.53250003f, -0.67500007f, -0.53250003f, + 0.60696000f, -0.67500001f, -0.44543999f, + 0.66738003f, -0.67500001f, -0.34722000f, + 0.71231997f, -0.67500001f, -0.23927999f, + 0.74033999f, -0.67500001f, -0.12305997f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.00000000f, -0.30000001f, 1.00000000f, + -0.16407999f, -0.29999998f, 0.98711991f, + -0.31904000f, -0.30000004f, 0.94976002f, + -0.46296000f, -0.30000004f, 0.88984001f, + -0.59392005f, -0.30000001f, 0.80928004f, + -0.71000004f, -0.30000001f, 0.71000004f, + -0.80928004f, -0.30000001f, 0.59391999f, + -0.88984001f, -0.30000001f, 0.46296003f, + -0.94976002f, -0.30000001f, 0.31904000f, + -0.98712003f, -0.30000001f, 0.16407993f, + -1.00000000f, -0.30000001f, -0.00000000f, + 0.00000000f, -0.36416247f, 0.99299991f, + -0.16293143f, -0.36416242f, 0.98021001f, + -0.31680670f, -0.36416250f, 0.94311166f, + -0.45971927f, -0.36416245f, 0.88361102f, + -0.58976257f, -0.36416247f, 0.80361503f, + -0.70502996f, -0.36416247f, 0.70502996f, + -0.80361497f, -0.36416250f, 0.58976251f, + -0.88361108f, -0.36416247f, 0.45971930f, + -0.94311166f, -0.36416250f, 0.31680670f, + -0.98021007f, -0.36416247f, 0.16293137f, + -0.99299991f, -0.36416247f, -0.00000000f, + 0.00000000f, -0.42180005f, 0.97400004f, + -0.15981390f, -0.42179999f, 0.96145481f, + -0.31074497f, -0.42180005f, 0.92506635f, + -0.45092306f, -0.42180002f, 0.86670411f, + -0.57847816f, -0.42180011f, 0.78823876f, + -0.69154000f, -0.42180008f, 0.69154000f, + -0.78823876f, -0.42180002f, 0.57847810f, + -0.86670417f, -0.42180005f, 0.45092303f, + -0.92506629f, -0.42180005f, 0.31074494f, + -0.96145493f, -0.42180002f, 0.15981385f, + -0.97400004f, -0.42180002f, -0.00000001f, + 0.00000000f, -0.47313753f, 0.94600004f, + -0.15521967f, -0.47313747f, 0.93381548f, + -0.30181187f, -0.47313756f, 0.89847302f, + -0.43796018f, -0.47313750f, 0.84178865f, + -0.56184840f, -0.47313753f, 0.76557899f, + -0.67166001f, -0.47313750f, 0.67166001f, + -0.76557899f, -0.47313753f, 0.56184828f, + -0.84178865f, -0.47313753f, 0.43796021f, + -0.89847302f, -0.47313750f, 0.30181184f, + -0.93381560f, -0.47313750f, 0.15521961f, + -0.94600004f, -0.47313750f, -0.00000001f, + 0.00000000f, -0.51840007f, 0.91200000f, + -0.14964095f, -0.51839995f, 0.90025330f, + -0.29096451f, -0.51840007f, 0.86618114f, + -0.42221951f, -0.51840007f, 0.81153411f, + -0.54165506f, -0.51840007f, 0.73806345f, + -0.64752001f, -0.51840007f, 0.64752001f, + -0.73806340f, -0.51840007f, 0.54165506f, + -0.81153411f, -0.51840007f, 0.42221954f, + -0.86618114f, -0.51840007f, 0.29096448f, + -0.90025342f, -0.51840001f, 0.14964090f, + -0.91200000f, -0.51840001f, -0.00000001f, + 0.00000000f, -0.55781251f, 0.87500000f, + -0.14356999f, -0.55781251f, 0.86372989f, + -0.27916002f, -0.55781251f, 0.83104002f, + -0.40509003f, -0.55781251f, 0.77860999f, + -0.51968002f, -0.55781257f, 0.70812005f, + -0.62125003f, -0.55781251f, 0.62125003f, + -0.70812005f, -0.55781251f, 0.51967996f, + -0.77860999f, -0.55781257f, 0.40509003f, + -0.83104002f, -0.55781251f, 0.27915999f, + -0.86373001f, -0.55781251f, 0.14356995f, + -0.87500000f, -0.55781251f, -0.00000001f, + 0.00000000f, -0.59160000f, 0.83800000f, + -0.13749902f, -0.59159994f, 0.82720655f, + -0.26735553f, -0.59160000f, 0.79589891f, + -0.38796049f, -0.59160000f, 0.74568588f, + -0.49770495f, -0.59160006f, 0.67817670f, + -0.59497994f, -0.59160000f, 0.59498000f, + -0.67817664f, -0.59160000f, 0.49770495f, + -0.74568594f, -0.59160006f, 0.38796049f, + -0.79589891f, -0.59160000f, 0.26735550f, + -0.82720655f, -0.59160000f, 0.13749899f, + -0.83800000f, -0.59160000f, -0.00000001f, + 0.00000000f, -0.61998749f, 0.80400008f, + -0.13192032f, -0.61998743f, 0.79364455f, + -0.25650817f, -0.61998749f, 0.76360714f, + -0.37221986f, -0.61998749f, 0.71543139f, + -0.47751173f, -0.61998749f, 0.65066117f, + -0.57084000f, -0.61998749f, 0.57084000f, + -0.65066117f, -0.61998749f, 0.47751170f, + -0.71543145f, -0.61998749f, 0.37221986f, + -0.76360714f, -0.61998749f, 0.25650814f, + -0.79364455f, -0.61998749f, 0.13192026f, + -0.80400008f, -0.61998749f, -0.00000001f, + 0.00000000f, -0.64320004f, 0.77600002f, + -0.12732607f, -0.64319998f, 0.76600504f, + -0.24757506f, -0.64320010f, 0.73701382f, + -0.35925698f, -0.64320004f, 0.69051588f, + -0.46088195f, -0.64320010f, 0.62800133f, + -0.55096000f, -0.64320004f, 0.55096000f, + -0.62800133f, -0.64320004f, 0.46088192f, + -0.69051588f, -0.64320004f, 0.35925698f, + -0.73701382f, -0.64320004f, 0.24757503f, + -0.76600516f, -0.64320004f, 0.12732603f, + -0.77600002f, -0.64320004f, -0.00000001f, + 0.00000000f, -0.66146249f, 0.75699997f, + -0.12420855f, -0.66146243f, 0.74724966f, + -0.24151328f, -0.66146255f, 0.71896833f, + -0.35046071f, -0.66146243f, 0.67360884f, + -0.44959745f, -0.66146255f, 0.61262494f, + -0.53746998f, -0.66146249f, 0.53746998f, + -0.61262494f, -0.66146249f, 0.44959742f, + -0.67360890f, -0.66146249f, 0.35046071f, + -0.71896827f, -0.66146255f, 0.24151327f, + -0.74724984f, -0.66146249f, 0.12420850f, + -0.75699997f, -0.66146249f, -0.00000001f, + 0.00000000f, -0.67500001f, 0.75000000f, + -0.12305999f, -0.67499995f, 0.74033999f, + -0.23928000f, -0.67500007f, 0.71232003f, + -0.34721997f, -0.67500001f, 0.66737998f, + -0.44544002f, -0.67500007f, 0.60696000f, + -0.53250003f, -0.67500007f, 0.53250003f, + -0.60696000f, -0.67500001f, 0.44543999f, + -0.66738003f, -0.67500001f, 0.34722000f, + -0.71231997f, -0.67500001f, 0.23927999f, + -0.74033999f, -0.67500001f, 0.12305995f, + -0.75000000f, -0.67500001f, -0.00000001f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.98711991f, -0.29999998f, -0.16407999f, + -0.94976002f, -0.30000004f, -0.31904000f, + -0.88984001f, -0.30000004f, -0.46296000f, + -0.80928004f, -0.30000001f, -0.59392005f, + -0.71000004f, -0.30000001f, -0.71000004f, + -0.59391999f, -0.30000001f, -0.80928004f, + -0.46296003f, -0.30000001f, -0.88984001f, + -0.31904000f, -0.30000001f, -0.94976002f, + -0.16407993f, -0.30000001f, -0.98712003f, + 0.00000000f, -0.30000001f, -1.00000000f, + -0.99299991f, -0.36416247f, -0.00000000f, + -0.98021001f, -0.36416242f, -0.16293143f, + -0.94311166f, -0.36416250f, -0.31680670f, + -0.88361102f, -0.36416245f, -0.45971927f, + -0.80361503f, -0.36416247f, -0.58976257f, + -0.70502996f, -0.36416247f, -0.70502996f, + -0.58976251f, -0.36416250f, -0.80361497f, + -0.45971930f, -0.36416247f, -0.88361108f, + -0.31680670f, -0.36416250f, -0.94311166f, + -0.16293137f, -0.36416247f, -0.98021007f, + 0.00000000f, -0.36416247f, -0.99299991f, + -0.97400004f, -0.42180002f, -0.00000001f, + -0.96145481f, -0.42179996f, -0.15981390f, + -0.92506635f, -0.42180005f, -0.31074497f, + -0.86670411f, -0.42180002f, -0.45092306f, + -0.78823876f, -0.42180005f, -0.57847816f, + -0.69154000f, -0.42180002f, -0.69154000f, + -0.57847810f, -0.42180002f, -0.78823876f, + -0.45092303f, -0.42180002f, -0.86670417f, + -0.31074494f, -0.42180002f, -0.92506629f, + -0.15981385f, -0.42180002f, -0.96145493f, + 0.00000000f, -0.42180002f, -0.97400004f, + -0.94600004f, -0.47313750f, -0.00000001f, + -0.93381548f, -0.47313747f, -0.15521967f, + -0.89847302f, -0.47313756f, -0.30181187f, + -0.84178865f, -0.47313750f, -0.43796018f, + -0.76557899f, -0.47313753f, -0.56184840f, + -0.67166001f, -0.47313750f, -0.67166001f, + -0.56184828f, -0.47313750f, -0.76557899f, + -0.43796021f, -0.47313750f, -0.84178865f, + -0.30181184f, -0.47313750f, -0.89847302f, + -0.15521961f, -0.47313750f, -0.93381560f, + 0.00000000f, -0.47313750f, -0.94600004f, + -0.91200000f, -0.51840001f, -0.00000001f, + -0.90025330f, -0.51839995f, -0.14964096f, + -0.86618114f, -0.51840001f, -0.29096451f, + -0.81153411f, -0.51840001f, -0.42221951f, + -0.73806345f, -0.51840007f, -0.54165506f, + -0.64752001f, -0.51840001f, -0.64752001f, + -0.54165506f, -0.51840001f, -0.73806340f, + -0.42221954f, -0.51840007f, -0.81153411f, + -0.29096448f, -0.51840007f, -0.86618114f, + -0.14964090f, -0.51840001f, -0.90025342f, + 0.00000000f, -0.51840001f, -0.91200000f, + -0.87500000f, -0.55781251f, -0.00000001f, + -0.86372989f, -0.55781251f, -0.14357001f, + -0.83104002f, -0.55781251f, -0.27916002f, + -0.77860999f, -0.55781251f, -0.40509003f, + -0.70812005f, -0.55781257f, -0.51968002f, + -0.62125003f, -0.55781251f, -0.62125003f, + -0.51967996f, -0.55781251f, -0.70812005f, + -0.40509003f, -0.55781257f, -0.77860999f, + -0.27915999f, -0.55781251f, -0.83104002f, + -0.14356995f, -0.55781251f, -0.86373001f, + 0.00000000f, -0.55781251f, -0.87500000f, + -0.83800000f, -0.59160000f, -0.00000001f, + -0.82720655f, -0.59159994f, -0.13749903f, + -0.79589891f, -0.59160000f, -0.26735553f, + -0.74568588f, -0.59160000f, -0.38796049f, + -0.67817670f, -0.59160006f, -0.49770495f, + -0.59498000f, -0.59160000f, -0.59497994f, + -0.49770495f, -0.59160000f, -0.67817664f, + -0.38796049f, -0.59160006f, -0.74568594f, + -0.26735550f, -0.59160000f, -0.79589891f, + -0.13749899f, -0.59160000f, -0.82720655f, + 0.00000000f, -0.59160000f, -0.83800000f, + -0.80400008f, -0.61998749f, -0.00000001f, + -0.79364455f, -0.61998743f, -0.13192032f, + -0.76360714f, -0.61998749f, -0.25650817f, + -0.71543139f, -0.61998749f, -0.37221986f, + -0.65066117f, -0.61998749f, -0.47751173f, + -0.57084000f, -0.61998749f, -0.57084000f, + -0.47751170f, -0.61998749f, -0.65066117f, + -0.37221986f, -0.61998749f, -0.71543145f, + -0.25650814f, -0.61998749f, -0.76360714f, + -0.13192026f, -0.61998749f, -0.79364455f, + 0.00000000f, -0.61998749f, -0.80400008f, + -0.77600002f, -0.64320004f, -0.00000001f, + -0.76600504f, -0.64319998f, -0.12732607f, + -0.73701382f, -0.64320010f, -0.24757506f, + -0.69051588f, -0.64320004f, -0.35925698f, + -0.62800133f, -0.64320010f, -0.46088195f, + -0.55096000f, -0.64320004f, -0.55096000f, + -0.46088192f, -0.64320004f, -0.62800133f, + -0.35925698f, -0.64320004f, -0.69051588f, + -0.24757503f, -0.64320004f, -0.73701382f, + -0.12732603f, -0.64320004f, -0.76600516f, + 0.00000000f, -0.64320004f, -0.77600002f, + -0.75699997f, -0.66146249f, -0.00000001f, + -0.74724966f, -0.66146243f, -0.12420855f, + -0.71896833f, -0.66146255f, -0.24151328f, + -0.67360884f, -0.66146243f, -0.35046071f, + -0.61262494f, -0.66146255f, -0.44959745f, + -0.53746998f, -0.66146249f, -0.53746998f, + -0.44959742f, -0.66146249f, -0.61262494f, + -0.35046071f, -0.66146249f, -0.67360890f, + -0.24151327f, -0.66146255f, -0.71896827f, + -0.12420851f, -0.66146249f, -0.74724984f, + 0.00000000f, -0.66146249f, -0.75699997f, + -0.75000000f, -0.67500001f, -0.00000001f, + -0.74033999f, -0.67499995f, -0.12306000f, + -0.71232003f, -0.67500007f, -0.23928000f, + -0.66737998f, -0.67500001f, -0.34721997f, + -0.60696000f, -0.67500007f, -0.44544002f, + -0.53250003f, -0.67500007f, -0.53250003f, + -0.44543999f, -0.67500001f, -0.60696000f, + -0.34722000f, -0.67500001f, -0.66738003f, + -0.23927999f, -0.67500001f, -0.71231997f, + -0.12305996f, -0.67500001f, -0.74033999f, + 0.00000000f, -0.67500001f, -0.75000000f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.09730000f, 0.82072502f, 0.00000001f, + 0.09605332f, 0.82072490f, 0.01602404f, + 0.09243499f, 0.82072508f, 0.03113591f, + 0.08662736f, 0.82072502f, 0.04515318f, + 0.07881293f, 0.82072508f, 0.05789340f, + 0.06917413f, 0.82072502f, 0.06917413f, + 0.05789339f, 0.82072502f, 0.07881294f, + 0.04515317f, 0.82072508f, 0.08662736f, + 0.03113590f, 0.82072502f, 0.09243499f, + 0.01602403f, 0.82072502f, 0.09605335f, + 0.00000000f, 0.82072502f, 0.09730001f, + 0.15440002f, 0.80880016f, 0.00000001f, + 0.15242170f, 0.80880004f, 0.02542728f, + 0.14667983f, 0.80880022f, 0.04940725f, + 0.13746388f, 0.80880022f, 0.07165039f, + 0.12506345f, 0.80880028f, 0.09186716f, + 0.10976801f, 0.80880016f, 0.10976802f, + 0.09186715f, 0.80880016f, 0.12506345f, + 0.07165038f, 0.80880022f, 0.13746390f, + 0.04940724f, 0.80880022f, 0.14667983f, + 0.02542726f, 0.80880016f, 0.15242171f, + 0.00000000f, 0.80880016f, 0.15440002f, + 0.17909999f, 0.79057503f, 0.00000001f, + 0.17680508f, 0.79057491f, 0.02949390f, + 0.17014435f, 0.79057509f, 0.05730941f, + 0.15945368f, 0.79057509f, 0.08311062f, + 0.14506906f, 0.79057503f, 0.10656159f, + 0.12732637f, 0.79057503f, 0.12732637f, + 0.10656157f, 0.79057503f, 0.14506905f, + 0.08311062f, 0.79057503f, 0.15945369f, + 0.05730940f, 0.79057503f, 0.17014435f, + 0.02949388f, 0.79057503f, 0.17680509f, + 0.00000000f, 0.79057503f, 0.17909999f, + 0.17920001f, 0.76740009f, 0.00000001f, + 0.17690356f, 0.76740003f, 0.02950812f, + 0.17023848f, 0.76740015f, 0.05733787f, + 0.15954098f, 0.76740009f, 0.08315296f, + 0.14514741f, 0.76740015f, 0.10661709f, + 0.12739401f, 0.76740009f, 0.12739401f, + 0.10661709f, 0.76740009f, 0.14514741f, + 0.08315295f, 0.76740009f, 0.15954098f, + 0.05733785f, 0.76740015f, 0.17023847f, + 0.02950810f, 0.76740009f, 0.17690358f, + 0.00000000f, 0.76740009f, 0.17920001f, + 0.16250001f, 0.74062496f, 0.00000001f, + 0.16041712f, 0.74062496f, 0.02675413f, + 0.15437202f, 0.74062502f, 0.05198801f, + 0.14466989f, 0.74062496f, 0.07539638f, + 0.13161601f, 0.74062496f, 0.09667401f, + 0.11551563f, 0.74062496f, 0.11551563f, + 0.09667400f, 0.74062496f, 0.13161600f, + 0.07539637f, 0.74062502f, 0.14466989f, + 0.05198800f, 0.74062502f, 0.15437201f, + 0.02675411f, 0.74062496f, 0.16041712f, + 0.00000000f, 0.74062496f, 0.16250001f, + 0.13680001f, 0.71160007f, 0.00000001f, + 0.13504578f, 0.71160001f, 0.02251614f, + 0.12995483f, 0.71160012f, 0.04375527f, + 0.12178454f, 0.71160007f, 0.06345994f, + 0.11079245f, 0.71160012f, 0.08137268f, + 0.09723600f, 0.71160007f, 0.09723601f, + 0.08137267f, 0.71160007f, 0.11079245f, + 0.06345994f, 0.71160007f, 0.12178455f, + 0.04375526f, 0.71160007f, 0.12995481f, + 0.02251612f, 0.71160007f, 0.13504580f, + 0.00000000f, 0.71160007f, 0.13680001f, + 0.10990001f, 0.68167502f, 0.00000001f, + 0.10848958f, 0.68167496f, 0.01807833f, + 0.10439678f, 0.68167502f, 0.03513508f, + 0.09782913f, 0.68167496f, 0.05096266f, + 0.08899431f, 0.68167502f, 0.06535347f, + 0.07809988f, 0.68167502f, 0.07809989f, + 0.06535345f, 0.68167502f, 0.08899432f, + 0.05096266f, 0.68167502f, 0.09782915f, + 0.03513507f, 0.68167502f, 0.10439678f, + 0.01807831f, 0.68167502f, 0.10848960f, + 0.00000000f, 0.68167502f, 0.10990001f, + 0.08960000f, 0.65219998f, 0.00000001f, + 0.08844854f, 0.65219992f, 0.01472490f, + 0.08510771f, 0.65220004f, 0.02862286f, + 0.07974781f, 0.65219998f, 0.04152356f, + 0.07253914f, 0.65219998f, 0.05325671f, + 0.06365200f, 0.65219998f, 0.06365201f, + 0.05325670f, 0.65219998f, 0.07253915f, + 0.04152355f, 0.65219998f, 0.07974782f, + 0.02862285f, 0.65219998f, 0.08510773f, + 0.01472489f, 0.65219998f, 0.08844855f, + 0.00000000f, 0.65219998f, 0.08960001f, + 0.08370000f, 0.62452501f, 0.00000001f, + 0.08262266f, 0.62452495f, 0.01374006f, + 0.07949751f, 0.62452501f, 0.02671403f, + 0.07448471f, 0.62452501f, 0.03876166f, + 0.06774452f, 0.62452507f, 0.04972278f, + 0.05943713f, 0.62452501f, 0.05943713f, + 0.04972277f, 0.62452501f, 0.06774452f, + 0.03876166f, 0.62452501f, 0.07448472f, + 0.02671402f, 0.62452501f, 0.07949752f, + 0.01374005f, 0.62452501f, 0.08262268f, + 0.00000000f, 0.62452501f, 0.08370001f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.09871199f, 0.59999996f, 0.01640801f, + 0.09497602f, 0.60000008f, 0.03190401f, + 0.08898400f, 0.60000008f, 0.04629601f, + 0.08092801f, 0.60000002f, 0.05939201f, + 0.07100000f, 0.60000002f, 0.07100001f, + 0.05939200f, 0.60000002f, 0.08092801f, + 0.04629600f, 0.60000002f, 0.08898401f, + 0.03190400f, 0.60000002f, 0.09497601f, + 0.01640799f, 0.60000002f, 0.09871200f, + 0.00000000f, 0.60000002f, 0.10000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82072502f, -0.09729999f, + 0.01602403f, 0.82072490f, -0.09605332f, + 0.03113590f, 0.82072508f, -0.09243497f, + 0.04515317f, 0.82072502f, -0.08662734f, + 0.05789339f, 0.82072508f, -0.07881292f, + 0.06917413f, 0.82072502f, -0.06917411f, + 0.07881293f, 0.82072502f, -0.05789338f, + 0.08662736f, 0.82072508f, -0.04515316f, + 0.09243498f, 0.82072502f, -0.03113589f, + 0.09605334f, 0.82072502f, -0.01602402f, + 0.09730000f, 0.82072502f, 0.00000001f, + 0.00000000f, 0.80880016f, -0.15440002f, + 0.02542727f, 0.80880004f, -0.15242170f, + 0.04940724f, 0.80880022f, -0.14667983f, + 0.07165038f, 0.80880022f, -0.13746388f, + 0.09186715f, 0.80880028f, -0.12506345f, + 0.10976800f, 0.80880016f, -0.10976801f, + 0.12506345f, 0.80880016f, -0.09186714f, + 0.13746388f, 0.80880022f, -0.07165038f, + 0.14667982f, 0.80880022f, -0.04940723f, + 0.15242171f, 0.80880016f, -0.02542725f, + 0.15440002f, 0.80880016f, 0.00000001f, + 0.00000000f, 0.79057503f, -0.17909999f, + 0.02949389f, 0.79057491f, -0.17680508f, + 0.05730940f, 0.79057509f, -0.17014435f, + 0.08311061f, 0.79057509f, -0.15945368f, + 0.10656158f, 0.79057503f, -0.14506905f, + 0.12732637f, 0.79057503f, -0.12732637f, + 0.14506905f, 0.79057503f, -0.10656157f, + 0.15945369f, 0.79057503f, -0.08311061f, + 0.17014435f, 0.79057503f, -0.05730939f, + 0.17680509f, 0.79057503f, -0.02949387f, + 0.17909999f, 0.79057503f, 0.00000001f, + 0.00000000f, 0.76740009f, -0.17920001f, + 0.02950811f, 0.76740003f, -0.17690356f, + 0.05733786f, 0.76740015f, -0.17023848f, + 0.08315294f, 0.76740009f, -0.15954097f, + 0.10661709f, 0.76740015f, -0.14514740f, + 0.12739401f, 0.76740009f, -0.12739399f, + 0.14514741f, 0.76740009f, -0.10661709f, + 0.15954098f, 0.76740009f, -0.08315295f, + 0.17023847f, 0.76740015f, -0.05733785f, + 0.17690358f, 0.76740009f, -0.02950809f, + 0.17920001f, 0.76740009f, 0.00000001f, + 0.00000000f, 0.74062496f, -0.16250001f, + 0.02675412f, 0.74062496f, -0.16041712f, + 0.05198800f, 0.74062502f, -0.15437202f, + 0.07539637f, 0.74062496f, -0.14466989f, + 0.09667401f, 0.74062496f, -0.13161601f, + 0.11551563f, 0.74062496f, -0.11551563f, + 0.13161600f, 0.74062496f, -0.09667400f, + 0.14466989f, 0.74062502f, -0.07539637f, + 0.15437201f, 0.74062502f, -0.05198799f, + 0.16041712f, 0.74062496f, -0.02675411f, + 0.16250001f, 0.74062496f, 0.00000001f, + 0.00000000f, 0.71160007f, -0.13679999f, + 0.02251613f, 0.71160001f, -0.13504578f, + 0.04375527f, 0.71160012f, -0.12995481f, + 0.06345994f, 0.71160007f, -0.12178454f, + 0.08137268f, 0.71160012f, -0.11079244f, + 0.09723600f, 0.71160007f, -0.09723599f, + 0.11079245f, 0.71160007f, -0.08137266f, + 0.12178455f, 0.71160007f, -0.06345993f, + 0.12995481f, 0.71160007f, -0.04375526f, + 0.13504580f, 0.71160007f, -0.02251611f, + 0.13680001f, 0.71160007f, 0.00000001f, + 0.00000000f, 0.68167502f, -0.10990000f, + 0.01807832f, 0.68167496f, -0.10848958f, + 0.03513507f, 0.68167502f, -0.10439678f, + 0.05096266f, 0.68167496f, -0.09782913f, + 0.06535346f, 0.68167502f, -0.08899431f, + 0.07809988f, 0.68167502f, -0.07809987f, + 0.08899431f, 0.68167502f, -0.06535345f, + 0.09782915f, 0.68167502f, -0.05096265f, + 0.10439678f, 0.68167502f, -0.03513506f, + 0.10848960f, 0.68167502f, -0.01807830f, + 0.10990001f, 0.68167502f, 0.00000001f, + 0.00000000f, 0.65219998f, -0.08960000f, + 0.01472490f, 0.65219992f, -0.08844854f, + 0.02862285f, 0.65220004f, -0.08510771f, + 0.04152355f, 0.65219998f, -0.07974780f, + 0.05325671f, 0.65219998f, -0.07253914f, + 0.06365199f, 0.65219998f, -0.06365199f, + 0.07253914f, 0.65219998f, -0.05325670f, + 0.07974781f, 0.65219998f, -0.04152355f, + 0.08510771f, 0.65219998f, -0.02862284f, + 0.08844854f, 0.65219998f, -0.01472488f, + 0.08960000f, 0.65219998f, 0.00000001f, + 0.00000000f, 0.62452501f, -0.08369999f, + 0.01374006f, 0.62452495f, -0.08262266f, + 0.02671402f, 0.62452501f, -0.07949750f, + 0.03876166f, 0.62452501f, -0.07448471f, + 0.04972277f, 0.62452507f, -0.06774451f, + 0.05943713f, 0.62452501f, -0.05943712f, + 0.06774452f, 0.62452501f, -0.04972276f, + 0.07448471f, 0.62452501f, -0.03876166f, + 0.07949751f, 0.62452501f, -0.02671401f, + 0.08262268f, 0.62452501f, -0.01374004f, + 0.08370000f, 0.62452501f, 0.00000001f, + 0.00000000f, 0.60000002f, -0.09999999f, + 0.01640800f, 0.59999996f, -0.09871199f, + 0.03190400f, 0.60000008f, -0.09497599f, + 0.04629600f, 0.60000008f, -0.08898398f, + 0.05939200f, 0.60000002f, -0.08092800f, + 0.07100000f, 0.60000002f, -0.07099999f, + 0.08092801f, 0.60000002f, -0.05939199f, + 0.08898400f, 0.60000002f, -0.04629600f, + 0.09497601f, 0.60000002f, -0.03190399f, + 0.09871200f, 0.60000002f, -0.01640799f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82072502f, 0.09730001f, + -0.01602403f, 0.82072490f, 0.09605334f, + -0.03113590f, 0.82072508f, 0.09243499f, + -0.04515317f, 0.82072502f, 0.08662736f, + -0.05789339f, 0.82072508f, 0.07881294f, + -0.06917413f, 0.82072502f, 0.06917413f, + -0.07881293f, 0.82072502f, 0.05789340f, + -0.08662736f, 0.82072508f, 0.04515318f, + -0.09243498f, 0.82072502f, 0.03113591f, + -0.09605334f, 0.82072502f, 0.01602404f, + -0.09730000f, 0.82072502f, 0.00000001f, + 0.00000000f, 0.80880016f, 0.15440002f, + -0.02542727f, 0.80880004f, 0.15242170f, + -0.04940724f, 0.80880022f, 0.14667983f, + -0.07165038f, 0.80880022f, 0.13746390f, + -0.09186715f, 0.80880028f, 0.12506345f, + -0.10976800f, 0.80880016f, 0.10976802f, + -0.12506345f, 0.80880016f, 0.09186715f, + -0.13746388f, 0.80880022f, 0.07165039f, + -0.14667982f, 0.80880022f, 0.04940725f, + -0.15242171f, 0.80880016f, 0.02542727f, + -0.15440002f, 0.80880016f, 0.00000001f, + 0.00000000f, 0.79057503f, 0.17909999f, + -0.02949389f, 0.79057491f, 0.17680508f, + -0.05730940f, 0.79057509f, 0.17014435f, + -0.08311061f, 0.79057509f, 0.15945368f, + -0.10656158f, 0.79057503f, 0.14506906f, + -0.12732637f, 0.79057503f, 0.12732637f, + -0.14506905f, 0.79057503f, 0.10656157f, + -0.15945369f, 0.79057503f, 0.08311062f, + -0.17014435f, 0.79057503f, 0.05730941f, + -0.17680509f, 0.79057503f, 0.02949389f, + -0.17909999f, 0.79057503f, 0.00000001f, + 0.00000000f, 0.76740009f, 0.17920001f, + -0.02950811f, 0.76740003f, 0.17690358f, + -0.05733786f, 0.76740015f, 0.17023848f, + -0.08315294f, 0.76740009f, 0.15954098f, + -0.10661709f, 0.76740015f, 0.14514741f, + -0.12739401f, 0.76740009f, 0.12739401f, + -0.14514741f, 0.76740009f, 0.10661709f, + -0.15954098f, 0.76740009f, 0.08315295f, + -0.17023847f, 0.76740015f, 0.05733786f, + -0.17690358f, 0.76740009f, 0.02950811f, + -0.17920001f, 0.76740009f, 0.00000001f, + 0.00000000f, 0.74062496f, 0.16250001f, + -0.02675412f, 0.74062496f, 0.16041712f, + -0.05198800f, 0.74062502f, 0.15437202f, + -0.07539637f, 0.74062496f, 0.14466989f, + -0.09667401f, 0.74062496f, 0.13161601f, + -0.11551563f, 0.74062496f, 0.11551563f, + -0.13161600f, 0.74062496f, 0.09667400f, + -0.14466989f, 0.74062502f, 0.07539638f, + -0.15437201f, 0.74062502f, 0.05198800f, + -0.16041712f, 0.74062496f, 0.02675412f, + -0.16250001f, 0.74062496f, 0.00000001f, + 0.00000000f, 0.71160007f, 0.13680001f, + -0.02251613f, 0.71160001f, 0.13504578f, + -0.04375527f, 0.71160012f, 0.12995483f, + -0.06345994f, 0.71160007f, 0.12178455f, + -0.08137268f, 0.71160012f, 0.11079246f, + -0.09723600f, 0.71160007f, 0.09723601f, + -0.11079245f, 0.71160007f, 0.08137267f, + -0.12178455f, 0.71160007f, 0.06345994f, + -0.12995481f, 0.71160007f, 0.04375527f, + -0.13504580f, 0.71160007f, 0.02251613f, + -0.13680001f, 0.71160007f, 0.00000001f, + 0.00000000f, 0.68167502f, 0.10990001f, + -0.01807832f, 0.68167496f, 0.10848960f, + -0.03513507f, 0.68167502f, 0.10439679f, + -0.05096266f, 0.68167496f, 0.09782915f, + -0.06535346f, 0.68167502f, 0.08899432f, + -0.07809988f, 0.68167502f, 0.07809988f, + -0.08899431f, 0.68167502f, 0.06535346f, + -0.09782915f, 0.68167502f, 0.05096267f, + -0.10439678f, 0.68167502f, 0.03513508f, + -0.10848960f, 0.68167502f, 0.01807832f, + -0.10990001f, 0.68167502f, 0.00000001f, + 0.00000000f, 0.65219998f, 0.08960001f, + -0.01472490f, 0.65219992f, 0.08844855f, + -0.02862285f, 0.65220004f, 0.08510773f, + -0.04152355f, 0.65219998f, 0.07974782f, + -0.05325671f, 0.65219998f, 0.07253915f, + -0.06365199f, 0.65219998f, 0.06365201f, + -0.07253914f, 0.65219998f, 0.05325671f, + -0.07974781f, 0.65219998f, 0.04152356f, + -0.08510771f, 0.65219998f, 0.02862286f, + -0.08844854f, 0.65219998f, 0.01472490f, + -0.08960000f, 0.65219998f, 0.00000001f, + 0.00000000f, 0.62452501f, 0.08370001f, + -0.01374006f, 0.62452495f, 0.08262267f, + -0.02671402f, 0.62452501f, 0.07949752f, + -0.03876166f, 0.62452501f, 0.07448472f, + -0.04972277f, 0.62452507f, 0.06774452f, + -0.05943713f, 0.62452501f, 0.05943713f, + -0.06774452f, 0.62452501f, 0.04972278f, + -0.07448471f, 0.62452501f, 0.03876167f, + -0.07949751f, 0.62452501f, 0.02671402f, + -0.08262268f, 0.62452501f, 0.01374006f, + -0.08370000f, 0.62452501f, 0.00000001f, + 0.00000000f, 0.60000002f, 0.10000001f, + -0.01640800f, 0.59999996f, 0.09871200f, + -0.03190400f, 0.60000008f, 0.09497602f, + -0.04629600f, 0.60000008f, 0.08898401f, + -0.05939200f, 0.60000002f, 0.08092801f, + -0.07100000f, 0.60000002f, 0.07100001f, + -0.08092801f, 0.60000002f, 0.05939201f, + -0.08898400f, 0.60000002f, 0.04629601f, + -0.09497601f, 0.60000002f, 0.03190401f, + -0.09871200f, 0.60000002f, 0.01640800f, + -0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500017f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500011f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + 0.00000000f, 0.82500005f, 0.00000001f, + -0.09730000f, 0.82072502f, 0.00000001f, + -0.09605332f, 0.82072490f, -0.01602402f, + -0.09243499f, 0.82072508f, -0.03113589f, + -0.08662736f, 0.82072502f, -0.04515316f, + -0.07881293f, 0.82072508f, -0.05789338f, + -0.06917413f, 0.82072502f, -0.06917411f, + -0.05789339f, 0.82072502f, -0.07881292f, + -0.04515317f, 0.82072508f, -0.08662735f, + -0.03113590f, 0.82072502f, -0.09243497f, + -0.01602403f, 0.82072502f, -0.09605333f, + 0.00000000f, 0.82072502f, -0.09729999f, + -0.15440002f, 0.80880016f, 0.00000001f, + -0.15242170f, 0.80880004f, -0.02542726f, + -0.14667983f, 0.80880022f, -0.04940723f, + -0.13746388f, 0.80880022f, -0.07165037f, + -0.12506345f, 0.80880028f, -0.09186714f, + -0.10976801f, 0.80880016f, -0.10976800f, + -0.09186715f, 0.80880016f, -0.12506345f, + -0.07165038f, 0.80880022f, -0.13746388f, + -0.04940724f, 0.80880022f, -0.14667982f, + -0.02542726f, 0.80880016f, -0.15242171f, + 0.00000000f, 0.80880016f, -0.15440002f, + -0.17909999f, 0.79057503f, 0.00000001f, + -0.17680508f, 0.79057491f, -0.02949388f, + -0.17014435f, 0.79057509f, -0.05730940f, + -0.15945368f, 0.79057509f, -0.08311061f, + -0.14506906f, 0.79057503f, -0.10656158f, + -0.12732637f, 0.79057503f, -0.12732637f, + -0.10656157f, 0.79057503f, -0.14506905f, + -0.08311062f, 0.79057503f, -0.15945369f, + -0.05730940f, 0.79057503f, -0.17014435f, + -0.02949388f, 0.79057503f, -0.17680509f, + 0.00000000f, 0.79057503f, -0.17909999f, + -0.17920001f, 0.76740009f, 0.00000001f, + -0.17690356f, 0.76740003f, -0.02950810f, + -0.17023848f, 0.76740015f, -0.05733785f, + -0.15954098f, 0.76740009f, -0.08315294f, + -0.14514741f, 0.76740015f, -0.10661709f, + -0.12739401f, 0.76740009f, -0.12739401f, + -0.10661709f, 0.76740009f, -0.14514740f, + -0.08315295f, 0.76740009f, -0.15954098f, + -0.05733785f, 0.76740015f, -0.17023847f, + -0.02950810f, 0.76740009f, -0.17690358f, + 0.00000000f, 0.76740009f, -0.17920001f, + -0.16250001f, 0.74062496f, 0.00000001f, + -0.16041712f, 0.74062496f, -0.02675412f, + -0.15437202f, 0.74062502f, -0.05198800f, + -0.14466989f, 0.74062496f, -0.07539637f, + -0.13161601f, 0.74062496f, -0.09667400f, + -0.11551563f, 0.74062496f, -0.11551563f, + -0.09667400f, 0.74062496f, -0.13161600f, + -0.07539637f, 0.74062502f, -0.14466989f, + -0.05198800f, 0.74062502f, -0.15437201f, + -0.02675411f, 0.74062496f, -0.16041712f, + 0.00000000f, 0.74062496f, -0.16250001f, + -0.13680001f, 0.71160007f, 0.00000001f, + -0.13504578f, 0.71160001f, -0.02251612f, + -0.12995483f, 0.71160012f, -0.04375526f, + -0.12178454f, 0.71160007f, -0.06345993f, + -0.11079245f, 0.71160012f, -0.08137267f, + -0.09723600f, 0.71160007f, -0.09723599f, + -0.08137267f, 0.71160007f, -0.11079245f, + -0.06345994f, 0.71160007f, -0.12178454f, + -0.04375526f, 0.71160007f, -0.12995481f, + -0.02251612f, 0.71160007f, -0.13504578f, + 0.00000000f, 0.71160007f, -0.13679999f, + -0.10990001f, 0.68167502f, 0.00000001f, + -0.10848958f, 0.68167496f, -0.01807831f, + -0.10439678f, 0.68167502f, -0.03513507f, + -0.09782913f, 0.68167496f, -0.05096265f, + -0.08899431f, 0.68167502f, -0.06535345f, + -0.07809988f, 0.68167502f, -0.07809987f, + -0.06535345f, 0.68167502f, -0.08899430f, + -0.05096266f, 0.68167502f, -0.09782913f, + -0.03513507f, 0.68167502f, -0.10439677f, + -0.01807831f, 0.68167502f, -0.10848959f, + 0.00000000f, 0.68167502f, -0.10990000f, + -0.08960000f, 0.65219998f, 0.00000001f, + -0.08844854f, 0.65219992f, -0.01472489f, + -0.08510771f, 0.65220004f, -0.02862284f, + -0.07974781f, 0.65219998f, -0.04152355f, + -0.07253914f, 0.65219998f, -0.05325670f, + -0.06365200f, 0.65219998f, -0.06365199f, + -0.05325670f, 0.65219998f, -0.07253914f, + -0.04152355f, 0.65219998f, -0.07974781f, + -0.02862285f, 0.65219998f, -0.08510771f, + -0.01472489f, 0.65219998f, -0.08844854f, + 0.00000000f, 0.65219998f, -0.08960000f, + -0.08370000f, 0.62452501f, 0.00000001f, + -0.08262266f, 0.62452495f, -0.01374005f, + -0.07949751f, 0.62452501f, -0.02671401f, + -0.07448471f, 0.62452501f, -0.03876165f, + -0.06774452f, 0.62452507f, -0.04972276f, + -0.05943713f, 0.62452501f, -0.05943712f, + -0.04972277f, 0.62452501f, -0.06774451f, + -0.03876166f, 0.62452501f, -0.07448471f, + -0.02671402f, 0.62452501f, -0.07949750f, + -0.01374005f, 0.62452501f, -0.08262267f, + 0.00000000f, 0.62452501f, -0.08369999f, + -0.10000000f, 0.60000002f, 0.00000001f, + -0.09871199f, 0.59999996f, -0.01640799f, + -0.09497602f, 0.60000008f, -0.03190399f, + -0.08898400f, 0.60000008f, -0.04629599f, + -0.08092801f, 0.60000002f, -0.05939199f, + -0.07100000f, 0.60000002f, -0.07099999f, + -0.05939200f, 0.60000002f, -0.08092800f, + -0.04629600f, 0.60000002f, -0.08898400f, + -0.03190400f, 0.60000002f, -0.09497599f, + -0.01640799f, 0.60000002f, -0.09871200f, + 0.00000000f, 0.60000002f, -0.09999999f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.09871199f, 0.59999996f, 0.01640801f, + 0.09497602f, 0.60000008f, 0.03190401f, + 0.08898400f, 0.60000008f, 0.04629601f, + 0.08092801f, 0.60000002f, 0.05939201f, + 0.07100000f, 0.60000002f, 0.07100001f, + 0.05939200f, 0.60000002f, 0.08092801f, + 0.04629600f, 0.60000002f, 0.08898401f, + 0.03190400f, 0.60000002f, 0.09497601f, + 0.01640799f, 0.60000002f, 0.09871200f, + 0.00000000f, 0.60000002f, 0.10000001f, + 0.13969998f, 0.57959992f, 0.00000001f, + 0.13790064f, 0.57959986f, 0.02292198f, + 0.13268146f, 0.57959998f, 0.04456990f, + 0.12431064f, 0.57959992f, 0.06467552f, + 0.11305641f, 0.57959998f, 0.08297063f, + 0.09918699f, 0.57959992f, 0.09918701f, + 0.08297062f, 0.57959992f, 0.11305644f, + 0.06467551f, 0.57959998f, 0.12431066f, + 0.04456988f, 0.57959992f, 0.13268149f, + 0.02292197f, 0.57959992f, 0.13790068f, + 0.00000000f, 0.57959992f, 0.13970001f, + 0.19560002f, 0.56280005f, 0.00000001f, + 0.19308068f, 0.56279999f, 0.03209405f, + 0.18577307f, 0.56280011f, 0.06240424f, + 0.17405272f, 0.56280005f, 0.09055499f, + 0.15829518f, 0.56280005f, 0.11617076f, + 0.13887602f, 0.56280005f, 0.13887601f, + 0.11617076f, 0.56280005f, 0.15829518f, + 0.09055499f, 0.56280005f, 0.17405272f, + 0.06240423f, 0.56280005f, 0.18577307f, + 0.03209404f, 0.56280005f, 0.19308069f, + 0.00000000f, 0.56280005f, 0.19560002f, + 0.26289999f, 0.54869998f, 0.00000001f, + 0.25951380f, 0.54869998f, 0.04313663f, + 0.24969190f, 0.54869998f, 0.08387563f, + 0.23393893f, 0.54870003f, 0.12171219f, + 0.21275970f, 0.54869998f, 0.15614158f, + 0.18665901f, 0.54869998f, 0.18665899f, + 0.15614155f, 0.54869998f, 0.21275972f, + 0.12171219f, 0.54869998f, 0.23393893f, + 0.08387562f, 0.54869998f, 0.24969190f, + 0.04313662f, 0.54869998f, 0.25951385f, + 0.00000000f, 0.54869998f, 0.26289999f, + 0.33680001f, 0.53640002f, 0.00000001f, + 0.33246198f, 0.53639996f, 0.05526215f, + 0.31987920f, 0.53640002f, 0.10745268f, + 0.29969811f, 0.53640002f, 0.15592493f, + 0.27256551f, 0.53640002f, 0.20003228f, + 0.23912801f, 0.53640002f, 0.23912801f, + 0.20003226f, 0.53640002f, 0.27256551f, + 0.15592495f, 0.53640008f, 0.29969811f, + 0.10745268f, 0.53640008f, 0.31987917f, + 0.05526213f, 0.53640002f, 0.33246201f, + 0.00000000f, 0.53640002f, 0.33680001f, + 0.41250002f, 0.52499998f, 0.00000001f, + 0.40718701f, 0.52499992f, 0.06768300f, + 0.39177606f, 0.52499998f, 0.13160400f, + 0.36705902f, 0.52499998f, 0.19097100f, + 0.33382803f, 0.52499998f, 0.24499202f, + 0.29287499f, 0.52499998f, 0.29287499f, + 0.24499199f, 0.52499998f, 0.33382803f, + 0.19097102f, 0.52499998f, 0.36705902f, + 0.13160400f, 0.52499998f, 0.39177603f, + 0.06768297f, 0.52499998f, 0.40718701f, + 0.00000000f, 0.52499998f, 0.41250002f, + 0.48519999f, 0.51359999f, 0.00000001f, + 0.47895056f, 0.51359999f, 0.07961162f, + 0.46082360f, 0.51359999f, 0.15479822f, + 0.43175036f, 0.51359999f, 0.22462820f, + 0.39266267f, 0.51360005f, 0.28817001f, + 0.34449199f, 0.51359999f, 0.34449199f, + 0.28816998f, 0.51359999f, 0.39266264f, + 0.22462821f, 0.51359999f, 0.43175036f, + 0.15479821f, 0.51359999f, 0.46082354f, + 0.07961158f, 0.51359999f, 0.47895062f, + 0.00000000f, 0.51359999f, 0.48519999f, + 0.55009997f, 0.50129998f, 0.00000001f, + 0.54301465f, 0.50129992f, 0.09026041f, + 0.52246296f, 0.50129998f, 0.17550392f, + 0.48950094f, 0.50129998f, 0.25467429f, + 0.44518495f, 0.50129998f, 0.32671541f, + 0.39057100f, 0.50129998f, 0.39057100f, + 0.32671538f, 0.50129998f, 0.44518495f, + 0.25467432f, 0.50129998f, 0.48950097f, + 0.17550389f, 0.50129998f, 0.52246296f, + 0.09026037f, 0.50129998f, 0.54301471f, + 0.00000000f, 0.50129998f, 0.55009997f, + 0.60240000f, 0.48720002f, 0.00000001f, + 0.59464109f, 0.48719996f, 0.09884179f, + 0.57213551f, 0.48720005f, 0.19218971f, + 0.53603959f, 0.48719999f, 0.27888709f, + 0.48751029f, 0.48720002f, 0.35777742f, + 0.42770401f, 0.48720002f, 0.42770401f, + 0.35777742f, 0.48720002f, 0.48751026f, + 0.27888709f, 0.48720005f, 0.53603959f, + 0.19218969f, 0.48720002f, 0.57213545f, + 0.09884176f, 0.48720002f, 0.59464109f, + 0.00000000f, 0.48720002f, 0.60240000f, + 0.63730001f, 0.47040004f, 0.00000001f, + 0.62909156f, 0.47039998f, 0.10456818f, + 0.60528207f, 0.47040007f, 0.20332420f, + 0.56709504f, 0.47040001f, 0.29504442f, + 0.51575416f, 0.47040004f, 0.37850523f, + 0.45248300f, 0.47040004f, 0.45248300f, + 0.37850523f, 0.47040004f, 0.51575416f, + 0.29504442f, 0.47040004f, 0.56709504f, + 0.20332420f, 0.47040004f, 0.60528207f, + 0.10456815f, 0.47040004f, 0.62909162f, + 0.00000000f, 0.47040004f, 0.63730001f, + 0.64999998f, 0.45000005f, 0.00000001f, + 0.64162791f, 0.44999996f, 0.10665201f, + 0.61734402f, 0.45000011f, 0.20737600f, + 0.57839590f, 0.45000002f, 0.30092400f, + 0.52603197f, 0.45000008f, 0.38604802f, + 0.46149999f, 0.45000005f, 0.46149999f, + 0.38604796f, 0.45000005f, 0.52603197f, + 0.30092400f, 0.45000008f, 0.57839596f, + 0.20737599f, 0.45000005f, 0.61734402f, + 0.10665196f, 0.45000005f, 0.64162797f, + 0.00000000f, 0.45000005f, 0.64999998f, + 0.00000000f, 0.60000002f, -0.09999999f, + 0.01640800f, 0.59999996f, -0.09871199f, + 0.03190400f, 0.60000008f, -0.09497599f, + 0.04629600f, 0.60000008f, -0.08898398f, + 0.05939200f, 0.60000002f, -0.08092800f, + 0.07100000f, 0.60000002f, -0.07099999f, + 0.08092801f, 0.60000002f, -0.05939199f, + 0.08898400f, 0.60000002f, -0.04629600f, + 0.09497601f, 0.60000002f, -0.03190399f, + 0.09871200f, 0.60000002f, -0.01640799f, + 0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.57959992f, -0.13969998f, + 0.02292197f, 0.57959986f, -0.13790064f, + 0.04456988f, 0.57959998f, -0.13268146f, + 0.06467551f, 0.57959992f, -0.12431064f, + 0.08297062f, 0.57959998f, -0.11305641f, + 0.09918699f, 0.57959992f, -0.09918699f, + 0.11305641f, 0.57959992f, -0.08297061f, + 0.12431064f, 0.57959998f, -0.06467551f, + 0.13268146f, 0.57959992f, -0.04456988f, + 0.13790065f, 0.57959992f, -0.02292196f, + 0.13969998f, 0.57959992f, 0.00000001f, + 0.00000000f, 0.56280005f, -0.19560000f, + 0.03209405f, 0.56279999f, -0.19308066f, + 0.06240423f, 0.56280011f, -0.18577306f, + 0.09055499f, 0.56280005f, -0.17405270f, + 0.11617076f, 0.56280005f, -0.15829517f, + 0.13887601f, 0.56280005f, -0.13887601f, + 0.15829518f, 0.56280005f, -0.11617075f, + 0.17405272f, 0.56280005f, -0.09055498f, + 0.18577307f, 0.56280005f, -0.06240422f, + 0.19308069f, 0.56280005f, -0.03209403f, + 0.19560002f, 0.56280005f, 0.00000001f, + 0.00000000f, 0.54869998f, -0.26289999f, + 0.04313663f, 0.54869998f, -0.25951380f, + 0.08387563f, 0.54869998f, -0.24969190f, + 0.12171219f, 0.54870003f, -0.23393893f, + 0.15614158f, 0.54869998f, -0.21275970f, + 0.18665899f, 0.54869998f, -0.18665899f, + 0.21275972f, 0.54869998f, -0.15614155f, + 0.23393893f, 0.54869998f, -0.12171219f, + 0.24969190f, 0.54869998f, -0.08387561f, + 0.25951385f, 0.54869998f, -0.04313661f, + 0.26289999f, 0.54869998f, 0.00000001f, + 0.00000000f, 0.53640002f, -0.33680001f, + 0.05526214f, 0.53639996f, -0.33246198f, + 0.10745268f, 0.53640002f, -0.31987920f, + 0.15592493f, 0.53640002f, -0.29969811f, + 0.20003228f, 0.53640002f, -0.27256551f, + 0.23912801f, 0.53640002f, -0.23912799f, + 0.27256551f, 0.53640002f, -0.20003225f, + 0.29969811f, 0.53640008f, -0.15592495f, + 0.31987917f, 0.53640008f, -0.10745267f, + 0.33246201f, 0.53640002f, -0.05526212f, + 0.33680001f, 0.53640002f, 0.00000001f, + 0.00000000f, 0.52499998f, -0.41250002f, + 0.06768300f, 0.52499992f, -0.40718701f, + 0.13160400f, 0.52499998f, -0.39177606f, + 0.19097100f, 0.52499998f, -0.36705902f, + 0.24499202f, 0.52499998f, -0.33382803f, + 0.29287499f, 0.52499998f, -0.29287499f, + 0.33382803f, 0.52499998f, -0.24499199f, + 0.36705902f, 0.52499998f, -0.19097102f, + 0.39177603f, 0.52499998f, -0.13160400f, + 0.40718701f, 0.52499998f, -0.06768297f, + 0.41250002f, 0.52499998f, 0.00000001f, + 0.00000000f, 0.51359999f, -0.48519999f, + 0.07961161f, 0.51359999f, -0.47895056f, + 0.15479822f, 0.51359999f, -0.46082360f, + 0.22462820f, 0.51359999f, -0.43175036f, + 0.28817001f, 0.51360005f, -0.39266267f, + 0.34449199f, 0.51359999f, -0.34449199f, + 0.39266264f, 0.51359999f, -0.28816998f, + 0.43175036f, 0.51359999f, -0.22462821f, + 0.46082354f, 0.51359999f, -0.15479821f, + 0.47895062f, 0.51359999f, -0.07961158f, + 0.48519999f, 0.51359999f, 0.00000001f, + 0.00000000f, 0.50129998f, -0.55009997f, + 0.09026040f, 0.50129992f, -0.54301465f, + 0.17550392f, 0.50129998f, -0.52246296f, + 0.25467429f, 0.50129998f, -0.48950094f, + 0.32671541f, 0.50129998f, -0.44518495f, + 0.39057100f, 0.50129998f, -0.39057100f, + 0.44518495f, 0.50129998f, -0.32671538f, + 0.48950097f, 0.50129998f, -0.25467432f, + 0.52246296f, 0.50129998f, -0.17550389f, + 0.54301471f, 0.50129998f, -0.09026036f, + 0.55009997f, 0.50129998f, 0.00000001f, + 0.00000000f, 0.48720002f, -0.60240000f, + 0.09884179f, 0.48719996f, -0.59464109f, + 0.19218971f, 0.48720005f, -0.57213551f, + 0.27888709f, 0.48719999f, -0.53603959f, + 0.35777742f, 0.48720002f, -0.48751029f, + 0.42770401f, 0.48720002f, -0.42770401f, + 0.48751026f, 0.48720002f, -0.35777742f, + 0.53603959f, 0.48720005f, -0.27888709f, + 0.57213545f, 0.48720002f, -0.19218969f, + 0.59464109f, 0.48720002f, -0.09884175f, + 0.60240000f, 0.48720002f, 0.00000001f, + 0.00000000f, 0.47040004f, -0.63730001f, + 0.10456818f, 0.47039998f, -0.62909156f, + 0.20332420f, 0.47040007f, -0.60528207f, + 0.29504442f, 0.47040001f, -0.56709504f, + 0.37850523f, 0.47040004f, -0.51575416f, + 0.45248300f, 0.47040004f, -0.45248300f, + 0.51575416f, 0.47040004f, -0.37850523f, + 0.56709504f, 0.47040004f, -0.29504442f, + 0.60528207f, 0.47040004f, -0.20332420f, + 0.62909162f, 0.47040004f, -0.10456815f, + 0.63730001f, 0.47040004f, 0.00000001f, + 0.00000000f, 0.45000005f, -0.64999998f, + 0.10665200f, 0.44999996f, -0.64162791f, + 0.20737600f, 0.45000011f, -0.61734402f, + 0.30092400f, 0.45000002f, -0.57839590f, + 0.38604802f, 0.45000008f, -0.52603197f, + 0.46149999f, 0.45000005f, -0.46149999f, + 0.52603197f, 0.45000005f, -0.38604796f, + 0.57839596f, 0.45000008f, -0.30092400f, + 0.61734402f, 0.45000005f, -0.20737599f, + 0.64162797f, 0.45000005f, -0.10665195f, + 0.64999998f, 0.45000005f, 0.00000001f, + 0.00000000f, 0.60000002f, 0.10000001f, + -0.01640800f, 0.59999996f, 0.09871200f, + -0.03190400f, 0.60000008f, 0.09497602f, + -0.04629600f, 0.60000008f, 0.08898401f, + -0.05939200f, 0.60000002f, 0.08092801f, + -0.07100000f, 0.60000002f, 0.07100001f, + -0.08092801f, 0.60000002f, 0.05939201f, + -0.08898400f, 0.60000002f, 0.04629601f, + -0.09497601f, 0.60000002f, 0.03190401f, + -0.09871200f, 0.60000002f, 0.01640800f, + -0.10000000f, 0.60000002f, 0.00000001f, + 0.00000000f, 0.57959992f, 0.13970001f, + -0.02292197f, 0.57959986f, 0.13790067f, + -0.04456988f, 0.57959998f, 0.13268149f, + -0.06467551f, 0.57959992f, 0.12431065f, + -0.08297062f, 0.57959998f, 0.11305643f, + -0.09918699f, 0.57959992f, 0.09918701f, + -0.11305641f, 0.57959992f, 0.08297063f, + -0.12431064f, 0.57959998f, 0.06467552f, + -0.13268146f, 0.57959992f, 0.04456989f, + -0.13790065f, 0.57959992f, 0.02292197f, + -0.13969998f, 0.57959992f, 0.00000001f, + 0.00000000f, 0.56280005f, 0.19560002f, + -0.03209405f, 0.56279999f, 0.19308068f, + -0.06240423f, 0.56280011f, 0.18577307f, + -0.09055499f, 0.56280005f, 0.17405272f, + -0.11617076f, 0.56280005f, 0.15829518f, + -0.13887601f, 0.56280005f, 0.13887602f, + -0.15829518f, 0.56280005f, 0.11617076f, + -0.17405272f, 0.56280005f, 0.09055499f, + -0.18577307f, 0.56280005f, 0.06240423f, + -0.19308069f, 0.56280005f, 0.03209404f, + -0.19560002f, 0.56280005f, 0.00000001f, + 0.00000000f, 0.54869998f, 0.26289999f, + -0.04313663f, 0.54869998f, 0.25951380f, + -0.08387563f, 0.54869998f, 0.24969190f, + -0.12171219f, 0.54870003f, 0.23393893f, + -0.15614158f, 0.54869998f, 0.21275970f, + -0.18665899f, 0.54869998f, 0.18665901f, + -0.21275972f, 0.54869998f, 0.15614155f, + -0.23393893f, 0.54869998f, 0.12171219f, + -0.24969190f, 0.54869998f, 0.08387562f, + -0.25951385f, 0.54869998f, 0.04313662f, + -0.26289999f, 0.54869998f, 0.00000001f, + 0.00000000f, 0.53640002f, 0.33680001f, + -0.05526214f, 0.53639996f, 0.33246198f, + -0.10745268f, 0.53640002f, 0.31987920f, + -0.15592493f, 0.53640002f, 0.29969811f, + -0.20003228f, 0.53640002f, 0.27256551f, + -0.23912801f, 0.53640002f, 0.23912801f, + -0.27256551f, 0.53640002f, 0.20003226f, + -0.29969811f, 0.53640008f, 0.15592495f, + -0.31987917f, 0.53640008f, 0.10745268f, + -0.33246201f, 0.53640002f, 0.05526213f, + -0.33680001f, 0.53640002f, 0.00000001f, + 0.00000000f, 0.52499998f, 0.41250002f, + -0.06768300f, 0.52499992f, 0.40718701f, + -0.13160400f, 0.52499998f, 0.39177606f, + -0.19097100f, 0.52499998f, 0.36705902f, + -0.24499202f, 0.52499998f, 0.33382803f, + -0.29287499f, 0.52499998f, 0.29287499f, + -0.33382803f, 0.52499998f, 0.24499199f, + -0.36705902f, 0.52499998f, 0.19097102f, + -0.39177603f, 0.52499998f, 0.13160400f, + -0.40718701f, 0.52499998f, 0.06768298f, + -0.41250002f, 0.52499998f, 0.00000001f, + 0.00000000f, 0.51359999f, 0.48519999f, + -0.07961161f, 0.51359999f, 0.47895056f, + -0.15479822f, 0.51359999f, 0.46082360f, + -0.22462820f, 0.51359999f, 0.43175036f, + -0.28817001f, 0.51360005f, 0.39266267f, + -0.34449199f, 0.51359999f, 0.34449199f, + -0.39266264f, 0.51359999f, 0.28816998f, + -0.43175036f, 0.51359999f, 0.22462821f, + -0.46082354f, 0.51359999f, 0.15479821f, + -0.47895062f, 0.51359999f, 0.07961159f, + -0.48519999f, 0.51359999f, 0.00000001f, + 0.00000000f, 0.50129998f, 0.55009997f, + -0.09026040f, 0.50129992f, 0.54301465f, + -0.17550392f, 0.50129998f, 0.52246296f, + -0.25467429f, 0.50129998f, 0.48950094f, + -0.32671541f, 0.50129998f, 0.44518495f, + -0.39057100f, 0.50129998f, 0.39057100f, + -0.44518495f, 0.50129998f, 0.32671538f, + -0.48950097f, 0.50129998f, 0.25467432f, + -0.52246296f, 0.50129998f, 0.17550389f, + -0.54301471f, 0.50129998f, 0.09026038f, + -0.55009997f, 0.50129998f, 0.00000001f, + 0.00000000f, 0.48720002f, 0.60240000f, + -0.09884179f, 0.48719996f, 0.59464109f, + -0.19218971f, 0.48720005f, 0.57213551f, + -0.27888709f, 0.48719999f, 0.53603959f, + -0.35777742f, 0.48720002f, 0.48751029f, + -0.42770401f, 0.48720002f, 0.42770401f, + -0.48751026f, 0.48720002f, 0.35777742f, + -0.53603959f, 0.48720005f, 0.27888709f, + -0.57213545f, 0.48720002f, 0.19218969f, + -0.59464109f, 0.48720002f, 0.09884176f, + -0.60240000f, 0.48720002f, 0.00000001f, + 0.00000000f, 0.47040004f, 0.63730001f, + -0.10456818f, 0.47039998f, 0.62909156f, + -0.20332420f, 0.47040007f, 0.60528207f, + -0.29504442f, 0.47040001f, 0.56709504f, + -0.37850523f, 0.47040004f, 0.51575416f, + -0.45248300f, 0.47040004f, 0.45248300f, + -0.51575416f, 0.47040004f, 0.37850523f, + -0.56709504f, 0.47040004f, 0.29504442f, + -0.60528207f, 0.47040004f, 0.20332420f, + -0.62909162f, 0.47040004f, 0.10456816f, + -0.63730001f, 0.47040004f, 0.00000001f, + 0.00000000f, 0.45000005f, 0.64999998f, + -0.10665200f, 0.44999996f, 0.64162791f, + -0.20737600f, 0.45000011f, 0.61734402f, + -0.30092400f, 0.45000002f, 0.57839590f, + -0.38604802f, 0.45000008f, 0.52603197f, + -0.46149999f, 0.45000005f, 0.46149999f, + -0.52603197f, 0.45000005f, 0.38604796f, + -0.57839596f, 0.45000008f, 0.30092400f, + -0.61734402f, 0.45000005f, 0.20737599f, + -0.64162797f, 0.45000005f, 0.10665197f, + -0.64999998f, 0.45000005f, 0.00000001f, + -0.10000000f, 0.60000002f, 0.00000001f, + -0.09871199f, 0.59999996f, -0.01640799f, + -0.09497602f, 0.60000008f, -0.03190399f, + -0.08898400f, 0.60000008f, -0.04629599f, + -0.08092801f, 0.60000002f, -0.05939199f, + -0.07100000f, 0.60000002f, -0.07099999f, + -0.05939200f, 0.60000002f, -0.08092800f, + -0.04629600f, 0.60000002f, -0.08898400f, + -0.03190400f, 0.60000002f, -0.09497599f, + -0.01640799f, 0.60000002f, -0.09871200f, + 0.00000000f, 0.60000002f, -0.09999999f, + -0.13969998f, 0.57959992f, 0.00000001f, + -0.13790064f, 0.57959986f, -0.02292197f, + -0.13268146f, 0.57959998f, -0.04456988f, + -0.12431064f, 0.57959992f, -0.06467550f, + -0.11305641f, 0.57959998f, -0.08297062f, + -0.09918699f, 0.57959992f, -0.09918699f, + -0.08297062f, 0.57959992f, -0.11305641f, + -0.06467551f, 0.57959998f, -0.12431063f, + -0.04456988f, 0.57959992f, -0.13268146f, + -0.02292197f, 0.57959992f, -0.13790065f, + 0.00000000f, 0.57959992f, -0.13969998f, + -0.19560002f, 0.56280005f, 0.00000001f, + -0.19308068f, 0.56279999f, -0.03209404f, + -0.18577307f, 0.56280011f, -0.06240422f, + -0.17405272f, 0.56280005f, -0.09055497f, + -0.15829518f, 0.56280005f, -0.11617076f, + -0.13887602f, 0.56280005f, -0.13887601f, + -0.11617076f, 0.56280005f, -0.15829517f, + -0.09055499f, 0.56280005f, -0.17405272f, + -0.06240423f, 0.56280005f, -0.18577306f, + -0.03209404f, 0.56280005f, -0.19308066f, + 0.00000000f, 0.56280005f, -0.19560000f, + -0.26289999f, 0.54869998f, 0.00000001f, + -0.25951380f, 0.54869998f, -0.04313662f, + -0.24969190f, 0.54869998f, -0.08387562f, + -0.23393893f, 0.54870003f, -0.12171219f, + -0.21275970f, 0.54869998f, -0.15614156f, + -0.18665901f, 0.54869998f, -0.18665899f, + -0.15614155f, 0.54869998f, -0.21275972f, + -0.12171219f, 0.54869998f, -0.23393893f, + -0.08387562f, 0.54869998f, -0.24969190f, + -0.04313662f, 0.54869998f, -0.25951385f, + 0.00000000f, 0.54869998f, -0.26289999f, + -0.33680001f, 0.53640002f, 0.00000001f, + -0.33246198f, 0.53639996f, -0.05526214f, + -0.31987920f, 0.53640002f, -0.10745268f, + -0.29969811f, 0.53640002f, -0.15592493f, + -0.27256551f, 0.53640002f, -0.20003226f, + -0.23912801f, 0.53640002f, -0.23912801f, + -0.20003226f, 0.53640002f, -0.27256551f, + -0.15592495f, 0.53640008f, -0.29969811f, + -0.10745268f, 0.53640008f, -0.31987917f, + -0.05526213f, 0.53640002f, -0.33246201f, + 0.00000000f, 0.53640002f, -0.33680001f, + -0.41250002f, 0.52499998f, 0.00000001f, + -0.40718701f, 0.52499992f, -0.06768300f, + -0.39177606f, 0.52499998f, -0.13160400f, + -0.36705902f, 0.52499998f, -0.19097100f, + -0.33382803f, 0.52499998f, -0.24499202f, + -0.29287499f, 0.52499998f, -0.29287499f, + -0.24499199f, 0.52499998f, -0.33382803f, + -0.19097102f, 0.52499998f, -0.36705902f, + -0.13160400f, 0.52499998f, -0.39177603f, + -0.06768297f, 0.52499998f, -0.40718701f, + 0.00000000f, 0.52499998f, -0.41250002f, + -0.48519999f, 0.51359999f, 0.00000001f, + -0.47895056f, 0.51359999f, -0.07961161f, + -0.46082360f, 0.51359999f, -0.15479822f, + -0.43175036f, 0.51359999f, -0.22462820f, + -0.39266267f, 0.51360005f, -0.28817001f, + -0.34449199f, 0.51359999f, -0.34449199f, + -0.28816998f, 0.51359999f, -0.39266264f, + -0.22462821f, 0.51359999f, -0.43175036f, + -0.15479821f, 0.51359999f, -0.46082354f, + -0.07961158f, 0.51359999f, -0.47895062f, + 0.00000000f, 0.51359999f, -0.48519999f, + -0.55009997f, 0.50129998f, 0.00000001f, + -0.54301465f, 0.50129992f, -0.09026039f, + -0.52246296f, 0.50129998f, -0.17550392f, + -0.48950094f, 0.50129998f, -0.25467429f, + -0.44518495f, 0.50129998f, -0.32671541f, + -0.39057100f, 0.50129998f, -0.39057100f, + -0.32671538f, 0.50129998f, -0.44518495f, + -0.25467432f, 0.50129998f, -0.48950097f, + -0.17550389f, 0.50129998f, -0.52246296f, + -0.09026037f, 0.50129998f, -0.54301471f, + 0.00000000f, 0.50129998f, -0.55009997f, + -0.60240000f, 0.48720002f, 0.00000001f, + -0.59464109f, 0.48719996f, -0.09884178f, + -0.57213551f, 0.48720005f, -0.19218971f, + -0.53603959f, 0.48719999f, -0.27888709f, + -0.48751029f, 0.48720002f, -0.35777742f, + -0.42770401f, 0.48720002f, -0.42770401f, + -0.35777742f, 0.48720002f, -0.48751026f, + -0.27888709f, 0.48720005f, -0.53603959f, + -0.19218969f, 0.48720002f, -0.57213545f, + -0.09884176f, 0.48720002f, -0.59464109f, + 0.00000000f, 0.48720002f, -0.60240000f, + -0.63730001f, 0.47040004f, 0.00000001f, + -0.62909156f, 0.47039998f, -0.10456817f, + -0.60528207f, 0.47040007f, -0.20332420f, + -0.56709504f, 0.47040001f, -0.29504442f, + -0.51575416f, 0.47040004f, -0.37850523f, + -0.45248300f, 0.47040004f, -0.45248300f, + -0.37850523f, 0.47040004f, -0.51575416f, + -0.29504442f, 0.47040004f, -0.56709504f, + -0.20332420f, 0.47040004f, -0.60528207f, + -0.10456815f, 0.47040004f, -0.62909162f, + 0.00000000f, 0.47040004f, -0.63730001f, + -0.64999998f, 0.45000005f, 0.00000001f, + -0.64162791f, 0.44999996f, -0.10665199f, + -0.61734402f, 0.45000011f, -0.20737600f, + -0.57839590f, 0.45000002f, -0.30092400f, + -0.52603197f, 0.45000008f, -0.38604802f, + -0.46149999f, 0.45000005f, -0.46149999f, + -0.38604796f, 0.45000005f, -0.52603197f, + -0.30092400f, 0.45000008f, -0.57839596f, + -0.20737599f, 0.45000005f, -0.61734402f, + -0.10665196f, 0.45000005f, -0.64162797f, + 0.00000000f, 0.45000005f, -0.64999998f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.74891251f, 0.19413748f, + 0.03185407f, -0.74891245f, 0.19163698f, + 0.06193763f, -0.74891251f, 0.18438403f, + 0.08987789f, -0.74891245f, 0.17275129f, + 0.11530213f, -0.74891257f, 0.15711159f, + 0.13783762f, -0.74891251f, 0.13783762f, + 0.15711159f, -0.74891251f, 0.11530213f, + 0.17275131f, -0.74891257f, 0.08987789f, + 0.18438402f, -0.74891257f, 0.06193761f, + 0.19163698f, -0.74891251f, 0.03185406f, + 0.19413748f, -0.74891251f, -0.00000001f, + 0.00000000f, -0.74580008f, 0.35160002f, + 0.05769053f, -0.74579996f, 0.34707141f, + 0.11217448f, -0.74580014f, 0.33393562f, + 0.16277674f, -0.74580008f, 0.31286776f, + 0.20882230f, -0.74580008f, 0.28454286f, + 0.24963601f, -0.74580008f, 0.24963601f, + 0.28454286f, -0.74580008f, 0.20882228f, + 0.31286776f, -0.74580008f, 0.16277674f, + 0.33393565f, -0.74580008f, 0.11217446f, + 0.34707141f, -0.74580002f, 0.05769050f, + 0.35160002f, -0.74580008f, -0.00000001f, + 0.00000000f, -0.74088752f, 0.47621247f, + 0.07813694f, -0.74088746f, 0.47007880f, + 0.15193085f, -0.74088758f, 0.45228758f, + 0.22046733f, -0.74088752f, 0.42375290f, + 0.28283215f, -0.74088758f, 0.38538927f, + 0.33811086f, -0.74088752f, 0.33811086f, + 0.38538924f, -0.74088752f, 0.28283212f, + 0.42375290f, -0.74088752f, 0.22046733f, + 0.45228755f, -0.74088752f, 0.15193082f, + 0.47007886f, -0.74088752f, 0.07813691f, + 0.47621247f, -0.74088752f, -0.00000001f, + 0.00000000f, -0.73440003f, 0.57179999f, + 0.09382094f, -0.73439997f, 0.56443512f, + 0.18242709f, -0.73440009f, 0.54307282f, + 0.26472056f, -0.73440003f, 0.50881052f, + 0.33960345f, -0.73440003f, 0.46274635f, + 0.40597799f, -0.73440003f, 0.40597799f, + 0.46274632f, -0.73440009f, 0.33960345f, + 0.50881052f, -0.73440003f, 0.26472053f, + 0.54307282f, -0.73440003f, 0.18242708f, + 0.56443524f, -0.73440003f, 0.09382091f, + 0.57179999f, -0.73440003f, -0.00000001f, + 0.00000000f, -0.72656250f, 0.64218748f, + 0.10537013f, -0.72656244f, 0.63391608f, + 0.20488352f, -0.72656256f, 0.60992402f, + 0.29730710f, -0.72656256f, 0.57144409f, + 0.38140801f, -0.72656256f, 0.51970953f, + 0.45595312f, -0.72656250f, 0.45595312f, + 0.51970953f, -0.72656250f, 0.38140798f, + 0.57144415f, -0.72656250f, 0.29730713f, + 0.60992396f, -0.72656250f, 0.20488349f, + 0.63391614f, -0.72656250f, 0.10537008f, + 0.64218748f, -0.72656250f, -0.00000001f, + 0.00000000f, -0.71759999f, 0.69119996f, + 0.11341208f, -0.71759993f, 0.68229729f, + 0.22052045f, -0.71760005f, 0.65647405f, + 0.31999791f, -0.71759999f, 0.61505735f, + 0.41051748f, -0.71759999f, 0.55937433f, + 0.49075195f, -0.71759999f, 0.49075198f, + 0.55937433f, -0.71759999f, 0.41051745f, + 0.61505735f, -0.71759999f, 0.31999797f, + 0.65647411f, -0.71759999f, 0.22052044f, + 0.68229735f, -0.71759999f, 0.11341204f, + 0.69119996f, -0.71759999f, -0.00000001f, + 0.00000000f, -0.70773757f, 0.72266257f, + 0.11857446f, -0.70773745f, 0.71335465f, + 0.23055826f, -0.70773757f, 0.68635601f, + 0.33456385f, -0.70773745f, 0.64305401f, + 0.42920375f, -0.70773751f, 0.58483636f, + 0.51309043f, -0.70773751f, 0.51309037f, + 0.58483636f, -0.70773751f, 0.42920372f, + 0.64305407f, -0.70773757f, 0.33456385f, + 0.68635601f, -0.70773751f, 0.23055825f, + 0.71335471f, -0.70773751f, 0.11857441f, + 0.72266257f, -0.70773751f, -0.00000001f, + 0.00000000f, -0.69720000f, 0.74039996f, + 0.12148482f, -0.69719994f, 0.73086351f, + 0.23621722f, -0.69720006f, 0.70320231f, + 0.34277558f, -0.69720000f, 0.65883750f, + 0.43973836f, -0.69720006f, 0.59919089f, + 0.52568400f, -0.69719994f, 0.52568400f, + 0.59919089f, -0.69720006f, 0.43973833f, + 0.65883750f, -0.69720000f, 0.34277558f, + 0.70320225f, -0.69720006f, 0.23621720f, + 0.73086363f, -0.69720000f, 0.12148478f, + 0.74039996f, -0.69720000f, -0.00000001f, + 0.00000000f, -0.68621248f, 0.74823749f, + 0.12277080f, -0.68621248f, 0.73860013f, + 0.23871772f, -0.68621254f, 0.71064609f, + 0.34640405f, -0.68621248f, 0.66581166f, + 0.44439322f, -0.68621248f, 0.60553366f, + 0.53124863f, -0.68621248f, 0.53124863f, + 0.60553366f, -0.68621248f, 0.44439319f, + 0.66581166f, -0.68621254f, 0.34640405f, + 0.71064603f, -0.68621254f, 0.23871769f, + 0.73860019f, -0.68621248f, 0.12277076f, + 0.74823749f, -0.68621248f, -0.00000001f, + 0.00000000f, -0.67500001f, 0.75000000f, + 0.12305999f, -0.67499995f, 0.74033999f, + 0.23928000f, -0.67500007f, 0.71232003f, + 0.34721997f, -0.67500001f, 0.66737998f, + 0.44544002f, -0.67500007f, 0.60696000f, + 0.53250003f, -0.67500007f, 0.53250003f, + 0.60696000f, -0.67500001f, 0.44543999f, + 0.66738003f, -0.67500001f, 0.34722000f, + 0.71231997f, -0.67500001f, 0.23927999f, + 0.74033999f, -0.67500001f, 0.12305995f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.19413748f, -0.74891251f, -0.00000001f, + 0.19163698f, -0.74891245f, -0.03185408f, + 0.18438403f, -0.74891251f, -0.06193763f, + 0.17275129f, -0.74891245f, -0.08987790f, + 0.15711159f, -0.74891257f, -0.11530215f, + 0.13783762f, -0.74891251f, -0.13783762f, + 0.11530213f, -0.74891251f, -0.15711159f, + 0.08987789f, -0.74891257f, -0.17275131f, + 0.06193762f, -0.74891257f, -0.18438402f, + 0.03185407f, -0.74891251f, -0.19163698f, + 0.00000000f, -0.74891251f, -0.19413748f, + 0.35160002f, -0.74580008f, -0.00000001f, + 0.34707141f, -0.74579996f, -0.05769053f, + 0.33393562f, -0.74580014f, -0.11217449f, + 0.31286776f, -0.74580008f, -0.16277674f, + 0.28454286f, -0.74580008f, -0.20882230f, + 0.24963601f, -0.74580008f, -0.24963601f, + 0.20882228f, -0.74580008f, -0.28454286f, + 0.16277674f, -0.74580008f, -0.31286776f, + 0.11217447f, -0.74580008f, -0.33393565f, + 0.05769051f, -0.74580002f, -0.34707141f, + 0.00000000f, -0.74580008f, -0.35160002f, + 0.47621247f, -0.74088752f, -0.00000001f, + 0.47007880f, -0.74088746f, -0.07813694f, + 0.45228758f, -0.74088758f, -0.15193085f, + 0.42375290f, -0.74088752f, -0.22046733f, + 0.38538927f, -0.74088758f, -0.28283215f, + 0.33811086f, -0.74088752f, -0.33811086f, + 0.28283212f, -0.74088752f, -0.38538924f, + 0.22046733f, -0.74088752f, -0.42375290f, + 0.15193082f, -0.74088752f, -0.45228755f, + 0.07813691f, -0.74088752f, -0.47007886f, + 0.00000000f, -0.74088752f, -0.47621247f, + 0.57179999f, -0.73440003f, -0.00000001f, + 0.56443512f, -0.73439997f, -0.09382095f, + 0.54307282f, -0.73440009f, -0.18242709f, + 0.50881052f, -0.73440003f, -0.26472056f, + 0.46274635f, -0.73440003f, -0.33960345f, + 0.40597799f, -0.73440003f, -0.40597799f, + 0.33960345f, -0.73440009f, -0.46274632f, + 0.26472053f, -0.73440003f, -0.50881052f, + 0.18242708f, -0.73440003f, -0.54307282f, + 0.09382091f, -0.73440003f, -0.56443524f, + 0.00000000f, -0.73440003f, -0.57179999f, + 0.64218748f, -0.72656250f, -0.00000001f, + 0.63391608f, -0.72656244f, -0.10537013f, + 0.60992402f, -0.72656256f, -0.20488352f, + 0.57144409f, -0.72656256f, -0.29730710f, + 0.51970953f, -0.72656256f, -0.38140801f, + 0.45595312f, -0.72656250f, -0.45595312f, + 0.38140798f, -0.72656250f, -0.51970953f, + 0.29730713f, -0.72656250f, -0.57144415f, + 0.20488349f, -0.72656250f, -0.60992396f, + 0.10537009f, -0.72656250f, -0.63391614f, + 0.00000000f, -0.72656250f, -0.64218748f, + 0.69119996f, -0.71759999f, -0.00000001f, + 0.68229729f, -0.71759993f, -0.11341209f, + 0.65647405f, -0.71760005f, -0.22052045f, + 0.61505735f, -0.71759999f, -0.31999791f, + 0.55937433f, -0.71759999f, -0.41051748f, + 0.49075198f, -0.71759999f, -0.49075195f, + 0.41051745f, -0.71759999f, -0.55937433f, + 0.31999797f, -0.71759999f, -0.61505735f, + 0.22052044f, -0.71759999f, -0.65647411f, + 0.11341204f, -0.71759999f, -0.68229735f, + 0.00000000f, -0.71759999f, -0.69119996f, + 0.72266257f, -0.70773751f, -0.00000001f, + 0.71335465f, -0.70773745f, -0.11857446f, + 0.68635601f, -0.70773757f, -0.23055826f, + 0.64305401f, -0.70773745f, -0.33456385f, + 0.58483636f, -0.70773751f, -0.42920375f, + 0.51309037f, -0.70773751f, -0.51309043f, + 0.42920372f, -0.70773751f, -0.58483636f, + 0.33456385f, -0.70773751f, -0.64305407f, + 0.23055825f, -0.70773751f, -0.68635601f, + 0.11857442f, -0.70773751f, -0.71335471f, + 0.00000000f, -0.70773751f, -0.72266257f, + 0.74039996f, -0.69720000f, -0.00000001f, + 0.73086351f, -0.69719994f, -0.12148483f, + 0.70320231f, -0.69720006f, -0.23621722f, + 0.65883750f, -0.69720000f, -0.34277558f, + 0.59919089f, -0.69720006f, -0.43973836f, + 0.52568400f, -0.69719994f, -0.52568400f, + 0.43973833f, -0.69720006f, -0.59919089f, + 0.34277558f, -0.69720000f, -0.65883750f, + 0.23621720f, -0.69720006f, -0.70320225f, + 0.12148479f, -0.69720000f, -0.73086363f, + 0.00000000f, -0.69720000f, -0.74039996f, + 0.74823749f, -0.68621248f, -0.00000001f, + 0.73860013f, -0.68621248f, -0.12277081f, + 0.71064609f, -0.68621254f, -0.23871772f, + 0.66581166f, -0.68621248f, -0.34640405f, + 0.60553366f, -0.68621248f, -0.44439322f, + 0.53124863f, -0.68621248f, -0.53124863f, + 0.44439319f, -0.68621248f, -0.60553366f, + 0.34640405f, -0.68621254f, -0.66581166f, + 0.23871769f, -0.68621254f, -0.71064603f, + 0.12277076f, -0.68621248f, -0.73860019f, + 0.00000000f, -0.68621248f, -0.74823749f, + 0.75000000f, -0.67500001f, -0.00000001f, + 0.74033999f, -0.67499995f, -0.12306000f, + 0.71232003f, -0.67500007f, -0.23928000f, + 0.66737998f, -0.67500001f, -0.34721997f, + 0.60696000f, -0.67500007f, -0.44544002f, + 0.53250003f, -0.67500007f, -0.53250003f, + 0.44543999f, -0.67500001f, -0.60696000f, + 0.34722000f, -0.67500001f, -0.66738003f, + 0.23927999f, -0.67500001f, -0.71231997f, + 0.12305996f, -0.67500001f, -0.74033999f, + 0.00000000f, -0.67500001f, -0.75000000f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + -0.19413748f, -0.74891251f, -0.00000001f, + -0.19163698f, -0.74891245f, 0.03185407f, + -0.18438403f, -0.74891251f, 0.06193762f, + -0.17275129f, -0.74891245f, 0.08987788f, + -0.15711159f, -0.74891257f, 0.11530213f, + -0.13783762f, -0.74891251f, 0.13783762f, + -0.11530213f, -0.74891251f, 0.15711159f, + -0.08987789f, -0.74891257f, 0.17275131f, + -0.06193762f, -0.74891257f, 0.18438402f, + -0.03185407f, -0.74891251f, 0.19163698f, + 0.00000000f, -0.74891251f, 0.19413748f, + -0.35160002f, -0.74580008f, -0.00000001f, + -0.34707141f, -0.74579996f, 0.05769052f, + -0.33393562f, -0.74580014f, 0.11217447f, + -0.31286776f, -0.74580008f, 0.16277674f, + -0.28454286f, -0.74580008f, 0.20882230f, + -0.24963601f, -0.74580008f, 0.24963601f, + -0.20882228f, -0.74580008f, 0.28454286f, + -0.16277674f, -0.74580008f, 0.31286776f, + -0.11217447f, -0.74580008f, 0.33393565f, + -0.05769051f, -0.74580002f, 0.34707141f, + 0.00000000f, -0.74580008f, 0.35160002f, + -0.47621247f, -0.74088752f, -0.00000001f, + -0.47007880f, -0.74088746f, 0.07813693f, + -0.45228758f, -0.74088758f, 0.15193084f, + -0.42375290f, -0.74088752f, 0.22046733f, + -0.38538927f, -0.74088758f, 0.28283215f, + -0.33811086f, -0.74088752f, 0.33811086f, + -0.28283212f, -0.74088752f, 0.38538924f, + -0.22046733f, -0.74088752f, 0.42375290f, + -0.15193082f, -0.74088752f, 0.45228755f, + -0.07813691f, -0.74088752f, 0.47007886f, + 0.00000000f, -0.74088752f, 0.47621247f, + -0.57179999f, -0.73440003f, -0.00000001f, + -0.56443512f, -0.73439997f, 0.09382094f, + -0.54307282f, -0.73440009f, 0.18242709f, + -0.50881052f, -0.73440003f, 0.26472056f, + -0.46274635f, -0.73440003f, 0.33960345f, + -0.40597799f, -0.73440003f, 0.40597799f, + -0.33960345f, -0.73440009f, 0.46274632f, + -0.26472053f, -0.73440003f, 0.50881052f, + -0.18242708f, -0.73440003f, 0.54307282f, + -0.09382091f, -0.73440003f, 0.56443524f, + 0.00000000f, -0.73440003f, 0.57179999f, + -0.64218748f, -0.72656250f, -0.00000001f, + -0.63391608f, -0.72656244f, 0.10537011f, + -0.60992402f, -0.72656256f, 0.20488352f, + -0.57144409f, -0.72656256f, 0.29730710f, + -0.51970953f, -0.72656256f, 0.38140801f, + -0.45595312f, -0.72656250f, 0.45595312f, + -0.38140798f, -0.72656250f, 0.51970953f, + -0.29730713f, -0.72656250f, 0.57144415f, + -0.20488349f, -0.72656250f, 0.60992396f, + -0.10537009f, -0.72656250f, 0.63391614f, + 0.00000000f, -0.72656250f, 0.64218748f, + -0.69119996f, -0.71759999f, -0.00000001f, + -0.68229729f, -0.71759993f, 0.11341207f, + -0.65647405f, -0.71760005f, 0.22052045f, + -0.61505735f, -0.71759999f, 0.31999791f, + -0.55937433f, -0.71759999f, 0.41051748f, + -0.49075198f, -0.71759999f, 0.49075195f, + -0.41051745f, -0.71759999f, 0.55937433f, + -0.31999797f, -0.71759999f, 0.61505735f, + -0.22052044f, -0.71759999f, 0.65647411f, + -0.11341204f, -0.71759999f, 0.68229735f, + 0.00000000f, -0.71759999f, 0.69119996f, + -0.72266257f, -0.70773751f, -0.00000001f, + -0.71335465f, -0.70773745f, 0.11857444f, + -0.68635601f, -0.70773757f, 0.23055826f, + -0.64305401f, -0.70773751f, 0.33456385f, + -0.58483636f, -0.70773751f, 0.42920375f, + -0.51309037f, -0.70773751f, 0.51309043f, + -0.42920372f, -0.70773751f, 0.58483636f, + -0.33456385f, -0.70773757f, 0.64305407f, + -0.23055825f, -0.70773757f, 0.68635601f, + -0.11857442f, -0.70773757f, 0.71335471f, + 0.00000000f, -0.70773757f, 0.72266257f, + -0.74039996f, -0.69720000f, -0.00000001f, + -0.73086351f, -0.69719994f, 0.12148482f, + -0.70320231f, -0.69720006f, 0.23621722f, + -0.65883750f, -0.69720000f, 0.34277558f, + -0.59919089f, -0.69720006f, 0.43973836f, + -0.52568400f, -0.69719994f, 0.52568400f, + -0.43973833f, -0.69720006f, 0.59919089f, + -0.34277558f, -0.69720000f, 0.65883750f, + -0.23621720f, -0.69720006f, 0.70320225f, + -0.12148479f, -0.69720000f, 0.73086363f, + 0.00000000f, -0.69720000f, 0.74039996f, + -0.74823749f, -0.68621248f, -0.00000001f, + -0.73860013f, -0.68621248f, 0.12277079f, + -0.71064609f, -0.68621254f, 0.23871772f, + -0.66581166f, -0.68621248f, 0.34640405f, + -0.60553366f, -0.68621248f, 0.44439322f, + -0.53124863f, -0.68621248f, 0.53124863f, + -0.44439319f, -0.68621248f, 0.60553366f, + -0.34640405f, -0.68621254f, 0.66581166f, + -0.23871769f, -0.68621254f, 0.71064603f, + -0.12277076f, -0.68621248f, 0.73860019f, + 0.00000000f, -0.68621248f, 0.74823749f, + -0.75000000f, -0.67500001f, -0.00000001f, + -0.74033999f, -0.67499995f, 0.12305998f, + -0.71232003f, -0.67500007f, 0.23928000f, + -0.66737998f, -0.67500001f, 0.34721997f, + -0.60696000f, -0.67500007f, 0.44544002f, + -0.53250003f, -0.67500007f, 0.53250003f, + -0.44543999f, -0.67500001f, 0.60696000f, + -0.34722000f, -0.67500001f, 0.66738003f, + -0.23927999f, -0.67500001f, 0.71231997f, + -0.12305996f, -0.67500001f, 0.74033999f, + 0.00000000f, -0.67500001f, 0.75000000f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000006f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.75000000f, -0.00000001f, + 0.00000000f, -0.74891251f, -0.19413748f, + -0.03185407f, -0.74891245f, -0.19163698f, + -0.06193763f, -0.74891251f, -0.18438403f, + -0.08987789f, -0.74891245f, -0.17275129f, + -0.11530213f, -0.74891257f, -0.15711159f, + -0.13783762f, -0.74891251f, -0.13783762f, + -0.15711159f, -0.74891251f, -0.11530213f, + -0.17275131f, -0.74891257f, -0.08987790f, + -0.18438402f, -0.74891257f, -0.06193763f, + -0.19163698f, -0.74891251f, -0.03185407f, + -0.19413748f, -0.74891251f, -0.00000001f, + 0.00000000f, -0.74580008f, -0.35160002f, + -0.05769053f, -0.74579996f, -0.34707141f, + -0.11217448f, -0.74580014f, -0.33393562f, + -0.16277674f, -0.74580008f, -0.31286776f, + -0.20882230f, -0.74580008f, -0.28454286f, + -0.24963601f, -0.74580008f, -0.24963601f, + -0.28454286f, -0.74580008f, -0.20882228f, + -0.31286776f, -0.74580008f, -0.16277674f, + -0.33393565f, -0.74580008f, -0.11217447f, + -0.34707141f, -0.74580002f, -0.05769052f, + -0.35160002f, -0.74580008f, -0.00000001f, + 0.00000000f, -0.74088752f, -0.47621247f, + -0.07813694f, -0.74088746f, -0.47007880f, + -0.15193085f, -0.74088758f, -0.45228758f, + -0.22046733f, -0.74088752f, -0.42375290f, + -0.28283215f, -0.74088758f, -0.38538927f, + -0.33811086f, -0.74088752f, -0.33811086f, + -0.38538924f, -0.74088752f, -0.28283212f, + -0.42375290f, -0.74088752f, -0.22046733f, + -0.45228755f, -0.74088752f, -0.15193082f, + -0.47007886f, -0.74088752f, -0.07813692f, + -0.47621247f, -0.74088752f, -0.00000001f, + 0.00000000f, -0.73440003f, -0.57179999f, + -0.09382094f, -0.73439997f, -0.56443512f, + -0.18242709f, -0.73440009f, -0.54307282f, + -0.26472056f, -0.73440003f, -0.50881052f, + -0.33960345f, -0.73440003f, -0.46274635f, + -0.40597799f, -0.73440003f, -0.40597799f, + -0.46274632f, -0.73440009f, -0.33960345f, + -0.50881052f, -0.73440003f, -0.26472053f, + -0.54307282f, -0.73440003f, -0.18242708f, + -0.56443524f, -0.73440003f, -0.09382092f, + -0.57179999f, -0.73440003f, -0.00000001f, + 0.00000000f, -0.72656250f, -0.64218748f, + -0.10537013f, -0.72656244f, -0.63391608f, + -0.20488352f, -0.72656256f, -0.60992402f, + -0.29730710f, -0.72656256f, -0.57144409f, + -0.38140801f, -0.72656256f, -0.51970953f, + -0.45595312f, -0.72656250f, -0.45595312f, + -0.51970953f, -0.72656250f, -0.38140798f, + -0.57144415f, -0.72656250f, -0.29730713f, + -0.60992396f, -0.72656250f, -0.20488349f, + -0.63391614f, -0.72656250f, -0.10537010f, + -0.64218748f, -0.72656250f, -0.00000001f, + 0.00000000f, -0.71759999f, -0.69119996f, + -0.11341208f, -0.71759993f, -0.68229729f, + -0.22052045f, -0.71760005f, -0.65647405f, + -0.31999791f, -0.71759999f, -0.61505735f, + -0.41051748f, -0.71759999f, -0.55937433f, + -0.49075195f, -0.71759999f, -0.49075198f, + -0.55937433f, -0.71759999f, -0.41051745f, + -0.61505735f, -0.71759999f, -0.31999797f, + -0.65647411f, -0.71759999f, -0.22052044f, + -0.68229735f, -0.71759999f, -0.11341205f, + -0.69119996f, -0.71759999f, -0.00000001f, + 0.00000000f, -0.70773751f, -0.72266257f, + -0.11857446f, -0.70773745f, -0.71335465f, + -0.23055826f, -0.70773757f, -0.68635601f, + -0.33456385f, -0.70773745f, -0.64305401f, + -0.42920375f, -0.70773751f, -0.58483636f, + -0.51309043f, -0.70773751f, -0.51309037f, + -0.58483636f, -0.70773751f, -0.42920372f, + -0.64305407f, -0.70773751f, -0.33456385f, + -0.68635601f, -0.70773751f, -0.23055825f, + -0.71335471f, -0.70773751f, -0.11857443f, + -0.72266257f, -0.70773751f, -0.00000001f, + 0.00000000f, -0.69720000f, -0.74039996f, + -0.12148482f, -0.69719994f, -0.73086351f, + -0.23621722f, -0.69720006f, -0.70320231f, + -0.34277558f, -0.69720000f, -0.65883750f, + -0.43973836f, -0.69720006f, -0.59919089f, + -0.52568400f, -0.69719994f, -0.52568400f, + -0.59919089f, -0.69720006f, -0.43973833f, + -0.65883750f, -0.69720000f, -0.34277558f, + -0.70320225f, -0.69720006f, -0.23621720f, + -0.73086363f, -0.69720000f, -0.12148479f, + -0.74039996f, -0.69720000f, -0.00000001f, + 0.00000000f, -0.68621248f, -0.74823749f, + -0.12277080f, -0.68621248f, -0.73860013f, + -0.23871772f, -0.68621254f, -0.71064609f, + -0.34640405f, -0.68621248f, -0.66581166f, + -0.44439322f, -0.68621248f, -0.60553366f, + -0.53124863f, -0.68621248f, -0.53124863f, + -0.60553366f, -0.68621248f, -0.44439319f, + -0.66581166f, -0.68621254f, -0.34640405f, + -0.71064603f, -0.68621254f, -0.23871769f, + -0.73860019f, -0.68621248f, -0.12277077f, + -0.74823749f, -0.68621248f, -0.00000001f, + 0.00000000f, -0.67500001f, -0.75000000f, + -0.12305999f, -0.67499995f, -0.74033999f, + -0.23928000f, -0.67500007f, -0.71232003f, + -0.34721997f, -0.67500001f, -0.66737998f, + -0.44544002f, -0.67500007f, -0.60696000f, + -0.53250003f, -0.67500007f, -0.53250003f, + -0.60696000f, -0.67500001f, -0.44543999f, + -0.66738003f, -0.67500001f, -0.34722000f, + -0.71231997f, -0.67500001f, -0.23927999f, + -0.74033999f, -0.67500001f, -0.12305997f, + -0.75000000f, -0.67500001f, -0.00000001f, + -0.80000001f, 0.26250005f, 0.00000000f, + -0.79859996f, 0.26565003f, 0.04050000f, + -0.79480010f, 0.27420005f, 0.07200000f, + -0.78920001f, 0.28680006f, 0.09450001f, + -0.78240001f, 0.30210006f, 0.10800001f, + -0.77499998f, 0.31875002f, 0.11250000f, + -0.76760000f, 0.33540002f, 0.10800000f, + -0.76080006f, 0.35070002f, 0.09450001f, + -0.75519997f, 0.36330000f, 0.07200000f, + -0.75139999f, 0.37185001f, 0.04049999f, + -0.75000000f, 0.37500000f, 0.00000000f, + -0.90044993f, 0.26238751f, 0.00000000f, + -0.90022725f, 0.26553434f, 0.04050000f, + -0.89962322f, 0.27407584f, 0.07200000f, + -0.89873272f, 0.28666320f, 0.09449999f, + -0.89765161f, 0.30194792f, 0.10800000f, + -0.89647496f, 0.31858125f, 0.11250000f, + -0.89529836f, 0.33521461f, 0.10800000f, + -0.89421713f, 0.35049930f, 0.09449999f, + -0.89332676f, 0.36308670f, 0.07200000f, + -0.89272249f, 0.37162814f, 0.04049999f, + -0.89249992f, 0.37477499f, 0.00000000f, + -0.99160004f, 0.26160005f, 0.00000000f, + -0.99239522f, 0.26472485f, 0.04050001f, + -0.99455369f, 0.27320647f, 0.07200002f, + -0.99773449f, 0.28570563f, 0.09450001f, + -1.00159693f, 0.30088326f, 0.10800002f, + -1.00580013f, 0.31740004f, 0.11250001f, + -1.01000333f, 0.33391684f, 0.10800001f, + -1.01386571f, 0.34909445f, 0.09450001f, + -1.01704645f, 0.36159366f, 0.07200001f, + -1.01920485f, 0.37007523f, 0.04050000f, + -1.02000010f, 0.37320003f, 0.00000000f, + -1.07315004f, 0.25946254f, 0.00000000f, + -1.07481182f, 0.26252747f, 0.04050001f, + -1.07932246f, 0.27084666f, 0.07200002f, + -1.08596969f, 0.28310645f, 0.09450001f, + -1.09404123f, 0.29799330f, 0.10800002f, + -1.10282505f, 0.31419379f, 0.11250001f, + -1.11160886f, 0.33039424f, 0.10800001f, + -1.11968040f, 0.34528112f, 0.09450001f, + -1.12632775f, 0.35754091f, 0.07200001f, + -1.13083827f, 0.36586004f, 0.04050000f, + -1.13250005f, 0.36892501f, 0.00000000f, + -1.14480007f, 0.25530005f, 0.00000000f, + -1.14718556f, 0.25824845f, 0.04050000f, + -1.15366089f, 0.26625127f, 0.07200000f, + -1.16320336f, 0.27804485f, 0.09450001f, + -1.17479050f, 0.29236567f, 0.10800001f, + -1.18740010f, 0.30795002f, 0.11250000f, + -1.20000958f, 0.32353443f, 0.10800000f, + -1.21159685f, 0.33785522f, 0.09450001f, + -1.22113919f, 0.34964880f, 0.07200000f, + -1.22761440f, 0.35765159f, 0.04049999f, + -1.23000002f, 0.36059999f, 0.00000000f, + -1.20625007f, 0.24843754f, 0.00000000f, + -1.20922494f, 0.25119376f, 0.04050000f, + -1.21730018f, 0.25867507f, 0.07200000f, + -1.22920001f, 0.26970002f, 0.09450001f, + -1.24365008f, 0.28308752f, 0.10800001f, + -1.25937510f, 0.29765627f, 0.11250000f, + -1.27509999f, 0.31222504f, 0.10800000f, + -1.28955007f, 0.32561252f, 0.09450001f, + -1.30145001f, 0.33663750f, 0.07200000f, + -1.30952501f, 0.34411874f, 0.04049999f, + -1.31250000f, 0.34687501f, 0.00000000f, + -1.25720000f, 0.23820004f, 0.00000000f, + -1.26063836f, 0.24066961f, 0.04050000f, + -1.26997125f, 0.24737285f, 0.07200000f, + -1.28372490f, 0.25725123f, 0.09450001f, + -1.30042577f, 0.26924643f, 0.10800001f, + -1.31860006f, 0.28230003f, 0.11250000f, + -1.33677435f, 0.29535359f, 0.10800000f, + -1.35347521f, 0.30734879f, 0.09450001f, + -1.36722875f, 0.31722721f, 0.07200000f, + -1.37656152f, 0.32393038f, 0.04049999f, + -1.38000000f, 0.32639998f, 0.00000000f, + -1.29735005f, 0.22391254f, 0.00000000f, + -1.30113411f, 0.22598207f, 0.04050000f, + -1.31140578f, 0.23159945f, 0.07200000f, + -1.32654238f, 0.23987763f, 0.09450001f, + -1.34492302f, 0.24992974f, 0.10800001f, + -1.36492503f, 0.26086879f, 0.11250000f, + -1.38492727f, 0.27180785f, 0.10800000f, + -1.40330780f, 0.28185993f, 0.09450001f, + -1.41844463f, 0.29013813f, 0.07200000f, + -1.42871594f, 0.29575545f, 0.04049999f, + -1.43250012f, 0.29782501f, 0.00000000f, + -1.32640004f, 0.20490001f, 0.00000000f, + -1.33042073f, 0.20643719f, 0.04050000f, + -1.34133446f, 0.21060961f, 0.07200000f, + -1.35741770f, 0.21675842f, 0.09450001f, + -1.37694728f, 0.22422481f, 0.10800001f, + -1.39820004f, 0.23234999f, 0.11250000f, + -1.41945279f, 0.24047519f, 0.10800000f, + -1.43898249f, 0.24794158f, 0.09450001f, + -1.45506573f, 0.25409040f, 0.07200000f, + -1.46597922f, 0.25826281f, 0.04049999f, + -1.47000003f, 0.25979999f, 0.00000000f, + -1.34404993f, 0.18048748f, 0.00000000f, + -1.34820640f, 0.18134111f, 0.04050000f, + -1.35948884f, 0.18365818f, 0.07200000f, + -1.37611520f, 0.18707278f, 0.09450001f, + -1.39630449f, 0.19121909f, 0.10800001f, + -1.41827500f, 0.19573122f, 0.11250000f, + -1.44024563f, 0.20024337f, 0.10800000f, + -1.46043491f, 0.20438966f, 0.09450001f, + -1.47706127f, 0.20780426f, 0.07200000f, + -1.48834348f, 0.21012132f, 0.04049999f, + -1.49250007f, 0.21097496f, 0.00000000f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.35419989f, 0.14999996f, 0.04050000f, + -1.36559999f, 0.14999999f, 0.07200000f, + -1.38239992f, 0.14999998f, 0.09450001f, + -1.40280008f, 0.14999999f, 0.10800001f, + -1.42499995f, 0.14999998f, 0.11250000f, + -1.44719994f, 0.14999998f, 0.10800000f, + -1.46760011f, 0.14999998f, 0.09450001f, + -1.48440003f, 0.14999998f, 0.07200000f, + -1.49580002f, 0.14999998f, 0.04049999f, + -1.50000000f, 0.14999998f, 0.00000000f, + -0.75000000f, 0.37500000f, 0.00000000f, + -0.75139999f, 0.37184998f, -0.04050000f, + -0.75520003f, 0.36330003f, -0.07200000f, + -0.76080000f, 0.35070002f, -0.09450001f, + -0.76760006f, 0.33540004f, -0.10800001f, + -0.77500004f, 0.31875002f, -0.11250000f, + -0.78240001f, 0.30210003f, -0.10800000f, + -0.78920001f, 0.28680006f, -0.09450001f, + -0.79480004f, 0.27420002f, -0.07200000f, + -0.79860002f, 0.26565003f, -0.04049999f, + -0.80000001f, 0.26250005f, 0.00000000f, + -0.89249992f, 0.37477499f, 0.00000000f, + -0.89272243f, 0.37162811f, -0.04049999f, + -0.89332676f, 0.36308670f, -0.07200000f, + -0.89421707f, 0.35049930f, -0.09449999f, + -0.89529842f, 0.33521461f, -0.10800000f, + -0.89647490f, 0.31858125f, -0.11250000f, + -0.89765155f, 0.30194789f, -0.10800000f, + -0.89873278f, 0.28666323f, -0.09449999f, + -0.89962316f, 0.27407581f, -0.07200000f, + -0.90022731f, 0.26553437f, -0.04049998f, + -0.90044993f, 0.26238751f, 0.00000000f, + -1.02000010f, 0.37320003f, 0.00000000f, + -1.01920474f, 0.37007520f, -0.04050000f, + -1.01704657f, 0.36159366f, -0.07200001f, + -1.01386571f, 0.34909445f, -0.09450001f, + -1.01000333f, 0.33391684f, -0.10800002f, + -1.00580001f, 0.31740004f, -0.11250001f, + -1.00159681f, 0.30088323f, -0.10800001f, + -0.99773443f, 0.28570566f, -0.09450001f, + -0.99455369f, 0.27320647f, -0.07200001f, + -0.99239522f, 0.26472485f, -0.04049999f, + -0.99160004f, 0.26160005f, 0.00000000f, + -1.13250005f, 0.36892501f, 0.00000000f, + -1.13083816f, 0.36586002f, -0.04050000f, + -1.12632775f, 0.35754091f, -0.07200001f, + -1.11968040f, 0.34528109f, -0.09450001f, + -1.11160886f, 0.33039424f, -0.10800002f, + -1.10282505f, 0.31419379f, -0.11250001f, + -1.09404123f, 0.29799333f, -0.10800001f, + -1.08596969f, 0.28310645f, -0.09450001f, + -1.07932246f, 0.27084661f, -0.07200001f, + -1.07481182f, 0.26252750f, -0.04049999f, + -1.07315004f, 0.25946254f, 0.00000000f, + -1.23000002f, 0.36059999f, 0.00000000f, + -1.22761440f, 0.35765156f, -0.04050000f, + -1.22113931f, 0.34964883f, -0.07200000f, + -1.21159685f, 0.33785522f, -0.09450001f, + -1.20000970f, 0.32353443f, -0.10800001f, + -1.18740010f, 0.30795002f, -0.11250000f, + -1.17479038f, 0.29236564f, -0.10800000f, + -1.16320324f, 0.27804482f, -0.09450001f, + -1.15366089f, 0.26625124f, -0.07200000f, + -1.14718568f, 0.25824845f, -0.04049999f, + -1.14480007f, 0.25530005f, 0.00000000f, + -1.31250000f, 0.34687501f, 0.00000000f, + -1.30952489f, 0.34411871f, -0.04050000f, + -1.30145013f, 0.33663750f, -0.07200000f, + -1.28955019f, 0.32561252f, -0.09450001f, + -1.27510011f, 0.31222504f, -0.10800001f, + -1.25937498f, 0.29765630f, -0.11250000f, + -1.24365008f, 0.28308752f, -0.10800000f, + -1.22920012f, 0.26970005f, -0.09450001f, + -1.21730018f, 0.25867504f, -0.07200000f, + -1.20922506f, 0.25119379f, -0.04049999f, + -1.20625007f, 0.24843754f, 0.00000000f, + -1.38000000f, 0.32639998f, 0.00000000f, + -1.37656140f, 0.32393035f, -0.04050000f, + -1.36722875f, 0.31722721f, -0.07200000f, + -1.35347521f, 0.30734879f, -0.09450001f, + -1.33677447f, 0.29535362f, -0.10800001f, + -1.31860006f, 0.28230000f, -0.11250000f, + -1.30042553f, 0.26924643f, -0.10800000f, + -1.28372478f, 0.25725123f, -0.09450001f, + -1.26997125f, 0.24737284f, -0.07200000f, + -1.26063836f, 0.24066964f, -0.04049999f, + -1.25720000f, 0.23820004f, 0.00000000f, + -1.43250012f, 0.29782501f, 0.00000000f, + -1.42871583f, 0.29575542f, -0.04050000f, + -1.41844463f, 0.29013813f, -0.07200000f, + -1.40330768f, 0.28185993f, -0.09450001f, + -1.38492751f, 0.27180785f, -0.10800001f, + -1.36492515f, 0.26086876f, -0.11250000f, + -1.34492302f, 0.24992973f, -0.10800000f, + -1.32654250f, 0.23987764f, -0.09450001f, + -1.31140566f, 0.23159944f, -0.07200000f, + -1.30113423f, 0.22598210f, -0.04049999f, + -1.29735005f, 0.22391254f, 0.00000000f, + -1.47000003f, 0.25979999f, 0.00000000f, + -1.46597922f, 0.25826275f, -0.04050000f, + -1.45506561f, 0.25409040f, -0.07200000f, + -1.43898249f, 0.24794158f, -0.09450001f, + -1.41945291f, 0.24047521f, -0.10800001f, + -1.39820004f, 0.23234999f, -0.11250000f, + -1.37694728f, 0.22422481f, -0.10800000f, + -1.35741770f, 0.21675842f, -0.09450001f, + -1.34133446f, 0.21060961f, -0.07200000f, + -1.33042085f, 0.20643722f, -0.04049999f, + -1.32640004f, 0.20490001f, 0.00000000f, + -1.49250007f, 0.21097496f, 0.00000000f, + -1.48834324f, 0.21012127f, -0.04050000f, + -1.47706139f, 0.20780428f, -0.07200000f, + -1.46043479f, 0.20438966f, -0.09450001f, + -1.44024563f, 0.20024338f, -0.10800001f, + -1.41827488f, 0.19573122f, -0.11250000f, + -1.39630437f, 0.19121908f, -0.10800000f, + -1.37611520f, 0.18707278f, -0.09450001f, + -1.35948884f, 0.18365818f, -0.07200000f, + -1.34820652f, 0.18134113f, -0.04049999f, + -1.34404993f, 0.18048748f, 0.00000000f, + -1.50000000f, 0.14999998f, 0.00000000f, + -1.49580002f, 0.14999996f, -0.04050000f, + -1.48440015f, 0.14999999f, -0.07200000f, + -1.46759999f, 0.14999998f, -0.09450001f, + -1.44720006f, 0.14999999f, -0.10800001f, + -1.42500007f, 0.14999998f, -0.11250000f, + -1.40280008f, 0.14999998f, -0.10800000f, + -1.38240004f, 0.14999998f, -0.09450001f, + -1.36559999f, 0.14999998f, -0.07200000f, + -1.35420001f, 0.14999998f, -0.04049999f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.35419989f, 0.14999996f, 0.04050000f, + -1.36559999f, 0.14999999f, 0.07200000f, + -1.38239992f, 0.14999998f, 0.09450001f, + -1.40280008f, 0.14999999f, 0.10800001f, + -1.42499995f, 0.14999998f, 0.11250000f, + -1.44719994f, 0.14999998f, 0.10800000f, + -1.46760011f, 0.14999998f, 0.09450001f, + -1.48440003f, 0.14999998f, 0.07200000f, + -1.49580002f, 0.14999998f, 0.04049999f, + -1.50000000f, 0.14999998f, 0.00000000f, + -1.34694993f, 0.11309998f, 0.00000000f, + -1.35108757f, 0.11225944f, 0.04049999f, + -1.36231863f, 0.10997803f, 0.07200000f, + -1.37886941f, 0.10661592f, 0.09449999f, + -1.39896679f, 0.10253337f, 0.10800000f, + -1.42083752f, 0.09809060f, 0.11250000f, + -1.44270813f, 0.09364782f, 0.10800000f, + -1.46280551f, 0.08956527f, 0.09449999f, + -1.47935629f, 0.08620317f, 0.07200000f, + -1.49058723f, 0.08392174f, 0.04049999f, + -1.49472487f, 0.08308122f, 0.00000000f, + -1.33760011f, 0.07080000f, 0.00000000f, + -1.34155357f, 0.06930479f, 0.04050000f, + -1.35228503f, 0.06524640f, 0.07200002f, + -1.36809933f, 0.05926559f, 0.09450001f, + -1.38730252f, 0.05200320f, 0.10800002f, + -1.40820003f, 0.04410000f, 0.11250001f, + -1.42909765f, 0.03619679f, 0.10800001f, + -1.44830084f, 0.02893440f, 0.09450001f, + -1.46411526f, 0.02295359f, 0.07200001f, + -1.47484648f, 0.01889519f, 0.04049999f, + -1.47880006f, 0.01739999f, 0.00000000f, + -1.32164991f, 0.02445000f, 0.00000000f, + -1.32530177f, 0.02245132f, 0.04050000f, + -1.33521414f, 0.01702635f, 0.07200002f, + -1.34982169f, 0.00903165f, 0.09450001f, + -1.36755967f, -0.00067620f, 0.10800002f, + -1.38686240f, -0.01124063f, 0.11250001f, + -1.40616536f, -0.02180506f, 0.10800001f, + -1.42390323f, -0.03151290f, 0.09450001f, + -1.43851089f, -0.03950761f, 0.07200001f, + -1.44842303f, -0.04493258f, 0.04049999f, + -1.45207500f, -0.04693126f, -0.00000000f, + -1.29880011f, -0.02460000f, -0.00000000f, + -1.30203676f, -0.02698559f, 0.04050000f, + -1.31082261f, -0.03346080f, 0.07200000f, + -1.32376969f, -0.04300320f, 0.09450001f, + -1.33949125f, -0.05459040f, 0.10800001f, + -1.35660005f, -0.06720001f, 0.11250000f, + -1.37370884f, -0.07980961f, 0.10800000f, + -1.38943052f, -0.09139681f, 0.09450001f, + -1.40237761f, -0.10093921f, 0.07200000f, + -1.41116321f, -0.10741441f, 0.04049999f, + -1.41439998f, -0.10980001f, -0.00000000f, + -1.26874995f, -0.07500000f, -0.00000000f, + -1.27146244f, -0.07769062f, 0.04050000f, + -1.27882504f, -0.08499375f, 0.07200000f, + -1.28967500f, -0.09575625f, 0.09450001f, + -1.30285001f, -0.10882500f, 0.10800001f, + -1.31718755f, -0.12304687f, 0.11250000f, + -1.33152497f, -0.13726875f, 0.10800000f, + -1.34470010f, -0.15033749f, 0.09450001f, + -1.35555005f, -0.16110000f, 0.07200000f, + -1.36291242f, -0.16840312f, 0.04049999f, + -1.36562502f, -0.17109375f, -0.00000000f, + -1.23119998f, -0.12540001f, -0.00000000f, + -1.23328304f, -0.12834840f, 0.04050000f, + -1.23893762f, -0.13635120f, 0.07200000f, + -1.24727035f, -0.14814480f, 0.09450001f, + -1.25738895f, -0.16246562f, 0.10800001f, + -1.26839995f, -0.17805001f, 0.11250000f, + -1.27941120f, -0.19363439f, 0.10800000f, + -1.28952956f, -0.20795521f, 0.09450001f, + -1.29786241f, -0.21974881f, 0.07200000f, + -1.30351675f, -0.22775160f, 0.04049999f, + -1.30559993f, -0.23070000f, -0.00000000f, + -1.18585014f, -0.17445001f, -0.00000000f, + -1.18720317f, -0.17764355f, 0.04050000f, + -1.19087601f, -0.18631189f, 0.07200000f, + -1.19628823f, -0.19908616f, 0.09450001f, + -1.20286059f, -0.21459784f, 0.10800001f, + -1.21001256f, -0.23147814f, 0.11250000f, + -1.21716475f, -0.24835847f, 0.10800000f, + -1.22373688f, -0.26387012f, 0.09450001f, + -1.22914934f, -0.27664441f, 0.07200000f, + -1.23282194f, -0.28531271f, 0.04049999f, + -1.23417509f, -0.28850627f, -0.00000000f, + -1.13240004f, -0.22080001f, -0.00000000f, + -1.13292634f, -0.22426079f, 0.04050000f, + -1.13435543f, -0.23365441f, 0.07200000f, + -1.13646090f, -0.24749762f, 0.09450001f, + -1.13901782f, -0.26430723f, 0.10800001f, + -1.14180005f, -0.28260002f, 0.11250000f, + -1.14458251f, -0.30089283f, 0.10800000f, + -1.14713919f, -0.31770241f, 0.09450001f, + -1.14924490f, -0.33154562f, 0.07200000f, + -1.15067363f, -0.34093922f, 0.04049999f, + -1.15120006f, -0.34440002f, -0.00000000f, + -1.07054996f, -0.26310003f, -0.00000000f, + -1.07015729f, -0.26688471f, 0.04050000f, + -1.06909144f, -0.27715757f, 0.07200000f, + -1.06752050f, -0.29229647f, 0.09450001f, + -1.06561327f, -0.31067947f, 0.10800001f, + -1.06353748f, -0.33068439f, 0.11250000f, + -1.06146181f, -0.35068941f, 0.10800000f, + -1.05955434f, -0.36907232f, 0.09450001f, + -1.05798364f, -0.38421124f, 0.07200000f, + -1.05691767f, -0.39448404f, 0.04049999f, + -1.05652499f, -0.39826876f, -0.00000000f, + -1.00000000f, -0.30000001f, -0.00000000f, + -0.99859989f, -0.30419996f, 0.04050000f, + -0.99480003f, -0.31560001f, 0.07200000f, + -0.98920000f, -0.33240002f, 0.09450001f, + -0.98240000f, -0.35280001f, 0.10800001f, + -0.97499996f, -0.37500000f, 0.11250000f, + -0.96759999f, -0.39720002f, 0.10800000f, + -0.96080005f, -0.41759998f, 0.09450001f, + -0.95520002f, -0.43440002f, 0.07200000f, + -0.95139992f, -0.44580001f, 0.04049999f, + -0.94999999f, -0.44999999f, -0.00000001f, + -1.50000000f, 0.14999998f, 0.00000000f, + -1.49580002f, 0.14999996f, -0.04050000f, + -1.48440015f, 0.14999999f, -0.07200000f, + -1.46759999f, 0.14999998f, -0.09450001f, + -1.44720006f, 0.14999999f, -0.10800001f, + -1.42500007f, 0.14999998f, -0.11250000f, + -1.40280008f, 0.14999998f, -0.10800000f, + -1.38240004f, 0.14999998f, -0.09450001f, + -1.36559999f, 0.14999998f, -0.07200000f, + -1.35420001f, 0.14999998f, -0.04049999f, + -1.35000002f, 0.14999998f, 0.00000000f, + -1.49472487f, 0.08308122f, 0.00000000f, + -1.49058712f, 0.08392174f, -0.04049999f, + -1.47935617f, 0.08620317f, -0.07200000f, + -1.46280551f, 0.08956527f, -0.09449999f, + -1.44270813f, 0.09364782f, -0.10800000f, + -1.42083728f, 0.09809060f, -0.11250000f, + -1.39896679f, 0.10253337f, -0.10800000f, + -1.37886930f, 0.10661593f, -0.09449999f, + -1.36231852f, 0.10997803f, -0.07200000f, + -1.35108757f, 0.11225945f, -0.04049999f, + -1.34694993f, 0.11309998f, 0.00000000f, + -1.47880006f, 0.01739999f, 0.00000000f, + -1.47484636f, 0.01889519f, -0.04050000f, + -1.46411538f, 0.02295360f, -0.07200002f, + -1.44830084f, 0.02893439f, -0.09450001f, + -1.42909777f, 0.03619680f, -0.10800002f, + -1.40820003f, 0.04409999f, -0.11250001f, + -1.38730240f, 0.05200320f, -0.10800001f, + -1.36809933f, 0.05926560f, -0.09450001f, + -1.35228491f, 0.06524640f, -0.07200001f, + -1.34155369f, 0.06930479f, -0.04049999f, + -1.33760011f, 0.07080000f, 0.00000000f, + -1.45207500f, -0.04693126f, -0.00000000f, + -1.44842303f, -0.04493257f, -0.04050000f, + -1.43851078f, -0.03950761f, -0.07200002f, + -1.42390323f, -0.03151290f, -0.09450001f, + -1.40616548f, -0.02180506f, -0.10800002f, + -1.38686240f, -0.01124063f, -0.11250001f, + -1.36755955f, -0.00067620f, -0.10800001f, + -1.34982181f, 0.00903165f, -0.09450001f, + -1.33521414f, 0.01702635f, -0.07200001f, + -1.32530189f, 0.02245133f, -0.04049999f, + -1.32164991f, 0.02445000f, 0.00000000f, + -1.41439998f, -0.10980001f, -0.00000000f, + -1.41116297f, -0.10741439f, -0.04050000f, + -1.40237772f, -0.10093922f, -0.07200000f, + -1.38943040f, -0.09139680f, -0.09450001f, + -1.37370884f, -0.07980961f, -0.10800001f, + -1.35660017f, -0.06720000f, -0.11250000f, + -1.33949125f, -0.05459040f, -0.10800000f, + -1.32376981f, -0.04300320f, -0.09450001f, + -1.31082249f, -0.03346080f, -0.07200000f, + -1.30203688f, -0.02698559f, -0.04049999f, + -1.29880011f, -0.02460000f, -0.00000000f, + -1.36562502f, -0.17109375f, -0.00000000f, + -1.36291230f, -0.16840309f, -0.04050000f, + -1.35555005f, -0.16110000f, -0.07200000f, + -1.34469998f, -0.15033749f, -0.09450001f, + -1.33152509f, -0.13726877f, -0.10800001f, + -1.31718755f, -0.12304687f, -0.11250000f, + -1.30285001f, -0.10882499f, -0.10800000f, + -1.28967500f, -0.09575625f, -0.09450001f, + -1.27882504f, -0.08499375f, -0.07200000f, + -1.27146244f, -0.07769062f, -0.04049999f, + -1.26874995f, -0.07500000f, -0.00000000f, + -1.30559993f, -0.23070000f, -0.00000000f, + -1.30351651f, -0.22775158f, -0.04050000f, + -1.29786241f, -0.21974881f, -0.07200000f, + -1.28952956f, -0.20795520f, -0.09450001f, + -1.27941132f, -0.19363441f, -0.10800001f, + -1.26839995f, -0.17805001f, -0.11250000f, + -1.25738883f, -0.16246560f, -0.10800000f, + -1.24727035f, -0.14814481f, -0.09450001f, + -1.23893762f, -0.13635120f, -0.07200000f, + -1.23328316f, -0.12834841f, -0.04049999f, + -1.23119998f, -0.12540001f, -0.00000000f, + -1.23417509f, -0.28850627f, -0.00000000f, + -1.23282194f, -0.28531265f, -0.04050000f, + -1.22914934f, -0.27664444f, -0.07200000f, + -1.22373676f, -0.26387009f, -0.09450001f, + -1.21716475f, -0.24835847f, -0.10800001f, + -1.21001267f, -0.23147814f, -0.11250000f, + -1.20286047f, -0.21459781f, -0.10800000f, + -1.19628835f, -0.19908617f, -0.09450001f, + -1.19087601f, -0.18631187f, -0.07200000f, + -1.18720317f, -0.17764360f, -0.04049999f, + -1.18585014f, -0.17445001f, -0.00000000f, + -1.15120006f, -0.34440002f, -0.00000000f, + -1.15067351f, -0.34093919f, -0.04050000f, + -1.14924490f, -0.33154565f, -0.07200001f, + -1.14713919f, -0.31770241f, -0.09450001f, + -1.14458263f, -0.30089283f, -0.10800001f, + -1.14180017f, -0.28259999f, -0.11250000f, + -1.13901758f, -0.26430720f, -0.10800000f, + -1.13646090f, -0.24749762f, -0.09450001f, + -1.13435531f, -0.23365441f, -0.07200000f, + -1.13292646f, -0.22426081f, -0.04049999f, + -1.13240004f, -0.22080001f, -0.00000000f, + -1.05652499f, -0.39826876f, -0.00000000f, + -1.05691755f, -0.39448401f, -0.04050000f, + -1.05798364f, -0.38421121f, -0.07200001f, + -1.05955434f, -0.36907232f, -0.09450001f, + -1.06146181f, -0.35068938f, -0.10800001f, + -1.06353748f, -0.33068442f, -0.11250000f, + -1.06561315f, -0.31067941f, -0.10800000f, + -1.06752062f, -0.29229647f, -0.09450001f, + -1.06909132f, -0.27715760f, -0.07200000f, + -1.07015729f, -0.26688474f, -0.04049999f, + -1.07054996f, -0.26310003f, -0.00000000f, + -0.94999999f, -0.44999999f, -0.00000001f, + -0.95139986f, -0.44579995f, -0.04050000f, + -0.95520008f, -0.43440005f, -0.07200001f, + -0.96079999f, -0.41760001f, -0.09450001f, + -0.96759999f, -0.39719999f, -0.10800001f, + -0.97500002f, -0.37500000f, -0.11250000f, + -0.98240000f, -0.35280001f, -0.10800000f, + -0.98920006f, -0.33240002f, -0.09450001f, + -0.99480003f, -0.31560001f, -0.07200000f, + -0.99860001f, -0.30419999f, -0.04049999f, + -1.00000000f, -0.30000001f, -0.00000000f, + 0.85000002f, -0.03750002f, -0.00000000f, + 0.84999996f, -0.04905002f, 0.08910000f, + 0.85000008f, -0.08040002f, 0.15840001f, + 0.85000002f, -0.12660003f, 0.20790000f, + 0.85000008f, -0.18270002f, 0.23760001f, + 0.85000002f, -0.24375001f, 0.24750000f, + 0.85000002f, -0.30480003f, 0.23760000f, + 0.85000002f, -0.36089998f, 0.20790002f, + 0.85000002f, -0.40710002f, 0.15840001f, + 0.85000002f, -0.43845001f, 0.08909997f, + 0.85000002f, -0.44999999f, -0.00000001f, + 0.96794993f, -0.02790002f, -0.00000000f, + 0.96969706f, -0.03838952f, 0.08755019f, + 0.97443962f, -0.06686102f, 0.15564480f, + 0.98142833f, -0.10881902f, 0.20428377f, + 0.98991477f, -0.15976802f, 0.23346718f, + 0.99914992f, -0.21521249f, 0.24319497f, + 1.00838506f, -0.27065700f, 0.23346716f, + 1.01687145f, -0.32160598f, 0.20428379f, + 1.02386022f, -0.36356401f, 0.15564479f, + 1.02860272f, -0.39203548f, 0.08755016f, + 1.03034985f, -0.40252498f, -0.00000000f, + 1.05560005f, -0.00120003f, -0.00000000f, + 1.05848956f, -0.01044003f, 0.08334360f, + 1.06633294f, -0.03552003f, 0.14816642f, + 1.07789123f, -0.07248003f, 0.19446841f, + 1.09192657f, -0.11736003f, 0.22224963f, + 1.10720015f, -0.16620001f, 0.23151001f, + 1.12247372f, -0.21504003f, 0.22224963f, + 1.13650894f, -0.25992000f, 0.19446844f, + 1.14806736f, -0.29688001f, 0.14816642f, + 1.15591049f, -0.32196000f, 0.08334358f, + 1.15880013f, -0.33120000f, -0.00000000f, + 1.11864996f, 0.03944998f, 0.00000000f, + 1.12222838f, 0.03158548f, 0.07714440f, + 1.13194120f, 0.01023899f, 0.13714561f, + 1.14625478f, -0.02121901f, 0.18000358f, + 1.16363573f, -0.05941800f, 0.20571840f, + 1.18255007f, -0.10098749f, 0.21428999f, + 1.20146441f, -0.14255700f, 0.20571840f, + 1.21884537f, -0.18075597f, 0.18000360f, + 1.23315883f, -0.21221398f, 0.13714559f, + 1.24287164f, -0.23356047f, 0.07714437f, + 1.24645007f, -0.24142496f, -0.00000000f, + 1.16280007f, 0.09089998f, 0.00000000f, + 1.16676486f, 0.08447397f, 0.06961680f, + 1.17752659f, 0.06703199f, 0.12376321f, + 1.19338572f, 0.04132799f, 0.16243920f, + 1.21264338f, 0.01011599f, 0.18564481f, + 1.23360014f, -0.02385001f, 0.19338000f, + 1.25455689f, -0.05781601f, 0.18564481f, + 1.27381444f, -0.08902800f, 0.16243921f, + 1.28967381f, -0.11473200f, 0.12376320f, + 1.30043530f, -0.13217400f, 0.06961678f, + 1.30440009f, -0.13859999f, -0.00000000f, + 1.19375002f, 0.14999998f, 0.00000000f, + 1.19794989f, 0.14501247f, 0.06142500f, + 1.20935011f, 0.13147499f, 0.10920001f, + 1.22614992f, 0.11152498f, 0.14332500f, + 1.24654996f, 0.08730000f, 0.16380000f, + 1.26874995f, 0.06093749f, 0.17062500f, + 1.29094982f, 0.03457499f, 0.16380000f, + 1.31134987f, 0.01035001f, 0.14332500f, + 1.32814991f, -0.00959999f, 0.10920000f, + 1.33954990f, -0.02313749f, 0.06142498f, + 1.34374988f, -0.02812499f, -0.00000000f, + 1.21719992f, 0.21360001f, 0.00000000f, + 1.22163498f, 0.20998798f, 0.05323320f, + 1.23367357f, 0.20018403f, 0.09463681f, + 1.25141430f, 0.18573602f, 0.12421080f, + 1.27295685f, 0.16819203f, 0.14195521f, + 1.29639995f, 0.14910004f, 0.14787000f, + 1.31984317f, 0.13000804f, 0.14195520f, + 1.34138560f, 0.11246406f, 0.12421081f, + 1.35912633f, 0.09801605f, 0.09463680f, + 1.37116480f, 0.08821206f, 0.05323318f, + 1.37559998f, 0.08460006f, 0.00000000f, + 1.23885000f, 0.27855000f, 0.00000000f, + 1.24367154f, 0.27618748f, 0.04570561f, + 1.25675893f, 0.26977503f, 0.08125442f, + 1.27604520f, 0.26032501f, 0.10664641f, + 1.29946446f, 0.24885003f, 0.12188162f, + 1.32495010f, 0.23636252f, 0.12696001f, + 1.35043573f, 0.22387503f, 0.12188161f, + 1.37385488f, 0.21240003f, 0.10664642f, + 1.39314127f, 0.20295003f, 0.08125441f, + 1.40622854f, 0.19653754f, 0.04570559f, + 1.41105008f, 0.19417503f, 0.00000000f, + 1.26440001f, 0.34170002f, 0.00000000f, + 1.26991034f, 0.34039798f, 0.03950640f, + 1.28486729f, 0.33686405f, 0.07023361f, + 1.30690885f, 0.33165601f, 0.09218160f, + 1.33367372f, 0.32533202f, 0.10535040f, + 1.36280000f, 0.31845003f, 0.10974000f, + 1.39192641f, 0.31156802f, 0.10535040f, + 1.41869128f, 0.30524403f, 0.09218161f, + 1.44073272f, 0.30003607f, 0.07023360f, + 1.45568967f, 0.29650205f, 0.03950639f, + 1.46120000f, 0.29520005f, 0.00000000f, + 1.29955006f, 0.39990005f, 0.00000000f, + 1.30620289f, 0.39940652f, 0.03529980f, + 1.32426059f, 0.39806706f, 0.06275520f, + 1.35087168f, 0.39609307f, 0.08236619f, + 1.38318527f, 0.39369607f, 0.09413280f, + 1.41835010f, 0.39108756f, 0.09805499f, + 1.45351493f, 0.38847905f, 0.09413280f, + 1.48582840f, 0.38608208f, 0.08236620f, + 1.51243973f, 0.38410807f, 0.06275519f, + 1.53049719f, 0.38276857f, 0.03529979f, + 1.53715003f, 0.38227507f, 0.00000000f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.35839975f, 0.44999996f, 0.03375000f, + 1.38120008f, 0.45000011f, 0.06000001f, + 1.41480005f, 0.45000002f, 0.07875000f, + 1.45560014f, 0.45000008f, 0.09000000f, + 1.50000000f, 0.45000005f, 0.09375000f, + 1.54439998f, 0.45000005f, 0.09000000f, + 1.58520007f, 0.45000008f, 0.07875000f, + 1.61880004f, 0.45000005f, 0.06000000f, + 1.64159989f, 0.45000005f, 0.03374999f, + 1.64999998f, 0.45000005f, 0.00000001f, + 0.85000002f, -0.44999999f, -0.00000001f, + 0.84999996f, -0.43844995f, -0.08910000f, + 0.85000008f, -0.40710002f, -0.15840001f, + 0.85000002f, -0.36089998f, -0.20790000f, + 0.85000008f, -0.30480000f, -0.23760001f, + 0.85000002f, -0.24375001f, -0.24750000f, + 0.85000002f, -0.18269999f, -0.23760000f, + 0.85000002f, -0.12660003f, -0.20790002f, + 0.85000002f, -0.08040002f, -0.15840001f, + 0.85000002f, -0.04905001f, -0.08909997f, + 0.85000002f, -0.03750002f, -0.00000000f, + 1.03034985f, -0.40252498f, -0.00000000f, + 1.02860260f, -0.39203545f, -0.08755019f, + 1.02386034f, -0.36356398f, -0.15564480f, + 1.01687145f, -0.32160601f, -0.20428377f, + 1.00838506f, -0.27065703f, -0.23346718f, + 0.99914992f, -0.21521249f, -0.24319497f, + 0.98991477f, -0.15976799f, -0.23346716f, + 0.98142833f, -0.10881902f, -0.20428379f, + 0.97443956f, -0.06686102f, -0.15564479f, + 0.96969712f, -0.03838951f, -0.08755016f, + 0.96794993f, -0.02790002f, -0.00000000f, + 1.15880013f, -0.33120000f, -0.00000000f, + 1.15591037f, -0.32195994f, -0.08334360f, + 1.14806736f, -0.29688004f, -0.14816642f, + 1.13650882f, -0.25992000f, -0.19446841f, + 1.12247384f, -0.21504001f, -0.22224963f, + 1.10720003f, -0.16620003f, -0.23151001f, + 1.09192646f, -0.11736001f, -0.22224963f, + 1.07789123f, -0.07248002f, -0.19446844f, + 1.06633282f, -0.03552002f, -0.14816642f, + 1.05848956f, -0.01044002f, -0.08334358f, + 1.05560005f, -0.00120003f, -0.00000000f, + 1.24645007f, -0.24142496f, -0.00000000f, + 1.24287152f, -0.23356044f, -0.07714440f, + 1.23315895f, -0.21221398f, -0.13714561f, + 1.21884525f, -0.18075597f, -0.18000358f, + 1.20146453f, -0.14255700f, -0.20571840f, + 1.18254995f, -0.10098749f, -0.21428999f, + 1.16363561f, -0.05941799f, -0.20571840f, + 1.14625478f, -0.02121901f, -0.18000360f, + 1.13194120f, 0.01023899f, -0.13714559f, + 1.12222838f, 0.03158549f, -0.07714437f, + 1.11864996f, 0.03944998f, 0.00000000f, + 1.30440009f, -0.13859999f, -0.00000000f, + 1.30043507f, -0.13217399f, -0.06961680f, + 1.28967369f, -0.11473200f, -0.12376321f, + 1.27381444f, -0.08902799f, -0.16243920f, + 1.25455701f, -0.05781601f, -0.18564481f, + 1.23360002f, -0.02385001f, -0.19338000f, + 1.21264338f, 0.01011600f, -0.18564481f, + 1.19338572f, 0.04132798f, -0.16243921f, + 1.17752647f, 0.06703199f, -0.12376320f, + 1.16676486f, 0.08447399f, -0.06961678f, + 1.16280007f, 0.09089998f, 0.00000000f, + 1.34374988f, -0.02812499f, -0.00000000f, + 1.33954978f, -0.02313748f, -0.06142500f, + 1.32814991f, -0.00959999f, -0.10920001f, + 1.31134987f, 0.01035001f, -0.14332500f, + 1.29095006f, 0.03457500f, -0.16380000f, + 1.26874995f, 0.06093749f, -0.17062500f, + 1.24654996f, 0.08730000f, -0.16380000f, + 1.22615004f, 0.11152498f, -0.14332500f, + 1.20935011f, 0.13147499f, -0.10920000f, + 1.19795001f, 0.14501248f, -0.06142498f, + 1.19375002f, 0.14999998f, 0.00000000f, + 1.37559998f, 0.08460006f, 0.00000000f, + 1.37116480f, 0.08821206f, -0.05323320f, + 1.35912645f, 0.09801605f, -0.09463680f, + 1.34138560f, 0.11246406f, -0.12421079f, + 1.31984317f, 0.13000806f, -0.14195520f, + 1.29639995f, 0.14910004f, -0.14786999f, + 1.27295685f, 0.16819203f, -0.14195520f, + 1.25141430f, 0.18573603f, -0.12421080f, + 1.23367357f, 0.20018402f, -0.09463680f, + 1.22163510f, 0.20998801f, -0.05323318f, + 1.21719992f, 0.21360001f, 0.00000000f, + 1.41105008f, 0.19417503f, 0.00000000f, + 1.40622830f, 0.19653751f, -0.04570560f, + 1.39314139f, 0.20295003f, -0.08125441f, + 1.37385488f, 0.21240002f, -0.10664640f, + 1.35043585f, 0.22387503f, -0.12188162f, + 1.32494998f, 0.23636252f, -0.12696001f, + 1.29946434f, 0.24885002f, -0.12188160f, + 1.27604532f, 0.26032501f, -0.10664640f, + 1.25675893f, 0.26977503f, -0.08125440f, + 1.24367166f, 0.27618751f, -0.04570558f, + 1.23885000f, 0.27855000f, 0.00000000f, + 1.46120000f, 0.29520005f, 0.00000000f, + 1.45568943f, 0.29650205f, -0.03950639f, + 1.44073284f, 0.30003604f, -0.07023360f, + 1.41869116f, 0.30524403f, -0.09218159f, + 1.39192653f, 0.31156805f, -0.10535039f, + 1.36280012f, 0.31845003f, -0.10973999f, + 1.33367360f, 0.32533202f, -0.10535039f, + 1.30690885f, 0.33165604f, -0.09218159f, + 1.28486729f, 0.33686402f, -0.07023359f, + 1.26991045f, 0.34039801f, -0.03950638f, + 1.26440001f, 0.34170002f, 0.00000000f, + 1.53715003f, 0.38227507f, 0.00000000f, + 1.53049695f, 0.38276854f, -0.03529979f, + 1.51243961f, 0.38410810f, -0.06275519f, + 1.48582840f, 0.38608205f, -0.08236619f, + 1.45351493f, 0.38847908f, -0.09413280f, + 1.41835010f, 0.39108753f, -0.09805499f, + 1.38318527f, 0.39369607f, -0.09413280f, + 1.35087168f, 0.39609307f, -0.08236620f, + 1.32426047f, 0.39806706f, -0.06275519f, + 1.30620289f, 0.39940655f, -0.03529978f, + 1.29955006f, 0.39990005f, 0.00000000f, + 1.64999998f, 0.45000005f, 0.00000001f, + 1.64159989f, 0.44999996f, -0.03374999f, + 1.61880016f, 0.45000011f, -0.05999999f, + 1.58519995f, 0.45000002f, -0.07874999f, + 1.54440010f, 0.45000008f, -0.09000000f, + 1.50000000f, 0.45000005f, -0.09374999f, + 1.45560002f, 0.45000005f, -0.08999999f, + 1.41480005f, 0.45000008f, -0.07875000f, + 1.38120008f, 0.45000005f, -0.05999999f, + 1.35839999f, 0.45000005f, -0.03374998f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.35839975f, 0.44999996f, 0.03375000f, + 1.38120008f, 0.45000011f, 0.06000001f, + 1.41480005f, 0.45000002f, 0.07875000f, + 1.45560014f, 0.45000008f, 0.09000000f, + 1.50000000f, 0.45000005f, 0.09375000f, + 1.54439998f, 0.45000005f, 0.09000000f, + 1.58520007f, 0.45000008f, 0.07875000f, + 1.61880004f, 0.45000005f, 0.06000000f, + 1.64159989f, 0.45000005f, 0.03374999f, + 1.64999998f, 0.45000005f, 0.00000001f, + 1.36489987f, 0.46012494f, 0.00000001f, + 1.37370336f, 0.46020287f, 0.03337200f, + 1.39759886f, 0.46041453f, 0.05932800f, + 1.43281305f, 0.46072635f, 0.07786799f, + 1.47557306f, 0.46110505f, 0.08899199f, + 1.52210617f, 0.46151716f, 0.09269999f, + 1.56863928f, 0.46192923f, 0.08899198f, + 1.61139929f, 0.46230793f, 0.07786799f, + 1.64661360f, 0.46261978f, 0.05932799f, + 1.67050874f, 0.46283138f, 0.03337199f, + 1.67931235f, 0.46290934f, 0.00000001f, + 1.37919998f, 0.46800002f, 0.00000001f, + 1.38818228f, 0.46815118f, 0.03234600f, + 1.41256332f, 0.46856162f, 0.05750401f, + 1.44849277f, 0.46916643f, 0.07547401f, + 1.49212170f, 0.46990088f, 0.08625601f, + 1.53960001f, 0.47070009f, 0.08985001f, + 1.58707845f, 0.47149926f, 0.08625601f, + 1.63070726f, 0.47223371f, 0.07547401f, + 1.66663694f, 0.47283852f, 0.05750401f, + 1.69101763f, 0.47324890f, 0.03234600f, + 1.70000005f, 0.47340012f, 0.00000001f, + 1.39230001f, 0.47362497f, 0.00000001f, + 1.40126371f, 0.47383994f, 0.03083400f, + 1.42559445f, 0.47442356f, 0.05481601f, + 1.46144974f, 0.47528344f, 0.07194600f, + 1.50498855f, 0.47632772f, 0.08222401f, + 1.55236876f, 0.47746408f, 0.08565000f, + 1.59974909f, 0.47860044f, 0.08222400f, + 1.64328790f, 0.47964469f, 0.07194600f, + 1.67914319f, 0.48050463f, 0.05481600f, + 1.70347357f, 0.48108816f, 0.03083399f, + 1.71243751f, 0.48130316f, 0.00000001f, + 1.40359998f, 0.47700000f, 0.00000001f, + 1.41237509f, 0.47726458f, 0.02899800f, + 1.43619370f, 0.47798279f, 0.05155201f, + 1.47129452f, 0.47904122f, 0.06766201f, + 1.51391697f, 0.48032647f, 0.07732801f, + 1.56030011f, 0.48172504f, 0.08055001f, + 1.60668325f, 0.48312366f, 0.07732800f, + 1.64930582f, 0.48440886f, 0.06766201f, + 1.68440676f, 0.48546728f, 0.05155201f, + 1.70822501f, 0.48618549f, 0.02899799f, + 1.71700025f, 0.48645008f, 0.00000001f, + 1.41249990f, 0.47812498f, 0.00000001f, + 1.42094362f, 0.47842023f, 0.02700000f, + 1.44386256f, 0.47922191f, 0.04800001f, + 1.47763753f, 0.48040313f, 0.06300000f, + 1.51865005f, 0.48183754f, 0.07200000f, + 1.56328130f, 0.48339847f, 0.07500000f, + 1.60791266f, 0.48495942f, 0.07200000f, + 1.64892507f, 0.48639381f, 0.06300000f, + 1.68270016f, 0.48757505f, 0.04800000f, + 1.70561898f, 0.48837662f, 0.02700000f, + 1.71406269f, 0.48867193f, 0.00000001f, + 1.41839993f, 0.47700000f, 0.00000001f, + 1.42639649f, 0.47730237f, 0.02500200f, + 1.44810247f, 0.47812322f, 0.04444801f, + 1.48008955f, 0.47933280f, 0.05833800f, + 1.51893127f, 0.48080164f, 0.06667200f, + 1.56120014f, 0.48240003f, 0.06945001f, + 1.60346889f, 0.48399845f, 0.06667200f, + 1.64231050f, 0.48546726f, 0.05833801f, + 1.67429769f, 0.48667687f, 0.04444801f, + 1.69600332f, 0.48749766f, 0.02500200f, + 1.70400012f, 0.48780006f, 0.00000001f, + 1.42070007f, 0.47362500f, 0.00000001f, + 1.42816150f, 0.47390610f, 0.02316600f, + 1.44841480f, 0.47466925f, 0.04118401f, + 1.47826135f, 0.47579375f, 0.05405401f, + 1.51450372f, 0.47715935f, 0.06177600f, + 1.55394375f, 0.47864535f, 0.06435001f, + 1.59338403f, 0.48013136f, 0.06177600f, + 1.62962627f, 0.48149687f, 0.05405401f, + 1.65947294f, 0.48262146f, 0.04118400f, + 1.67972589f, 0.48338455f, 0.02316600f, + 1.68718755f, 0.48366567f, 0.00000001f, + 1.41880012f, 0.46799999f, 0.00000001f, + 1.42566562f, 0.46822673f, 0.02165400f, + 1.44430101f, 0.46884245f, 0.03849601f, + 1.47176325f, 0.46974963f, 0.05052601f, + 1.50511050f, 0.47085124f, 0.05774401f, + 1.54139996f, 0.47205001f, 0.06015000f, + 1.57768965f, 0.47324884f, 0.05774400f, + 1.61103690f, 0.47435045f, 0.05052601f, + 1.63849926f, 0.47525764f, 0.03849601f, + 1.65713453f, 0.47587326f, 0.02165400f, + 1.66400003f, 0.47610006f, 0.00000001f, + 1.41209996f, 0.46012503f, 0.00000001f, + 1.41833580f, 0.46025965f, 0.02062801f, + 1.43526208f, 0.46062523f, 0.03667201f, + 1.46020591f, 0.46116382f, 0.04813201f, + 1.49049485f, 0.46181795f, 0.05500801f, + 1.52345622f, 0.46252972f, 0.05730001f, + 1.55641770f, 0.46324152f, 0.05500801f, + 1.58670676f, 0.46389559f, 0.04813201f, + 1.61165047f, 0.46443427f, 0.03667201f, + 1.62857664f, 0.46479973f, 0.02062800f, + 1.63481259f, 0.46493441f, 0.00000001f, + 1.39999998f, 0.45000005f, 0.00000001f, + 1.40559983f, 0.44999996f, 0.02025001f, + 1.42079997f, 0.45000011f, 0.03600001f, + 1.44319999f, 0.45000002f, 0.04725001f, + 1.47040009f, 0.45000008f, 0.05400001f, + 1.50000000f, 0.45000005f, 0.05625001f, + 1.52960002f, 0.45000005f, 0.05400001f, + 1.55680001f, 0.45000008f, 0.04725001f, + 1.57920003f, 0.45000005f, 0.03600001f, + 1.59440005f, 0.45000005f, 0.02025000f, + 1.60000002f, 0.45000005f, 0.00000001f, + 1.64999998f, 0.45000005f, 0.00000001f, + 1.64159989f, 0.44999996f, -0.03374999f, + 1.61880016f, 0.45000011f, -0.05999999f, + 1.58519995f, 0.45000002f, -0.07874999f, + 1.54440010f, 0.45000008f, -0.09000000f, + 1.50000000f, 0.45000005f, -0.09374999f, + 1.45560002f, 0.45000005f, -0.08999999f, + 1.41480005f, 0.45000008f, -0.07875000f, + 1.38120008f, 0.45000005f, -0.05999999f, + 1.35839999f, 0.45000005f, -0.03374998f, + 1.35000002f, 0.45000005f, 0.00000001f, + 1.67931235f, 0.46290934f, 0.00000001f, + 1.67050874f, 0.46283132f, -0.03337199f, + 1.64661360f, 0.46261978f, -0.05932799f, + 1.61139941f, 0.46230793f, -0.07786798f, + 1.56863928f, 0.46192926f, -0.08899198f, + 1.52210605f, 0.46151716f, -0.09269998f, + 1.47557306f, 0.46110505f, -0.08899198f, + 1.43281293f, 0.46072638f, -0.07786798f, + 1.39759874f, 0.46041453f, -0.05932799f, + 1.37370336f, 0.46020290f, -0.03337198f, + 1.36489987f, 0.46012494f, 0.00000001f, + 1.70000005f, 0.47340012f, 0.00000001f, + 1.69101751f, 0.47324887f, -0.03234600f, + 1.66663706f, 0.47283855f, -0.05750400f, + 1.63070726f, 0.47223368f, -0.07547399f, + 1.58707845f, 0.47149929f, -0.08625600f, + 1.53960001f, 0.47070006f, -0.08985000f, + 1.49212158f, 0.46990088f, -0.08625600f, + 1.44849288f, 0.46916646f, -0.07547400f, + 1.41256320f, 0.46856165f, -0.05750399f, + 1.38818240f, 0.46815124f, -0.03234598f, + 1.37919998f, 0.46800002f, 0.00000001f, + 1.71243751f, 0.48130316f, 0.00000001f, + 1.70347345f, 0.48108810f, -0.03083399f, + 1.67914343f, 0.48050466f, -0.05481599f, + 1.64328778f, 0.47964469f, -0.07194599f, + 1.59974921f, 0.47860047f, -0.08222400f, + 1.55236864f, 0.47746405f, -0.08564999f, + 1.50498843f, 0.47632769f, -0.08222400f, + 1.46144986f, 0.47528344f, -0.07194600f, + 1.42559433f, 0.47442353f, -0.05481599f, + 1.40126395f, 0.47383997f, -0.03083398f, + 1.39230001f, 0.47362497f, 0.00000001f, + 1.71700025f, 0.48645008f, 0.00000001f, + 1.70822489f, 0.48618543f, -0.02899799f, + 1.68440676f, 0.48546731f, -0.05155200f, + 1.64930582f, 0.48440889f, -0.06766199f, + 1.60668337f, 0.48312369f, -0.07732800f, + 1.56030011f, 0.48172504f, -0.08055000f, + 1.51391685f, 0.48032641f, -0.07732800f, + 1.47129452f, 0.47904122f, -0.06766200f, + 1.43619370f, 0.47798282f, -0.05155200f, + 1.41237521f, 0.47726461f, -0.02899799f, + 1.40359998f, 0.47700000f, 0.00000001f, + 1.71406269f, 0.48867193f, 0.00000001f, + 1.70561886f, 0.48837656f, -0.02699999f, + 1.68270016f, 0.48757505f, -0.04800000f, + 1.64892519f, 0.48639381f, -0.06299999f, + 1.60791278f, 0.48495948f, -0.07200000f, + 1.56328130f, 0.48339844f, -0.07500000f, + 1.51865005f, 0.48183751f, -0.07200000f, + 1.47763753f, 0.48040313f, -0.06299999f, + 1.44386244f, 0.47922188f, -0.04799999f, + 1.42094362f, 0.47842029f, -0.02699998f, + 1.41249990f, 0.47812498f, 0.00000001f, + 1.70400012f, 0.48780006f, 0.00000001f, + 1.69600320f, 0.48749760f, -0.02500199f, + 1.67429769f, 0.48667687f, -0.04444800f, + 1.64231050f, 0.48546728f, -0.05833799f, + 1.60346901f, 0.48399848f, -0.06667200f, + 1.56120002f, 0.48240003f, -0.06944999f, + 1.51893127f, 0.48080158f, -0.06667199f, + 1.48008966f, 0.47933280f, -0.05833799f, + 1.44810236f, 0.47812319f, -0.04444799f, + 1.42639661f, 0.47730240f, -0.02500199f, + 1.41839993f, 0.47700000f, 0.00000001f, + 1.68718755f, 0.48366567f, 0.00000001f, + 1.67972589f, 0.48338449f, -0.02316599f, + 1.65947306f, 0.48262149f, -0.04118400f, + 1.62962627f, 0.48149690f, -0.05405399f, + 1.59338415f, 0.48013139f, -0.06177600f, + 1.55394387f, 0.47864535f, -0.06434999f, + 1.51450372f, 0.47715932f, -0.06177600f, + 1.47826147f, 0.47579378f, -0.05405399f, + 1.44841480f, 0.47466925f, -0.04118399f, + 1.42816162f, 0.47390613f, -0.02316599f, + 1.42070007f, 0.47362500f, 0.00000001f, + 1.66400003f, 0.47610006f, 0.00000001f, + 1.65713418f, 0.47587320f, -0.02165399f, + 1.63849938f, 0.47525769f, -0.03849600f, + 1.61103702f, 0.47435045f, -0.05052599f, + 1.57768977f, 0.47324887f, -0.05774400f, + 1.54140007f, 0.47205001f, -0.06015000f, + 1.50511050f, 0.47085121f, -0.05774400f, + 1.47176337f, 0.46974963f, -0.05052600f, + 1.44430089f, 0.46884239f, -0.03849599f, + 1.42566574f, 0.46822679f, -0.02165399f, + 1.41880012f, 0.46799999f, 0.00000001f, + 1.63481259f, 0.46493441f, 0.00000001f, + 1.62857652f, 0.46479967f, -0.02062799f, + 1.61165059f, 0.46443427f, -0.03667200f, + 1.58670664f, 0.46389559f, -0.04813199f, + 1.55641782f, 0.46324155f, -0.05500800f, + 1.52345634f, 0.46252972f, -0.05730000f, + 1.49049485f, 0.46181795f, -0.05500799f, + 1.46020591f, 0.46116385f, -0.04813200f, + 1.43526220f, 0.46062523f, -0.03667200f, + 1.41833591f, 0.46025968f, -0.02062799f, + 1.41209996f, 0.46012503f, 0.00000001f, + 1.60000002f, 0.45000005f, 0.00000001f, + 1.59439981f, 0.44999996f, -0.02024999f, + 1.57920015f, 0.45000011f, -0.03600000f, + 1.55680001f, 0.45000002f, -0.04725000f, + 1.52960002f, 0.45000008f, -0.05400000f, + 1.50000000f, 0.45000005f, -0.05625000f, + 1.47039998f, 0.45000005f, -0.05400000f, + 1.44320011f, 0.45000008f, -0.04725000f, + 1.42079997f, 0.45000005f, -0.03599999f, + 1.40559995f, 0.45000005f, -0.02024999f, + 1.39999998f, 0.45000005f, 0.00000001f, + }; + + std::vector<uint32_t> m_teapotIndices = + { + 0, 1, 11, 11, 1, 12, + 1, 2, 12, 12, 2, 13, + 2, 3, 13, 13, 3, 14, + 3, 4, 14, 14, 4, 15, + 4, 5, 15, 15, 5, 16, + 5, 6, 16, 16, 6, 17, + 6, 7, 17, 17, 7, 18, + 7, 8, 18, 18, 8, 19, + 8, 9, 19, 19, 9, 20, + 9, 10, 20, 20, 10, 21, + 11, 12, 22, 22, 12, 23, + 12, 13, 23, 23, 13, 24, + 13, 14, 24, 24, 14, 25, + 14, 15, 25, 25, 15, 26, + 15, 16, 26, 26, 16, 27, + 16, 17, 27, 27, 17, 28, + 17, 18, 28, 28, 18, 29, + 18, 19, 29, 29, 19, 30, + 19, 20, 30, 30, 20, 31, + 20, 21, 31, 31, 21, 32, + 22, 23, 33, 33, 23, 34, + 23, 24, 34, 34, 24, 35, + 24, 25, 35, 35, 25, 36, + 25, 26, 36, 36, 26, 37, + 26, 27, 37, 37, 27, 38, + 27, 28, 38, 38, 28, 39, + 28, 29, 39, 39, 29, 40, + 29, 30, 40, 40, 30, 41, + 30, 31, 41, 41, 31, 42, + 31, 32, 42, 42, 32, 43, + 33, 34, 44, 44, 34, 45, + 34, 35, 45, 45, 35, 46, + 35, 36, 46, 46, 36, 47, + 36, 37, 47, 47, 37, 48, + 37, 38, 48, 48, 38, 49, + 38, 39, 49, 49, 39, 50, + 39, 40, 50, 50, 40, 51, + 40, 41, 51, 51, 41, 52, + 41, 42, 52, 52, 42, 53, + 42, 43, 53, 53, 43, 54, + 44, 45, 55, 55, 45, 56, + 45, 46, 56, 56, 46, 57, + 46, 47, 57, 57, 47, 58, + 47, 48, 58, 58, 48, 59, + 48, 49, 59, 59, 49, 60, + 49, 50, 60, 60, 50, 61, + 50, 51, 61, 61, 51, 62, + 51, 52, 62, 62, 52, 63, + 52, 53, 63, 63, 53, 64, + 53, 54, 64, 64, 54, 65, + 55, 56, 66, 66, 56, 67, + 56, 57, 67, 67, 57, 68, + 57, 58, 68, 68, 58, 69, + 58, 59, 69, 69, 59, 70, + 59, 60, 70, 70, 60, 71, + 60, 61, 71, 71, 61, 72, + 61, 62, 72, 72, 62, 73, + 62, 63, 73, 73, 63, 74, + 63, 64, 74, 74, 64, 75, + 64, 65, 75, 75, 65, 76, + 66, 67, 77, 77, 67, 78, + 67, 68, 78, 78, 68, 79, + 68, 69, 79, 79, 69, 80, + 69, 70, 80, 80, 70, 81, + 70, 71, 81, 81, 71, 82, + 71, 72, 82, 82, 72, 83, + 72, 73, 83, 83, 73, 84, + 73, 74, 84, 84, 74, 85, + 74, 75, 85, 85, 75, 86, + 75, 76, 86, 86, 76, 87, + 77, 78, 88, 88, 78, 89, + 78, 79, 89, 89, 79, 90, + 79, 80, 90, 90, 80, 91, + 80, 81, 91, 91, 81, 92, + 81, 82, 92, 92, 82, 93, + 82, 83, 93, 93, 83, 94, + 83, 84, 94, 94, 84, 95, + 84, 85, 95, 95, 85, 96, + 85, 86, 96, 96, 86, 97, + 86, 87, 97, 97, 87, 98, + 88, 89, 99, 99, 89, 100, + 89, 90, 100, 100, 90, 101, + 90, 91, 101, 101, 91, 102, + 91, 92, 102, 102, 92, 103, + 92, 93, 103, 103, 93, 104, + 93, 94, 104, 104, 94, 105, + 94, 95, 105, 105, 95, 106, + 95, 96, 106, 106, 96, 107, + 96, 97, 107, 107, 97, 108, + 97, 98, 108, 108, 98, 109, + 99, 100, 110, 110, 100, 111, + 100, 101, 111, 111, 101, 112, + 101, 102, 112, 112, 102, 113, + 102, 103, 113, 113, 103, 114, + 103, 104, 114, 114, 104, 115, + 104, 105, 115, 115, 105, 116, + 105, 106, 116, 116, 106, 117, + 106, 107, 117, 117, 107, 118, + 107, 108, 118, 118, 108, 119, + 108, 109, 119, 119, 109, 120, + 121, 122, 132, 132, 122, 133, + 122, 123, 133, 133, 123, 134, + 123, 124, 134, 134, 124, 135, + 124, 125, 135, 135, 125, 136, + 125, 126, 136, 136, 126, 137, + 126, 127, 137, 137, 127, 138, + 127, 128, 138, 138, 128, 139, + 128, 129, 139, 139, 129, 140, + 129, 130, 140, 140, 130, 141, + 130, 131, 141, 141, 131, 142, + 132, 133, 143, 143, 133, 144, + 133, 134, 144, 144, 134, 145, + 134, 135, 145, 145, 135, 146, + 135, 136, 146, 146, 136, 147, + 136, 137, 147, 147, 137, 148, + 137, 138, 148, 148, 138, 149, + 138, 139, 149, 149, 139, 150, + 139, 140, 150, 150, 140, 151, + 140, 141, 151, 151, 141, 152, + 141, 142, 152, 152, 142, 153, + 143, 144, 154, 154, 144, 155, + 144, 145, 155, 155, 145, 156, + 145, 146, 156, 156, 146, 157, + 146, 147, 157, 157, 147, 158, + 147, 148, 158, 158, 148, 159, + 148, 149, 159, 159, 149, 160, + 149, 150, 160, 160, 150, 161, + 150, 151, 161, 161, 151, 162, + 151, 152, 162, 162, 152, 163, + 152, 153, 163, 163, 153, 164, + 154, 155, 165, 165, 155, 166, + 155, 156, 166, 166, 156, 167, + 156, 157, 167, 167, 157, 168, + 157, 158, 168, 168, 158, 169, + 158, 159, 169, 169, 159, 170, + 159, 160, 170, 170, 160, 171, + 160, 161, 171, 171, 161, 172, + 161, 162, 172, 172, 162, 173, + 162, 163, 173, 173, 163, 174, + 163, 164, 174, 174, 164, 175, + 165, 166, 176, 176, 166, 177, + 166, 167, 177, 177, 167, 178, + 167, 168, 178, 178, 168, 179, + 168, 169, 179, 179, 169, 180, + 169, 170, 180, 180, 170, 181, + 170, 171, 181, 181, 171, 182, + 171, 172, 182, 182, 172, 183, + 172, 173, 183, 183, 173, 184, + 173, 174, 184, 184, 174, 185, + 174, 175, 185, 185, 175, 186, + 176, 177, 187, 187, 177, 188, + 177, 178, 188, 188, 178, 189, + 178, 179, 189, 189, 179, 190, + 179, 180, 190, 190, 180, 191, + 180, 181, 191, 191, 181, 192, + 181, 182, 192, 192, 182, 193, + 182, 183, 193, 193, 183, 194, + 183, 184, 194, 194, 184, 195, + 184, 185, 195, 195, 185, 196, + 185, 186, 196, 196, 186, 197, + 187, 188, 198, 198, 188, 199, + 188, 189, 199, 199, 189, 200, + 189, 190, 200, 200, 190, 201, + 190, 191, 201, 201, 191, 202, + 191, 192, 202, 202, 192, 203, + 192, 193, 203, 203, 193, 204, + 193, 194, 204, 204, 194, 205, + 194, 195, 205, 205, 195, 206, + 195, 196, 206, 206, 196, 207, + 196, 197, 207, 207, 197, 208, + 198, 199, 209, 209, 199, 210, + 199, 200, 210, 210, 200, 211, + 200, 201, 211, 211, 201, 212, + 201, 202, 212, 212, 202, 213, + 202, 203, 213, 213, 203, 214, + 203, 204, 214, 214, 204, 215, + 204, 205, 215, 215, 205, 216, + 205, 206, 216, 216, 206, 217, + 206, 207, 217, 217, 207, 218, + 207, 208, 218, 218, 208, 219, + 209, 210, 220, 220, 210, 221, + 210, 211, 221, 221, 211, 222, + 211, 212, 222, 222, 212, 223, + 212, 213, 223, 223, 213, 224, + 213, 214, 224, 224, 214, 225, + 214, 215, 225, 225, 215, 226, + 215, 216, 226, 226, 216, 227, + 216, 217, 227, 227, 217, 228, + 217, 218, 228, 228, 218, 229, + 218, 219, 229, 229, 219, 230, + 220, 221, 231, 231, 221, 232, + 221, 222, 232, 232, 222, 233, + 222, 223, 233, 233, 223, 234, + 223, 224, 234, 234, 224, 235, + 224, 225, 235, 235, 225, 236, + 225, 226, 236, 236, 226, 237, + 226, 227, 237, 237, 227, 238, + 227, 228, 238, 238, 228, 239, + 228, 229, 239, 239, 229, 240, + 229, 230, 240, 240, 230, 241, + 242, 243, 253, 253, 243, 254, + 243, 244, 254, 254, 244, 255, + 244, 245, 255, 255, 245, 256, + 245, 246, 256, 256, 246, 257, + 246, 247, 257, 257, 247, 258, + 247, 248, 258, 258, 248, 259, + 248, 249, 259, 259, 249, 260, + 249, 250, 260, 260, 250, 261, + 250, 251, 261, 261, 251, 262, + 251, 252, 262, 262, 252, 263, + 253, 254, 264, 264, 254, 265, + 254, 255, 265, 265, 255, 266, + 255, 256, 266, 266, 256, 267, + 256, 257, 267, 267, 257, 268, + 257, 258, 268, 268, 258, 269, + 258, 259, 269, 269, 259, 270, + 259, 260, 270, 270, 260, 271, + 260, 261, 271, 271, 261, 272, + 261, 262, 272, 272, 262, 273, + 262, 263, 273, 273, 263, 274, + 264, 265, 275, 275, 265, 276, + 265, 266, 276, 276, 266, 277, + 266, 267, 277, 277, 267, 278, + 267, 268, 278, 278, 268, 279, + 268, 269, 279, 279, 269, 280, + 269, 270, 280, 280, 270, 281, + 270, 271, 281, 281, 271, 282, + 271, 272, 282, 282, 272, 283, + 272, 273, 283, 283, 273, 284, + 273, 274, 284, 284, 274, 285, + 275, 276, 286, 286, 276, 287, + 276, 277, 287, 287, 277, 288, + 277, 278, 288, 288, 278, 289, + 278, 279, 289, 289, 279, 290, + 279, 280, 290, 290, 280, 291, + 280, 281, 291, 291, 281, 292, + 281, 282, 292, 292, 282, 293, + 282, 283, 293, 293, 283, 294, + 283, 284, 294, 294, 284, 295, + 284, 285, 295, 295, 285, 296, + 286, 287, 297, 297, 287, 298, + 287, 288, 298, 298, 288, 299, + 288, 289, 299, 299, 289, 300, + 289, 290, 300, 300, 290, 301, + 290, 291, 301, 301, 291, 302, + 291, 292, 302, 302, 292, 303, + 292, 293, 303, 303, 293, 304, + 293, 294, 304, 304, 294, 305, + 294, 295, 305, 305, 295, 306, + 295, 296, 306, 306, 296, 307, + 297, 298, 308, 308, 298, 309, + 298, 299, 309, 309, 299, 310, + 299, 300, 310, 310, 300, 311, + 300, 301, 311, 311, 301, 312, + 301, 302, 312, 312, 302, 313, + 302, 303, 313, 313, 303, 314, + 303, 304, 314, 314, 304, 315, + 304, 305, 315, 315, 305, 316, + 305, 306, 316, 316, 306, 317, + 306, 307, 317, 317, 307, 318, + 308, 309, 319, 319, 309, 320, + 309, 310, 320, 320, 310, 321, + 310, 311, 321, 321, 311, 322, + 311, 312, 322, 322, 312, 323, + 312, 313, 323, 323, 313, 324, + 313, 314, 324, 324, 314, 325, + 314, 315, 325, 325, 315, 326, + 315, 316, 326, 326, 316, 327, + 316, 317, 327, 327, 317, 328, + 317, 318, 328, 328, 318, 329, + 319, 320, 330, 330, 320, 331, + 320, 321, 331, 331, 321, 332, + 321, 322, 332, 332, 322, 333, + 322, 323, 333, 333, 323, 334, + 323, 324, 334, 334, 324, 335, + 324, 325, 335, 335, 325, 336, + 325, 326, 336, 336, 326, 337, + 326, 327, 337, 337, 327, 338, + 327, 328, 338, 338, 328, 339, + 328, 329, 339, 339, 329, 340, + 330, 331, 341, 341, 331, 342, + 331, 332, 342, 342, 332, 343, + 332, 333, 343, 343, 333, 344, + 333, 334, 344, 344, 334, 345, + 334, 335, 345, 345, 335, 346, + 335, 336, 346, 346, 336, 347, + 336, 337, 347, 347, 337, 348, + 337, 338, 348, 348, 338, 349, + 338, 339, 349, 349, 339, 350, + 339, 340, 350, 350, 340, 351, + 341, 342, 352, 352, 342, 353, + 342, 343, 353, 353, 343, 354, + 343, 344, 354, 354, 344, 355, + 344, 345, 355, 355, 345, 356, + 345, 346, 356, 356, 346, 357, + 346, 347, 357, 357, 347, 358, + 347, 348, 358, 358, 348, 359, + 348, 349, 359, 359, 349, 360, + 349, 350, 360, 360, 350, 361, + 350, 351, 361, 361, 351, 362, + 363, 364, 374, 374, 364, 375, + 364, 365, 375, 375, 365, 376, + 365, 366, 376, 376, 366, 377, + 366, 367, 377, 377, 367, 378, + 367, 368, 378, 378, 368, 379, + 368, 369, 379, 379, 369, 380, + 369, 370, 380, 380, 370, 381, + 370, 371, 381, 381, 371, 382, + 371, 372, 382, 382, 372, 383, + 372, 373, 383, 383, 373, 384, + 374, 375, 385, 385, 375, 386, + 375, 376, 386, 386, 376, 387, + 376, 377, 387, 387, 377, 388, + 377, 378, 388, 388, 378, 389, + 378, 379, 389, 389, 379, 390, + 379, 380, 390, 390, 380, 391, + 380, 381, 391, 391, 381, 392, + 381, 382, 392, 392, 382, 393, + 382, 383, 393, 393, 383, 394, + 383, 384, 394, 394, 384, 395, + 385, 386, 396, 396, 386, 397, + 386, 387, 397, 397, 387, 398, + 387, 388, 398, 398, 388, 399, + 388, 389, 399, 399, 389, 400, + 389, 390, 400, 400, 390, 401, + 390, 391, 401, 401, 391, 402, + 391, 392, 402, 402, 392, 403, + 392, 393, 403, 403, 393, 404, + 393, 394, 404, 404, 394, 405, + 394, 395, 405, 405, 395, 406, + 396, 397, 407, 407, 397, 408, + 397, 398, 408, 408, 398, 409, + 398, 399, 409, 409, 399, 410, + 399, 400, 410, 410, 400, 411, + 400, 401, 411, 411, 401, 412, + 401, 402, 412, 412, 402, 413, + 402, 403, 413, 413, 403, 414, + 403, 404, 414, 414, 404, 415, + 404, 405, 415, 415, 405, 416, + 405, 406, 416, 416, 406, 417, + 407, 408, 418, 418, 408, 419, + 408, 409, 419, 419, 409, 420, + 409, 410, 420, 420, 410, 421, + 410, 411, 421, 421, 411, 422, + 411, 412, 422, 422, 412, 423, + 412, 413, 423, 423, 413, 424, + 413, 414, 424, 424, 414, 425, + 414, 415, 425, 425, 415, 426, + 415, 416, 426, 426, 416, 427, + 416, 417, 427, 427, 417, 428, + 418, 419, 429, 429, 419, 430, + 419, 420, 430, 430, 420, 431, + 420, 421, 431, 431, 421, 432, + 421, 422, 432, 432, 422, 433, + 422, 423, 433, 433, 423, 434, + 423, 424, 434, 434, 424, 435, + 424, 425, 435, 435, 425, 436, + 425, 426, 436, 436, 426, 437, + 426, 427, 437, 437, 427, 438, + 427, 428, 438, 438, 428, 439, + 429, 430, 440, 440, 430, 441, + 430, 431, 441, 441, 431, 442, + 431, 432, 442, 442, 432, 443, + 432, 433, 443, 443, 433, 444, + 433, 434, 444, 444, 434, 445, + 434, 435, 445, 445, 435, 446, + 435, 436, 446, 446, 436, 447, + 436, 437, 447, 447, 437, 448, + 437, 438, 448, 448, 438, 449, + 438, 439, 449, 449, 439, 450, + 440, 441, 451, 451, 441, 452, + 441, 442, 452, 452, 442, 453, + 442, 443, 453, 453, 443, 454, + 443, 444, 454, 454, 444, 455, + 444, 445, 455, 455, 445, 456, + 445, 446, 456, 456, 446, 457, + 446, 447, 457, 457, 447, 458, + 447, 448, 458, 458, 448, 459, + 448, 449, 459, 459, 449, 460, + 449, 450, 460, 460, 450, 461, + 451, 452, 462, 462, 452, 463, + 452, 453, 463, 463, 453, 464, + 453, 454, 464, 464, 454, 465, + 454, 455, 465, 465, 455, 466, + 455, 456, 466, 466, 456, 467, + 456, 457, 467, 467, 457, 468, + 457, 458, 468, 468, 458, 469, + 458, 459, 469, 469, 459, 470, + 459, 460, 470, 470, 460, 471, + 460, 461, 471, 471, 461, 472, + 462, 463, 473, 473, 463, 474, + 463, 464, 474, 474, 464, 475, + 464, 465, 475, 475, 465, 476, + 465, 466, 476, 476, 466, 477, + 466, 467, 477, 477, 467, 478, + 467, 468, 478, 478, 468, 479, + 468, 469, 479, 479, 469, 480, + 469, 470, 480, 480, 470, 481, + 470, 471, 481, 481, 471, 482, + 471, 472, 482, 482, 472, 483, + 484, 485, 495, 495, 485, 496, + 485, 486, 496, 496, 486, 497, + 486, 487, 497, 497, 487, 498, + 487, 488, 498, 498, 488, 499, + 488, 489, 499, 499, 489, 500, + 489, 490, 500, 500, 490, 501, + 490, 491, 501, 501, 491, 502, + 491, 492, 502, 502, 492, 503, + 492, 493, 503, 503, 493, 504, + 493, 494, 504, 504, 494, 505, + 495, 496, 506, 506, 496, 507, + 496, 497, 507, 507, 497, 508, + 497, 498, 508, 508, 498, 509, + 498, 499, 509, 509, 499, 510, + 499, 500, 510, 510, 500, 511, + 500, 501, 511, 511, 501, 512, + 501, 502, 512, 512, 502, 513, + 502, 503, 513, 513, 503, 514, + 503, 504, 514, 514, 504, 515, + 504, 505, 515, 515, 505, 516, + 506, 507, 517, 517, 507, 518, + 507, 508, 518, 518, 508, 519, + 508, 509, 519, 519, 509, 520, + 509, 510, 520, 520, 510, 521, + 510, 511, 521, 521, 511, 522, + 511, 512, 522, 522, 512, 523, + 512, 513, 523, 523, 513, 524, + 513, 514, 524, 524, 514, 525, + 514, 515, 525, 525, 515, 526, + 515, 516, 526, 526, 516, 527, + 517, 518, 528, 528, 518, 529, + 518, 519, 529, 529, 519, 530, + 519, 520, 530, 530, 520, 531, + 520, 521, 531, 531, 521, 532, + 521, 522, 532, 532, 522, 533, + 522, 523, 533, 533, 523, 534, + 523, 524, 534, 534, 524, 535, + 524, 525, 535, 535, 525, 536, + 525, 526, 536, 536, 526, 537, + 526, 527, 537, 537, 527, 538, + 528, 529, 539, 539, 529, 540, + 529, 530, 540, 540, 530, 541, + 530, 531, 541, 541, 531, 542, + 531, 532, 542, 542, 532, 543, + 532, 533, 543, 543, 533, 544, + 533, 534, 544, 544, 534, 545, + 534, 535, 545, 545, 535, 546, + 535, 536, 546, 546, 536, 547, + 536, 537, 547, 547, 537, 548, + 537, 538, 548, 548, 538, 549, + 539, 540, 550, 550, 540, 551, + 540, 541, 551, 551, 541, 552, + 541, 542, 552, 552, 542, 553, + 542, 543, 553, 553, 543, 554, + 543, 544, 554, 554, 544, 555, + 544, 545, 555, 555, 545, 556, + 545, 546, 556, 556, 546, 557, + 546, 547, 557, 557, 547, 558, + 547, 548, 558, 558, 548, 559, + 548, 549, 559, 559, 549, 560, + 550, 551, 561, 561, 551, 562, + 551, 552, 562, 562, 552, 563, + 552, 553, 563, 563, 553, 564, + 553, 554, 564, 564, 554, 565, + 554, 555, 565, 565, 555, 566, + 555, 556, 566, 566, 556, 567, + 556, 557, 567, 567, 557, 568, + 557, 558, 568, 568, 558, 569, + 558, 559, 569, 569, 559, 570, + 559, 560, 570, 570, 560, 571, + 561, 562, 572, 572, 562, 573, + 562, 563, 573, 573, 563, 574, + 563, 564, 574, 574, 564, 575, + 564, 565, 575, 575, 565, 576, + 565, 566, 576, 576, 566, 577, + 566, 567, 577, 577, 567, 578, + 567, 568, 578, 578, 568, 579, + 568, 569, 579, 579, 569, 580, + 569, 570, 580, 580, 570, 581, + 570, 571, 581, 581, 571, 582, + 572, 573, 583, 583, 573, 584, + 573, 574, 584, 584, 574, 585, + 574, 575, 585, 585, 575, 586, + 575, 576, 586, 586, 576, 587, + 576, 577, 587, 587, 577, 588, + 577, 578, 588, 588, 578, 589, + 578, 579, 589, 589, 579, 590, + 579, 580, 590, 590, 580, 591, + 580, 581, 591, 591, 581, 592, + 581, 582, 592, 592, 582, 593, + 583, 584, 594, 594, 584, 595, + 584, 585, 595, 595, 585, 596, + 585, 586, 596, 596, 586, 597, + 586, 587, 597, 597, 587, 598, + 587, 588, 598, 598, 588, 599, + 588, 589, 599, 599, 589, 600, + 589, 590, 600, 600, 590, 601, + 590, 591, 601, 601, 591, 602, + 591, 592, 602, 602, 592, 603, + 592, 593, 603, 603, 593, 604, + 605, 606, 616, 616, 606, 617, + 606, 607, 617, 617, 607, 618, + 607, 608, 618, 618, 608, 619, + 608, 609, 619, 619, 609, 620, + 609, 610, 620, 620, 610, 621, + 610, 611, 621, 621, 611, 622, + 611, 612, 622, 622, 612, 623, + 612, 613, 623, 623, 613, 624, + 613, 614, 624, 624, 614, 625, + 614, 615, 625, 625, 615, 626, + 616, 617, 627, 627, 617, 628, + 617, 618, 628, 628, 618, 629, + 618, 619, 629, 629, 619, 630, + 619, 620, 630, 630, 620, 631, + 620, 621, 631, 631, 621, 632, + 621, 622, 632, 632, 622, 633, + 622, 623, 633, 633, 623, 634, + 623, 624, 634, 634, 624, 635, + 624, 625, 635, 635, 625, 636, + 625, 626, 636, 636, 626, 637, + 627, 628, 638, 638, 628, 639, + 628, 629, 639, 639, 629, 640, + 629, 630, 640, 640, 630, 641, + 630, 631, 641, 641, 631, 642, + 631, 632, 642, 642, 632, 643, + 632, 633, 643, 643, 633, 644, + 633, 634, 644, 644, 634, 645, + 634, 635, 645, 645, 635, 646, + 635, 636, 646, 646, 636, 647, + 636, 637, 647, 647, 637, 648, + 638, 639, 649, 649, 639, 650, + 639, 640, 650, 650, 640, 651, + 640, 641, 651, 651, 641, 652, + 641, 642, 652, 652, 642, 653, + 642, 643, 653, 653, 643, 654, + 643, 644, 654, 654, 644, 655, + 644, 645, 655, 655, 645, 656, + 645, 646, 656, 656, 646, 657, + 646, 647, 657, 657, 647, 658, + 647, 648, 658, 658, 648, 659, + 649, 650, 660, 660, 650, 661, + 650, 651, 661, 661, 651, 662, + 651, 652, 662, 662, 652, 663, + 652, 653, 663, 663, 653, 664, + 653, 654, 664, 664, 654, 665, + 654, 655, 665, 665, 655, 666, + 655, 656, 666, 666, 656, 667, + 656, 657, 667, 667, 657, 668, + 657, 658, 668, 668, 658, 669, + 658, 659, 669, 669, 659, 670, + 660, 661, 671, 671, 661, 672, + 661, 662, 672, 672, 662, 673, + 662, 663, 673, 673, 663, 674, + 663, 664, 674, 674, 664, 675, + 664, 665, 675, 675, 665, 676, + 665, 666, 676, 676, 666, 677, + 666, 667, 677, 677, 667, 678, + 667, 668, 678, 678, 668, 679, + 668, 669, 679, 679, 669, 680, + 669, 670, 680, 680, 670, 681, + 671, 672, 682, 682, 672, 683, + 672, 673, 683, 683, 673, 684, + 673, 674, 684, 684, 674, 685, + 674, 675, 685, 685, 675, 686, + 675, 676, 686, 686, 676, 687, + 676, 677, 687, 687, 677, 688, + 677, 678, 688, 688, 678, 689, + 678, 679, 689, 689, 679, 690, + 679, 680, 690, 690, 680, 691, + 680, 681, 691, 691, 681, 692, + 682, 683, 693, 693, 683, 694, + 683, 684, 694, 694, 684, 695, + 684, 685, 695, 695, 685, 696, + 685, 686, 696, 696, 686, 697, + 686, 687, 697, 697, 687, 698, + 687, 688, 698, 698, 688, 699, + 688, 689, 699, 699, 689, 700, + 689, 690, 700, 700, 690, 701, + 690, 691, 701, 701, 691, 702, + 691, 692, 702, 702, 692, 703, + 693, 694, 704, 704, 694, 705, + 694, 695, 705, 705, 695, 706, + 695, 696, 706, 706, 696, 707, + 696, 697, 707, 707, 697, 708, + 697, 698, 708, 708, 698, 709, + 698, 699, 709, 709, 699, 710, + 699, 700, 710, 710, 700, 711, + 700, 701, 711, 711, 701, 712, + 701, 702, 712, 712, 702, 713, + 702, 703, 713, 713, 703, 714, + 704, 705, 715, 715, 705, 716, + 705, 706, 716, 716, 706, 717, + 706, 707, 717, 717, 707, 718, + 707, 708, 718, 718, 708, 719, + 708, 709, 719, 719, 709, 720, + 709, 710, 720, 720, 710, 721, + 710, 711, 721, 721, 711, 722, + 711, 712, 722, 722, 712, 723, + 712, 713, 723, 723, 713, 724, + 713, 714, 724, 724, 714, 725, + 726, 727, 737, 737, 727, 738, + 727, 728, 738, 738, 728, 739, + 728, 729, 739, 739, 729, 740, + 729, 730, 740, 740, 730, 741, + 730, 731, 741, 741, 731, 742, + 731, 732, 742, 742, 732, 743, + 732, 733, 743, 743, 733, 744, + 733, 734, 744, 744, 734, 745, + 734, 735, 745, 745, 735, 746, + 735, 736, 746, 746, 736, 747, + 737, 738, 748, 748, 738, 749, + 738, 739, 749, 749, 739, 750, + 739, 740, 750, 750, 740, 751, + 740, 741, 751, 751, 741, 752, + 741, 742, 752, 752, 742, 753, + 742, 743, 753, 753, 743, 754, + 743, 744, 754, 754, 744, 755, + 744, 745, 755, 755, 745, 756, + 745, 746, 756, 756, 746, 757, + 746, 747, 757, 757, 747, 758, + 748, 749, 759, 759, 749, 760, + 749, 750, 760, 760, 750, 761, + 750, 751, 761, 761, 751, 762, + 751, 752, 762, 762, 752, 763, + 752, 753, 763, 763, 753, 764, + 753, 754, 764, 764, 754, 765, + 754, 755, 765, 765, 755, 766, + 755, 756, 766, 766, 756, 767, + 756, 757, 767, 767, 757, 768, + 757, 758, 768, 768, 758, 769, + 759, 760, 770, 770, 760, 771, + 760, 761, 771, 771, 761, 772, + 761, 762, 772, 772, 762, 773, + 762, 763, 773, 773, 763, 774, + 763, 764, 774, 774, 764, 775, + 764, 765, 775, 775, 765, 776, + 765, 766, 776, 776, 766, 777, + 766, 767, 777, 777, 767, 778, + 767, 768, 778, 778, 768, 779, + 768, 769, 779, 779, 769, 780, + 770, 771, 781, 781, 771, 782, + 771, 772, 782, 782, 772, 783, + 772, 773, 783, 783, 773, 784, + 773, 774, 784, 784, 774, 785, + 774, 775, 785, 785, 775, 786, + 775, 776, 786, 786, 776, 787, + 776, 777, 787, 787, 777, 788, + 777, 778, 788, 788, 778, 789, + 778, 779, 789, 789, 779, 790, + 779, 780, 790, 790, 780, 791, + 781, 782, 792, 792, 782, 793, + 782, 783, 793, 793, 783, 794, + 783, 784, 794, 794, 784, 795, + 784, 785, 795, 795, 785, 796, + 785, 786, 796, 796, 786, 797, + 786, 787, 797, 797, 787, 798, + 787, 788, 798, 798, 788, 799, + 788, 789, 799, 799, 789, 800, + 789, 790, 800, 800, 790, 801, + 790, 791, 801, 801, 791, 802, + 792, 793, 803, 803, 793, 804, + 793, 794, 804, 804, 794, 805, + 794, 795, 805, 805, 795, 806, + 795, 796, 806, 806, 796, 807, + 796, 797, 807, 807, 797, 808, + 797, 798, 808, 808, 798, 809, + 798, 799, 809, 809, 799, 810, + 799, 800, 810, 810, 800, 811, + 800, 801, 811, 811, 801, 812, + 801, 802, 812, 812, 802, 813, + 803, 804, 814, 814, 804, 815, + 804, 805, 815, 815, 805, 816, + 805, 806, 816, 816, 806, 817, + 806, 807, 817, 817, 807, 818, + 807, 808, 818, 818, 808, 819, + 808, 809, 819, 819, 809, 820, + 809, 810, 820, 820, 810, 821, + 810, 811, 821, 821, 811, 822, + 811, 812, 822, 822, 812, 823, + 812, 813, 823, 823, 813, 824, + 814, 815, 825, 825, 815, 826, + 815, 816, 826, 826, 816, 827, + 816, 817, 827, 827, 817, 828, + 817, 818, 828, 828, 818, 829, + 818, 819, 829, 829, 819, 830, + 819, 820, 830, 830, 820, 831, + 820, 821, 831, 831, 821, 832, + 821, 822, 832, 832, 822, 833, + 822, 823, 833, 833, 823, 834, + 823, 824, 834, 834, 824, 835, + 825, 826, 836, 836, 826, 837, + 826, 827, 837, 837, 827, 838, + 827, 828, 838, 838, 828, 839, + 828, 829, 839, 839, 829, 840, + 829, 830, 840, 840, 830, 841, + 830, 831, 841, 841, 831, 842, + 831, 832, 842, 842, 832, 843, + 832, 833, 843, 843, 833, 844, + 833, 834, 844, 844, 834, 845, + 834, 835, 845, 845, 835, 846, + 847, 848, 858, 858, 848, 859, + 848, 849, 859, 859, 849, 860, + 849, 850, 860, 860, 850, 861, + 850, 851, 861, 861, 851, 862, + 851, 852, 862, 862, 852, 863, + 852, 853, 863, 863, 853, 864, + 853, 854, 864, 864, 854, 865, + 854, 855, 865, 865, 855, 866, + 855, 856, 866, 866, 856, 867, + 856, 857, 867, 867, 857, 868, + 858, 859, 869, 869, 859, 870, + 859, 860, 870, 870, 860, 871, + 860, 861, 871, 871, 861, 872, + 861, 862, 872, 872, 862, 873, + 862, 863, 873, 873, 863, 874, + 863, 864, 874, 874, 864, 875, + 864, 865, 875, 875, 865, 876, + 865, 866, 876, 876, 866, 877, + 866, 867, 877, 877, 867, 878, + 867, 868, 878, 878, 868, 879, + 869, 870, 880, 880, 870, 881, + 870, 871, 881, 881, 871, 882, + 871, 872, 882, 882, 872, 883, + 872, 873, 883, 883, 873, 884, + 873, 874, 884, 884, 874, 885, + 874, 875, 885, 885, 875, 886, + 875, 876, 886, 886, 876, 887, + 876, 877, 887, 887, 877, 888, + 877, 878, 888, 888, 878, 889, + 878, 879, 889, 889, 879, 890, + 880, 881, 891, 891, 881, 892, + 881, 882, 892, 892, 882, 893, + 882, 883, 893, 893, 883, 894, + 883, 884, 894, 894, 884, 895, + 884, 885, 895, 895, 885, 896, + 885, 886, 896, 896, 886, 897, + 886, 887, 897, 897, 887, 898, + 887, 888, 898, 898, 888, 899, + 888, 889, 899, 899, 889, 900, + 889, 890, 900, 900, 890, 901, + 891, 892, 902, 902, 892, 903, + 892, 893, 903, 903, 893, 904, + 893, 894, 904, 904, 894, 905, + 894, 895, 905, 905, 895, 906, + 895, 896, 906, 906, 896, 907, + 896, 897, 907, 907, 897, 908, + 897, 898, 908, 908, 898, 909, + 898, 899, 909, 909, 899, 910, + 899, 900, 910, 910, 900, 911, + 900, 901, 911, 911, 901, 912, + 902, 903, 913, 913, 903, 914, + 903, 904, 914, 914, 904, 915, + 904, 905, 915, 915, 905, 916, + 905, 906, 916, 916, 906, 917, + 906, 907, 917, 917, 907, 918, + 907, 908, 918, 918, 908, 919, + 908, 909, 919, 919, 909, 920, + 909, 910, 920, 920, 910, 921, + 910, 911, 921, 921, 911, 922, + 911, 912, 922, 922, 912, 923, + 913, 914, 924, 924, 914, 925, + 914, 915, 925, 925, 915, 926, + 915, 916, 926, 926, 916, 927, + 916, 917, 927, 927, 917, 928, + 917, 918, 928, 928, 918, 929, + 918, 919, 929, 929, 919, 930, + 919, 920, 930, 930, 920, 931, + 920, 921, 931, 931, 921, 932, + 921, 922, 932, 932, 922, 933, + 922, 923, 933, 933, 923, 934, + 924, 925, 935, 935, 925, 936, + 925, 926, 936, 936, 926, 937, + 926, 927, 937, 937, 927, 938, + 927, 928, 938, 938, 928, 939, + 928, 929, 939, 939, 929, 940, + 929, 930, 940, 940, 930, 941, + 930, 931, 941, 941, 931, 942, + 931, 932, 942, 942, 932, 943, + 932, 933, 943, 943, 933, 944, + 933, 934, 944, 944, 934, 945, + 935, 936, 946, 946, 936, 947, + 936, 937, 947, 947, 937, 948, + 937, 938, 948, 948, 938, 949, + 938, 939, 949, 949, 939, 950, + 939, 940, 950, 950, 940, 951, + 940, 941, 951, 951, 941, 952, + 941, 942, 952, 952, 942, 953, + 942, 943, 953, 953, 943, 954, + 943, 944, 954, 954, 944, 955, + 944, 945, 955, 955, 945, 956, + 946, 947, 957, 957, 947, 958, + 947, 948, 958, 958, 948, 959, + 948, 949, 959, 959, 949, 960, + 949, 950, 960, 960, 950, 961, + 950, 951, 961, 961, 951, 962, + 951, 952, 962, 962, 952, 963, + 952, 953, 963, 963, 953, 964, + 953, 954, 964, 964, 954, 965, + 954, 955, 965, 965, 955, 966, + 955, 956, 966, 966, 956, 967, + 968, 969, 979, 979, 969, 980, + 969, 970, 980, 980, 970, 981, + 970, 971, 981, 981, 971, 982, + 971, 972, 982, 982, 972, 983, + 972, 973, 983, 983, 973, 984, + 973, 974, 984, 984, 974, 985, + 974, 975, 985, 985, 975, 986, + 975, 976, 986, 986, 976, 987, + 976, 977, 987, 987, 977, 988, + 977, 978, 988, 988, 978, 989, + 979, 980, 990, 990, 980, 991, + 980, 981, 991, 991, 981, 992, + 981, 982, 992, 992, 982, 993, + 982, 983, 993, 993, 983, 994, + 983, 984, 994, 994, 984, 995, + 984, 985, 995, 995, 985, 996, + 985, 986, 996, 996, 986, 997, + 986, 987, 997, 997, 987, 998, + 987, 988, 998, 998, 988, 999, + 988, 989, 999, 999, 989, 1000, + 990, 991, 1001, 1001, 991, 1002, + 991, 992, 1002, 1002, 992, 1003, + 992, 993, 1003, 1003, 993, 1004, + 993, 994, 1004, 1004, 994, 1005, + 994, 995, 1005, 1005, 995, 1006, + 995, 996, 1006, 1006, 996, 1007, + 996, 997, 1007, 1007, 997, 1008, + 997, 998, 1008, 1008, 998, 1009, + 998, 999, 1009, 1009, 999, 1010, + 999, 1000, 1010, 1010, 1000, 1011, + 1001, 1002, 1012, 1012, 1002, 1013, + 1002, 1003, 1013, 1013, 1003, 1014, + 1003, 1004, 1014, 1014, 1004, 1015, + 1004, 1005, 1015, 1015, 1005, 1016, + 1005, 1006, 1016, 1016, 1006, 1017, + 1006, 1007, 1017, 1017, 1007, 1018, + 1007, 1008, 1018, 1018, 1008, 1019, + 1008, 1009, 1019, 1019, 1009, 1020, + 1009, 1010, 1020, 1020, 1010, 1021, + 1010, 1011, 1021, 1021, 1011, 1022, + 1012, 1013, 1023, 1023, 1013, 1024, + 1013, 1014, 1024, 1024, 1014, 1025, + 1014, 1015, 1025, 1025, 1015, 1026, + 1015, 1016, 1026, 1026, 1016, 1027, + 1016, 1017, 1027, 1027, 1017, 1028, + 1017, 1018, 1028, 1028, 1018, 1029, + 1018, 1019, 1029, 1029, 1019, 1030, + 1019, 1020, 1030, 1030, 1020, 1031, + 1020, 1021, 1031, 1031, 1021, 1032, + 1021, 1022, 1032, 1032, 1022, 1033, + 1023, 1024, 1034, 1034, 1024, 1035, + 1024, 1025, 1035, 1035, 1025, 1036, + 1025, 1026, 1036, 1036, 1026, 1037, + 1026, 1027, 1037, 1037, 1027, 1038, + 1027, 1028, 1038, 1038, 1028, 1039, + 1028, 1029, 1039, 1039, 1029, 1040, + 1029, 1030, 1040, 1040, 1030, 1041, + 1030, 1031, 1041, 1041, 1031, 1042, + 1031, 1032, 1042, 1042, 1032, 1043, + 1032, 1033, 1043, 1043, 1033, 1044, + 1034, 1035, 1045, 1045, 1035, 1046, + 1035, 1036, 1046, 1046, 1036, 1047, + 1036, 1037, 1047, 1047, 1037, 1048, + 1037, 1038, 1048, 1048, 1038, 1049, + 1038, 1039, 1049, 1049, 1039, 1050, + 1039, 1040, 1050, 1050, 1040, 1051, + 1040, 1041, 1051, 1051, 1041, 1052, + 1041, 1042, 1052, 1052, 1042, 1053, + 1042, 1043, 1053, 1053, 1043, 1054, + 1043, 1044, 1054, 1054, 1044, 1055, + 1045, 1046, 1056, 1056, 1046, 1057, + 1046, 1047, 1057, 1057, 1047, 1058, + 1047, 1048, 1058, 1058, 1048, 1059, + 1048, 1049, 1059, 1059, 1049, 1060, + 1049, 1050, 1060, 1060, 1050, 1061, + 1050, 1051, 1061, 1061, 1051, 1062, + 1051, 1052, 1062, 1062, 1052, 1063, + 1052, 1053, 1063, 1063, 1053, 1064, + 1053, 1054, 1064, 1064, 1054, 1065, + 1054, 1055, 1065, 1065, 1055, 1066, + 1056, 1057, 1067, 1067, 1057, 1068, + 1057, 1058, 1068, 1068, 1058, 1069, + 1058, 1059, 1069, 1069, 1059, 1070, + 1059, 1060, 1070, 1070, 1060, 1071, + 1060, 1061, 1071, 1071, 1061, 1072, + 1061, 1062, 1072, 1072, 1062, 1073, + 1062, 1063, 1073, 1073, 1063, 1074, + 1063, 1064, 1074, 1074, 1064, 1075, + 1064, 1065, 1075, 1075, 1065, 1076, + 1065, 1066, 1076, 1076, 1066, 1077, + 1067, 1068, 1078, 1078, 1068, 1079, + 1068, 1069, 1079, 1079, 1069, 1080, + 1069, 1070, 1080, 1080, 1070, 1081, + 1070, 1071, 1081, 1081, 1071, 1082, + 1071, 1072, 1082, 1082, 1072, 1083, + 1072, 1073, 1083, 1083, 1073, 1084, + 1073, 1074, 1084, 1084, 1074, 1085, + 1074, 1075, 1085, 1085, 1075, 1086, + 1075, 1076, 1086, 1086, 1076, 1087, + 1076, 1077, 1087, 1087, 1077, 1088, + 1089, 1090, 1100, 1100, 1090, 1101, + 1090, 1091, 1101, 1101, 1091, 1102, + 1091, 1092, 1102, 1102, 1092, 1103, + 1092, 1093, 1103, 1103, 1093, 1104, + 1093, 1094, 1104, 1104, 1094, 1105, + 1094, 1095, 1105, 1105, 1095, 1106, + 1095, 1096, 1106, 1106, 1096, 1107, + 1096, 1097, 1107, 1107, 1097, 1108, + 1097, 1098, 1108, 1108, 1098, 1109, + 1098, 1099, 1109, 1109, 1099, 1110, + 1100, 1101, 1111, 1111, 1101, 1112, + 1101, 1102, 1112, 1112, 1102, 1113, + 1102, 1103, 1113, 1113, 1103, 1114, + 1103, 1104, 1114, 1114, 1104, 1115, + 1104, 1105, 1115, 1115, 1105, 1116, + 1105, 1106, 1116, 1116, 1106, 1117, + 1106, 1107, 1117, 1117, 1107, 1118, + 1107, 1108, 1118, 1118, 1108, 1119, + 1108, 1109, 1119, 1119, 1109, 1120, + 1109, 1110, 1120, 1120, 1110, 1121, + 1111, 1112, 1122, 1122, 1112, 1123, + 1112, 1113, 1123, 1123, 1113, 1124, + 1113, 1114, 1124, 1124, 1114, 1125, + 1114, 1115, 1125, 1125, 1115, 1126, + 1115, 1116, 1126, 1126, 1116, 1127, + 1116, 1117, 1127, 1127, 1117, 1128, + 1117, 1118, 1128, 1128, 1118, 1129, + 1118, 1119, 1129, 1129, 1119, 1130, + 1119, 1120, 1130, 1130, 1120, 1131, + 1120, 1121, 1131, 1131, 1121, 1132, + 1122, 1123, 1133, 1133, 1123, 1134, + 1123, 1124, 1134, 1134, 1124, 1135, + 1124, 1125, 1135, 1135, 1125, 1136, + 1125, 1126, 1136, 1136, 1126, 1137, + 1126, 1127, 1137, 1137, 1127, 1138, + 1127, 1128, 1138, 1138, 1128, 1139, + 1128, 1129, 1139, 1139, 1129, 1140, + 1129, 1130, 1140, 1140, 1130, 1141, + 1130, 1131, 1141, 1141, 1131, 1142, + 1131, 1132, 1142, 1142, 1132, 1143, + 1133, 1134, 1144, 1144, 1134, 1145, + 1134, 1135, 1145, 1145, 1135, 1146, + 1135, 1136, 1146, 1146, 1136, 1147, + 1136, 1137, 1147, 1147, 1137, 1148, + 1137, 1138, 1148, 1148, 1138, 1149, + 1138, 1139, 1149, 1149, 1139, 1150, + 1139, 1140, 1150, 1150, 1140, 1151, + 1140, 1141, 1151, 1151, 1141, 1152, + 1141, 1142, 1152, 1152, 1142, 1153, + 1142, 1143, 1153, 1153, 1143, 1154, + 1144, 1145, 1155, 1155, 1145, 1156, + 1145, 1146, 1156, 1156, 1146, 1157, + 1146, 1147, 1157, 1157, 1147, 1158, + 1147, 1148, 1158, 1158, 1148, 1159, + 1148, 1149, 1159, 1159, 1149, 1160, + 1149, 1150, 1160, 1160, 1150, 1161, + 1150, 1151, 1161, 1161, 1151, 1162, + 1151, 1152, 1162, 1162, 1152, 1163, + 1152, 1153, 1163, 1163, 1153, 1164, + 1153, 1154, 1164, 1164, 1154, 1165, + 1155, 1156, 1166, 1166, 1156, 1167, + 1156, 1157, 1167, 1167, 1157, 1168, + 1157, 1158, 1168, 1168, 1158, 1169, + 1158, 1159, 1169, 1169, 1159, 1170, + 1159, 1160, 1170, 1170, 1160, 1171, + 1160, 1161, 1171, 1171, 1161, 1172, + 1161, 1162, 1172, 1172, 1162, 1173, + 1162, 1163, 1173, 1173, 1163, 1174, + 1163, 1164, 1174, 1174, 1164, 1175, + 1164, 1165, 1175, 1175, 1165, 1176, + 1166, 1167, 1177, 1177, 1167, 1178, + 1167, 1168, 1178, 1178, 1168, 1179, + 1168, 1169, 1179, 1179, 1169, 1180, + 1169, 1170, 1180, 1180, 1170, 1181, + 1170, 1171, 1181, 1181, 1171, 1182, + 1171, 1172, 1182, 1182, 1172, 1183, + 1172, 1173, 1183, 1183, 1173, 1184, + 1173, 1174, 1184, 1184, 1174, 1185, + 1174, 1175, 1185, 1185, 1175, 1186, + 1175, 1176, 1186, 1186, 1176, 1187, + 1177, 1178, 1188, 1188, 1178, 1189, + 1178, 1179, 1189, 1189, 1179, 1190, + 1179, 1180, 1190, 1190, 1180, 1191, + 1180, 1181, 1191, 1191, 1181, 1192, + 1181, 1182, 1192, 1192, 1182, 1193, + 1182, 1183, 1193, 1193, 1183, 1194, + 1183, 1184, 1194, 1194, 1184, 1195, + 1184, 1185, 1195, 1195, 1185, 1196, + 1185, 1186, 1196, 1196, 1186, 1197, + 1186, 1187, 1197, 1197, 1187, 1198, + 1188, 1189, 1199, 1199, 1189, 1200, + 1189, 1190, 1200, 1200, 1190, 1201, + 1190, 1191, 1201, 1201, 1191, 1202, + 1191, 1192, 1202, 1202, 1192, 1203, + 1192, 1193, 1203, 1203, 1193, 1204, + 1193, 1194, 1204, 1204, 1194, 1205, + 1194, 1195, 1205, 1205, 1195, 1206, + 1195, 1196, 1206, 1206, 1196, 1207, + 1196, 1197, 1207, 1207, 1197, 1208, + 1197, 1198, 1208, 1208, 1198, 1209, + 1210, 1211, 1221, 1221, 1211, 1222, + 1211, 1212, 1222, 1222, 1212, 1223, + 1212, 1213, 1223, 1223, 1213, 1224, + 1213, 1214, 1224, 1224, 1214, 1225, + 1214, 1215, 1225, 1225, 1215, 1226, + 1215, 1216, 1226, 1226, 1216, 1227, + 1216, 1217, 1227, 1227, 1217, 1228, + 1217, 1218, 1228, 1228, 1218, 1229, + 1218, 1219, 1229, 1229, 1219, 1230, + 1219, 1220, 1230, 1230, 1220, 1231, + 1221, 1222, 1232, 1232, 1222, 1233, + 1222, 1223, 1233, 1233, 1223, 1234, + 1223, 1224, 1234, 1234, 1224, 1235, + 1224, 1225, 1235, 1235, 1225, 1236, + 1225, 1226, 1236, 1236, 1226, 1237, + 1226, 1227, 1237, 1237, 1227, 1238, + 1227, 1228, 1238, 1238, 1228, 1239, + 1228, 1229, 1239, 1239, 1229, 1240, + 1229, 1230, 1240, 1240, 1230, 1241, + 1230, 1231, 1241, 1241, 1231, 1242, + 1232, 1233, 1243, 1243, 1233, 1244, + 1233, 1234, 1244, 1244, 1234, 1245, + 1234, 1235, 1245, 1245, 1235, 1246, + 1235, 1236, 1246, 1246, 1236, 1247, + 1236, 1237, 1247, 1247, 1237, 1248, + 1237, 1238, 1248, 1248, 1238, 1249, + 1238, 1239, 1249, 1249, 1239, 1250, + 1239, 1240, 1250, 1250, 1240, 1251, + 1240, 1241, 1251, 1251, 1241, 1252, + 1241, 1242, 1252, 1252, 1242, 1253, + 1243, 1244, 1254, 1254, 1244, 1255, + 1244, 1245, 1255, 1255, 1245, 1256, + 1245, 1246, 1256, 1256, 1246, 1257, + 1246, 1247, 1257, 1257, 1247, 1258, + 1247, 1248, 1258, 1258, 1248, 1259, + 1248, 1249, 1259, 1259, 1249, 1260, + 1249, 1250, 1260, 1260, 1250, 1261, + 1250, 1251, 1261, 1261, 1251, 1262, + 1251, 1252, 1262, 1262, 1252, 1263, + 1252, 1253, 1263, 1263, 1253, 1264, + 1254, 1255, 1265, 1265, 1255, 1266, + 1255, 1256, 1266, 1266, 1256, 1267, + 1256, 1257, 1267, 1267, 1257, 1268, + 1257, 1258, 1268, 1268, 1258, 1269, + 1258, 1259, 1269, 1269, 1259, 1270, + 1259, 1260, 1270, 1270, 1260, 1271, + 1260, 1261, 1271, 1271, 1261, 1272, + 1261, 1262, 1272, 1272, 1262, 1273, + 1262, 1263, 1273, 1273, 1263, 1274, + 1263, 1264, 1274, 1274, 1264, 1275, + 1265, 1266, 1276, 1276, 1266, 1277, + 1266, 1267, 1277, 1277, 1267, 1278, + 1267, 1268, 1278, 1278, 1268, 1279, + 1268, 1269, 1279, 1279, 1269, 1280, + 1269, 1270, 1280, 1280, 1270, 1281, + 1270, 1271, 1281, 1281, 1271, 1282, + 1271, 1272, 1282, 1282, 1272, 1283, + 1272, 1273, 1283, 1283, 1273, 1284, + 1273, 1274, 1284, 1284, 1274, 1285, + 1274, 1275, 1285, 1285, 1275, 1286, + 1276, 1277, 1287, 1287, 1277, 1288, + 1277, 1278, 1288, 1288, 1278, 1289, + 1278, 1279, 1289, 1289, 1279, 1290, + 1279, 1280, 1290, 1290, 1280, 1291, + 1280, 1281, 1291, 1291, 1281, 1292, + 1281, 1282, 1292, 1292, 1282, 1293, + 1282, 1283, 1293, 1293, 1283, 1294, + 1283, 1284, 1294, 1294, 1284, 1295, + 1284, 1285, 1295, 1295, 1285, 1296, + 1285, 1286, 1296, 1296, 1286, 1297, + 1287, 1288, 1298, 1298, 1288, 1299, + 1288, 1289, 1299, 1299, 1289, 1300, + 1289, 1290, 1300, 1300, 1290, 1301, + 1290, 1291, 1301, 1301, 1291, 1302, + 1291, 1292, 1302, 1302, 1292, 1303, + 1292, 1293, 1303, 1303, 1293, 1304, + 1293, 1294, 1304, 1304, 1294, 1305, + 1294, 1295, 1305, 1305, 1295, 1306, + 1295, 1296, 1306, 1306, 1296, 1307, + 1296, 1297, 1307, 1307, 1297, 1308, + 1298, 1299, 1309, 1309, 1299, 1310, + 1299, 1300, 1310, 1310, 1300, 1311, + 1300, 1301, 1311, 1311, 1301, 1312, + 1301, 1302, 1312, 1312, 1302, 1313, + 1302, 1303, 1313, 1313, 1303, 1314, + 1303, 1304, 1314, 1314, 1304, 1315, + 1304, 1305, 1315, 1315, 1305, 1316, + 1305, 1306, 1316, 1316, 1306, 1317, + 1306, 1307, 1317, 1317, 1307, 1318, + 1307, 1308, 1318, 1318, 1308, 1319, + 1309, 1310, 1320, 1320, 1310, 1321, + 1310, 1311, 1321, 1321, 1311, 1322, + 1311, 1312, 1322, 1322, 1312, 1323, + 1312, 1313, 1323, 1323, 1313, 1324, + 1313, 1314, 1324, 1324, 1314, 1325, + 1314, 1315, 1325, 1325, 1315, 1326, + 1315, 1316, 1326, 1326, 1316, 1327, + 1316, 1317, 1327, 1327, 1317, 1328, + 1317, 1318, 1328, 1328, 1318, 1329, + 1318, 1319, 1329, 1329, 1319, 1330, + 1331, 1332, 1342, 1342, 1332, 1343, + 1332, 1333, 1343, 1343, 1333, 1344, + 1333, 1334, 1344, 1344, 1334, 1345, + 1334, 1335, 1345, 1345, 1335, 1346, + 1335, 1336, 1346, 1346, 1336, 1347, + 1336, 1337, 1347, 1347, 1337, 1348, + 1337, 1338, 1348, 1348, 1338, 1349, + 1338, 1339, 1349, 1349, 1339, 1350, + 1339, 1340, 1350, 1350, 1340, 1351, + 1340, 1341, 1351, 1351, 1341, 1352, + 1342, 1343, 1353, 1353, 1343, 1354, + 1343, 1344, 1354, 1354, 1344, 1355, + 1344, 1345, 1355, 1355, 1345, 1356, + 1345, 1346, 1356, 1356, 1346, 1357, + 1346, 1347, 1357, 1357, 1347, 1358, + 1347, 1348, 1358, 1358, 1348, 1359, + 1348, 1349, 1359, 1359, 1349, 1360, + 1349, 1350, 1360, 1360, 1350, 1361, + 1350, 1351, 1361, 1361, 1351, 1362, + 1351, 1352, 1362, 1362, 1352, 1363, + 1353, 1354, 1364, 1364, 1354, 1365, + 1354, 1355, 1365, 1365, 1355, 1366, + 1355, 1356, 1366, 1366, 1356, 1367, + 1356, 1357, 1367, 1367, 1357, 1368, + 1357, 1358, 1368, 1368, 1358, 1369, + 1358, 1359, 1369, 1369, 1359, 1370, + 1359, 1360, 1370, 1370, 1360, 1371, + 1360, 1361, 1371, 1371, 1361, 1372, + 1361, 1362, 1372, 1372, 1362, 1373, + 1362, 1363, 1373, 1373, 1363, 1374, + 1364, 1365, 1375, 1375, 1365, 1376, + 1365, 1366, 1376, 1376, 1366, 1377, + 1366, 1367, 1377, 1377, 1367, 1378, + 1367, 1368, 1378, 1378, 1368, 1379, + 1368, 1369, 1379, 1379, 1369, 1380, + 1369, 1370, 1380, 1380, 1370, 1381, + 1370, 1371, 1381, 1381, 1371, 1382, + 1371, 1372, 1382, 1382, 1372, 1383, + 1372, 1373, 1383, 1383, 1373, 1384, + 1373, 1374, 1384, 1384, 1374, 1385, + 1375, 1376, 1386, 1386, 1376, 1387, + 1376, 1377, 1387, 1387, 1377, 1388, + 1377, 1378, 1388, 1388, 1378, 1389, + 1378, 1379, 1389, 1389, 1379, 1390, + 1379, 1380, 1390, 1390, 1380, 1391, + 1380, 1381, 1391, 1391, 1381, 1392, + 1381, 1382, 1392, 1392, 1382, 1393, + 1382, 1383, 1393, 1393, 1383, 1394, + 1383, 1384, 1394, 1394, 1384, 1395, + 1384, 1385, 1395, 1395, 1385, 1396, + 1386, 1387, 1397, 1397, 1387, 1398, + 1387, 1388, 1398, 1398, 1388, 1399, + 1388, 1389, 1399, 1399, 1389, 1400, + 1389, 1390, 1400, 1400, 1390, 1401, + 1390, 1391, 1401, 1401, 1391, 1402, + 1391, 1392, 1402, 1402, 1392, 1403, + 1392, 1393, 1403, 1403, 1393, 1404, + 1393, 1394, 1404, 1404, 1394, 1405, + 1394, 1395, 1405, 1405, 1395, 1406, + 1395, 1396, 1406, 1406, 1396, 1407, + 1397, 1398, 1408, 1408, 1398, 1409, + 1398, 1399, 1409, 1409, 1399, 1410, + 1399, 1400, 1410, 1410, 1400, 1411, + 1400, 1401, 1411, 1411, 1401, 1412, + 1401, 1402, 1412, 1412, 1402, 1413, + 1402, 1403, 1413, 1413, 1403, 1414, + 1403, 1404, 1414, 1414, 1404, 1415, + 1404, 1405, 1415, 1415, 1405, 1416, + 1405, 1406, 1416, 1416, 1406, 1417, + 1406, 1407, 1417, 1417, 1407, 1418, + 1408, 1409, 1419, 1419, 1409, 1420, + 1409, 1410, 1420, 1420, 1410, 1421, + 1410, 1411, 1421, 1421, 1411, 1422, + 1411, 1412, 1422, 1422, 1412, 1423, + 1412, 1413, 1423, 1423, 1413, 1424, + 1413, 1414, 1424, 1424, 1414, 1425, + 1414, 1415, 1425, 1425, 1415, 1426, + 1415, 1416, 1426, 1426, 1416, 1427, + 1416, 1417, 1427, 1427, 1417, 1428, + 1417, 1418, 1428, 1428, 1418, 1429, + 1419, 1420, 1430, 1430, 1420, 1431, + 1420, 1421, 1431, 1431, 1421, 1432, + 1421, 1422, 1432, 1432, 1422, 1433, + 1422, 1423, 1433, 1433, 1423, 1434, + 1423, 1424, 1434, 1434, 1424, 1435, + 1424, 1425, 1435, 1435, 1425, 1436, + 1425, 1426, 1436, 1436, 1426, 1437, + 1426, 1427, 1437, 1437, 1427, 1438, + 1427, 1428, 1438, 1438, 1428, 1439, + 1428, 1429, 1439, 1439, 1429, 1440, + 1430, 1431, 1441, 1441, 1431, 1442, + 1431, 1432, 1442, 1442, 1432, 1443, + 1432, 1433, 1443, 1443, 1433, 1444, + 1433, 1434, 1444, 1444, 1434, 1445, + 1434, 1435, 1445, 1445, 1435, 1446, + 1435, 1436, 1446, 1446, 1436, 1447, + 1436, 1437, 1447, 1447, 1437, 1448, + 1437, 1438, 1448, 1448, 1438, 1449, + 1438, 1439, 1449, 1449, 1439, 1450, + 1439, 1440, 1450, 1450, 1440, 1451, + 1452, 1453, 1463, 1463, 1453, 1464, + 1453, 1454, 1464, 1464, 1454, 1465, + 1454, 1455, 1465, 1465, 1455, 1466, + 1455, 1456, 1466, 1466, 1456, 1467, + 1456, 1457, 1467, 1467, 1457, 1468, + 1457, 1458, 1468, 1468, 1458, 1469, + 1458, 1459, 1469, 1469, 1459, 1470, + 1459, 1460, 1470, 1470, 1460, 1471, + 1460, 1461, 1471, 1471, 1461, 1472, + 1461, 1462, 1472, 1472, 1462, 1473, + 1463, 1464, 1474, 1474, 1464, 1475, + 1464, 1465, 1475, 1475, 1465, 1476, + 1465, 1466, 1476, 1476, 1466, 1477, + 1466, 1467, 1477, 1477, 1467, 1478, + 1467, 1468, 1478, 1478, 1468, 1479, + 1468, 1469, 1479, 1479, 1469, 1480, + 1469, 1470, 1480, 1480, 1470, 1481, + 1470, 1471, 1481, 1481, 1471, 1482, + 1471, 1472, 1482, 1482, 1472, 1483, + 1472, 1473, 1483, 1483, 1473, 1484, + 1474, 1475, 1485, 1485, 1475, 1486, + 1475, 1476, 1486, 1486, 1476, 1487, + 1476, 1477, 1487, 1487, 1477, 1488, + 1477, 1478, 1488, 1488, 1478, 1489, + 1478, 1479, 1489, 1489, 1479, 1490, + 1479, 1480, 1490, 1490, 1480, 1491, + 1480, 1481, 1491, 1491, 1481, 1492, + 1481, 1482, 1492, 1492, 1482, 1493, + 1482, 1483, 1493, 1493, 1483, 1494, + 1483, 1484, 1494, 1494, 1484, 1495, + 1485, 1486, 1496, 1496, 1486, 1497, + 1486, 1487, 1497, 1497, 1487, 1498, + 1487, 1488, 1498, 1498, 1488, 1499, + 1488, 1489, 1499, 1499, 1489, 1500, + 1489, 1490, 1500, 1500, 1490, 1501, + 1490, 1491, 1501, 1501, 1491, 1502, + 1491, 1492, 1502, 1502, 1492, 1503, + 1492, 1493, 1503, 1503, 1493, 1504, + 1493, 1494, 1504, 1504, 1494, 1505, + 1494, 1495, 1505, 1505, 1495, 1506, + 1496, 1497, 1507, 1507, 1497, 1508, + 1497, 1498, 1508, 1508, 1498, 1509, + 1498, 1499, 1509, 1509, 1499, 1510, + 1499, 1500, 1510, 1510, 1500, 1511, + 1500, 1501, 1511, 1511, 1501, 1512, + 1501, 1502, 1512, 1512, 1502, 1513, + 1502, 1503, 1513, 1513, 1503, 1514, + 1503, 1504, 1514, 1514, 1504, 1515, + 1504, 1505, 1515, 1515, 1505, 1516, + 1505, 1506, 1516, 1516, 1506, 1517, + 1507, 1508, 1518, 1518, 1508, 1519, + 1508, 1509, 1519, 1519, 1509, 1520, + 1509, 1510, 1520, 1520, 1510, 1521, + 1510, 1511, 1521, 1521, 1511, 1522, + 1511, 1512, 1522, 1522, 1512, 1523, + 1512, 1513, 1523, 1523, 1513, 1524, + 1513, 1514, 1524, 1524, 1514, 1525, + 1514, 1515, 1525, 1525, 1515, 1526, + 1515, 1516, 1526, 1526, 1516, 1527, + 1516, 1517, 1527, 1527, 1517, 1528, + 1518, 1519, 1529, 1529, 1519, 1530, + 1519, 1520, 1530, 1530, 1520, 1531, + 1520, 1521, 1531, 1531, 1521, 1532, + 1521, 1522, 1532, 1532, 1522, 1533, + 1522, 1523, 1533, 1533, 1523, 1534, + 1523, 1524, 1534, 1534, 1524, 1535, + 1524, 1525, 1535, 1535, 1525, 1536, + 1525, 1526, 1536, 1536, 1526, 1537, + 1526, 1527, 1537, 1537, 1527, 1538, + 1527, 1528, 1538, 1538, 1528, 1539, + 1529, 1530, 1540, 1540, 1530, 1541, + 1530, 1531, 1541, 1541, 1531, 1542, + 1531, 1532, 1542, 1542, 1532, 1543, + 1532, 1533, 1543, 1543, 1533, 1544, + 1533, 1534, 1544, 1544, 1534, 1545, + 1534, 1535, 1545, 1545, 1535, 1546, + 1535, 1536, 1546, 1546, 1536, 1547, + 1536, 1537, 1547, 1547, 1537, 1548, + 1537, 1538, 1548, 1548, 1538, 1549, + 1538, 1539, 1549, 1549, 1539, 1550, + 1540, 1541, 1551, 1551, 1541, 1552, + 1541, 1542, 1552, 1552, 1542, 1553, + 1542, 1543, 1553, 1553, 1543, 1554, + 1543, 1544, 1554, 1554, 1544, 1555, + 1544, 1545, 1555, 1555, 1545, 1556, + 1545, 1546, 1556, 1556, 1546, 1557, + 1546, 1547, 1557, 1557, 1547, 1558, + 1547, 1548, 1558, 1558, 1548, 1559, + 1548, 1549, 1559, 1559, 1549, 1560, + 1549, 1550, 1560, 1560, 1550, 1561, + 1551, 1552, 1562, 1562, 1552, 1563, + 1552, 1553, 1563, 1563, 1553, 1564, + 1553, 1554, 1564, 1564, 1554, 1565, + 1554, 1555, 1565, 1565, 1555, 1566, + 1555, 1556, 1566, 1566, 1556, 1567, + 1556, 1557, 1567, 1567, 1557, 1568, + 1557, 1558, 1568, 1568, 1558, 1569, + 1558, 1559, 1569, 1569, 1559, 1570, + 1559, 1560, 1570, 1570, 1560, 1571, + 1560, 1561, 1571, 1571, 1561, 1572, + 1573, 1574, 1584, 1584, 1574, 1585, + 1574, 1575, 1585, 1585, 1575, 1586, + 1575, 1576, 1586, 1586, 1576, 1587, + 1576, 1577, 1587, 1587, 1577, 1588, + 1577, 1578, 1588, 1588, 1578, 1589, + 1578, 1579, 1589, 1589, 1579, 1590, + 1579, 1580, 1590, 1590, 1580, 1591, + 1580, 1581, 1591, 1591, 1581, 1592, + 1581, 1582, 1592, 1592, 1582, 1593, + 1582, 1583, 1593, 1593, 1583, 1594, + 1584, 1585, 1595, 1595, 1585, 1596, + 1585, 1586, 1596, 1596, 1586, 1597, + 1586, 1587, 1597, 1597, 1587, 1598, + 1587, 1588, 1598, 1598, 1588, 1599, + 1588, 1589, 1599, 1599, 1589, 1600, + 1589, 1590, 1600, 1600, 1590, 1601, + 1590, 1591, 1601, 1601, 1591, 1602, + 1591, 1592, 1602, 1602, 1592, 1603, + 1592, 1593, 1603, 1603, 1593, 1604, + 1593, 1594, 1604, 1604, 1594, 1605, + 1595, 1596, 1606, 1606, 1596, 1607, + 1596, 1597, 1607, 1607, 1597, 1608, + 1597, 1598, 1608, 1608, 1598, 1609, + 1598, 1599, 1609, 1609, 1599, 1610, + 1599, 1600, 1610, 1610, 1600, 1611, + 1600, 1601, 1611, 1611, 1601, 1612, + 1601, 1602, 1612, 1612, 1602, 1613, + 1602, 1603, 1613, 1613, 1603, 1614, + 1603, 1604, 1614, 1614, 1604, 1615, + 1604, 1605, 1615, 1615, 1605, 1616, + 1606, 1607, 1617, 1617, 1607, 1618, + 1607, 1608, 1618, 1618, 1608, 1619, + 1608, 1609, 1619, 1619, 1609, 1620, + 1609, 1610, 1620, 1620, 1610, 1621, + 1610, 1611, 1621, 1621, 1611, 1622, + 1611, 1612, 1622, 1622, 1612, 1623, + 1612, 1613, 1623, 1623, 1613, 1624, + 1613, 1614, 1624, 1624, 1614, 1625, + 1614, 1615, 1625, 1625, 1615, 1626, + 1615, 1616, 1626, 1626, 1616, 1627, + 1617, 1618, 1628, 1628, 1618, 1629, + 1618, 1619, 1629, 1629, 1619, 1630, + 1619, 1620, 1630, 1630, 1620, 1631, + 1620, 1621, 1631, 1631, 1621, 1632, + 1621, 1622, 1632, 1632, 1622, 1633, + 1622, 1623, 1633, 1633, 1623, 1634, + 1623, 1624, 1634, 1634, 1624, 1635, + 1624, 1625, 1635, 1635, 1625, 1636, + 1625, 1626, 1636, 1636, 1626, 1637, + 1626, 1627, 1637, 1637, 1627, 1638, + 1628, 1629, 1639, 1639, 1629, 1640, + 1629, 1630, 1640, 1640, 1630, 1641, + 1630, 1631, 1641, 1641, 1631, 1642, + 1631, 1632, 1642, 1642, 1632, 1643, + 1632, 1633, 1643, 1643, 1633, 1644, + 1633, 1634, 1644, 1644, 1634, 1645, + 1634, 1635, 1645, 1645, 1635, 1646, + 1635, 1636, 1646, 1646, 1636, 1647, + 1636, 1637, 1647, 1647, 1637, 1648, + 1637, 1638, 1648, 1648, 1638, 1649, + 1639, 1640, 1650, 1650, 1640, 1651, + 1640, 1641, 1651, 1651, 1641, 1652, + 1641, 1642, 1652, 1652, 1642, 1653, + 1642, 1643, 1653, 1653, 1643, 1654, + 1643, 1644, 1654, 1654, 1644, 1655, + 1644, 1645, 1655, 1655, 1645, 1656, + 1645, 1646, 1656, 1656, 1646, 1657, + 1646, 1647, 1657, 1657, 1647, 1658, + 1647, 1648, 1658, 1658, 1648, 1659, + 1648, 1649, 1659, 1659, 1649, 1660, + 1650, 1651, 1661, 1661, 1651, 1662, + 1651, 1652, 1662, 1662, 1652, 1663, + 1652, 1653, 1663, 1663, 1653, 1664, + 1653, 1654, 1664, 1664, 1654, 1665, + 1654, 1655, 1665, 1665, 1655, 1666, + 1655, 1656, 1666, 1666, 1656, 1667, + 1656, 1657, 1667, 1667, 1657, 1668, + 1657, 1658, 1668, 1668, 1658, 1669, + 1658, 1659, 1669, 1669, 1659, 1670, + 1659, 1660, 1670, 1670, 1660, 1671, + 1661, 1662, 1672, 1672, 1662, 1673, + 1662, 1663, 1673, 1673, 1663, 1674, + 1663, 1664, 1674, 1674, 1664, 1675, + 1664, 1665, 1675, 1675, 1665, 1676, + 1665, 1666, 1676, 1676, 1666, 1677, + 1666, 1667, 1677, 1677, 1667, 1678, + 1667, 1668, 1678, 1678, 1668, 1679, + 1668, 1669, 1679, 1679, 1669, 1680, + 1669, 1670, 1680, 1680, 1670, 1681, + 1670, 1671, 1681, 1681, 1671, 1682, + 1672, 1673, 1683, 1683, 1673, 1684, + 1673, 1674, 1684, 1684, 1674, 1685, + 1674, 1675, 1685, 1685, 1675, 1686, + 1675, 1676, 1686, 1686, 1676, 1687, + 1676, 1677, 1687, 1687, 1677, 1688, + 1677, 1678, 1688, 1688, 1678, 1689, + 1678, 1679, 1689, 1689, 1679, 1690, + 1679, 1680, 1690, 1690, 1680, 1691, + 1680, 1681, 1691, 1691, 1681, 1692, + 1681, 1682, 1692, 1692, 1682, 1693, + 1694, 1695, 1705, 1705, 1695, 1706, + 1695, 1696, 1706, 1706, 1696, 1707, + 1696, 1697, 1707, 1707, 1697, 1708, + 1697, 1698, 1708, 1708, 1698, 1709, + 1698, 1699, 1709, 1709, 1699, 1710, + 1699, 1700, 1710, 1710, 1700, 1711, + 1700, 1701, 1711, 1711, 1701, 1712, + 1701, 1702, 1712, 1712, 1702, 1713, + 1702, 1703, 1713, 1713, 1703, 1714, + 1703, 1704, 1714, 1714, 1704, 1715, + 1705, 1706, 1716, 1716, 1706, 1717, + 1706, 1707, 1717, 1717, 1707, 1718, + 1707, 1708, 1718, 1718, 1708, 1719, + 1708, 1709, 1719, 1719, 1709, 1720, + 1709, 1710, 1720, 1720, 1710, 1721, + 1710, 1711, 1721, 1721, 1711, 1722, + 1711, 1712, 1722, 1722, 1712, 1723, + 1712, 1713, 1723, 1723, 1713, 1724, + 1713, 1714, 1724, 1724, 1714, 1725, + 1714, 1715, 1725, 1725, 1715, 1726, + 1716, 1717, 1727, 1727, 1717, 1728, + 1717, 1718, 1728, 1728, 1718, 1729, + 1718, 1719, 1729, 1729, 1719, 1730, + 1719, 1720, 1730, 1730, 1720, 1731, + 1720, 1721, 1731, 1731, 1721, 1732, + 1721, 1722, 1732, 1732, 1722, 1733, + 1722, 1723, 1733, 1733, 1723, 1734, + 1723, 1724, 1734, 1734, 1724, 1735, + 1724, 1725, 1735, 1735, 1725, 1736, + 1725, 1726, 1736, 1736, 1726, 1737, + 1727, 1728, 1738, 1738, 1728, 1739, + 1728, 1729, 1739, 1739, 1729, 1740, + 1729, 1730, 1740, 1740, 1730, 1741, + 1730, 1731, 1741, 1741, 1731, 1742, + 1731, 1732, 1742, 1742, 1732, 1743, + 1732, 1733, 1743, 1743, 1733, 1744, + 1733, 1734, 1744, 1744, 1734, 1745, + 1734, 1735, 1745, 1745, 1735, 1746, + 1735, 1736, 1746, 1746, 1736, 1747, + 1736, 1737, 1747, 1747, 1737, 1748, + 1738, 1739, 1749, 1749, 1739, 1750, + 1739, 1740, 1750, 1750, 1740, 1751, + 1740, 1741, 1751, 1751, 1741, 1752, + 1741, 1742, 1752, 1752, 1742, 1753, + 1742, 1743, 1753, 1753, 1743, 1754, + 1743, 1744, 1754, 1754, 1744, 1755, + 1744, 1745, 1755, 1755, 1745, 1756, + 1745, 1746, 1756, 1756, 1746, 1757, + 1746, 1747, 1757, 1757, 1747, 1758, + 1747, 1748, 1758, 1758, 1748, 1759, + 1749, 1750, 1760, 1760, 1750, 1761, + 1750, 1751, 1761, 1761, 1751, 1762, + 1751, 1752, 1762, 1762, 1752, 1763, + 1752, 1753, 1763, 1763, 1753, 1764, + 1753, 1754, 1764, 1764, 1754, 1765, + 1754, 1755, 1765, 1765, 1755, 1766, + 1755, 1756, 1766, 1766, 1756, 1767, + 1756, 1757, 1767, 1767, 1757, 1768, + 1757, 1758, 1768, 1768, 1758, 1769, + 1758, 1759, 1769, 1769, 1759, 1770, + 1760, 1761, 1771, 1771, 1761, 1772, + 1761, 1762, 1772, 1772, 1762, 1773, + 1762, 1763, 1773, 1773, 1763, 1774, + 1763, 1764, 1774, 1774, 1764, 1775, + 1764, 1765, 1775, 1775, 1765, 1776, + 1765, 1766, 1776, 1776, 1766, 1777, + 1766, 1767, 1777, 1777, 1767, 1778, + 1767, 1768, 1778, 1778, 1768, 1779, + 1768, 1769, 1779, 1779, 1769, 1780, + 1769, 1770, 1780, 1780, 1770, 1781, + 1771, 1772, 1782, 1782, 1772, 1783, + 1772, 1773, 1783, 1783, 1773, 1784, + 1773, 1774, 1784, 1784, 1774, 1785, + 1774, 1775, 1785, 1785, 1775, 1786, + 1775, 1776, 1786, 1786, 1776, 1787, + 1776, 1777, 1787, 1787, 1777, 1788, + 1777, 1778, 1788, 1788, 1778, 1789, + 1778, 1779, 1789, 1789, 1779, 1790, + 1779, 1780, 1790, 1790, 1780, 1791, + 1780, 1781, 1791, 1791, 1781, 1792, + 1782, 1783, 1793, 1793, 1783, 1794, + 1783, 1784, 1794, 1794, 1784, 1795, + 1784, 1785, 1795, 1795, 1785, 1796, + 1785, 1786, 1796, 1796, 1786, 1797, + 1786, 1787, 1797, 1797, 1787, 1798, + 1787, 1788, 1798, 1798, 1788, 1799, + 1788, 1789, 1799, 1799, 1789, 1800, + 1789, 1790, 1800, 1800, 1790, 1801, + 1790, 1791, 1801, 1801, 1791, 1802, + 1791, 1792, 1802, 1802, 1792, 1803, + 1793, 1794, 1804, 1804, 1794, 1805, + 1794, 1795, 1805, 1805, 1795, 1806, + 1795, 1796, 1806, 1806, 1796, 1807, + 1796, 1797, 1807, 1807, 1797, 1808, + 1797, 1798, 1808, 1808, 1798, 1809, + 1798, 1799, 1809, 1809, 1799, 1810, + 1799, 1800, 1810, 1810, 1800, 1811, + 1800, 1801, 1811, 1811, 1801, 1812, + 1801, 1802, 1812, 1812, 1802, 1813, + 1802, 1803, 1813, 1813, 1803, 1814, + 1815, 1816, 1826, 1826, 1816, 1827, + 1816, 1817, 1827, 1827, 1817, 1828, + 1817, 1818, 1828, 1828, 1818, 1829, + 1818, 1819, 1829, 1829, 1819, 1830, + 1819, 1820, 1830, 1830, 1820, 1831, + 1820, 1821, 1831, 1831, 1821, 1832, + 1821, 1822, 1832, 1832, 1822, 1833, + 1822, 1823, 1833, 1833, 1823, 1834, + 1823, 1824, 1834, 1834, 1824, 1835, + 1824, 1825, 1835, 1835, 1825, 1836, + 1826, 1827, 1837, 1837, 1827, 1838, + 1827, 1828, 1838, 1838, 1828, 1839, + 1828, 1829, 1839, 1839, 1829, 1840, + 1829, 1830, 1840, 1840, 1830, 1841, + 1830, 1831, 1841, 1841, 1831, 1842, + 1831, 1832, 1842, 1842, 1832, 1843, + 1832, 1833, 1843, 1843, 1833, 1844, + 1833, 1834, 1844, 1844, 1834, 1845, + 1834, 1835, 1845, 1845, 1835, 1846, + 1835, 1836, 1846, 1846, 1836, 1847, + 1837, 1838, 1848, 1848, 1838, 1849, + 1838, 1839, 1849, 1849, 1839, 1850, + 1839, 1840, 1850, 1850, 1840, 1851, + 1840, 1841, 1851, 1851, 1841, 1852, + 1841, 1842, 1852, 1852, 1842, 1853, + 1842, 1843, 1853, 1853, 1843, 1854, + 1843, 1844, 1854, 1854, 1844, 1855, + 1844, 1845, 1855, 1855, 1845, 1856, + 1845, 1846, 1856, 1856, 1846, 1857, + 1846, 1847, 1857, 1857, 1847, 1858, + 1848, 1849, 1859, 1859, 1849, 1860, + 1849, 1850, 1860, 1860, 1850, 1861, + 1850, 1851, 1861, 1861, 1851, 1862, + 1851, 1852, 1862, 1862, 1852, 1863, + 1852, 1853, 1863, 1863, 1853, 1864, + 1853, 1854, 1864, 1864, 1854, 1865, + 1854, 1855, 1865, 1865, 1855, 1866, + 1855, 1856, 1866, 1866, 1856, 1867, + 1856, 1857, 1867, 1867, 1857, 1868, + 1857, 1858, 1868, 1868, 1858, 1869, + 1859, 1860, 1870, 1870, 1860, 1871, + 1860, 1861, 1871, 1871, 1861, 1872, + 1861, 1862, 1872, 1872, 1862, 1873, + 1862, 1863, 1873, 1873, 1863, 1874, + 1863, 1864, 1874, 1874, 1864, 1875, + 1864, 1865, 1875, 1875, 1865, 1876, + 1865, 1866, 1876, 1876, 1866, 1877, + 1866, 1867, 1877, 1877, 1867, 1878, + 1867, 1868, 1878, 1878, 1868, 1879, + 1868, 1869, 1879, 1879, 1869, 1880, + 1870, 1871, 1881, 1881, 1871, 1882, + 1871, 1872, 1882, 1882, 1872, 1883, + 1872, 1873, 1883, 1883, 1873, 1884, + 1873, 1874, 1884, 1884, 1874, 1885, + 1874, 1875, 1885, 1885, 1875, 1886, + 1875, 1876, 1886, 1886, 1876, 1887, + 1876, 1877, 1887, 1887, 1877, 1888, + 1877, 1878, 1888, 1888, 1878, 1889, + 1878, 1879, 1889, 1889, 1879, 1890, + 1879, 1880, 1890, 1890, 1880, 1891, + 1881, 1882, 1892, 1892, 1882, 1893, + 1882, 1883, 1893, 1893, 1883, 1894, + 1883, 1884, 1894, 1894, 1884, 1895, + 1884, 1885, 1895, 1895, 1885, 1896, + 1885, 1886, 1896, 1896, 1886, 1897, + 1886, 1887, 1897, 1897, 1887, 1898, + 1887, 1888, 1898, 1898, 1888, 1899, + 1888, 1889, 1899, 1899, 1889, 1900, + 1889, 1890, 1900, 1900, 1890, 1901, + 1890, 1891, 1901, 1901, 1891, 1902, + 1892, 1893, 1903, 1903, 1893, 1904, + 1893, 1894, 1904, 1904, 1894, 1905, + 1894, 1895, 1905, 1905, 1895, 1906, + 1895, 1896, 1906, 1906, 1896, 1907, + 1896, 1897, 1907, 1907, 1897, 1908, + 1897, 1898, 1908, 1908, 1898, 1909, + 1898, 1899, 1909, 1909, 1899, 1910, + 1899, 1900, 1910, 1910, 1900, 1911, + 1900, 1901, 1911, 1911, 1901, 1912, + 1901, 1902, 1912, 1912, 1902, 1913, + 1903, 1904, 1914, 1914, 1904, 1915, + 1904, 1905, 1915, 1915, 1905, 1916, + 1905, 1906, 1916, 1916, 1906, 1917, + 1906, 1907, 1917, 1917, 1907, 1918, + 1907, 1908, 1918, 1918, 1908, 1919, + 1908, 1909, 1919, 1919, 1909, 1920, + 1909, 1910, 1920, 1920, 1910, 1921, + 1910, 1911, 1921, 1921, 1911, 1922, + 1911, 1912, 1922, 1922, 1912, 1923, + 1912, 1913, 1923, 1923, 1913, 1924, + 1914, 1915, 1925, 1925, 1915, 1926, + 1915, 1916, 1926, 1926, 1916, 1927, + 1916, 1917, 1927, 1927, 1917, 1928, + 1917, 1918, 1928, 1928, 1918, 1929, + 1918, 1919, 1929, 1929, 1919, 1930, + 1919, 1920, 1930, 1930, 1920, 1931, + 1920, 1921, 1931, 1931, 1921, 1932, + 1921, 1922, 1932, 1932, 1922, 1933, + 1922, 1923, 1933, 1933, 1923, 1934, + 1923, 1924, 1934, 1934, 1924, 1935, + 1936, 1937, 1947, 1947, 1937, 1948, + 1937, 1938, 1948, 1948, 1938, 1949, + 1938, 1939, 1949, 1949, 1939, 1950, + 1939, 1940, 1950, 1950, 1940, 1951, + 1940, 1941, 1951, 1951, 1941, 1952, + 1941, 1942, 1952, 1952, 1942, 1953, + 1942, 1943, 1953, 1953, 1943, 1954, + 1943, 1944, 1954, 1954, 1944, 1955, + 1944, 1945, 1955, 1955, 1945, 1956, + 1945, 1946, 1956, 1956, 1946, 1957, + 1947, 1948, 1958, 1958, 1948, 1959, + 1948, 1949, 1959, 1959, 1949, 1960, + 1949, 1950, 1960, 1960, 1950, 1961, + 1950, 1951, 1961, 1961, 1951, 1962, + 1951, 1952, 1962, 1962, 1952, 1963, + 1952, 1953, 1963, 1963, 1953, 1964, + 1953, 1954, 1964, 1964, 1954, 1965, + 1954, 1955, 1965, 1965, 1955, 1966, + 1955, 1956, 1966, 1966, 1956, 1967, + 1956, 1957, 1967, 1967, 1957, 1968, + 1958, 1959, 1969, 1969, 1959, 1970, + 1959, 1960, 1970, 1970, 1960, 1971, + 1960, 1961, 1971, 1971, 1961, 1972, + 1961, 1962, 1972, 1972, 1962, 1973, + 1962, 1963, 1973, 1973, 1963, 1974, + 1963, 1964, 1974, 1974, 1964, 1975, + 1964, 1965, 1975, 1975, 1965, 1976, + 1965, 1966, 1976, 1976, 1966, 1977, + 1966, 1967, 1977, 1977, 1967, 1978, + 1967, 1968, 1978, 1978, 1968, 1979, + 1969, 1970, 1980, 1980, 1970, 1981, + 1970, 1971, 1981, 1981, 1971, 1982, + 1971, 1972, 1982, 1982, 1972, 1983, + 1972, 1973, 1983, 1983, 1973, 1984, + 1973, 1974, 1984, 1984, 1974, 1985, + 1974, 1975, 1985, 1985, 1975, 1986, + 1975, 1976, 1986, 1986, 1976, 1987, + 1976, 1977, 1987, 1987, 1977, 1988, + 1977, 1978, 1988, 1988, 1978, 1989, + 1978, 1979, 1989, 1989, 1979, 1990, + 1980, 1981, 1991, 1991, 1981, 1992, + 1981, 1982, 1992, 1992, 1982, 1993, + 1982, 1983, 1993, 1993, 1983, 1994, + 1983, 1984, 1994, 1994, 1984, 1995, + 1984, 1985, 1995, 1995, 1985, 1996, + 1985, 1986, 1996, 1996, 1986, 1997, + 1986, 1987, 1997, 1997, 1987, 1998, + 1987, 1988, 1998, 1998, 1988, 1999, + 1988, 1989, 1999, 1999, 1989, 2000, + 1989, 1990, 2000, 2000, 1990, 2001, + 1991, 1992, 2002, 2002, 1992, 2003, + 1992, 1993, 2003, 2003, 1993, 2004, + 1993, 1994, 2004, 2004, 1994, 2005, + 1994, 1995, 2005, 2005, 1995, 2006, + 1995, 1996, 2006, 2006, 1996, 2007, + 1996, 1997, 2007, 2007, 1997, 2008, + 1997, 1998, 2008, 2008, 1998, 2009, + 1998, 1999, 2009, 2009, 1999, 2010, + 1999, 2000, 2010, 2010, 2000, 2011, + 2000, 2001, 2011, 2011, 2001, 2012, + 2002, 2003, 2013, 2013, 2003, 2014, + 2003, 2004, 2014, 2014, 2004, 2015, + 2004, 2005, 2015, 2015, 2005, 2016, + 2005, 2006, 2016, 2016, 2006, 2017, + 2006, 2007, 2017, 2017, 2007, 2018, + 2007, 2008, 2018, 2018, 2008, 2019, + 2008, 2009, 2019, 2019, 2009, 2020, + 2009, 2010, 2020, 2020, 2010, 2021, + 2010, 2011, 2021, 2021, 2011, 2022, + 2011, 2012, 2022, 2022, 2012, 2023, + 2013, 2014, 2024, 2024, 2014, 2025, + 2014, 2015, 2025, 2025, 2015, 2026, + 2015, 2016, 2026, 2026, 2016, 2027, + 2016, 2017, 2027, 2027, 2017, 2028, + 2017, 2018, 2028, 2028, 2018, 2029, + 2018, 2019, 2029, 2029, 2019, 2030, + 2019, 2020, 2030, 2030, 2020, 2031, + 2020, 2021, 2031, 2031, 2021, 2032, + 2021, 2022, 2032, 2032, 2022, 2033, + 2022, 2023, 2033, 2033, 2023, 2034, + 2024, 2025, 2035, 2035, 2025, 2036, + 2025, 2026, 2036, 2036, 2026, 2037, + 2026, 2027, 2037, 2037, 2027, 2038, + 2027, 2028, 2038, 2038, 2028, 2039, + 2028, 2029, 2039, 2039, 2029, 2040, + 2029, 2030, 2040, 2040, 2030, 2041, + 2030, 2031, 2041, 2041, 2031, 2042, + 2031, 2032, 2042, 2042, 2032, 2043, + 2032, 2033, 2043, 2043, 2033, 2044, + 2033, 2034, 2044, 2044, 2034, 2045, + 2035, 2036, 2046, 2046, 2036, 2047, + 2036, 2037, 2047, 2047, 2037, 2048, + 2037, 2038, 2048, 2048, 2038, 2049, + 2038, 2039, 2049, 2049, 2039, 2050, + 2039, 2040, 2050, 2050, 2040, 2051, + 2040, 2041, 2051, 2051, 2041, 2052, + 2041, 2042, 2052, 2052, 2042, 2053, + 2042, 2043, 2053, 2053, 2043, 2054, + 2043, 2044, 2054, 2054, 2044, 2055, + 2044, 2045, 2055, 2055, 2045, 2056, + 2057, 2058, 2068, 2068, 2058, 2069, + 2058, 2059, 2069, 2069, 2059, 2070, + 2059, 2060, 2070, 2070, 2060, 2071, + 2060, 2061, 2071, 2071, 2061, 2072, + 2061, 2062, 2072, 2072, 2062, 2073, + 2062, 2063, 2073, 2073, 2063, 2074, + 2063, 2064, 2074, 2074, 2064, 2075, + 2064, 2065, 2075, 2075, 2065, 2076, + 2065, 2066, 2076, 2076, 2066, 2077, + 2066, 2067, 2077, 2077, 2067, 2078, + 2068, 2069, 2079, 2079, 2069, 2080, + 2069, 2070, 2080, 2080, 2070, 2081, + 2070, 2071, 2081, 2081, 2071, 2082, + 2071, 2072, 2082, 2082, 2072, 2083, + 2072, 2073, 2083, 2083, 2073, 2084, + 2073, 2074, 2084, 2084, 2074, 2085, + 2074, 2075, 2085, 2085, 2075, 2086, + 2075, 2076, 2086, 2086, 2076, 2087, + 2076, 2077, 2087, 2087, 2077, 2088, + 2077, 2078, 2088, 2088, 2078, 2089, + 2079, 2080, 2090, 2090, 2080, 2091, + 2080, 2081, 2091, 2091, 2081, 2092, + 2081, 2082, 2092, 2092, 2082, 2093, + 2082, 2083, 2093, 2093, 2083, 2094, + 2083, 2084, 2094, 2094, 2084, 2095, + 2084, 2085, 2095, 2095, 2085, 2096, + 2085, 2086, 2096, 2096, 2086, 2097, + 2086, 2087, 2097, 2097, 2087, 2098, + 2087, 2088, 2098, 2098, 2088, 2099, + 2088, 2089, 2099, 2099, 2089, 2100, + 2090, 2091, 2101, 2101, 2091, 2102, + 2091, 2092, 2102, 2102, 2092, 2103, + 2092, 2093, 2103, 2103, 2093, 2104, + 2093, 2094, 2104, 2104, 2094, 2105, + 2094, 2095, 2105, 2105, 2095, 2106, + 2095, 2096, 2106, 2106, 2096, 2107, + 2096, 2097, 2107, 2107, 2097, 2108, + 2097, 2098, 2108, 2108, 2098, 2109, + 2098, 2099, 2109, 2109, 2099, 2110, + 2099, 2100, 2110, 2110, 2100, 2111, + 2101, 2102, 2112, 2112, 2102, 2113, + 2102, 2103, 2113, 2113, 2103, 2114, + 2103, 2104, 2114, 2114, 2104, 2115, + 2104, 2105, 2115, 2115, 2105, 2116, + 2105, 2106, 2116, 2116, 2106, 2117, + 2106, 2107, 2117, 2117, 2107, 2118, + 2107, 2108, 2118, 2118, 2108, 2119, + 2108, 2109, 2119, 2119, 2109, 2120, + 2109, 2110, 2120, 2120, 2110, 2121, + 2110, 2111, 2121, 2121, 2111, 2122, + 2112, 2113, 2123, 2123, 2113, 2124, + 2113, 2114, 2124, 2124, 2114, 2125, + 2114, 2115, 2125, 2125, 2115, 2126, + 2115, 2116, 2126, 2126, 2116, 2127, + 2116, 2117, 2127, 2127, 2117, 2128, + 2117, 2118, 2128, 2128, 2118, 2129, + 2118, 2119, 2129, 2129, 2119, 2130, + 2119, 2120, 2130, 2130, 2120, 2131, + 2120, 2121, 2131, 2131, 2121, 2132, + 2121, 2122, 2132, 2132, 2122, 2133, + 2123, 2124, 2134, 2134, 2124, 2135, + 2124, 2125, 2135, 2135, 2125, 2136, + 2125, 2126, 2136, 2136, 2126, 2137, + 2126, 2127, 2137, 2137, 2127, 2138, + 2127, 2128, 2138, 2138, 2128, 2139, + 2128, 2129, 2139, 2139, 2129, 2140, + 2129, 2130, 2140, 2140, 2130, 2141, + 2130, 2131, 2141, 2141, 2131, 2142, + 2131, 2132, 2142, 2142, 2132, 2143, + 2132, 2133, 2143, 2143, 2133, 2144, + 2134, 2135, 2145, 2145, 2135, 2146, + 2135, 2136, 2146, 2146, 2136, 2147, + 2136, 2137, 2147, 2147, 2137, 2148, + 2137, 2138, 2148, 2148, 2138, 2149, + 2138, 2139, 2149, 2149, 2139, 2150, + 2139, 2140, 2150, 2150, 2140, 2151, + 2140, 2141, 2151, 2151, 2141, 2152, + 2141, 2142, 2152, 2152, 2142, 2153, + 2142, 2143, 2153, 2153, 2143, 2154, + 2143, 2144, 2154, 2154, 2144, 2155, + 2145, 2146, 2156, 2156, 2146, 2157, + 2146, 2147, 2157, 2157, 2147, 2158, + 2147, 2148, 2158, 2158, 2148, 2159, + 2148, 2149, 2159, 2159, 2149, 2160, + 2149, 2150, 2160, 2160, 2150, 2161, + 2150, 2151, 2161, 2161, 2151, 2162, + 2151, 2152, 2162, 2162, 2152, 2163, + 2152, 2153, 2163, 2163, 2153, 2164, + 2153, 2154, 2164, 2164, 2154, 2165, + 2154, 2155, 2165, 2165, 2155, 2166, + 2156, 2157, 2167, 2167, 2157, 2168, + 2157, 2158, 2168, 2168, 2158, 2169, + 2158, 2159, 2169, 2169, 2159, 2170, + 2159, 2160, 2170, 2170, 2160, 2171, + 2160, 2161, 2171, 2171, 2161, 2172, + 2161, 2162, 2172, 2172, 2162, 2173, + 2162, 2163, 2173, 2173, 2163, 2174, + 2163, 2164, 2174, 2174, 2164, 2175, + 2164, 2165, 2175, 2175, 2165, 2176, + 2165, 2166, 2176, 2176, 2166, 2177, + 2178, 2179, 2189, 2189, 2179, 2190, + 2179, 2180, 2190, 2190, 2180, 2191, + 2180, 2181, 2191, 2191, 2181, 2192, + 2181, 2182, 2192, 2192, 2182, 2193, + 2182, 2183, 2193, 2193, 2183, 2194, + 2183, 2184, 2194, 2194, 2184, 2195, + 2184, 2185, 2195, 2195, 2185, 2196, + 2185, 2186, 2196, 2196, 2186, 2197, + 2186, 2187, 2197, 2197, 2187, 2198, + 2187, 2188, 2198, 2198, 2188, 2199, + 2189, 2190, 2200, 2200, 2190, 2201, + 2190, 2191, 2201, 2201, 2191, 2202, + 2191, 2192, 2202, 2202, 2192, 2203, + 2192, 2193, 2203, 2203, 2193, 2204, + 2193, 2194, 2204, 2204, 2194, 2205, + 2194, 2195, 2205, 2205, 2195, 2206, + 2195, 2196, 2206, 2206, 2196, 2207, + 2196, 2197, 2207, 2207, 2197, 2208, + 2197, 2198, 2208, 2208, 2198, 2209, + 2198, 2199, 2209, 2209, 2199, 2210, + 2200, 2201, 2211, 2211, 2201, 2212, + 2201, 2202, 2212, 2212, 2202, 2213, + 2202, 2203, 2213, 2213, 2203, 2214, + 2203, 2204, 2214, 2214, 2204, 2215, + 2204, 2205, 2215, 2215, 2205, 2216, + 2205, 2206, 2216, 2216, 2206, 2217, + 2206, 2207, 2217, 2217, 2207, 2218, + 2207, 2208, 2218, 2218, 2208, 2219, + 2208, 2209, 2219, 2219, 2209, 2220, + 2209, 2210, 2220, 2220, 2210, 2221, + 2211, 2212, 2222, 2222, 2212, 2223, + 2212, 2213, 2223, 2223, 2213, 2224, + 2213, 2214, 2224, 2224, 2214, 2225, + 2214, 2215, 2225, 2225, 2215, 2226, + 2215, 2216, 2226, 2226, 2216, 2227, + 2216, 2217, 2227, 2227, 2217, 2228, + 2217, 2218, 2228, 2228, 2218, 2229, + 2218, 2219, 2229, 2229, 2219, 2230, + 2219, 2220, 2230, 2230, 2220, 2231, + 2220, 2221, 2231, 2231, 2221, 2232, + 2222, 2223, 2233, 2233, 2223, 2234, + 2223, 2224, 2234, 2234, 2224, 2235, + 2224, 2225, 2235, 2235, 2225, 2236, + 2225, 2226, 2236, 2236, 2226, 2237, + 2226, 2227, 2237, 2237, 2227, 2238, + 2227, 2228, 2238, 2238, 2228, 2239, + 2228, 2229, 2239, 2239, 2229, 2240, + 2229, 2230, 2240, 2240, 2230, 2241, + 2230, 2231, 2241, 2241, 2231, 2242, + 2231, 2232, 2242, 2242, 2232, 2243, + 2233, 2234, 2244, 2244, 2234, 2245, + 2234, 2235, 2245, 2245, 2235, 2246, + 2235, 2236, 2246, 2246, 2236, 2247, + 2236, 2237, 2247, 2247, 2237, 2248, + 2237, 2238, 2248, 2248, 2238, 2249, + 2238, 2239, 2249, 2249, 2239, 2250, + 2239, 2240, 2250, 2250, 2240, 2251, + 2240, 2241, 2251, 2251, 2241, 2252, + 2241, 2242, 2252, 2252, 2242, 2253, + 2242, 2243, 2253, 2253, 2243, 2254, + 2244, 2245, 2255, 2255, 2245, 2256, + 2245, 2246, 2256, 2256, 2246, 2257, + 2246, 2247, 2257, 2257, 2247, 2258, + 2247, 2248, 2258, 2258, 2248, 2259, + 2248, 2249, 2259, 2259, 2249, 2260, + 2249, 2250, 2260, 2260, 2250, 2261, + 2250, 2251, 2261, 2261, 2251, 2262, + 2251, 2252, 2262, 2262, 2252, 2263, + 2252, 2253, 2263, 2263, 2253, 2264, + 2253, 2254, 2264, 2264, 2254, 2265, + 2255, 2256, 2266, 2266, 2256, 2267, + 2256, 2257, 2267, 2267, 2257, 2268, + 2257, 2258, 2268, 2268, 2258, 2269, + 2258, 2259, 2269, 2269, 2259, 2270, + 2259, 2260, 2270, 2270, 2260, 2271, + 2260, 2261, 2271, 2271, 2261, 2272, + 2261, 2262, 2272, 2272, 2262, 2273, + 2262, 2263, 2273, 2273, 2263, 2274, + 2263, 2264, 2274, 2274, 2264, 2275, + 2264, 2265, 2275, 2275, 2265, 2276, + 2266, 2267, 2277, 2277, 2267, 2278, + 2267, 2268, 2278, 2278, 2268, 2279, + 2268, 2269, 2279, 2279, 2269, 2280, + 2269, 2270, 2280, 2280, 2270, 2281, + 2270, 2271, 2281, 2281, 2271, 2282, + 2271, 2272, 2282, 2282, 2272, 2283, + 2272, 2273, 2283, 2283, 2273, 2284, + 2273, 2274, 2284, 2284, 2274, 2285, + 2274, 2275, 2285, 2285, 2275, 2286, + 2275, 2276, 2286, 2286, 2276, 2287, + 2277, 2278, 2288, 2288, 2278, 2289, + 2278, 2279, 2289, 2289, 2279, 2290, + 2279, 2280, 2290, 2290, 2280, 2291, + 2280, 2281, 2291, 2291, 2281, 2292, + 2281, 2282, 2292, 2292, 2282, 2293, + 2282, 2283, 2293, 2293, 2283, 2294, + 2283, 2284, 2294, 2294, 2284, 2295, + 2284, 2285, 2295, 2295, 2285, 2296, + 2285, 2286, 2296, 2296, 2286, 2297, + 2286, 2287, 2297, 2297, 2287, 2298, + 2299, 2300, 2310, 2310, 2300, 2311, + 2300, 2301, 2311, 2311, 2301, 2312, + 2301, 2302, 2312, 2312, 2302, 2313, + 2302, 2303, 2313, 2313, 2303, 2314, + 2303, 2304, 2314, 2314, 2304, 2315, + 2304, 2305, 2315, 2315, 2305, 2316, + 2305, 2306, 2316, 2316, 2306, 2317, + 2306, 2307, 2317, 2317, 2307, 2318, + 2307, 2308, 2318, 2318, 2308, 2319, + 2308, 2309, 2319, 2319, 2309, 2320, + 2310, 2311, 2321, 2321, 2311, 2322, + 2311, 2312, 2322, 2322, 2312, 2323, + 2312, 2313, 2323, 2323, 2313, 2324, + 2313, 2314, 2324, 2324, 2314, 2325, + 2314, 2315, 2325, 2325, 2315, 2326, + 2315, 2316, 2326, 2326, 2316, 2327, + 2316, 2317, 2327, 2327, 2317, 2328, + 2317, 2318, 2328, 2328, 2318, 2329, + 2318, 2319, 2329, 2329, 2319, 2330, + 2319, 2320, 2330, 2330, 2320, 2331, + 2321, 2322, 2332, 2332, 2322, 2333, + 2322, 2323, 2333, 2333, 2323, 2334, + 2323, 2324, 2334, 2334, 2324, 2335, + 2324, 2325, 2335, 2335, 2325, 2336, + 2325, 2326, 2336, 2336, 2326, 2337, + 2326, 2327, 2337, 2337, 2327, 2338, + 2327, 2328, 2338, 2338, 2328, 2339, + 2328, 2329, 2339, 2339, 2329, 2340, + 2329, 2330, 2340, 2340, 2330, 2341, + 2330, 2331, 2341, 2341, 2331, 2342, + 2332, 2333, 2343, 2343, 2333, 2344, + 2333, 2334, 2344, 2344, 2334, 2345, + 2334, 2335, 2345, 2345, 2335, 2346, + 2335, 2336, 2346, 2346, 2336, 2347, + 2336, 2337, 2347, 2347, 2337, 2348, + 2337, 2338, 2348, 2348, 2338, 2349, + 2338, 2339, 2349, 2349, 2339, 2350, + 2339, 2340, 2350, 2350, 2340, 2351, + 2340, 2341, 2351, 2351, 2341, 2352, + 2341, 2342, 2352, 2352, 2342, 2353, + 2343, 2344, 2354, 2354, 2344, 2355, + 2344, 2345, 2355, 2355, 2345, 2356, + 2345, 2346, 2356, 2356, 2346, 2357, + 2346, 2347, 2357, 2357, 2347, 2358, + 2347, 2348, 2358, 2358, 2348, 2359, + 2348, 2349, 2359, 2359, 2349, 2360, + 2349, 2350, 2360, 2360, 2350, 2361, + 2350, 2351, 2361, 2361, 2351, 2362, + 2351, 2352, 2362, 2362, 2352, 2363, + 2352, 2353, 2363, 2363, 2353, 2364, + 2354, 2355, 2365, 2365, 2355, 2366, + 2355, 2356, 2366, 2366, 2356, 2367, + 2356, 2357, 2367, 2367, 2357, 2368, + 2357, 2358, 2368, 2368, 2358, 2369, + 2358, 2359, 2369, 2369, 2359, 2370, + 2359, 2360, 2370, 2370, 2360, 2371, + 2360, 2361, 2371, 2371, 2361, 2372, + 2361, 2362, 2372, 2372, 2362, 2373, + 2362, 2363, 2373, 2373, 2363, 2374, + 2363, 2364, 2374, 2374, 2364, 2375, + 2365, 2366, 2376, 2376, 2366, 2377, + 2366, 2367, 2377, 2377, 2367, 2378, + 2367, 2368, 2378, 2378, 2368, 2379, + 2368, 2369, 2379, 2379, 2369, 2380, + 2369, 2370, 2380, 2380, 2370, 2381, + 2370, 2371, 2381, 2381, 2371, 2382, + 2371, 2372, 2382, 2382, 2372, 2383, + 2372, 2373, 2383, 2383, 2373, 2384, + 2373, 2374, 2384, 2384, 2374, 2385, + 2374, 2375, 2385, 2385, 2375, 2386, + 2376, 2377, 2387, 2387, 2377, 2388, + 2377, 2378, 2388, 2388, 2378, 2389, + 2378, 2379, 2389, 2389, 2379, 2390, + 2379, 2380, 2390, 2390, 2380, 2391, + 2380, 2381, 2391, 2391, 2381, 2392, + 2381, 2382, 2392, 2392, 2382, 2393, + 2382, 2383, 2393, 2393, 2383, 2394, + 2383, 2384, 2394, 2394, 2384, 2395, + 2384, 2385, 2395, 2395, 2385, 2396, + 2385, 2386, 2396, 2396, 2386, 2397, + 2387, 2388, 2398, 2398, 2388, 2399, + 2388, 2389, 2399, 2399, 2389, 2400, + 2389, 2390, 2400, 2400, 2390, 2401, + 2390, 2391, 2401, 2401, 2391, 2402, + 2391, 2392, 2402, 2402, 2392, 2403, + 2392, 2393, 2403, 2403, 2393, 2404, + 2393, 2394, 2404, 2404, 2394, 2405, + 2394, 2395, 2405, 2405, 2395, 2406, + 2395, 2396, 2406, 2406, 2396, 2407, + 2396, 2397, 2407, 2407, 2397, 2408, + 2398, 2399, 2409, 2409, 2399, 2410, + 2399, 2400, 2410, 2410, 2400, 2411, + 2400, 2401, 2411, 2411, 2401, 2412, + 2401, 2402, 2412, 2412, 2402, 2413, + 2402, 2403, 2413, 2413, 2403, 2414, + 2403, 2404, 2414, 2414, 2404, 2415, + 2404, 2405, 2415, 2415, 2405, 2416, + 2405, 2406, 2416, 2416, 2406, 2417, + 2406, 2407, 2417, 2417, 2407, 2418, + 2407, 2408, 2418, 2418, 2408, 2419, + 2420, 2421, 2431, 2431, 2421, 2432, + 2421, 2422, 2432, 2432, 2422, 2433, + 2422, 2423, 2433, 2433, 2423, 2434, + 2423, 2424, 2434, 2434, 2424, 2435, + 2424, 2425, 2435, 2435, 2425, 2436, + 2425, 2426, 2436, 2436, 2426, 2437, + 2426, 2427, 2437, 2437, 2427, 2438, + 2427, 2428, 2438, 2438, 2428, 2439, + 2428, 2429, 2439, 2439, 2429, 2440, + 2429, 2430, 2440, 2440, 2430, 2441, + 2431, 2432, 2442, 2442, 2432, 2443, + 2432, 2433, 2443, 2443, 2433, 2444, + 2433, 2434, 2444, 2444, 2434, 2445, + 2434, 2435, 2445, 2445, 2435, 2446, + 2435, 2436, 2446, 2446, 2436, 2447, + 2436, 2437, 2447, 2447, 2437, 2448, + 2437, 2438, 2448, 2448, 2438, 2449, + 2438, 2439, 2449, 2449, 2439, 2450, + 2439, 2440, 2450, 2450, 2440, 2451, + 2440, 2441, 2451, 2451, 2441, 2452, + 2442, 2443, 2453, 2453, 2443, 2454, + 2443, 2444, 2454, 2454, 2444, 2455, + 2444, 2445, 2455, 2455, 2445, 2456, + 2445, 2446, 2456, 2456, 2446, 2457, + 2446, 2447, 2457, 2457, 2447, 2458, + 2447, 2448, 2458, 2458, 2448, 2459, + 2448, 2449, 2459, 2459, 2449, 2460, + 2449, 2450, 2460, 2460, 2450, 2461, + 2450, 2451, 2461, 2461, 2451, 2462, + 2451, 2452, 2462, 2462, 2452, 2463, + 2453, 2454, 2464, 2464, 2454, 2465, + 2454, 2455, 2465, 2465, 2455, 2466, + 2455, 2456, 2466, 2466, 2456, 2467, + 2456, 2457, 2467, 2467, 2457, 2468, + 2457, 2458, 2468, 2468, 2458, 2469, + 2458, 2459, 2469, 2469, 2459, 2470, + 2459, 2460, 2470, 2470, 2460, 2471, + 2460, 2461, 2471, 2471, 2461, 2472, + 2461, 2462, 2472, 2472, 2462, 2473, + 2462, 2463, 2473, 2473, 2463, 2474, + 2464, 2465, 2475, 2475, 2465, 2476, + 2465, 2466, 2476, 2476, 2466, 2477, + 2466, 2467, 2477, 2477, 2467, 2478, + 2467, 2468, 2478, 2478, 2468, 2479, + 2468, 2469, 2479, 2479, 2469, 2480, + 2469, 2470, 2480, 2480, 2470, 2481, + 2470, 2471, 2481, 2481, 2471, 2482, + 2471, 2472, 2482, 2482, 2472, 2483, + 2472, 2473, 2483, 2483, 2473, 2484, + 2473, 2474, 2484, 2484, 2474, 2485, + 2475, 2476, 2486, 2486, 2476, 2487, + 2476, 2477, 2487, 2487, 2477, 2488, + 2477, 2478, 2488, 2488, 2478, 2489, + 2478, 2479, 2489, 2489, 2479, 2490, + 2479, 2480, 2490, 2490, 2480, 2491, + 2480, 2481, 2491, 2491, 2481, 2492, + 2481, 2482, 2492, 2492, 2482, 2493, + 2482, 2483, 2493, 2493, 2483, 2494, + 2483, 2484, 2494, 2494, 2484, 2495, + 2484, 2485, 2495, 2495, 2485, 2496, + 2486, 2487, 2497, 2497, 2487, 2498, + 2487, 2488, 2498, 2498, 2488, 2499, + 2488, 2489, 2499, 2499, 2489, 2500, + 2489, 2490, 2500, 2500, 2490, 2501, + 2490, 2491, 2501, 2501, 2491, 2502, + 2491, 2492, 2502, 2502, 2492, 2503, + 2492, 2493, 2503, 2503, 2493, 2504, + 2493, 2494, 2504, 2504, 2494, 2505, + 2494, 2495, 2505, 2505, 2495, 2506, + 2495, 2496, 2506, 2506, 2496, 2507, + 2497, 2498, 2508, 2508, 2498, 2509, + 2498, 2499, 2509, 2509, 2499, 2510, + 2499, 2500, 2510, 2510, 2500, 2511, + 2500, 2501, 2511, 2511, 2501, 2512, + 2501, 2502, 2512, 2512, 2502, 2513, + 2502, 2503, 2513, 2513, 2503, 2514, + 2503, 2504, 2514, 2514, 2504, 2515, + 2504, 2505, 2515, 2515, 2505, 2516, + 2505, 2506, 2516, 2516, 2506, 2517, + 2506, 2507, 2517, 2517, 2507, 2518, + 2508, 2509, 2519, 2519, 2509, 2520, + 2509, 2510, 2520, 2520, 2510, 2521, + 2510, 2511, 2521, 2521, 2511, 2522, + 2511, 2512, 2522, 2522, 2512, 2523, + 2512, 2513, 2523, 2523, 2513, 2524, + 2513, 2514, 2524, 2524, 2514, 2525, + 2514, 2515, 2525, 2525, 2515, 2526, + 2515, 2516, 2526, 2526, 2516, 2527, + 2516, 2517, 2527, 2527, 2517, 2528, + 2517, 2518, 2528, 2528, 2518, 2529, + 2519, 2520, 2530, 2530, 2520, 2531, + 2520, 2521, 2531, 2531, 2521, 2532, + 2521, 2522, 2532, 2532, 2522, 2533, + 2522, 2523, 2533, 2533, 2523, 2534, + 2523, 2524, 2534, 2534, 2524, 2535, + 2524, 2525, 2535, 2535, 2525, 2536, + 2525, 2526, 2536, 2536, 2526, 2537, + 2526, 2527, 2537, 2537, 2527, 2538, + 2527, 2528, 2538, 2538, 2528, 2539, + 2528, 2529, 2539, 2539, 2529, 2540, + 2541, 2542, 2552, 2552, 2542, 2553, + 2542, 2543, 2553, 2553, 2543, 2554, + 2543, 2544, 2554, 2554, 2544, 2555, + 2544, 2545, 2555, 2555, 2545, 2556, + 2545, 2546, 2556, 2556, 2546, 2557, + 2546, 2547, 2557, 2557, 2547, 2558, + 2547, 2548, 2558, 2558, 2548, 2559, + 2548, 2549, 2559, 2559, 2549, 2560, + 2549, 2550, 2560, 2560, 2550, 2561, + 2550, 2551, 2561, 2561, 2551, 2562, + 2552, 2553, 2563, 2563, 2553, 2564, + 2553, 2554, 2564, 2564, 2554, 2565, + 2554, 2555, 2565, 2565, 2555, 2566, + 2555, 2556, 2566, 2566, 2556, 2567, + 2556, 2557, 2567, 2567, 2557, 2568, + 2557, 2558, 2568, 2568, 2558, 2569, + 2558, 2559, 2569, 2569, 2559, 2570, + 2559, 2560, 2570, 2570, 2560, 2571, + 2560, 2561, 2571, 2571, 2561, 2572, + 2561, 2562, 2572, 2572, 2562, 2573, + 2563, 2564, 2574, 2574, 2564, 2575, + 2564, 2565, 2575, 2575, 2565, 2576, + 2565, 2566, 2576, 2576, 2566, 2577, + 2566, 2567, 2577, 2577, 2567, 2578, + 2567, 2568, 2578, 2578, 2568, 2579, + 2568, 2569, 2579, 2579, 2569, 2580, + 2569, 2570, 2580, 2580, 2570, 2581, + 2570, 2571, 2581, 2581, 2571, 2582, + 2571, 2572, 2582, 2582, 2572, 2583, + 2572, 2573, 2583, 2583, 2573, 2584, + 2574, 2575, 2585, 2585, 2575, 2586, + 2575, 2576, 2586, 2586, 2576, 2587, + 2576, 2577, 2587, 2587, 2577, 2588, + 2577, 2578, 2588, 2588, 2578, 2589, + 2578, 2579, 2589, 2589, 2579, 2590, + 2579, 2580, 2590, 2590, 2580, 2591, + 2580, 2581, 2591, 2591, 2581, 2592, + 2581, 2582, 2592, 2592, 2582, 2593, + 2582, 2583, 2593, 2593, 2583, 2594, + 2583, 2584, 2594, 2594, 2584, 2595, + 2585, 2586, 2596, 2596, 2586, 2597, + 2586, 2587, 2597, 2597, 2587, 2598, + 2587, 2588, 2598, 2598, 2588, 2599, + 2588, 2589, 2599, 2599, 2589, 2600, + 2589, 2590, 2600, 2600, 2590, 2601, + 2590, 2591, 2601, 2601, 2591, 2602, + 2591, 2592, 2602, 2602, 2592, 2603, + 2592, 2593, 2603, 2603, 2593, 2604, + 2593, 2594, 2604, 2604, 2594, 2605, + 2594, 2595, 2605, 2605, 2595, 2606, + 2596, 2597, 2607, 2607, 2597, 2608, + 2597, 2598, 2608, 2608, 2598, 2609, + 2598, 2599, 2609, 2609, 2599, 2610, + 2599, 2600, 2610, 2610, 2600, 2611, + 2600, 2601, 2611, 2611, 2601, 2612, + 2601, 2602, 2612, 2612, 2602, 2613, + 2602, 2603, 2613, 2613, 2603, 2614, + 2603, 2604, 2614, 2614, 2604, 2615, + 2604, 2605, 2615, 2615, 2605, 2616, + 2605, 2606, 2616, 2616, 2606, 2617, + 2607, 2608, 2618, 2618, 2608, 2619, + 2608, 2609, 2619, 2619, 2609, 2620, + 2609, 2610, 2620, 2620, 2610, 2621, + 2610, 2611, 2621, 2621, 2611, 2622, + 2611, 2612, 2622, 2622, 2612, 2623, + 2612, 2613, 2623, 2623, 2613, 2624, + 2613, 2614, 2624, 2624, 2614, 2625, + 2614, 2615, 2625, 2625, 2615, 2626, + 2615, 2616, 2626, 2626, 2616, 2627, + 2616, 2617, 2627, 2627, 2617, 2628, + 2618, 2619, 2629, 2629, 2619, 2630, + 2619, 2620, 2630, 2630, 2620, 2631, + 2620, 2621, 2631, 2631, 2621, 2632, + 2621, 2622, 2632, 2632, 2622, 2633, + 2622, 2623, 2633, 2633, 2623, 2634, + 2623, 2624, 2634, 2634, 2624, 2635, + 2624, 2625, 2635, 2635, 2625, 2636, + 2625, 2626, 2636, 2636, 2626, 2637, + 2626, 2627, 2637, 2637, 2627, 2638, + 2627, 2628, 2638, 2638, 2628, 2639, + 2629, 2630, 2640, 2640, 2630, 2641, + 2630, 2631, 2641, 2641, 2631, 2642, + 2631, 2632, 2642, 2642, 2632, 2643, + 2632, 2633, 2643, 2643, 2633, 2644, + 2633, 2634, 2644, 2644, 2634, 2645, + 2634, 2635, 2645, 2645, 2635, 2646, + 2635, 2636, 2646, 2646, 2636, 2647, + 2636, 2637, 2647, 2647, 2637, 2648, + 2637, 2638, 2648, 2648, 2638, 2649, + 2638, 2639, 2649, 2649, 2639, 2650, + 2640, 2641, 2651, 2651, 2641, 2652, + 2641, 2642, 2652, 2652, 2642, 2653, + 2642, 2643, 2653, 2653, 2643, 2654, + 2643, 2644, 2654, 2654, 2644, 2655, + 2644, 2645, 2655, 2655, 2645, 2656, + 2645, 2646, 2656, 2656, 2646, 2657, + 2646, 2647, 2657, 2657, 2647, 2658, + 2647, 2648, 2658, 2658, 2648, 2659, + 2648, 2649, 2659, 2659, 2649, 2660, + 2649, 2650, 2660, 2660, 2650, 2661, + 2662, 2663, 2673, 2673, 2663, 2674, + 2663, 2664, 2674, 2674, 2664, 2675, + 2664, 2665, 2675, 2675, 2665, 2676, + 2665, 2666, 2676, 2676, 2666, 2677, + 2666, 2667, 2677, 2677, 2667, 2678, + 2667, 2668, 2678, 2678, 2668, 2679, + 2668, 2669, 2679, 2679, 2669, 2680, + 2669, 2670, 2680, 2680, 2670, 2681, + 2670, 2671, 2681, 2681, 2671, 2682, + 2671, 2672, 2682, 2682, 2672, 2683, + 2673, 2674, 2684, 2684, 2674, 2685, + 2674, 2675, 2685, 2685, 2675, 2686, + 2675, 2676, 2686, 2686, 2676, 2687, + 2676, 2677, 2687, 2687, 2677, 2688, + 2677, 2678, 2688, 2688, 2678, 2689, + 2678, 2679, 2689, 2689, 2679, 2690, + 2679, 2680, 2690, 2690, 2680, 2691, + 2680, 2681, 2691, 2691, 2681, 2692, + 2681, 2682, 2692, 2692, 2682, 2693, + 2682, 2683, 2693, 2693, 2683, 2694, + 2684, 2685, 2695, 2695, 2685, 2696, + 2685, 2686, 2696, 2696, 2686, 2697, + 2686, 2687, 2697, 2697, 2687, 2698, + 2687, 2688, 2698, 2698, 2688, 2699, + 2688, 2689, 2699, 2699, 2689, 2700, + 2689, 2690, 2700, 2700, 2690, 2701, + 2690, 2691, 2701, 2701, 2691, 2702, + 2691, 2692, 2702, 2702, 2692, 2703, + 2692, 2693, 2703, 2703, 2693, 2704, + 2693, 2694, 2704, 2704, 2694, 2705, + 2695, 2696, 2706, 2706, 2696, 2707, + 2696, 2697, 2707, 2707, 2697, 2708, + 2697, 2698, 2708, 2708, 2698, 2709, + 2698, 2699, 2709, 2709, 2699, 2710, + 2699, 2700, 2710, 2710, 2700, 2711, + 2700, 2701, 2711, 2711, 2701, 2712, + 2701, 2702, 2712, 2712, 2702, 2713, + 2702, 2703, 2713, 2713, 2703, 2714, + 2703, 2704, 2714, 2714, 2704, 2715, + 2704, 2705, 2715, 2715, 2705, 2716, + 2706, 2707, 2717, 2717, 2707, 2718, + 2707, 2708, 2718, 2718, 2708, 2719, + 2708, 2709, 2719, 2719, 2709, 2720, + 2709, 2710, 2720, 2720, 2710, 2721, + 2710, 2711, 2721, 2721, 2711, 2722, + 2711, 2712, 2722, 2722, 2712, 2723, + 2712, 2713, 2723, 2723, 2713, 2724, + 2713, 2714, 2724, 2724, 2714, 2725, + 2714, 2715, 2725, 2725, 2715, 2726, + 2715, 2716, 2726, 2726, 2716, 2727, + 2717, 2718, 2728, 2728, 2718, 2729, + 2718, 2719, 2729, 2729, 2719, 2730, + 2719, 2720, 2730, 2730, 2720, 2731, + 2720, 2721, 2731, 2731, 2721, 2732, + 2721, 2722, 2732, 2732, 2722, 2733, + 2722, 2723, 2733, 2733, 2723, 2734, + 2723, 2724, 2734, 2734, 2724, 2735, + 2724, 2725, 2735, 2735, 2725, 2736, + 2725, 2726, 2736, 2736, 2726, 2737, + 2726, 2727, 2737, 2737, 2727, 2738, + 2728, 2729, 2739, 2739, 2729, 2740, + 2729, 2730, 2740, 2740, 2730, 2741, + 2730, 2731, 2741, 2741, 2731, 2742, + 2731, 2732, 2742, 2742, 2732, 2743, + 2732, 2733, 2743, 2743, 2733, 2744, + 2733, 2734, 2744, 2744, 2734, 2745, + 2734, 2735, 2745, 2745, 2735, 2746, + 2735, 2736, 2746, 2746, 2736, 2747, + 2736, 2737, 2747, 2747, 2737, 2748, + 2737, 2738, 2748, 2748, 2738, 2749, + 2739, 2740, 2750, 2750, 2740, 2751, + 2740, 2741, 2751, 2751, 2741, 2752, + 2741, 2742, 2752, 2752, 2742, 2753, + 2742, 2743, 2753, 2753, 2743, 2754, + 2743, 2744, 2754, 2754, 2744, 2755, + 2744, 2745, 2755, 2755, 2745, 2756, + 2745, 2746, 2756, 2756, 2746, 2757, + 2746, 2747, 2757, 2757, 2747, 2758, + 2747, 2748, 2758, 2758, 2748, 2759, + 2748, 2749, 2759, 2759, 2749, 2760, + 2750, 2751, 2761, 2761, 2751, 2762, + 2751, 2752, 2762, 2762, 2752, 2763, + 2752, 2753, 2763, 2763, 2753, 2764, + 2753, 2754, 2764, 2764, 2754, 2765, + 2754, 2755, 2765, 2765, 2755, 2766, + 2755, 2756, 2766, 2766, 2756, 2767, + 2756, 2757, 2767, 2767, 2757, 2768, + 2757, 2758, 2768, 2768, 2758, 2769, + 2758, 2759, 2769, 2769, 2759, 2770, + 2759, 2760, 2770, 2770, 2760, 2771, + 2761, 2762, 2772, 2772, 2762, 2773, + 2762, 2763, 2773, 2773, 2763, 2774, + 2763, 2764, 2774, 2774, 2764, 2775, + 2764, 2765, 2775, 2775, 2765, 2776, + 2765, 2766, 2776, 2776, 2766, 2777, + 2766, 2767, 2777, 2777, 2767, 2778, + 2767, 2768, 2778, 2778, 2768, 2779, + 2768, 2769, 2779, 2779, 2769, 2780, + 2769, 2770, 2780, 2780, 2770, 2781, + 2770, 2771, 2781, 2781, 2771, 2782, + 2783, 2784, 2794, 2794, 2784, 2795, + 2784, 2785, 2795, 2795, 2785, 2796, + 2785, 2786, 2796, 2796, 2786, 2797, + 2786, 2787, 2797, 2797, 2787, 2798, + 2787, 2788, 2798, 2798, 2788, 2799, + 2788, 2789, 2799, 2799, 2789, 2800, + 2789, 2790, 2800, 2800, 2790, 2801, + 2790, 2791, 2801, 2801, 2791, 2802, + 2791, 2792, 2802, 2802, 2792, 2803, + 2792, 2793, 2803, 2803, 2793, 2804, + 2794, 2795, 2805, 2805, 2795, 2806, + 2795, 2796, 2806, 2806, 2796, 2807, + 2796, 2797, 2807, 2807, 2797, 2808, + 2797, 2798, 2808, 2808, 2798, 2809, + 2798, 2799, 2809, 2809, 2799, 2810, + 2799, 2800, 2810, 2810, 2800, 2811, + 2800, 2801, 2811, 2811, 2801, 2812, + 2801, 2802, 2812, 2812, 2802, 2813, + 2802, 2803, 2813, 2813, 2803, 2814, + 2803, 2804, 2814, 2814, 2804, 2815, + 2805, 2806, 2816, 2816, 2806, 2817, + 2806, 2807, 2817, 2817, 2807, 2818, + 2807, 2808, 2818, 2818, 2808, 2819, + 2808, 2809, 2819, 2819, 2809, 2820, + 2809, 2810, 2820, 2820, 2810, 2821, + 2810, 2811, 2821, 2821, 2811, 2822, + 2811, 2812, 2822, 2822, 2812, 2823, + 2812, 2813, 2823, 2823, 2813, 2824, + 2813, 2814, 2824, 2824, 2814, 2825, + 2814, 2815, 2825, 2825, 2815, 2826, + 2816, 2817, 2827, 2827, 2817, 2828, + 2817, 2818, 2828, 2828, 2818, 2829, + 2818, 2819, 2829, 2829, 2819, 2830, + 2819, 2820, 2830, 2830, 2820, 2831, + 2820, 2821, 2831, 2831, 2821, 2832, + 2821, 2822, 2832, 2832, 2822, 2833, + 2822, 2823, 2833, 2833, 2823, 2834, + 2823, 2824, 2834, 2834, 2824, 2835, + 2824, 2825, 2835, 2835, 2825, 2836, + 2825, 2826, 2836, 2836, 2826, 2837, + 2827, 2828, 2838, 2838, 2828, 2839, + 2828, 2829, 2839, 2839, 2829, 2840, + 2829, 2830, 2840, 2840, 2830, 2841, + 2830, 2831, 2841, 2841, 2831, 2842, + 2831, 2832, 2842, 2842, 2832, 2843, + 2832, 2833, 2843, 2843, 2833, 2844, + 2833, 2834, 2844, 2844, 2834, 2845, + 2834, 2835, 2845, 2845, 2835, 2846, + 2835, 2836, 2846, 2846, 2836, 2847, + 2836, 2837, 2847, 2847, 2837, 2848, + 2838, 2839, 2849, 2849, 2839, 2850, + 2839, 2840, 2850, 2850, 2840, 2851, + 2840, 2841, 2851, 2851, 2841, 2852, + 2841, 2842, 2852, 2852, 2842, 2853, + 2842, 2843, 2853, 2853, 2843, 2854, + 2843, 2844, 2854, 2854, 2844, 2855, + 2844, 2845, 2855, 2855, 2845, 2856, + 2845, 2846, 2856, 2856, 2846, 2857, + 2846, 2847, 2857, 2857, 2847, 2858, + 2847, 2848, 2858, 2858, 2848, 2859, + 2849, 2850, 2860, 2860, 2850, 2861, + 2850, 2851, 2861, 2861, 2851, 2862, + 2851, 2852, 2862, 2862, 2852, 2863, + 2852, 2853, 2863, 2863, 2853, 2864, + 2853, 2854, 2864, 2864, 2854, 2865, + 2854, 2855, 2865, 2865, 2855, 2866, + 2855, 2856, 2866, 2866, 2856, 2867, + 2856, 2857, 2867, 2867, 2857, 2868, + 2857, 2858, 2868, 2868, 2858, 2869, + 2858, 2859, 2869, 2869, 2859, 2870, + 2860, 2861, 2871, 2871, 2861, 2872, + 2861, 2862, 2872, 2872, 2862, 2873, + 2862, 2863, 2873, 2873, 2863, 2874, + 2863, 2864, 2874, 2874, 2864, 2875, + 2864, 2865, 2875, 2875, 2865, 2876, + 2865, 2866, 2876, 2876, 2866, 2877, + 2866, 2867, 2877, 2877, 2867, 2878, + 2867, 2868, 2878, 2878, 2868, 2879, + 2868, 2869, 2879, 2879, 2869, 2880, + 2869, 2870, 2880, 2880, 2870, 2881, + 2871, 2872, 2882, 2882, 2872, 2883, + 2872, 2873, 2883, 2883, 2873, 2884, + 2873, 2874, 2884, 2884, 2874, 2885, + 2874, 2875, 2885, 2885, 2875, 2886, + 2875, 2876, 2886, 2886, 2876, 2887, + 2876, 2877, 2887, 2887, 2877, 2888, + 2877, 2878, 2888, 2888, 2878, 2889, + 2878, 2879, 2889, 2889, 2879, 2890, + 2879, 2880, 2890, 2890, 2880, 2891, + 2880, 2881, 2891, 2891, 2881, 2892, + 2882, 2883, 2893, 2893, 2883, 2894, + 2883, 2884, 2894, 2894, 2884, 2895, + 2884, 2885, 2895, 2895, 2885, 2896, + 2885, 2886, 2896, 2896, 2886, 2897, + 2886, 2887, 2897, 2897, 2887, 2898, + 2887, 2888, 2898, 2898, 2888, 2899, + 2888, 2889, 2899, 2899, 2889, 2900, + 2889, 2890, 2900, 2900, 2890, 2901, + 2890, 2891, 2901, 2901, 2891, 2902, + 2891, 2892, 2902, 2902, 2892, 2903, + 2904, 2905, 2915, 2915, 2905, 2916, + 2905, 2906, 2916, 2916, 2906, 2917, + 2906, 2907, 2917, 2917, 2907, 2918, + 2907, 2908, 2918, 2918, 2908, 2919, + 2908, 2909, 2919, 2919, 2909, 2920, + 2909, 2910, 2920, 2920, 2910, 2921, + 2910, 2911, 2921, 2921, 2911, 2922, + 2911, 2912, 2922, 2922, 2912, 2923, + 2912, 2913, 2923, 2923, 2913, 2924, + 2913, 2914, 2924, 2924, 2914, 2925, + 2915, 2916, 2926, 2926, 2916, 2927, + 2916, 2917, 2927, 2927, 2917, 2928, + 2917, 2918, 2928, 2928, 2918, 2929, + 2918, 2919, 2929, 2929, 2919, 2930, + 2919, 2920, 2930, 2930, 2920, 2931, + 2920, 2921, 2931, 2931, 2921, 2932, + 2921, 2922, 2932, 2932, 2922, 2933, + 2922, 2923, 2933, 2933, 2923, 2934, + 2923, 2924, 2934, 2934, 2924, 2935, + 2924, 2925, 2935, 2935, 2925, 2936, + 2926, 2927, 2937, 2937, 2927, 2938, + 2927, 2928, 2938, 2938, 2928, 2939, + 2928, 2929, 2939, 2939, 2929, 2940, + 2929, 2930, 2940, 2940, 2930, 2941, + 2930, 2931, 2941, 2941, 2931, 2942, + 2931, 2932, 2942, 2942, 2932, 2943, + 2932, 2933, 2943, 2943, 2933, 2944, + 2933, 2934, 2944, 2944, 2934, 2945, + 2934, 2935, 2945, 2945, 2935, 2946, + 2935, 2936, 2946, 2946, 2936, 2947, + 2937, 2938, 2948, 2948, 2938, 2949, + 2938, 2939, 2949, 2949, 2939, 2950, + 2939, 2940, 2950, 2950, 2940, 2951, + 2940, 2941, 2951, 2951, 2941, 2952, + 2941, 2942, 2952, 2952, 2942, 2953, + 2942, 2943, 2953, 2953, 2943, 2954, + 2943, 2944, 2954, 2954, 2944, 2955, + 2944, 2945, 2955, 2955, 2945, 2956, + 2945, 2946, 2956, 2956, 2946, 2957, + 2946, 2947, 2957, 2957, 2947, 2958, + 2948, 2949, 2959, 2959, 2949, 2960, + 2949, 2950, 2960, 2960, 2950, 2961, + 2950, 2951, 2961, 2961, 2951, 2962, + 2951, 2952, 2962, 2962, 2952, 2963, + 2952, 2953, 2963, 2963, 2953, 2964, + 2953, 2954, 2964, 2964, 2954, 2965, + 2954, 2955, 2965, 2965, 2955, 2966, + 2955, 2956, 2966, 2966, 2956, 2967, + 2956, 2957, 2967, 2967, 2957, 2968, + 2957, 2958, 2968, 2968, 2958, 2969, + 2959, 2960, 2970, 2970, 2960, 2971, + 2960, 2961, 2971, 2971, 2961, 2972, + 2961, 2962, 2972, 2972, 2962, 2973, + 2962, 2963, 2973, 2973, 2963, 2974, + 2963, 2964, 2974, 2974, 2964, 2975, + 2964, 2965, 2975, 2975, 2965, 2976, + 2965, 2966, 2976, 2976, 2966, 2977, + 2966, 2967, 2977, 2977, 2967, 2978, + 2967, 2968, 2978, 2978, 2968, 2979, + 2968, 2969, 2979, 2979, 2969, 2980, + 2970, 2971, 2981, 2981, 2971, 2982, + 2971, 2972, 2982, 2982, 2972, 2983, + 2972, 2973, 2983, 2983, 2973, 2984, + 2973, 2974, 2984, 2984, 2974, 2985, + 2974, 2975, 2985, 2985, 2975, 2986, + 2975, 2976, 2986, 2986, 2976, 2987, + 2976, 2977, 2987, 2987, 2977, 2988, + 2977, 2978, 2988, 2988, 2978, 2989, + 2978, 2979, 2989, 2989, 2979, 2990, + 2979, 2980, 2990, 2990, 2980, 2991, + 2981, 2982, 2992, 2992, 2982, 2993, + 2982, 2983, 2993, 2993, 2983, 2994, + 2983, 2984, 2994, 2994, 2984, 2995, + 2984, 2985, 2995, 2995, 2985, 2996, + 2985, 2986, 2996, 2996, 2986, 2997, + 2986, 2987, 2997, 2997, 2987, 2998, + 2987, 2988, 2998, 2998, 2988, 2999, + 2988, 2989, 2999, 2999, 2989, 3000, + 2989, 2990, 3000, 3000, 2990, 3001, + 2990, 2991, 3001, 3001, 2991, 3002, + 2992, 2993, 3003, 3003, 2993, 3004, + 2993, 2994, 3004, 3004, 2994, 3005, + 2994, 2995, 3005, 3005, 2995, 3006, + 2995, 2996, 3006, 3006, 2996, 3007, + 2996, 2997, 3007, 3007, 2997, 3008, + 2997, 2998, 3008, 3008, 2998, 3009, + 2998, 2999, 3009, 3009, 2999, 3010, + 2999, 3000, 3010, 3010, 3000, 3011, + 3000, 3001, 3011, 3011, 3001, 3012, + 3001, 3002, 3012, 3012, 3002, 3013, + 3003, 3004, 3014, 3014, 3004, 3015, + 3004, 3005, 3015, 3015, 3005, 3016, + 3005, 3006, 3016, 3016, 3006, 3017, + 3006, 3007, 3017, 3017, 3007, 3018, + 3007, 3008, 3018, 3018, 3008, 3019, + 3008, 3009, 3019, 3019, 3009, 3020, + 3009, 3010, 3020, 3020, 3010, 3021, + 3010, 3011, 3021, 3021, 3011, 3022, + 3011, 3012, 3022, 3022, 3012, 3023, + 3012, 3013, 3023, 3023, 3013, 3024, + 3025, 3026, 3036, 3036, 3026, 3037, + 3026, 3027, 3037, 3037, 3027, 3038, + 3027, 3028, 3038, 3038, 3028, 3039, + 3028, 3029, 3039, 3039, 3029, 3040, + 3029, 3030, 3040, 3040, 3030, 3041, + 3030, 3031, 3041, 3041, 3031, 3042, + 3031, 3032, 3042, 3042, 3032, 3043, + 3032, 3033, 3043, 3043, 3033, 3044, + 3033, 3034, 3044, 3044, 3034, 3045, + 3034, 3035, 3045, 3045, 3035, 3046, + 3036, 3037, 3047, 3047, 3037, 3048, + 3037, 3038, 3048, 3048, 3038, 3049, + 3038, 3039, 3049, 3049, 3039, 3050, + 3039, 3040, 3050, 3050, 3040, 3051, + 3040, 3041, 3051, 3051, 3041, 3052, + 3041, 3042, 3052, 3052, 3042, 3053, + 3042, 3043, 3053, 3053, 3043, 3054, + 3043, 3044, 3054, 3054, 3044, 3055, + 3044, 3045, 3055, 3055, 3045, 3056, + 3045, 3046, 3056, 3056, 3046, 3057, + 3047, 3048, 3058, 3058, 3048, 3059, + 3048, 3049, 3059, 3059, 3049, 3060, + 3049, 3050, 3060, 3060, 3050, 3061, + 3050, 3051, 3061, 3061, 3051, 3062, + 3051, 3052, 3062, 3062, 3052, 3063, + 3052, 3053, 3063, 3063, 3053, 3064, + 3053, 3054, 3064, 3064, 3054, 3065, + 3054, 3055, 3065, 3065, 3055, 3066, + 3055, 3056, 3066, 3066, 3056, 3067, + 3056, 3057, 3067, 3067, 3057, 3068, + 3058, 3059, 3069, 3069, 3059, 3070, + 3059, 3060, 3070, 3070, 3060, 3071, + 3060, 3061, 3071, 3071, 3061, 3072, + 3061, 3062, 3072, 3072, 3062, 3073, + 3062, 3063, 3073, 3073, 3063, 3074, + 3063, 3064, 3074, 3074, 3064, 3075, + 3064, 3065, 3075, 3075, 3065, 3076, + 3065, 3066, 3076, 3076, 3066, 3077, + 3066, 3067, 3077, 3077, 3067, 3078, + 3067, 3068, 3078, 3078, 3068, 3079, + 3069, 3070, 3080, 3080, 3070, 3081, + 3070, 3071, 3081, 3081, 3071, 3082, + 3071, 3072, 3082, 3082, 3072, 3083, + 3072, 3073, 3083, 3083, 3073, 3084, + 3073, 3074, 3084, 3084, 3074, 3085, + 3074, 3075, 3085, 3085, 3075, 3086, + 3075, 3076, 3086, 3086, 3076, 3087, + 3076, 3077, 3087, 3087, 3077, 3088, + 3077, 3078, 3088, 3088, 3078, 3089, + 3078, 3079, 3089, 3089, 3079, 3090, + 3080, 3081, 3091, 3091, 3081, 3092, + 3081, 3082, 3092, 3092, 3082, 3093, + 3082, 3083, 3093, 3093, 3083, 3094, + 3083, 3084, 3094, 3094, 3084, 3095, + 3084, 3085, 3095, 3095, 3085, 3096, + 3085, 3086, 3096, 3096, 3086, 3097, + 3086, 3087, 3097, 3097, 3087, 3098, + 3087, 3088, 3098, 3098, 3088, 3099, + 3088, 3089, 3099, 3099, 3089, 3100, + 3089, 3090, 3100, 3100, 3090, 3101, + 3091, 3092, 3102, 3102, 3092, 3103, + 3092, 3093, 3103, 3103, 3093, 3104, + 3093, 3094, 3104, 3104, 3094, 3105, + 3094, 3095, 3105, 3105, 3095, 3106, + 3095, 3096, 3106, 3106, 3096, 3107, + 3096, 3097, 3107, 3107, 3097, 3108, + 3097, 3098, 3108, 3108, 3098, 3109, + 3098, 3099, 3109, 3109, 3099, 3110, + 3099, 3100, 3110, 3110, 3100, 3111, + 3100, 3101, 3111, 3111, 3101, 3112, + 3102, 3103, 3113, 3113, 3103, 3114, + 3103, 3104, 3114, 3114, 3104, 3115, + 3104, 3105, 3115, 3115, 3105, 3116, + 3105, 3106, 3116, 3116, 3106, 3117, + 3106, 3107, 3117, 3117, 3107, 3118, + 3107, 3108, 3118, 3118, 3108, 3119, + 3108, 3109, 3119, 3119, 3109, 3120, + 3109, 3110, 3120, 3120, 3110, 3121, + 3110, 3111, 3121, 3121, 3111, 3122, + 3111, 3112, 3122, 3122, 3112, 3123, + 3113, 3114, 3124, 3124, 3114, 3125, + 3114, 3115, 3125, 3125, 3115, 3126, + 3115, 3116, 3126, 3126, 3116, 3127, + 3116, 3117, 3127, 3127, 3117, 3128, + 3117, 3118, 3128, 3128, 3118, 3129, + 3118, 3119, 3129, 3129, 3119, 3130, + 3119, 3120, 3130, 3130, 3120, 3131, + 3120, 3121, 3131, 3131, 3121, 3132, + 3121, 3122, 3132, 3132, 3122, 3133, + 3122, 3123, 3133, 3133, 3123, 3134, + 3124, 3125, 3135, 3135, 3125, 3136, + 3125, 3126, 3136, 3136, 3126, 3137, + 3126, 3127, 3137, 3137, 3127, 3138, + 3127, 3128, 3138, 3138, 3128, 3139, + 3128, 3129, 3139, 3139, 3129, 3140, + 3129, 3130, 3140, 3140, 3130, 3141, + 3130, 3131, 3141, 3141, 3131, 3142, + 3131, 3132, 3142, 3142, 3132, 3143, + 3132, 3133, 3143, 3143, 3133, 3144, + 3133, 3134, 3144, 3144, 3134, 3145, + 3146, 3147, 3157, 3157, 3147, 3158, + 3147, 3148, 3158, 3158, 3148, 3159, + 3148, 3149, 3159, 3159, 3149, 3160, + 3149, 3150, 3160, 3160, 3150, 3161, + 3150, 3151, 3161, 3161, 3151, 3162, + 3151, 3152, 3162, 3162, 3152, 3163, + 3152, 3153, 3163, 3163, 3153, 3164, + 3153, 3154, 3164, 3164, 3154, 3165, + 3154, 3155, 3165, 3165, 3155, 3166, + 3155, 3156, 3166, 3166, 3156, 3167, + 3157, 3158, 3168, 3168, 3158, 3169, + 3158, 3159, 3169, 3169, 3159, 3170, + 3159, 3160, 3170, 3170, 3160, 3171, + 3160, 3161, 3171, 3171, 3161, 3172, + 3161, 3162, 3172, 3172, 3162, 3173, + 3162, 3163, 3173, 3173, 3163, 3174, + 3163, 3164, 3174, 3174, 3164, 3175, + 3164, 3165, 3175, 3175, 3165, 3176, + 3165, 3166, 3176, 3176, 3166, 3177, + 3166, 3167, 3177, 3177, 3167, 3178, + 3168, 3169, 3179, 3179, 3169, 3180, + 3169, 3170, 3180, 3180, 3170, 3181, + 3170, 3171, 3181, 3181, 3171, 3182, + 3171, 3172, 3182, 3182, 3172, 3183, + 3172, 3173, 3183, 3183, 3173, 3184, + 3173, 3174, 3184, 3184, 3174, 3185, + 3174, 3175, 3185, 3185, 3175, 3186, + 3175, 3176, 3186, 3186, 3176, 3187, + 3176, 3177, 3187, 3187, 3177, 3188, + 3177, 3178, 3188, 3188, 3178, 3189, + 3179, 3180, 3190, 3190, 3180, 3191, + 3180, 3181, 3191, 3191, 3181, 3192, + 3181, 3182, 3192, 3192, 3182, 3193, + 3182, 3183, 3193, 3193, 3183, 3194, + 3183, 3184, 3194, 3194, 3184, 3195, + 3184, 3185, 3195, 3195, 3185, 3196, + 3185, 3186, 3196, 3196, 3186, 3197, + 3186, 3187, 3197, 3197, 3187, 3198, + 3187, 3188, 3198, 3198, 3188, 3199, + 3188, 3189, 3199, 3199, 3189, 3200, + 3190, 3191, 3201, 3201, 3191, 3202, + 3191, 3192, 3202, 3202, 3192, 3203, + 3192, 3193, 3203, 3203, 3193, 3204, + 3193, 3194, 3204, 3204, 3194, 3205, + 3194, 3195, 3205, 3205, 3195, 3206, + 3195, 3196, 3206, 3206, 3196, 3207, + 3196, 3197, 3207, 3207, 3197, 3208, + 3197, 3198, 3208, 3208, 3198, 3209, + 3198, 3199, 3209, 3209, 3199, 3210, + 3199, 3200, 3210, 3210, 3200, 3211, + 3201, 3202, 3212, 3212, 3202, 3213, + 3202, 3203, 3213, 3213, 3203, 3214, + 3203, 3204, 3214, 3214, 3204, 3215, + 3204, 3205, 3215, 3215, 3205, 3216, + 3205, 3206, 3216, 3216, 3206, 3217, + 3206, 3207, 3217, 3217, 3207, 3218, + 3207, 3208, 3218, 3218, 3208, 3219, + 3208, 3209, 3219, 3219, 3209, 3220, + 3209, 3210, 3220, 3220, 3210, 3221, + 3210, 3211, 3221, 3221, 3211, 3222, + 3212, 3213, 3223, 3223, 3213, 3224, + 3213, 3214, 3224, 3224, 3214, 3225, + 3214, 3215, 3225, 3225, 3215, 3226, + 3215, 3216, 3226, 3226, 3216, 3227, + 3216, 3217, 3227, 3227, 3217, 3228, + 3217, 3218, 3228, 3228, 3218, 3229, + 3218, 3219, 3229, 3229, 3219, 3230, + 3219, 3220, 3230, 3230, 3220, 3231, + 3220, 3221, 3231, 3231, 3221, 3232, + 3221, 3222, 3232, 3232, 3222, 3233, + 3223, 3224, 3234, 3234, 3224, 3235, + 3224, 3225, 3235, 3235, 3225, 3236, + 3225, 3226, 3236, 3236, 3226, 3237, + 3226, 3227, 3237, 3237, 3227, 3238, + 3227, 3228, 3238, 3238, 3228, 3239, + 3228, 3229, 3239, 3239, 3229, 3240, + 3229, 3230, 3240, 3240, 3230, 3241, + 3230, 3231, 3241, 3241, 3231, 3242, + 3231, 3232, 3242, 3242, 3232, 3243, + 3232, 3233, 3243, 3243, 3233, 3244, + 3234, 3235, 3245, 3245, 3235, 3246, + 3235, 3236, 3246, 3246, 3236, 3247, + 3236, 3237, 3247, 3247, 3237, 3248, + 3237, 3238, 3248, 3248, 3238, 3249, + 3238, 3239, 3249, 3249, 3239, 3250, + 3239, 3240, 3250, 3250, 3240, 3251, + 3240, 3241, 3251, 3251, 3241, 3252, + 3241, 3242, 3252, 3252, 3242, 3253, + 3242, 3243, 3253, 3253, 3243, 3254, + 3243, 3244, 3254, 3254, 3244, 3255, + 3245, 3246, 3256, 3256, 3246, 3257, + 3246, 3247, 3257, 3257, 3247, 3258, + 3247, 3248, 3258, 3258, 3248, 3259, + 3248, 3249, 3259, 3259, 3249, 3260, + 3249, 3250, 3260, 3260, 3250, 3261, + 3250, 3251, 3261, 3261, 3251, 3262, + 3251, 3252, 3262, 3262, 3252, 3263, + 3252, 3253, 3263, 3263, 3253, 3264, + 3253, 3254, 3264, 3264, 3254, 3265, + 3254, 3255, 3265, 3265, 3255, 3266, + 3267, 3268, 3278, 3278, 3268, 3279, + 3268, 3269, 3279, 3279, 3269, 3280, + 3269, 3270, 3280, 3280, 3270, 3281, + 3270, 3271, 3281, 3281, 3271, 3282, + 3271, 3272, 3282, 3282, 3272, 3283, + 3272, 3273, 3283, 3283, 3273, 3284, + 3273, 3274, 3284, 3284, 3274, 3285, + 3274, 3275, 3285, 3285, 3275, 3286, + 3275, 3276, 3286, 3286, 3276, 3287, + 3276, 3277, 3287, 3287, 3277, 3288, + 3278, 3279, 3289, 3289, 3279, 3290, + 3279, 3280, 3290, 3290, 3280, 3291, + 3280, 3281, 3291, 3291, 3281, 3292, + 3281, 3282, 3292, 3292, 3282, 3293, + 3282, 3283, 3293, 3293, 3283, 3294, + 3283, 3284, 3294, 3294, 3284, 3295, + 3284, 3285, 3295, 3295, 3285, 3296, + 3285, 3286, 3296, 3296, 3286, 3297, + 3286, 3287, 3297, 3297, 3287, 3298, + 3287, 3288, 3298, 3298, 3288, 3299, + 3289, 3290, 3300, 3300, 3290, 3301, + 3290, 3291, 3301, 3301, 3291, 3302, + 3291, 3292, 3302, 3302, 3292, 3303, + 3292, 3293, 3303, 3303, 3293, 3304, + 3293, 3294, 3304, 3304, 3294, 3305, + 3294, 3295, 3305, 3305, 3295, 3306, + 3295, 3296, 3306, 3306, 3296, 3307, + 3296, 3297, 3307, 3307, 3297, 3308, + 3297, 3298, 3308, 3308, 3298, 3309, + 3298, 3299, 3309, 3309, 3299, 3310, + 3300, 3301, 3311, 3311, 3301, 3312, + 3301, 3302, 3312, 3312, 3302, 3313, + 3302, 3303, 3313, 3313, 3303, 3314, + 3303, 3304, 3314, 3314, 3304, 3315, + 3304, 3305, 3315, 3315, 3305, 3316, + 3305, 3306, 3316, 3316, 3306, 3317, + 3306, 3307, 3317, 3317, 3307, 3318, + 3307, 3308, 3318, 3318, 3308, 3319, + 3308, 3309, 3319, 3319, 3309, 3320, + 3309, 3310, 3320, 3320, 3310, 3321, + 3311, 3312, 3322, 3322, 3312, 3323, + 3312, 3313, 3323, 3323, 3313, 3324, + 3313, 3314, 3324, 3324, 3314, 3325, + 3314, 3315, 3325, 3325, 3315, 3326, + 3315, 3316, 3326, 3326, 3316, 3327, + 3316, 3317, 3327, 3327, 3317, 3328, + 3317, 3318, 3328, 3328, 3318, 3329, + 3318, 3319, 3329, 3329, 3319, 3330, + 3319, 3320, 3330, 3330, 3320, 3331, + 3320, 3321, 3331, 3331, 3321, 3332, + 3322, 3323, 3333, 3333, 3323, 3334, + 3323, 3324, 3334, 3334, 3324, 3335, + 3324, 3325, 3335, 3335, 3325, 3336, + 3325, 3326, 3336, 3336, 3326, 3337, + 3326, 3327, 3337, 3337, 3327, 3338, + 3327, 3328, 3338, 3338, 3328, 3339, + 3328, 3329, 3339, 3339, 3329, 3340, + 3329, 3330, 3340, 3340, 3330, 3341, + 3330, 3331, 3341, 3341, 3331, 3342, + 3331, 3332, 3342, 3342, 3332, 3343, + 3333, 3334, 3344, 3344, 3334, 3345, + 3334, 3335, 3345, 3345, 3335, 3346, + 3335, 3336, 3346, 3346, 3336, 3347, + 3336, 3337, 3347, 3347, 3337, 3348, + 3337, 3338, 3348, 3348, 3338, 3349, + 3338, 3339, 3349, 3349, 3339, 3350, + 3339, 3340, 3350, 3350, 3340, 3351, + 3340, 3341, 3351, 3351, 3341, 3352, + 3341, 3342, 3352, 3352, 3342, 3353, + 3342, 3343, 3353, 3353, 3343, 3354, + 3344, 3345, 3355, 3355, 3345, 3356, + 3345, 3346, 3356, 3356, 3346, 3357, + 3346, 3347, 3357, 3357, 3347, 3358, + 3347, 3348, 3358, 3358, 3348, 3359, + 3348, 3349, 3359, 3359, 3349, 3360, + 3349, 3350, 3360, 3360, 3350, 3361, + 3350, 3351, 3361, 3361, 3351, 3362, + 3351, 3352, 3362, 3362, 3352, 3363, + 3352, 3353, 3363, 3363, 3353, 3364, + 3353, 3354, 3364, 3364, 3354, 3365, + 3355, 3356, 3366, 3366, 3356, 3367, + 3356, 3357, 3367, 3367, 3357, 3368, + 3357, 3358, 3368, 3368, 3358, 3369, + 3358, 3359, 3369, 3369, 3359, 3370, + 3359, 3360, 3370, 3370, 3360, 3371, + 3360, 3361, 3371, 3371, 3361, 3372, + 3361, 3362, 3372, 3372, 3362, 3373, + 3362, 3363, 3373, 3373, 3363, 3374, + 3363, 3364, 3374, 3374, 3364, 3375, + 3364, 3365, 3375, 3375, 3365, 3376, + 3366, 3367, 3377, 3377, 3367, 3378, + 3367, 3368, 3378, 3378, 3368, 3379, + 3368, 3369, 3379, 3379, 3369, 3380, + 3369, 3370, 3380, 3380, 3370, 3381, + 3370, 3371, 3381, 3381, 3371, 3382, + 3371, 3372, 3382, 3382, 3372, 3383, + 3372, 3373, 3383, 3383, 3373, 3384, + 3373, 3374, 3384, 3384, 3374, 3385, + 3374, 3375, 3385, 3385, 3375, 3386, + 3375, 3376, 3386, 3386, 3376, 3387, + 3388, 3389, 3399, 3399, 3389, 3400, + 3389, 3390, 3400, 3400, 3390, 3401, + 3390, 3391, 3401, 3401, 3391, 3402, + 3391, 3392, 3402, 3402, 3392, 3403, + 3392, 3393, 3403, 3403, 3393, 3404, + 3393, 3394, 3404, 3404, 3394, 3405, + 3394, 3395, 3405, 3405, 3395, 3406, + 3395, 3396, 3406, 3406, 3396, 3407, + 3396, 3397, 3407, 3407, 3397, 3408, + 3397, 3398, 3408, 3408, 3398, 3409, + 3399, 3400, 3410, 3410, 3400, 3411, + 3400, 3401, 3411, 3411, 3401, 3412, + 3401, 3402, 3412, 3412, 3402, 3413, + 3402, 3403, 3413, 3413, 3403, 3414, + 3403, 3404, 3414, 3414, 3404, 3415, + 3404, 3405, 3415, 3415, 3405, 3416, + 3405, 3406, 3416, 3416, 3406, 3417, + 3406, 3407, 3417, 3417, 3407, 3418, + 3407, 3408, 3418, 3418, 3408, 3419, + 3408, 3409, 3419, 3419, 3409, 3420, + 3410, 3411, 3421, 3421, 3411, 3422, + 3411, 3412, 3422, 3422, 3412, 3423, + 3412, 3413, 3423, 3423, 3413, 3424, + 3413, 3414, 3424, 3424, 3414, 3425, + 3414, 3415, 3425, 3425, 3415, 3426, + 3415, 3416, 3426, 3426, 3416, 3427, + 3416, 3417, 3427, 3427, 3417, 3428, + 3417, 3418, 3428, 3428, 3418, 3429, + 3418, 3419, 3429, 3429, 3419, 3430, + 3419, 3420, 3430, 3430, 3420, 3431, + 3421, 3422, 3432, 3432, 3422, 3433, + 3422, 3423, 3433, 3433, 3423, 3434, + 3423, 3424, 3434, 3434, 3424, 3435, + 3424, 3425, 3435, 3435, 3425, 3436, + 3425, 3426, 3436, 3436, 3426, 3437, + 3426, 3427, 3437, 3437, 3427, 3438, + 3427, 3428, 3438, 3438, 3428, 3439, + 3428, 3429, 3439, 3439, 3429, 3440, + 3429, 3430, 3440, 3440, 3430, 3441, + 3430, 3431, 3441, 3441, 3431, 3442, + 3432, 3433, 3443, 3443, 3433, 3444, + 3433, 3434, 3444, 3444, 3434, 3445, + 3434, 3435, 3445, 3445, 3435, 3446, + 3435, 3436, 3446, 3446, 3436, 3447, + 3436, 3437, 3447, 3447, 3437, 3448, + 3437, 3438, 3448, 3448, 3438, 3449, + 3438, 3439, 3449, 3449, 3439, 3450, + 3439, 3440, 3450, 3450, 3440, 3451, + 3440, 3441, 3451, 3451, 3441, 3452, + 3441, 3442, 3452, 3452, 3442, 3453, + 3443, 3444, 3454, 3454, 3444, 3455, + 3444, 3445, 3455, 3455, 3445, 3456, + 3445, 3446, 3456, 3456, 3446, 3457, + 3446, 3447, 3457, 3457, 3447, 3458, + 3447, 3448, 3458, 3458, 3448, 3459, + 3448, 3449, 3459, 3459, 3449, 3460, + 3449, 3450, 3460, 3460, 3450, 3461, + 3450, 3451, 3461, 3461, 3451, 3462, + 3451, 3452, 3462, 3462, 3452, 3463, + 3452, 3453, 3463, 3463, 3453, 3464, + 3454, 3455, 3465, 3465, 3455, 3466, + 3455, 3456, 3466, 3466, 3456, 3467, + 3456, 3457, 3467, 3467, 3457, 3468, + 3457, 3458, 3468, 3468, 3458, 3469, + 3458, 3459, 3469, 3469, 3459, 3470, + 3459, 3460, 3470, 3470, 3460, 3471, + 3460, 3461, 3471, 3471, 3461, 3472, + 3461, 3462, 3472, 3472, 3462, 3473, + 3462, 3463, 3473, 3473, 3463, 3474, + 3463, 3464, 3474, 3474, 3464, 3475, + 3465, 3466, 3476, 3476, 3466, 3477, + 3466, 3467, 3477, 3477, 3467, 3478, + 3467, 3468, 3478, 3478, 3468, 3479, + 3468, 3469, 3479, 3479, 3469, 3480, + 3469, 3470, 3480, 3480, 3470, 3481, + 3470, 3471, 3481, 3481, 3471, 3482, + 3471, 3472, 3482, 3482, 3472, 3483, + 3472, 3473, 3483, 3483, 3473, 3484, + 3473, 3474, 3484, 3484, 3474, 3485, + 3474, 3475, 3485, 3485, 3475, 3486, + 3476, 3477, 3487, 3487, 3477, 3488, + 3477, 3478, 3488, 3488, 3478, 3489, + 3478, 3479, 3489, 3489, 3479, 3490, + 3479, 3480, 3490, 3490, 3480, 3491, + 3480, 3481, 3491, 3491, 3481, 3492, + 3481, 3482, 3492, 3492, 3482, 3493, + 3482, 3483, 3493, 3493, 3483, 3494, + 3483, 3484, 3494, 3494, 3484, 3495, + 3484, 3485, 3495, 3495, 3485, 3496, + 3485, 3486, 3496, 3496, 3486, 3497, + 3487, 3488, 3498, 3498, 3488, 3499, + 3488, 3489, 3499, 3499, 3489, 3500, + 3489, 3490, 3500, 3500, 3490, 3501, + 3490, 3491, 3501, 3501, 3491, 3502, + 3491, 3492, 3502, 3502, 3492, 3503, + 3492, 3493, 3503, 3503, 3493, 3504, + 3493, 3494, 3504, 3504, 3494, 3505, + 3494, 3495, 3505, 3505, 3495, 3506, + 3495, 3496, 3506, 3506, 3496, 3507, + 3496, 3497, 3507, 3507, 3497, 3508, + 3509, 3510, 3520, 3520, 3510, 3521, + 3510, 3511, 3521, 3521, 3511, 3522, + 3511, 3512, 3522, 3522, 3512, 3523, + 3512, 3513, 3523, 3523, 3513, 3524, + 3513, 3514, 3524, 3524, 3514, 3525, + 3514, 3515, 3525, 3525, 3515, 3526, + 3515, 3516, 3526, 3526, 3516, 3527, + 3516, 3517, 3527, 3527, 3517, 3528, + 3517, 3518, 3528, 3528, 3518, 3529, + 3518, 3519, 3529, 3529, 3519, 3530, + 3520, 3521, 3531, 3531, 3521, 3532, + 3521, 3522, 3532, 3532, 3522, 3533, + 3522, 3523, 3533, 3533, 3523, 3534, + 3523, 3524, 3534, 3534, 3524, 3535, + 3524, 3525, 3535, 3535, 3525, 3536, + 3525, 3526, 3536, 3536, 3526, 3537, + 3526, 3527, 3537, 3537, 3527, 3538, + 3527, 3528, 3538, 3538, 3528, 3539, + 3528, 3529, 3539, 3539, 3529, 3540, + 3529, 3530, 3540, 3540, 3530, 3541, + 3531, 3532, 3542, 3542, 3532, 3543, + 3532, 3533, 3543, 3543, 3533, 3544, + 3533, 3534, 3544, 3544, 3534, 3545, + 3534, 3535, 3545, 3545, 3535, 3546, + 3535, 3536, 3546, 3546, 3536, 3547, + 3536, 3537, 3547, 3547, 3537, 3548, + 3537, 3538, 3548, 3548, 3538, 3549, + 3538, 3539, 3549, 3549, 3539, 3550, + 3539, 3540, 3550, 3550, 3540, 3551, + 3540, 3541, 3551, 3551, 3541, 3552, + 3542, 3543, 3553, 3553, 3543, 3554, + 3543, 3544, 3554, 3554, 3544, 3555, + 3544, 3545, 3555, 3555, 3545, 3556, + 3545, 3546, 3556, 3556, 3546, 3557, + 3546, 3547, 3557, 3557, 3547, 3558, + 3547, 3548, 3558, 3558, 3548, 3559, + 3548, 3549, 3559, 3559, 3549, 3560, + 3549, 3550, 3560, 3560, 3550, 3561, + 3550, 3551, 3561, 3561, 3551, 3562, + 3551, 3552, 3562, 3562, 3552, 3563, + 3553, 3554, 3564, 3564, 3554, 3565, + 3554, 3555, 3565, 3565, 3555, 3566, + 3555, 3556, 3566, 3566, 3556, 3567, + 3556, 3557, 3567, 3567, 3557, 3568, + 3557, 3558, 3568, 3568, 3558, 3569, + 3558, 3559, 3569, 3569, 3559, 3570, + 3559, 3560, 3570, 3570, 3560, 3571, + 3560, 3561, 3571, 3571, 3561, 3572, + 3561, 3562, 3572, 3572, 3562, 3573, + 3562, 3563, 3573, 3573, 3563, 3574, + 3564, 3565, 3575, 3575, 3565, 3576, + 3565, 3566, 3576, 3576, 3566, 3577, + 3566, 3567, 3577, 3577, 3567, 3578, + 3567, 3568, 3578, 3578, 3568, 3579, + 3568, 3569, 3579, 3579, 3569, 3580, + 3569, 3570, 3580, 3580, 3570, 3581, + 3570, 3571, 3581, 3581, 3571, 3582, + 3571, 3572, 3582, 3582, 3572, 3583, + 3572, 3573, 3583, 3583, 3573, 3584, + 3573, 3574, 3584, 3584, 3574, 3585, + 3575, 3576, 3586, 3586, 3576, 3587, + 3576, 3577, 3587, 3587, 3577, 3588, + 3577, 3578, 3588, 3588, 3578, 3589, + 3578, 3579, 3589, 3589, 3579, 3590, + 3579, 3580, 3590, 3590, 3580, 3591, + 3580, 3581, 3591, 3591, 3581, 3592, + 3581, 3582, 3592, 3592, 3582, 3593, + 3582, 3583, 3593, 3593, 3583, 3594, + 3583, 3584, 3594, 3594, 3584, 3595, + 3584, 3585, 3595, 3595, 3585, 3596, + 3586, 3587, 3597, 3597, 3587, 3598, + 3587, 3588, 3598, 3598, 3588, 3599, + 3588, 3589, 3599, 3599, 3589, 3600, + 3589, 3590, 3600, 3600, 3590, 3601, + 3590, 3591, 3601, 3601, 3591, 3602, + 3591, 3592, 3602, 3602, 3592, 3603, + 3592, 3593, 3603, 3603, 3593, 3604, + 3593, 3594, 3604, 3604, 3594, 3605, + 3594, 3595, 3605, 3605, 3595, 3606, + 3595, 3596, 3606, 3606, 3596, 3607, + 3597, 3598, 3608, 3608, 3598, 3609, + 3598, 3599, 3609, 3609, 3599, 3610, + 3599, 3600, 3610, 3610, 3600, 3611, + 3600, 3601, 3611, 3611, 3601, 3612, + 3601, 3602, 3612, 3612, 3602, 3613, + 3602, 3603, 3613, 3613, 3603, 3614, + 3603, 3604, 3614, 3614, 3604, 3615, + 3604, 3605, 3615, 3615, 3605, 3616, + 3605, 3606, 3616, 3616, 3606, 3617, + 3606, 3607, 3617, 3617, 3607, 3618, + 3608, 3609, 3619, 3619, 3609, 3620, + 3609, 3610, 3620, 3620, 3610, 3621, + 3610, 3611, 3621, 3621, 3611, 3622, + 3611, 3612, 3622, 3622, 3612, 3623, + 3612, 3613, 3623, 3623, 3613, 3624, + 3613, 3614, 3624, 3624, 3614, 3625, + 3614, 3615, 3625, 3625, 3615, 3626, + 3615, 3616, 3626, 3626, 3616, 3627, + 3616, 3617, 3627, 3627, 3617, 3628, + 3617, 3618, 3628, 3628, 3618, 3629, + 3630, 3631, 3641, 3641, 3631, 3642, + 3631, 3632, 3642, 3642, 3632, 3643, + 3632, 3633, 3643, 3643, 3633, 3644, + 3633, 3634, 3644, 3644, 3634, 3645, + 3634, 3635, 3645, 3645, 3635, 3646, + 3635, 3636, 3646, 3646, 3636, 3647, + 3636, 3637, 3647, 3647, 3637, 3648, + 3637, 3638, 3648, 3648, 3638, 3649, + 3638, 3639, 3649, 3649, 3639, 3650, + 3639, 3640, 3650, 3650, 3640, 3651, + 3641, 3642, 3652, 3652, 3642, 3653, + 3642, 3643, 3653, 3653, 3643, 3654, + 3643, 3644, 3654, 3654, 3644, 3655, + 3644, 3645, 3655, 3655, 3645, 3656, + 3645, 3646, 3656, 3656, 3646, 3657, + 3646, 3647, 3657, 3657, 3647, 3658, + 3647, 3648, 3658, 3658, 3648, 3659, + 3648, 3649, 3659, 3659, 3649, 3660, + 3649, 3650, 3660, 3660, 3650, 3661, + 3650, 3651, 3661, 3661, 3651, 3662, + 3652, 3653, 3663, 3663, 3653, 3664, + 3653, 3654, 3664, 3664, 3654, 3665, + 3654, 3655, 3665, 3665, 3655, 3666, + 3655, 3656, 3666, 3666, 3656, 3667, + 3656, 3657, 3667, 3667, 3657, 3668, + 3657, 3658, 3668, 3668, 3658, 3669, + 3658, 3659, 3669, 3669, 3659, 3670, + 3659, 3660, 3670, 3670, 3660, 3671, + 3660, 3661, 3671, 3671, 3661, 3672, + 3661, 3662, 3672, 3672, 3662, 3673, + 3663, 3664, 3674, 3674, 3664, 3675, + 3664, 3665, 3675, 3675, 3665, 3676, + 3665, 3666, 3676, 3676, 3666, 3677, + 3666, 3667, 3677, 3677, 3667, 3678, + 3667, 3668, 3678, 3678, 3668, 3679, + 3668, 3669, 3679, 3679, 3669, 3680, + 3669, 3670, 3680, 3680, 3670, 3681, + 3670, 3671, 3681, 3681, 3671, 3682, + 3671, 3672, 3682, 3682, 3672, 3683, + 3672, 3673, 3683, 3683, 3673, 3684, + 3674, 3675, 3685, 3685, 3675, 3686, + 3675, 3676, 3686, 3686, 3676, 3687, + 3676, 3677, 3687, 3687, 3677, 3688, + 3677, 3678, 3688, 3688, 3678, 3689, + 3678, 3679, 3689, 3689, 3679, 3690, + 3679, 3680, 3690, 3690, 3680, 3691, + 3680, 3681, 3691, 3691, 3681, 3692, + 3681, 3682, 3692, 3692, 3682, 3693, + 3682, 3683, 3693, 3693, 3683, 3694, + 3683, 3684, 3694, 3694, 3684, 3695, + 3685, 3686, 3696, 3696, 3686, 3697, + 3686, 3687, 3697, 3697, 3687, 3698, + 3687, 3688, 3698, 3698, 3688, 3699, + 3688, 3689, 3699, 3699, 3689, 3700, + 3689, 3690, 3700, 3700, 3690, 3701, + 3690, 3691, 3701, 3701, 3691, 3702, + 3691, 3692, 3702, 3702, 3692, 3703, + 3692, 3693, 3703, 3703, 3693, 3704, + 3693, 3694, 3704, 3704, 3694, 3705, + 3694, 3695, 3705, 3705, 3695, 3706, + 3696, 3697, 3707, 3707, 3697, 3708, + 3697, 3698, 3708, 3708, 3698, 3709, + 3698, 3699, 3709, 3709, 3699, 3710, + 3699, 3700, 3710, 3710, 3700, 3711, + 3700, 3701, 3711, 3711, 3701, 3712, + 3701, 3702, 3712, 3712, 3702, 3713, + 3702, 3703, 3713, 3713, 3703, 3714, + 3703, 3704, 3714, 3714, 3704, 3715, + 3704, 3705, 3715, 3715, 3705, 3716, + 3705, 3706, 3716, 3716, 3706, 3717, + 3707, 3708, 3718, 3718, 3708, 3719, + 3708, 3709, 3719, 3719, 3709, 3720, + 3709, 3710, 3720, 3720, 3710, 3721, + 3710, 3711, 3721, 3721, 3711, 3722, + 3711, 3712, 3722, 3722, 3712, 3723, + 3712, 3713, 3723, 3723, 3713, 3724, + 3713, 3714, 3724, 3724, 3714, 3725, + 3714, 3715, 3725, 3725, 3715, 3726, + 3715, 3716, 3726, 3726, 3716, 3727, + 3716, 3717, 3727, 3727, 3717, 3728, + 3718, 3719, 3729, 3729, 3719, 3730, + 3719, 3720, 3730, 3730, 3720, 3731, + 3720, 3721, 3731, 3731, 3721, 3732, + 3721, 3722, 3732, 3732, 3722, 3733, + 3722, 3723, 3733, 3733, 3723, 3734, + 3723, 3724, 3734, 3734, 3724, 3735, + 3724, 3725, 3735, 3735, 3725, 3736, + 3725, 3726, 3736, 3736, 3726, 3737, + 3726, 3727, 3737, 3737, 3727, 3738, + 3727, 3728, 3738, 3738, 3728, 3739, + 3729, 3730, 3740, 3740, 3730, 3741, + 3730, 3731, 3741, 3741, 3731, 3742, + 3731, 3732, 3742, 3742, 3732, 3743, + 3732, 3733, 3743, 3743, 3733, 3744, + 3733, 3734, 3744, 3744, 3734, 3745, + 3734, 3735, 3745, 3745, 3735, 3746, + 3735, 3736, 3746, 3746, 3736, 3747, + 3736, 3737, 3747, 3747, 3737, 3748, + 3737, 3738, 3748, 3748, 3738, 3749, + 3738, 3739, 3749, 3749, 3739, 3750, + 3751, 3752, 3762, 3762, 3752, 3763, + 3752, 3753, 3763, 3763, 3753, 3764, + 3753, 3754, 3764, 3764, 3754, 3765, + 3754, 3755, 3765, 3765, 3755, 3766, + 3755, 3756, 3766, 3766, 3756, 3767, + 3756, 3757, 3767, 3767, 3757, 3768, + 3757, 3758, 3768, 3768, 3758, 3769, + 3758, 3759, 3769, 3769, 3759, 3770, + 3759, 3760, 3770, 3770, 3760, 3771, + 3760, 3761, 3771, 3771, 3761, 3772, + 3762, 3763, 3773, 3773, 3763, 3774, + 3763, 3764, 3774, 3774, 3764, 3775, + 3764, 3765, 3775, 3775, 3765, 3776, + 3765, 3766, 3776, 3776, 3766, 3777, + 3766, 3767, 3777, 3777, 3767, 3778, + 3767, 3768, 3778, 3778, 3768, 3779, + 3768, 3769, 3779, 3779, 3769, 3780, + 3769, 3770, 3780, 3780, 3770, 3781, + 3770, 3771, 3781, 3781, 3771, 3782, + 3771, 3772, 3782, 3782, 3772, 3783, + 3773, 3774, 3784, 3784, 3774, 3785, + 3774, 3775, 3785, 3785, 3775, 3786, + 3775, 3776, 3786, 3786, 3776, 3787, + 3776, 3777, 3787, 3787, 3777, 3788, + 3777, 3778, 3788, 3788, 3778, 3789, + 3778, 3779, 3789, 3789, 3779, 3790, + 3779, 3780, 3790, 3790, 3780, 3791, + 3780, 3781, 3791, 3791, 3781, 3792, + 3781, 3782, 3792, 3792, 3782, 3793, + 3782, 3783, 3793, 3793, 3783, 3794, + 3784, 3785, 3795, 3795, 3785, 3796, + 3785, 3786, 3796, 3796, 3786, 3797, + 3786, 3787, 3797, 3797, 3787, 3798, + 3787, 3788, 3798, 3798, 3788, 3799, + 3788, 3789, 3799, 3799, 3789, 3800, + 3789, 3790, 3800, 3800, 3790, 3801, + 3790, 3791, 3801, 3801, 3791, 3802, + 3791, 3792, 3802, 3802, 3792, 3803, + 3792, 3793, 3803, 3803, 3793, 3804, + 3793, 3794, 3804, 3804, 3794, 3805, + 3795, 3796, 3806, 3806, 3796, 3807, + 3796, 3797, 3807, 3807, 3797, 3808, + 3797, 3798, 3808, 3808, 3798, 3809, + 3798, 3799, 3809, 3809, 3799, 3810, + 3799, 3800, 3810, 3810, 3800, 3811, + 3800, 3801, 3811, 3811, 3801, 3812, + 3801, 3802, 3812, 3812, 3802, 3813, + 3802, 3803, 3813, 3813, 3803, 3814, + 3803, 3804, 3814, 3814, 3804, 3815, + 3804, 3805, 3815, 3815, 3805, 3816, + 3806, 3807, 3817, 3817, 3807, 3818, + 3807, 3808, 3818, 3818, 3808, 3819, + 3808, 3809, 3819, 3819, 3809, 3820, + 3809, 3810, 3820, 3820, 3810, 3821, + 3810, 3811, 3821, 3821, 3811, 3822, + 3811, 3812, 3822, 3822, 3812, 3823, + 3812, 3813, 3823, 3823, 3813, 3824, + 3813, 3814, 3824, 3824, 3814, 3825, + 3814, 3815, 3825, 3825, 3815, 3826, + 3815, 3816, 3826, 3826, 3816, 3827, + 3817, 3818, 3828, 3828, 3818, 3829, + 3818, 3819, 3829, 3829, 3819, 3830, + 3819, 3820, 3830, 3830, 3820, 3831, + 3820, 3821, 3831, 3831, 3821, 3832, + 3821, 3822, 3832, 3832, 3822, 3833, + 3822, 3823, 3833, 3833, 3823, 3834, + 3823, 3824, 3834, 3834, 3824, 3835, + 3824, 3825, 3835, 3835, 3825, 3836, + 3825, 3826, 3836, 3836, 3826, 3837, + 3826, 3827, 3837, 3837, 3827, 3838, + 3828, 3829, 3839, 3839, 3829, 3840, + 3829, 3830, 3840, 3840, 3830, 3841, + 3830, 3831, 3841, 3841, 3831, 3842, + 3831, 3832, 3842, 3842, 3832, 3843, + 3832, 3833, 3843, 3843, 3833, 3844, + 3833, 3834, 3844, 3844, 3834, 3845, + 3834, 3835, 3845, 3845, 3835, 3846, + 3835, 3836, 3846, 3846, 3836, 3847, + 3836, 3837, 3847, 3847, 3837, 3848, + 3837, 3838, 3848, 3848, 3838, 3849, + 3839, 3840, 3850, 3850, 3840, 3851, + 3840, 3841, 3851, 3851, 3841, 3852, + 3841, 3842, 3852, 3852, 3842, 3853, + 3842, 3843, 3853, 3853, 3843, 3854, + 3843, 3844, 3854, 3854, 3844, 3855, + 3844, 3845, 3855, 3855, 3845, 3856, + 3845, 3846, 3856, 3856, 3846, 3857, + 3846, 3847, 3857, 3857, 3847, 3858, + 3847, 3848, 3858, 3858, 3848, 3859, + 3848, 3849, 3859, 3859, 3849, 3860, + 3850, 3851, 3861, 3861, 3851, 3862, + 3851, 3852, 3862, 3862, 3852, 3863, + 3852, 3853, 3863, 3863, 3853, 3864, + 3853, 3854, 3864, 3864, 3854, 3865, + 3854, 3855, 3865, 3865, 3855, 3866, + 3855, 3856, 3866, 3866, 3856, 3867, + 3856, 3857, 3867, 3867, 3857, 3868, + 3857, 3858, 3868, 3868, 3858, 3869, + 3858, 3859, 3869, 3869, 3859, 3870, + 3859, 3860, 3870, 3870, 3860, 3871, + }; + +}; diff --git a/projects/sph/.gitignore b/projects/sph/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3968dc30067c92efde65779bc5eebecf167dfc75 --- /dev/null +++ b/projects/sph/.gitignore @@ -0,0 +1 @@ +sph \ No newline at end of file diff --git a/projects/sph/CMakeLists.txt b/projects/sph/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..685004f7be9d8f4ef3e0409543dbb0f964d55e9c --- /dev/null +++ b/projects/sph/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.16) +project(sph) + +# setting c++ standard for the project +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# adding source files to the project +add_project(sph + src/main.cpp + src/Particle.hpp + src/Particle.cpp + src/PipelineInit.hpp + src/PipelineInit.cpp) + +# including headers of dependencies and the VkCV framework +target_include_directories(sph SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_testing_include} + ${vkcv_camera_include} + ${vkcv_shader_compiler_include} + ${vkcv_effects_include}) + +# linking with libraries from all dependencies and the VkCV framework +target_link_libraries(sph + vkcv + vkcv_testing + vkcv_camera + vkcv_shader_compiler + vkcv_effects) diff --git a/projects/sph/README.md b/projects/sph/README.md new file mode 100644 index 0000000000000000000000000000000000000000..90e0452998158031b10022407cd3312f54beea9a --- /dev/null +++ b/projects/sph/README.md @@ -0,0 +1,10 @@ +# SPH +An example project to show the implementation of an SPH fluid simulation with the VkCV framework + + + +## Details + +The project shows off an example implementation of an SPH fluid simulation which are widely used +in computer graphics to simulate the behavior of fluids with a discrete amount of particles. The +implementation is able to run in realtime allowing interactive use. diff --git a/projects/sph/shaders/flip.comp b/projects/sph/shaders/flip.comp new file mode 100644 index 0000000000000000000000000000000000000000..f5cd7bf3ea5ed8d04de970c816989230421c0b52 --- /dev/null +++ b/projects/sph/shaders/flip.comp @@ -0,0 +1,53 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; +}; + +layout(std430, binding = 1) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 0) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if(id >= int(particleCount)) + { + return; + } + + outParticle[id].force = inParticle[id].force; + outParticle[id].density = inParticle[id].density; + outParticle[id].pressure = inParticle[id].pressure; + outParticle[id].position = inParticle[id].position; + outParticle[id].velocity = inParticle[id].velocity; +} diff --git a/projects/sph/shaders/force.comp b/projects/sph/shaders/force.comp new file mode 100644 index 0000000000000000000000000000000000000000..694ddbdf7009d4e493a5f8dd8ba462e42d1e8b68 --- /dev/null +++ b/projects/sph/shaders/force.comp @@ -0,0 +1,152 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +const float PI = 3.1415926535897932384626433832795; + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; + +}; + +layout(std430, binding = 1) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 0) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +float grad_spiky(float r) +{ + return -45.f / (PI * pow(h, 6)) * pow((h-r), 2); +} + +float laplacian(float r) +{ + return (45.f / (PI * pow(h,6)) * (h - r)); +} + +vec3 pressureForce = vec3(0, 0, 0); +vec3 viscosityForce = vec3(0, 0, 0); +vec3 externalForce = vec3(0, 0, 0); + +struct ParticleData +{ + vec3 position; + float density; + vec3 velocity; + float pressure; +}; + +shared ParticleData particle_data [256]; + +void main() { + uint id = gl_GlobalInvocationID.x; + + if (id >= int(particleCount)) { + particle_data[gl_LocalInvocationIndex].position = vec3(0.0f); + particle_data[gl_LocalInvocationIndex].density = 0.0f; + particle_data[gl_LocalInvocationIndex].velocity = vec3(0.0f); + particle_data[gl_LocalInvocationIndex].pressure = 0.0f; + } else { + particle_data[gl_LocalInvocationIndex].position = inParticle[id].position; + particle_data[gl_LocalInvocationIndex].density = inParticle[id].density; + particle_data[gl_LocalInvocationIndex].velocity = inParticle[id].velocity; + particle_data[gl_LocalInvocationIndex].pressure = inParticle[id].pressure; + } + + uint index_offset = gl_WorkGroupID.x * gl_WorkGroupSize.x; + uint group_size = min(index_offset + gl_WorkGroupSize.x, int(particleCount)) - index_offset; + + memoryBarrierShared(); + barrier(); + + const float h6 = pow(h, 6); + externalForce = particle_data[gl_LocalInvocationIndex].density * gravity * vec3(-gravityDir.x,gravityDir.y,gravityDir.z); + + for(uint j = 1; j < group_size; j++) { + uint i = (gl_LocalInvocationIndex + j) % group_size; + + vec3 dir = particle_data[gl_LocalInvocationIndex].position - particle_data[i].position; + float dist = length(dir); + + if ((dist > 0.0f) && (dist <= h)) + { + const float h_dist = (h - dist); + + float laplacian = 45.f / (PI * h6) * h_dist; + float grad_spiky = -1.0f * laplacian * h_dist; + + pressureForce += mass * -(particle_data[gl_LocalInvocationIndex].pressure + particle_data[i].pressure)/(2.f * particle_data[i].density) * grad_spiky * normalize(dir); + viscosityForce += mass * (particle_data[i].velocity - particle_data[gl_LocalInvocationIndex].velocity)/particle_data[i].density * laplacian; + } + } + + for(uint i = 0; i < index_offset; i++) + { + vec3 dir = particle_data[gl_LocalInvocationIndex].position - inParticle[i].position; + float dist = length(dir); + + if ((dist > 0.0f) && (dist <= h)) + { + const float h_dist = (h - dist); + + float laplacian = 45.f / (PI * h6) * h_dist; + float grad_spiky = -1.0f * laplacian * h_dist; + + pressureForce += mass * -(particle_data[gl_LocalInvocationIndex].pressure + inParticle[i].pressure)/(2.f * inParticle[i].density) * grad_spiky * normalize(dir); + viscosityForce += mass * (inParticle[i].velocity - particle_data[gl_LocalInvocationIndex].velocity)/inParticle[i].density * laplacian; + } + } + + for(uint i = index_offset + group_size; i < int(particleCount); i++) + { + vec3 dir = particle_data[gl_LocalInvocationIndex].position - inParticle[i].position; + float dist = length(dir); + + if ((dist > 0.0f) && (dist <= h)) + { + const float h_dist = (h - dist); + + float laplacian = 45.f / (PI * h6) * h_dist; + float grad_spiky = -1.0f * laplacian * h_dist; + + pressureForce += mass * -(particle_data[gl_LocalInvocationIndex].pressure + inParticle[i].pressure)/(2.f * inParticle[i].density) * grad_spiky * normalize(dir); + viscosityForce += mass * (inParticle[i].velocity - particle_data[gl_LocalInvocationIndex].velocity)/inParticle[i].density * laplacian; + } + } + + viscosityForce *= viscosity; + + if (id < int(particleCount)) { + outParticle[id].force = externalForce + pressureForce + viscosityForce; + outParticle[id].density = particle_data[gl_LocalInvocationIndex].density; + outParticle[id].pressure = particle_data[gl_LocalInvocationIndex].pressure; + outParticle[id].position = particle_data[gl_LocalInvocationIndex].position; + outParticle[id].velocity = particle_data[gl_LocalInvocationIndex].velocity; + } +} diff --git a/projects/sph/shaders/particleShading.inc b/projects/sph/shaders/particleShading.inc new file mode 100644 index 0000000000000000000000000000000000000000..b2d1832b9ccd6ba05a585b59bdfdedd4729e80f8 --- /dev/null +++ b/projects/sph/shaders/particleShading.inc @@ -0,0 +1,6 @@ +float circleFactor(vec2 triangleCoordinates){ + // percentage of distance from center to circle edge + float p = clamp((0.4 - length(triangleCoordinates)) / 0.4, 0, 1); + // remapping for nice falloff + return sqrt(p); +} \ No newline at end of file diff --git a/projects/sph/shaders/particle_params.inc b/projects/sph/shaders/particle_params.inc new file mode 100644 index 0000000000000000000000000000000000000000..e35cce13be1bc84f045da276fe46666d0b852984 --- /dev/null +++ b/projects/sph/shaders/particle_params.inc @@ -0,0 +1,8 @@ +#define h 0.4 +#define mass 0.15 +#define gasConstant 3000 +#define offset 1800 +#define gravity -1000 +#define viscosity 1500 +#define ABSORBTION 0.9 +#define dt 0.0005 diff --git a/projects/sph/shaders/pressure.comp b/projects/sph/shaders/pressure.comp new file mode 100644 index 0000000000000000000000000000000000000000..8fa2e4762bddb3b9b28d8a3c184ceaaf7ab4421c --- /dev/null +++ b/projects/sph/shaders/pressure.comp @@ -0,0 +1,94 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +const float PI = 3.1415926535897932384626433832795; + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; + +}; + +layout(std430, binding = 0) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 1) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +float poly6(float r) +{ + return (315.f * pow((pow(h,2)-pow(r,2)), 3)/(64.f*PI*pow(h, 9))) * int(r<=h); +} + +float densitySum = 0.f; + +shared vec3 position_data [256]; + +void main() { + + uint id = gl_GlobalInvocationID.x; + + if (id >= int(particleCount)) { + position_data[gl_LocalInvocationIndex] = vec3(0.0f); + } else { + position_data[gl_LocalInvocationIndex] = inParticle[id].position; + } + + uint index_offset = gl_WorkGroupID.x * gl_WorkGroupSize.x; + uint group_size = min(index_offset + gl_WorkGroupSize.x, int(particleCount)) - index_offset; + + memoryBarrierShared(); + barrier(); + + for(uint j = 1; j < group_size; j++) { + uint i = (gl_LocalInvocationIndex + j) % group_size; + + float dist = distance(position_data[gl_LocalInvocationIndex], position_data[i]); + densitySum += mass * poly6(dist); + } + + for(uint i = 0; i < index_offset; i++) + { + float dist = distance(position_data[gl_LocalInvocationIndex], inParticle[i].position); + densitySum += mass * poly6(dist); + } + + for(uint i = index_offset + group_size; i < int(particleCount); i++) + { + float dist = distance(position_data[gl_LocalInvocationIndex], inParticle[i].position); + densitySum += mass * poly6(dist); + } + + if (id < int(particleCount)) { + outParticle[id].density = max(densitySum, 0.0000001f); + outParticle[id].pressure = max((densitySum - offset), 0.0000001f) * gasConstant; + outParticle[id].position = position_data[gl_LocalInvocationIndex]; + outParticle[id].velocity = inParticle[id].velocity; + outParticle[id].force = inParticle[id].force; + } +} diff --git a/projects/sph/shaders/shader.vert b/projects/sph/shaders/shader.vert new file mode 100644 index 0000000000000000000000000000000000000000..f5531ffa4f26d3652e8e35971c16af6dda2e3b45 --- /dev/null +++ b/projects/sph/shaders/shader.vert @@ -0,0 +1,49 @@ +#version 460 core +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 particle; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; +}; + +layout(std430, binding = 2) readonly buffer buffer_inParticle1 +{ + Particle inParticle1[]; +}; + +layout(std430, binding = 3) readonly buffer buffer_inParticle2 +{ + Particle inParticle2[]; +}; + +layout( push_constant ) uniform constants{ + mat4 view; + mat4 projection; +}; + +layout(location = 0) out vec2 passTriangleCoordinates; +layout(location = 1) out vec3 passVelocity; + +void main() +{ + int id = gl_InstanceIndex; + passVelocity = inParticle1[id].velocity; + + // particle position in view space + vec4 positionView = view * vec4(inParticle1[id].position, 1); + // by adding the triangle position in view space the mesh is always camera facing + positionView.xyz += particle; + // multiply with projection matrix for final position + gl_Position = projection * positionView; + + // 0.01 corresponds to vertex position size in main + float normalizationDivider = 0.012; + passTriangleCoordinates = particle.xy / normalizationDivider; +} \ No newline at end of file diff --git a/projects/sph/shaders/shader_water.frag b/projects/sph/shaders/shader_water.frag new file mode 100644 index 0000000000000000000000000000000000000000..2aee4ec692a2ada060a77389099b2c279e9c338c --- /dev/null +++ b/projects/sph/shaders/shader_water.frag @@ -0,0 +1,28 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "particleShading.inc" + +layout(location = 0) in vec2 passTriangleCoordinates; +layout(location = 1) in vec3 passVelocity; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform uColor { + vec4 color; +} Color; + +layout(set=0,binding=1) uniform uPosition{ + vec2 position; +} Position; + +void main() +{ + float p = length(passVelocity)/100.f; + outColor = vec3(0.f+p/3.f, 0.05f+p/2.f, 0.4f+p); + + // make the triangle look like a circle + outColor *= circleFactor(passTriangleCoordinates); + +} diff --git a/projects/sph/shaders/tonemapping.comp b/projects/sph/shaders/tonemapping.comp new file mode 100644 index 0000000000000000000000000000000000000000..26f0232d66e3475afdd1266c0cc6288b47ed1c38 --- /dev/null +++ b/projects/sph/shaders/tonemapping.comp @@ -0,0 +1,19 @@ +#version 440 + +layout(set=0, binding=0, rgba16f) uniform image2D inImage; +layout(set=0, binding=1, rgba8) uniform image2D outImage; + + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(inImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + vec3 linearColor = imageLoad(inImage, uv).rgb; + vec3 tonemapped = linearColor / (dot(linearColor, vec3(0.21, 0.71, 0.08)) + 1); // reinhard tonemapping + vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); + imageStore(outImage, uv, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/sph/shaders/updateData.comp b/projects/sph/shaders/updateData.comp new file mode 100644 index 0000000000000000000000000000000000000000..3c2321b0d2fcfdd7e6691da2df7e7609af8eb6cf --- /dev/null +++ b/projects/sph/shaders/updateData.comp @@ -0,0 +1,101 @@ +#version 450 core +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 256) in; + +struct Particle +{ + vec3 position; + float padding; + vec3 velocity; + float density; + vec3 force; + float pressure; + +}; + +layout(std430, binding = 0) readonly buffer buffer_inParticle +{ + Particle inParticle[]; +}; + +layout(std430, binding = 1) writeonly buffer buffer_outParticle +{ + Particle outParticle[]; +}; + +layout( push_constant ) uniform constants{ + float h; + float mass; + float gasConstant; + float offset; + float gravity; + float viscosity; + float ABSORBTION; + float dt; + vec3 gravityDir; + float particleCount; +}; + +void main() { + + uint id = gl_GlobalInvocationID.x; + + if(id >= int(particleCount)) + { + return; + } + + vec3 accel = inParticle[id].force / inParticle[id].density; + vec3 vel_new = inParticle[id].velocity + (dt * accel); + + vec3 out_force = inParticle[id].force; + float out_density = inParticle[id].density; + float out_pressure = inParticle[id].pressure; + + vec3 pos_new = inParticle[id].position + (dt * vel_new); + + // Ãœberprüfe Randbedingungen x + if (inParticle[id].position.x < -1.0) + { + vel_new = reflect(vel_new.xyz, vec3(1.f,0.f,0.f)) * ABSORBTION; + pos_new.x = -1.0 + 0.01f; + } + else if (inParticle[id].position.x > 1.0) + { + vel_new = reflect(vel_new,vec3(1.f,0.f,0.f)) * ABSORBTION; + pos_new.x = 1.0 - 0.01f; + } + + // Ãœberprüfe Randbedingungen y + if (inParticle[id].position.y < -1.0) + { + vel_new = reflect(vel_new,vec3(0.f,1.f,0.f)) * ABSORBTION; + pos_new.y = -1.0 + 0.01f; + + } + else if (inParticle[id].position.y > 1.0) + { + vel_new = reflect(vel_new,vec3(0.f,1.f,0.f)) * ABSORBTION; + pos_new.y = 1.0 - 0.01f; + } + + // Ãœberprüfe Randbedingungen z + if (inParticle[id].position.z < -1.0 ) + { + vel_new = reflect(vel_new,vec3(0.f,0.f,1.f)) * ABSORBTION; + pos_new.z = -1.0 + 0.01f; + } + else if (inParticle[id].position.z > 1.0 ) + { + vel_new = reflect(vel_new,vec3(0.f,0.f,1.f)) * ABSORBTION; + pos_new.z = 1.0 - 0.01f; + } + + outParticle[id].force = out_force; + outParticle[id].density = out_density; + outParticle[id].pressure = out_pressure; + outParticle[id].position = pos_new; + outParticle[id].velocity = vel_new; +} diff --git a/projects/sph/src/Particle.cpp b/projects/sph/src/Particle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..329236989b7cab502e7a4e1bb5aa27869bed53cb --- /dev/null +++ b/projects/sph/src/Particle.cpp @@ -0,0 +1,39 @@ + +#include "Particle.hpp" + +Particle::Particle(glm::vec3 position, glm::vec3 velocity) +: m_position(position), + m_velocity(velocity) +{ + m_density = 0.f; + m_force = glm::vec3(0.f); + m_pressure = 0.f; +} + +const glm::vec3& Particle::getPosition()const{ + return m_position; +} + +void Particle::setPosition( const glm::vec3 pos ){ + m_position = pos; +} + +const glm::vec3& Particle::getVelocity()const{ + return m_velocity; +} + +void Particle::setVelocity( const glm::vec3 vel ){ + m_velocity = vel; +} + +const float& Particle::getDensity()const { + return m_density; +} + +const glm::vec3& Particle::getForce()const { + return m_force; +} + +const float& Particle::getPressure()const { + return m_pressure; +} \ No newline at end of file diff --git a/projects/sph/src/Particle.hpp b/projects/sph/src/Particle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6c4ab50b74cc4544976318c23e36f4b91989ee66 --- /dev/null +++ b/projects/sph/src/Particle.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include <glm/glm.hpp> + +class Particle { + +public: + Particle(glm::vec3 position, glm::vec3 velocity); + + const glm::vec3& getPosition()const; + + void setPosition( const glm::vec3 pos ); + + const glm::vec3& getVelocity()const; + + void setVelocity( const glm::vec3 vel ); + + const float& getDensity()const; + + const glm::vec3& getForce()const; + + const float& getPressure()const; + + + +private: + // all properties of the Particle + glm::vec3 m_position; + float m_padding1; + glm::vec3 m_velocity; + float m_density; + glm::vec3 m_force; + float m_pressure; +}; diff --git a/projects/sph/src/PipelineInit.cpp b/projects/sph/src/PipelineInit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e507f1edebf6e39fb65bf81dbd4cf4915602313b --- /dev/null +++ b/projects/sph/src/PipelineInit.cpp @@ -0,0 +1,28 @@ +#include "PipelineInit.hpp" + +vkcv::DescriptorSetHandle PipelineInit::ComputePipelineInit(vkcv::Core *pCore, vkcv::ShaderStage shaderStage, std::filesystem::path includePath, + vkcv::ComputePipelineHandle &pipeline) { + vkcv::ShaderProgram shaderProgram; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(shaderStage, includePath, + [&](vkcv::ShaderStage shaderStage1, const std::filesystem::path& path1) {shaderProgram.addShader(shaderStage1, path1); + }); + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = pCore->createDescriptorSetLayout( + shaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle descriptorSet = pCore->createDescriptorSet(descriptorSetLayout); + + const std::vector<vkcv::VertexAttachment> vertexAttachments = shaderProgram.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] })); + } + const vkcv::VertexLayout layout { bindings }; + + pipeline = pCore->createComputePipeline({ + shaderProgram, + { descriptorSetLayout } + }); + + return descriptorSet; +} \ No newline at end of file diff --git a/projects/sph/src/PipelineInit.hpp b/projects/sph/src/PipelineInit.hpp new file mode 100644 index 0000000000000000000000000000000000000000..54979cfdaffb2aba0915f87552e75e6d2ad3a958 --- /dev/null +++ b/projects/sph/src/PipelineInit.hpp @@ -0,0 +1,20 @@ +#pragma once +#include <vkcv/Core.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <fstream> + +class PipelineInit{ +public: + /** + * Helperfunction to initialize a compute Pipeline. Goal is to reduce repetitive code in main. + * @param pCore Pointer to core object + * @param shaderStage Type of shaderstage - currently only supports COMPUTE + * @param includePath filepath to shaderprogram + * @param pipeline handle of the pipeline that is to be initialized. This handle is replaced with the handle of the generated pipeline + * @return returns the descriptorset handle from the generated descriptorset of the reflected shader + */ + static vkcv::DescriptorSetHandle ComputePipelineInit(vkcv::Core *pCore, + vkcv::ShaderStage shaderStage, + std::filesystem::path includePath, + vkcv::ComputePipelineHandle& pipeline); +}; diff --git a/projects/sph/src/main.cpp b/projects/sph/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..989201a996757e79f8fe29e78f8517c1354a14dc --- /dev/null +++ b/projects/sph/src/main.cpp @@ -0,0 +1,429 @@ +#include <iostream> +#include <vkcv/Core.hpp> +#include <vkcv/Buffer.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/camera/CameraManager.hpp> +#include <chrono> +#include <random> +#include <ctime> +#include <vkcv/shader/GLSLCompiler.hpp> +#include <vkcv/effects/BloomAndFlaresEffect.hpp> +#include "PipelineInit.hpp" +#include "Particle.hpp" + +int main(int argc, const char **argv) { + const std::string applicationName = "SPH"; + + // creating core object that will handle all vulkan objects + vkcv::Core core = vkcv::Core::create( + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer, vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + { VK_KHR_SWAPCHAIN_EXTENSION_NAME } + ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, 1280, 720, true); + vkcv::Window& window = core.getWindow(windowHandle); + + vkcv::camera::CameraManager cameraManager(window); + + auto particleIndexBuffer = vkcv::buffer<uint16_t>(core, vkcv::BufferType::INDEX, 3); + uint16_t indices[3] = {0, 1, 2}; + particleIndexBuffer.fill(&indices[0], sizeof(indices)); + + vk::Format colorFormat = vk::Format::eR16G16B16A16Sfloat; + vkcv::PassHandle particlePass = vkcv::passFormat(core, colorFormat); + + //rotation + float rotationx = 0; + float rotationy = 0; + + // params + float param_h = 0.20; + float param_mass = 0.03; + float param_gasConstant = 3500; + float param_offset = 200; + float param_gravity = -5000; + float param_viscosity = 10; + float param_ABSORBTION = 0.5; + float param_dt = 0.0005; + + if (!particlePass) + { + std::cout << "Error. Could not create renderpass. Exiting." << std::endl; + return EXIT_FAILURE; + } + vkcv::shader::GLSLCompiler compiler; + + // pressure shader --> computes the pressure for all particles + vkcv::ComputePipelineHandle pressurePipeline; + vkcv::DescriptorSetHandle pressureDescriptorSet = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/pressure.comp", pressurePipeline); + // force shader --> computes the force that effects the particles + vkcv::ComputePipelineHandle forcePipeline; + vkcv::DescriptorSetHandle forceDescriptorSet = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/force.comp", forcePipeline); + + // update data shader --> applies the force on all particles and updates their position + vkcv::ComputePipelineHandle updateDataPipeline; + vkcv::DescriptorSetHandle updateDataDescriptorSet = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/updateData.comp", updateDataPipeline); + + // flip shader --> flips input and output buffer + vkcv::ComputePipelineHandle flipPipeline; + vkcv::DescriptorSetHandle flipDescriptorSet = PipelineInit::ComputePipelineInit(&core, vkcv::ShaderStage::COMPUTE, + "shaders/flip.comp", flipPipeline); + + // particle rendering shaders + vkcv::ShaderProgram particleShaderProgram{}; + compiler.compile(vkcv::ShaderStage::VERTEX, "shaders/shader.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "shaders/shader_water.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + particleShaderProgram.addShader(shaderStage, path); + }); + + // generating descriptorsets from shader reflection + vkcv::DescriptorSetLayoutHandle descriptorSetLayout = core.createDescriptorSetLayout( + particleShaderProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle descriptorSet = core.createDescriptorSet(descriptorSetLayout); + + vkcv::Buffer<glm::vec3> vertexBuffer = vkcv::buffer<glm::vec3>( + core, + vkcv::BufferType::VERTEX, + 3 + ); + const std::vector<vkcv::VertexAttachment> vertexAttachments = particleShaderProgram.getVertexAttachments(); + + const std::vector<vkcv::VertexBufferBinding> vertexBufferBindings = { + vkcv::vertexBufferBinding(vertexBuffer.getHandle()) + }; + + std::vector<vkcv::VertexBinding> bindings; + for (size_t i = 0; i < vertexAttachments.size(); i++) { + bindings.push_back(vkcv::createVertexBinding(i, {vertexAttachments[i]})); + } + + const vkcv::VertexLayout particleLayout { bindings }; + + // initializing graphics pipeline + vkcv::GraphicsPipelineConfig particlePipelineDefinition ( + particleShaderProgram, + particlePass, + {particleLayout}, + {descriptorSetLayout} + ); + + particlePipelineDefinition.setBlendMode(vkcv::BlendMode::Additive); + + const std::vector<glm::vec3> vertices = {glm::vec3(-0.012, 0.012, 0), + glm::vec3(0.012, 0.012, 0), + glm::vec3(0, -0.012, 0)}; + + vertexBuffer.fill(vertices); + + vkcv::GraphicsPipelineHandle particlePipeline = core.createGraphicsPipeline(particlePipelineDefinition); + + vkcv::Buffer<glm::vec4> color = vkcv::buffer<glm::vec4>( + core, + vkcv::BufferType::UNIFORM, + 1 + ); + + vkcv::Buffer<glm::vec2> position = vkcv::buffer<glm::vec2>( + core, + vkcv::BufferType::UNIFORM, + 1 + ); + + // generating particles + int numberParticles = 20000; + std::vector<Particle> particles; + for (int i = 0; i < numberParticles; i++) { + const float lo = 0.6; + const float hi = 0.9; + const float vlo = 0; + const float vhi = 70; + float x = lo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(hi-lo))); + float y = lo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(hi-lo))); + float z = lo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(hi-lo))); + float vx = vlo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(vhi-vlo))); + float vy = vlo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(vhi-vlo))); + float vz = vlo + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(vhi-vlo))); + glm::vec3 pos = glm::vec3(x,y,z); + glm::vec3 vel = glm::vec3(vx,vy,vz); + //glm::vec3 vel = glm::vec3(0.0,0.0,0.0); + particles.push_back(Particle(pos, vel)); + } + + // creating and filling particle buffer + vkcv::Buffer<Particle> particleBuffer1 = vkcv::buffer<Particle>( + core, + vkcv::BufferType::STORAGE, + numberParticles + ); + + vkcv::Buffer<Particle> particleBuffer2 = vkcv::buffer<Particle>( + core, + vkcv::BufferType::STORAGE, + numberParticles + ); + + particleBuffer1.fill(particles); + particleBuffer2.fill(particles); + + vkcv::DescriptorWrites setWrites; + setWrites.writeUniformBuffer(0, color.getHandle()).writeUniformBuffer(1, position.getHandle()); + setWrites.writeStorageBuffer(2, particleBuffer1.getHandle()).writeStorageBuffer(3, particleBuffer2.getHandle()); + core.writeDescriptorSet(descriptorSet, setWrites); + + vkcv::DescriptorWrites computeWrites; + computeWrites.writeStorageBuffer( + 0, particleBuffer1.getHandle() + ).writeStorageBuffer( + 1, particleBuffer2.getHandle() + ); + + core.writeDescriptorSet(pressureDescriptorSet, computeWrites); + core.writeDescriptorSet(forceDescriptorSet, computeWrites); + core.writeDescriptorSet(updateDataDescriptorSet, computeWrites); + core.writeDescriptorSet(flipDescriptorSet, computeWrites); + + // error message if creation of one pipeline failed + if (!particlePipeline || !pressurePipeline || !forcePipeline || !updateDataPipeline || !flipPipeline) + { + std::cout << "Error. Could not create at least one pipeline. Exiting." << std::endl; + return EXIT_FAILURE; + } + + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + + vkcv::VertexData vertexData (vertexBufferBindings); + vertexData.setIndexBuffer(particleIndexBuffer.getHandle()); + vertexData.setCount(particleIndexBuffer.getCount()); + + auto pos = glm::vec2(0.f); + + vkcv::InstanceDrawcall drawcall (vertexData, numberParticles); + drawcall.useDescriptorSet(0, descriptorSet); + + glm::vec4 colorData = glm::vec4(1.0f, 1.0f, 0.0f, 1.0f); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setNearFar(0.1f, 30.0f); + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(0.0f, 0.0f, -2.5f)); + + cameraManager.setActiveCamera(camHandle1); + + cameraManager.getCamera(camHandle1).setNearFar(0.1f, 30.0f); + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0.0f, 0.0f, -2.5f)); + cameraManager.getCamera(camHandle1).setCenter(glm::vec3(0.0f, 0.0f, 0.0f)); + + const auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::ImageConfig colorBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + colorBufferConfig.setSupportingStorage(true); + colorBufferConfig.setSupportingColorAttachment(true); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + + vkcv::effects::BloomAndFlaresEffect bloomAndFlares (core); + bloomAndFlares.setUpsamplingLimit(3); + + //tone mapping shader & pipeline + vkcv::ComputePipelineHandle tonemappingPipe; + vkcv::DescriptorSetHandle tonemappingDescriptor = PipelineInit::ComputePipelineInit( + &core, + vkcv::ShaderStage::COMPUTE, + "shaders/tonemapping.comp", + tonemappingPipe + ); + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + if ((core.getImageWidth(colorBuffer) != swapchainWidth) || + (core.getImageHeight(colorBuffer) != swapchainHeight)) { + colorBufferConfig.setWidth(swapchainWidth); + colorBufferConfig.setHeight(swapchainHeight); + + colorBuffer = core.createImage( + colorFormat, + colorBufferConfig + ); + } + + color.fill(&colorData); + position.fill(&pos); + + cameraManager.update(dt); + + // split view and projection to allow for easy billboarding in shader + struct { + glm::mat4 view; + glm::mat4 projection; + } renderingMatrices; + + glm::vec3 gravityDir = glm::rotate(glm::mat4(1.0), glm::radians(rotationx), glm::vec3(0.f,0.f,1.f)) * glm::vec4(0.f,1.f,0.f,0.f); + gravityDir = glm::rotate(glm::mat4(1.0), glm::radians(rotationy), glm::vec3(0.f,1.f,0.f)) * glm::vec4(gravityDir,0.f); + + renderingMatrices.view = cameraManager.getActiveCamera().getView(); + renderingMatrices.view = glm::rotate(renderingMatrices.view, glm::radians(rotationx), glm::vec3(0.f, 0.f, 1.f)); + renderingMatrices.view = glm::rotate(renderingMatrices.view, glm::radians(rotationy), glm::vec3(0.f, 1.f, 0.f)); + renderingMatrices.projection = cameraManager.getActiveCamera().getProjection(); + + // keybindings rotation + if (glfwGetKey(window.getWindow(), GLFW_KEY_LEFT) == GLFW_PRESS) + rotationx += dt * 50; + if (glfwGetKey(window.getWindow(), GLFW_KEY_RIGHT) == GLFW_PRESS) + rotationx -= dt * 50; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_UP) == GLFW_PRESS) + rotationy += dt * 50; + if (glfwGetKey(window.getWindow(), GLFW_KEY_DOWN) == GLFW_PRESS) + rotationy -= dt * 50; + + // keybindings params + if (glfwGetKey(window.getWindow(), GLFW_KEY_T) == GLFW_PRESS) + param_h += dt * 0.2; + if (glfwGetKey(window.getWindow(), GLFW_KEY_G) == GLFW_PRESS) + param_h -= dt * 0.2; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_Y) == GLFW_PRESS) + param_mass += dt * 0.2; + if (glfwGetKey(window.getWindow(), GLFW_KEY_H) == GLFW_PRESS) + param_mass -= dt * 0.2; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_U) == GLFW_PRESS) + param_gasConstant += dt * 1500.0; + if (glfwGetKey(window.getWindow(), GLFW_KEY_J) == GLFW_PRESS) + param_gasConstant -= dt * 1500.0; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_I) == GLFW_PRESS) + param_offset += dt * 400.0; + if (glfwGetKey(window.getWindow(), GLFW_KEY_K) == GLFW_PRESS) + param_offset -= dt * 400.0; + + if (glfwGetKey(window.getWindow(), GLFW_KEY_O) == GLFW_PRESS) + param_viscosity = 50; + if (glfwGetKey(window.getWindow(), GLFW_KEY_L) == GLFW_PRESS) + param_viscosity = 1200; + + + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); + + glm::vec4 pushData[3] = { + glm::vec4(param_h,param_mass,param_gasConstant,param_offset), + glm::vec4(param_gravity,param_viscosity,param_ABSORBTION,param_dt), + glm::vec4(gravityDir.x,gravityDir.y,gravityDir.z,(float)numberParticles) + }; + + std::cout << "h: " << param_h << " | mass: " << param_mass << " | gasConstant: " << param_gasConstant << " | offset: " << param_offset << " | viscosity: " << param_viscosity << std::endl; + + vkcv::PushConstants pushConstantsCompute (sizeof(pushData)); + pushConstantsCompute.appendDrawcall(pushData); + + const auto computeDispatchCount = vkcv::dispatchInvocations(numberParticles, 256); + + // computing pressure pipeline + core.recordComputeDispatchToCmdStream( + cmdStream, + pressurePipeline, + computeDispatchCount, + { vkcv::useDescriptorSet(0, pressureDescriptorSet) }, + pushConstantsCompute + ); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + // computing force pipeline + core.recordComputeDispatchToCmdStream( + cmdStream, + forcePipeline, + computeDispatchCount, + { vkcv::useDescriptorSet(0, forceDescriptorSet) }, + pushConstantsCompute + ); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + // computing update data pipeline + core.recordComputeDispatchToCmdStream( + cmdStream, + updateDataPipeline, + computeDispatchCount, + { vkcv::useDescriptorSet(0, updateDataDescriptorSet) }, + pushConstantsCompute + ); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + // computing flip pipeline + core.recordComputeDispatchToCmdStream( + cmdStream, + flipPipeline, + computeDispatchCount, + { vkcv::useDescriptorSet(0, flipDescriptorSet) }, + pushConstantsCompute + ); + + core.recordBufferMemoryBarrier(cmdStream, particleBuffer1.getHandle()); + core.recordBufferMemoryBarrier(cmdStream, particleBuffer2.getHandle()); + + + // bloomAndFlares & tonemapping + vkcv::PushConstants pushConstantsDraw (sizeof(renderingMatrices)); + pushConstantsDraw.appendDrawcall(renderingMatrices); + + core.recordDrawcallsToCmdStream( + cmdStream, + particlePipeline, + pushConstantsDraw, + { drawcall }, + { colorBuffer }, + windowHandle + ); + + bloomAndFlares.recordEffect(cmdStream, colorBuffer, colorBuffer); + + core.prepareImageForStorage(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, swapchainInput); + + vkcv::DescriptorWrites tonemappingDescriptorWrites; + tonemappingDescriptorWrites.writeStorageImage( + 0, colorBuffer + ).writeStorageImage( + 1, swapchainInput + ); + + core.writeDescriptorSet(tonemappingDescriptor, tonemappingDescriptorWrites); + + const auto tonemappingDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(8, 8) + ); + + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipe, + tonemappingDispatchCount, + { vkcv::useDescriptorSet(0, tonemappingDescriptor) }, + vkcv::PushConstants(0) + ); + + core.prepareSwapchainImageForPresent(cmdStream); + core.submitCommandStream(cmdStream); + }); + + return 0; +} diff --git a/projects/voxelization/CMakeLists.txt b/projects/voxelization/CMakeLists.txt index bc87996096226af4e3f3d05c3e10bb287c61cc8d..34178021a517485e54ef8dcafe0f170a834e5186 100644 --- a/projects/voxelization/CMakeLists.txt +++ b/projects/voxelization/CMakeLists.txt @@ -2,31 +2,39 @@ cmake_minimum_required(VERSION 3.16) project(voxelization) # setting c++ standard for the project -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# this should fix the execution path to load local files from the project -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - # adding source files to the project -add_executable(voxelization src/main.cpp) +add_project(voxelization src/main.cpp) target_sources(voxelization PRIVATE src/Voxelization.hpp - src/Voxelization.cpp) - -# this should fix the execution path to load local files from the project (for MSVC) -if(MSVC) - set_target_properties(voxelization PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - set_target_properties(voxelization PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) - - # in addition to setting the output directory, the working directory has to be set - # by default visual studio sets the working directory to the build directory, when using the debugger - set_target_properties(voxelization PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -endif() + src/Voxelization.cpp + src/ShadowMapping.hpp + src/ShadowMapping.cpp) # including headers of dependencies and the VkCV framework -target_include_directories(voxelization SYSTEM BEFORE PRIVATE ${vkcv_include} ${vkcv_includes} ${vkcv_asset_loader_include} ${vkcv_camera_include} ${vkcv_shader_compiler_include} ${vkcv_gui_include}) +target_include_directories(voxelization SYSTEM BEFORE PRIVATE + ${vkcv_include} + ${vkcv_includes} + ${vkcv_asset_loader_include} + ${vkcv_camera_include} + ${vkcv_shader_compiler_include} + ${vkcv_gui_include} + ${vkcv_upscaling_include} + ${vkcv_effects_include} + ${vkcv_algorithm_include}) # linking with libraries from all dependencies and the VkCV framework -target_link_libraries(voxelization vkcv ${vkcv_libraries} vkcv_asset_loader ${vkcv_asset_loader_libraries} vkcv_camera vkcv_shader_compiler vkcv_gui) +target_link_libraries(voxelization + vkcv + ${vkcv_libraries} + vkcv_asset_loader + ${vkcv_asset_loader_libraries} + vkcv_camera + vkcv_shader_compiler + vkcv_gui + vkcv_upscaling + vkcv_effects + vkcv_algorithm) diff --git a/projects/voxelization/README.md b/projects/voxelization/README.md new file mode 100644 index 0000000000000000000000000000000000000000..569b911ade302d8dfbd55ce0b31e9c28738c6b41 --- /dev/null +++ b/projects/voxelization/README.md @@ -0,0 +1,21 @@ +# Voxelization +An example project to show a volumetric global illumination technique with the VkCV framework + + + +## Details + +The project utilizes multiple Vulkan extensions (some of them optionally) to implement multiple +graphics effects in an advanced visualization of a whole scene. Together this brings visual +features like shadow maps, indirect lighting via discrete voxels and multiple post-processing +effects like bloom and lens-flares to the screen. To make all of this available in realtime even +on low powered hardware, it is possible to utilize multiple different upscaling methods. + +## Extensions + +Here is a list of the used extensions: + +- [VK_EXT_descriptor_indexing](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html) +- [VK_KHR_shader_subgroup_extended_types](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_subgroup_extended_types.html) +- [VK_KHR_shader_float16_int8](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_float16_int8.html) +- [VK_KHR_16bit_storage](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_16bit_storage.html) diff --git a/projects/voxelization/assets/RadialLUT.png b/projects/voxelization/assets/RadialLUT.png new file mode 100644 index 0000000000000000000000000000000000000000..8b7056cf2a35c4d41f142e52bbc48dd1a91e4758 --- /dev/null +++ b/projects/voxelization/assets/RadialLUT.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70d59d4e9c1ce2a077ed60c19c8c4665bf0723c952612a2ca8ec32c55f9ec498 +size 900 diff --git a/projects/voxelization/assets/Sponza/Sponza.bin b/projects/voxelization/assets/Sponza/Sponza.bin new file mode 100644 index 0000000000000000000000000000000000000000..eb0523cb55746451c1b20f25fb4ecfed22ef6047 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Sponza.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:232d9c216b72dc0ee6089bc070cf8a12cabecd6a00c1f04aea78ac361da53839 +size 10819832 diff --git a/projects/voxelization/assets/Sponza/Sponza.gltf b/projects/voxelization/assets/Sponza/Sponza.gltf new file mode 100644 index 0000000000000000000000000000000000000000..18d697f622eab38c3b3089a56c1680ff4a443171 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Sponza.gltf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:748597f56b7228dddbe4c5d1cb84a35d645941933faf89c4c0f81dd9b602291e +size 73667 diff --git a/projects/voxelization/assets/Sponza/Textures/Arch_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Arch_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..95900bdf9c8d57666b92929109a6750dc04a548b --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Arch_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8b68080eb4c5709536dfb7858d595d51d4bb725003e11b0f383acf76a7e05a3 +size 521947 diff --git a/projects/voxelization/assets/Sponza/Textures/Arch_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Arch_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..28e407a7c96acc4fbc9e75c7c8d6399914d409e3 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Arch_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86defcb4c007b80b9d23bcc9fdccd994f6a68f710eb75f11396d92faa2fbe368 +size 206244 diff --git a/projects/voxelization/assets/Sponza/Textures/Arch_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Arch_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1e0df9e5b0613ee476440b7e86d23535d5f4d05 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Arch_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:259b6659cb2681e06ef04becd49298d379f2f25b2cc468acf0c2ade4d190637c +size 570081 diff --git a/projects/voxelization/assets/Sponza/Textures/Background_Albedo.png b/projects/voxelization/assets/Sponza/Textures/Background_Albedo.png new file mode 100644 index 0000000000000000000000000000000000000000..668c3b6d389e9675bf8e482a51936fe1dff0c889 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Background_Albedo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a64b98be19acc30b95d071fb9dd1f0eaaf066612a2c757b32f8b7487f4600e9 +size 1653594 diff --git a/projects/voxelization/assets/Sponza/Textures/Background_Normal.png b/projects/voxelization/assets/Sponza/Textures/Background_Normal.png new file mode 100644 index 0000000000000000000000000000000000000000..6f03475bdcf3d26f1b152c30680998fbb6dec321 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Background_Normal.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e7fe693f80bbf4b543d3afc614390e498bb84664fd0251c3cfb4e99b4885a12 +size 1299002 diff --git a/projects/voxelization/assets/Sponza/Textures/Background_Roughness.png b/projects/voxelization/assets/Sponza/Textures/Background_Roughness.png new file mode 100644 index 0000000000000000000000000000000000000000..1f47f6a071463d13bb0f719570e3c53c4a65c079 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Background_Roughness.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15339be54f1f86593a48875dfde6947adf4ba49b3b945619ce3b4fbea64afbd1 +size 791544 diff --git a/projects/voxelization/assets/Sponza/Textures/Bricks_A_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cff7cf6ceb272d99378ce4b7facd09ade9f7c61a --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9330cf1d51c8f6924b6c9ce09f2dedb21a82e1c7406c37d49ce95beca40bb3be +size 564801 diff --git a/projects/voxelization/assets/Sponza/Textures/Bricks_A_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f83f46eb02cbf045886b8867b2dac93321800bf5 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0821ec8dc8226fd5ac70f068e134d014a9952276a7100cc7f0660853471b39f6 +size 339486 diff --git a/projects/voxelization/assets/Sponza/Textures/Bricks_A_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da82e3992c1cdac7b90f294809193f4227da08cf --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Bricks_A_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bee42826d56d3ff719c4908c11ef65584fbe19e08273862fde7ec1408e44ba25 +size 530384 diff --git a/projects/voxelization/assets/Sponza/Textures/Ceiling_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Ceiling_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..20f8871aa9bb13d4c2ccc7a0aa9ddddcd226080b --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Ceiling_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5bbd0cbf2da7416d5620514ebb0c4837b0ccc841b649cf4070281aa333ff8520 +size 520162 diff --git a/projects/voxelization/assets/Sponza/Textures/Ceiling_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Ceiling_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a8fba3de7e4b68e5d5a095cb30442fb30c53be6 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Ceiling_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e309f1e6310dc0bb85df0a5f3b139207876f067378796a7aa666b0bb169587ae +size 919134 diff --git a/projects/voxelization/assets/Sponza/Textures/Ceiling_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Ceiling_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..59ec4fabc2df618dce40d73a1e71c23e780b0422 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Ceiling_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81b21b9352f1fb9ccfeaf237f8547b985ddbf0cfd7ade7a4a8670b73a72b2cb5 +size 608687 diff --git a/projects/voxelization/assets/Sponza/Textures/Chain_Diff.png b/projects/voxelization/assets/Sponza/Textures/Chain_Diff.png new file mode 100644 index 0000000000000000000000000000000000000000..9629f2f279c8b2d338724b96538f2858a9f3f4ff --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Chain_Diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6df943a0dcf535d9dfb009c2833e0c7dc21939cfe3a2c10316c552b3dc3441b +size 1077432 diff --git a/projects/voxelization/assets/Sponza/Textures/Chain_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Chain_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..127484f4ff3aa31597262c8e675c9d1f4dc979f1 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Chain_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:763740af8d22f0bfc6a43bd46050ef24bb661833bcace92f1f3326b9d5d7dc16 +size 137384 diff --git a/projects/voxelization/assets/Sponza/Textures/Cloth1_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth1_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cd117615246364139418024db5474e3e29ccc40b --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Cloth1_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3af8848a1a952ad8cc83199bbda0c095f9ed136be839659aa668f72afac91508 +size 1034060 diff --git a/projects/voxelization/assets/Sponza/Textures/Cloth1_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth1_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e505337ce76a797c4b385c89c25f9486eccab9a8 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Cloth1_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21bb124f5b5d4d5e9c8d9773cd31dedfc41b30eb302af36e7257cf930152908e +size 486634 diff --git a/projects/voxelization/assets/Sponza/Textures/Cloth2_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth2_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..35885a182fc5cf119afbebbeb2cc060850970731 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Cloth2_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:231d919288db7c977fda3091549195c1bd5197e9e63c039e972da8764f5435c1 +size 844768 diff --git a/projects/voxelization/assets/Sponza/Textures/Cloth2_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Cloth2_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..982536602c4633893fa19c27c0b0e08a8ea0aec8 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Cloth2_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ad0e8a9ca6de602225a527361bc718881de8741bc48927e27259a8803e7fed7 +size 623144 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothBlue1_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothBlue1_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae58b3bb674bec1f07a3f7de7eb52c8b4c23f4be --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothBlue1_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:005b774a968c1f6af00e4bf97afd668a6a045126a94f524df722ba19edc77d61 +size 1151448 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8597404fcae24e14967eb2220faa1d5cd4d8170 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:751606c94a5fe5bda3bf6709e7f6a74363bd7902bf2353d54e55ef3b50007487 +size 785678 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff_jpg.jpg b/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff_jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae58b3bb674bec1f07a3f7de7eb52c8b4c23f4be --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothBlue2_Diff_jpg.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:005b774a968c1f6af00e4bf97afd668a6a045126a94f524df722ba19edc77d61 +size 1151448 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothGreen1_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothGreen1_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..833de50475dc9d4db7c36e0447bd8e17a5de41f5 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothGreen1_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f55814430f289e89c7d675c3db925eee904fbc566ff83f4b49904de53138e98 +size 1044330 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothGreen2_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothGreen2_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63ca00ba7ca17e80054fdf35b87aad4227edadef --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothGreen2_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d5fd79619a52db0232edb2287f5f05c0124c304355e3b035a4e48c0871090bf +size 764529 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothRed1_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothRed1_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..406d620bf21db33765c00f87f3750f73c8df2de0 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothRed1_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23c32277c33d8eac091cd73daf81561ebe3640eede49b037546ed5c244a01347 +size 1079152 diff --git a/projects/voxelization/assets/Sponza/Textures/ClothRed2_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/ClothRed2_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f5b06c8dcee807f739981bfc061e4cd3d8f0a291 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/ClothRed2_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:88861c8c7f10517c1d7942f316aae3b74dc5b8ae2faa2021d9dfe9d2c09fb24f +size 780166 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_B_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Column_B_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ba6158fe4718103e6f37ee0f1e482e4e5dfc25d8 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_B_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:170f6c637ebd71a101244eb4d80c3dcf7063caf89e42053d3114d8aeca755cdf +size 643517 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_B_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Column_B_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..af99619ce8ff2b71246c00a79d94999c89f40e2e --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_B_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:46ffc9123ee3ad94b2595843b4446fd67dbf5a2f79dc1e3b4e637f5ea4578630 +size 652476 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_B_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Column_B_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..52c8e41dbc885800512ccc8527932be0f6964c63 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_B_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93d495e33d754c5723c910e322ac94108fc753b65af082a19be5252905aa741b +size 388462 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_C_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Column_C_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..53c725db4dece66f1458bc93dfb95ab20f8927cb --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_C_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9edc7c1c73661658cd8ed7733728002d45fecd17cb37a962fb654ff1d8c1933d +size 665796 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_C_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Column_C_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d2c5f3fe9792c825c50743e80969cb3bca660a9 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_C_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58942b73c8f209ab9cc6ea174375adfde9c1875cbfe093f993086a0ab6570724 +size 698410 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_C_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Column_C_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cf4a888d7023a9d23248a68d6217e5e1317c9b46 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_C_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:06c6480e207ce64f9e7c08df3f7f21638da7da43f77682657f4c823acd8c81a5 +size 193077 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Column_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f6e1fc5dfdbc52d8bf80bd736b6c7881f260b10d --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c39c9f304c2605be791b4c0a58cd0f8eb20de2dd6a4a4af893c4eb112d158353 +size 518310 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Column_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..549b3a7e290e79a4275124ce339c2d7a6b308cb2 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d54c520da7e60e2a62630e9cf946faf139648a7ab831c4531272238e4ade8fa8 +size 557865 diff --git a/projects/voxelization/assets/Sponza/Textures/Column_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Column_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5c3770a5faebbe1e98df4b39367a87246b0161cc --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Column_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f06c93f8e9f90be44a2e78847e57acec82bd793f58f7be7f86b088150730b88 +size 260119 diff --git a/projects/voxelization/assets/Sponza/Textures/Detail_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Detail_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8ede3e8fff423456faf51cb1419261a33ab708d0 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Detail_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98e4c4a091fea684cd693f3ae7ebe83e491969b501b4cd6e1fdd6a3cc1fa5cab +size 321375 diff --git a/projects/voxelization/assets/Sponza/Textures/Detail_norm.jpg b/projects/voxelization/assets/Sponza/Textures/Detail_norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6dad5879bba40f4273658e28f6d7fa3e3d8a663b --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Detail_norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9552937c9fa0481a07dc66768958b4be382544add46d1d66a687af54c51ab6da +size 547553 diff --git a/projects/voxelization/assets/Sponza/Textures/Detail_spec.jpg b/projects/voxelization/assets/Sponza/Textures/Detail_spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d3918da21cdc2bcb27db235749791c45b1fb88f4 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Detail_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:967005ccfbf4d38f674df41aebb3e58ac1fdcd91f554ead7acfcc132372f8ec6 +size 551432 diff --git a/projects/voxelization/assets/Sponza/Textures/Fill_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Fill_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b7dfccb5c93c5d215f3466b9352b6e77f4034555 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Fill_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5562f0e76ca213d72295e48b8a14a59ee1971f58a47f95e5c022a4e78371c5aa +size 12575 diff --git a/projects/voxelization/assets/Sponza/Textures/Flagpole_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Flagpole_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..43e93c048438d4d57501c198f45d5888d5a478fd --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Flagpole_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbdb7bcced57a005b84b4c3bbf744bae6309804cca51dabd9237e03a72fbc505 +size 399027 diff --git a/projects/voxelization/assets/Sponza/Textures/Flagpole_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Flagpole_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a8c2891f2c44d175806df7d1451dea97649154a --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Flagpole_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62a52f200082c02a0fbfa25c0bc40ea7d105a21f10d5aa0ec9408c4f45523794 +size 772201 diff --git a/projects/voxelization/assets/Sponza/Textures/Flagpole_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Flagpole_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..145dc9d42327723970227151f945048b34471287 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Flagpole_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d939d2e8bf11c2cf375154129f861b982c786b83db79cb9fde1a2b94b46caf62 +size 682743 diff --git a/projects/voxelization/assets/Sponza/Textures/Floor_A_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Floor_A_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1c351ca22197a2556ff0acc08c73912f63fdac5 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Floor_A_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34b464edddbd2295b0ee23fa7a7a440a19456888c586acdc47032f841b302abd +size 573157 diff --git a/projects/voxelization/assets/Sponza/Textures/Floor_A_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Floor_A_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0304c61dbae476fb226b151d44612fca91892e44 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Floor_A_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3a2c2aeb87a490a9bd37a8aa912a00e5f5c13974879fcb4f4f0ef39b17c52a7a +size 718904 diff --git a/projects/voxelization/assets/Sponza/Textures/Floor_A_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Floor_A_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90621f970579c5c8e268be5d415cab1162cf5910 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Floor_A_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9158388312a76ba25a40d3b57978e1ace224882e53198a766494d98f9118aeba +size 313460 diff --git a/projects/voxelization/assets/Sponza/Textures/Flower_Diff.png b/projects/voxelization/assets/Sponza/Textures/Flower_Diff.png new file mode 100644 index 0000000000000000000000000000000000000000..0f297bbb9581dddb4bc61f8e29c14537a3415736 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Flower_Diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75328d1aac87f8539be2456e6a5392cef7c6d08476d75585eafcd334dbeee0c5 +size 1225157 diff --git a/projects/voxelization/assets/Sponza/Textures/Flower_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Flower_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..23b8b1d0bc30d90e0dbf2964b7591e3a4c90c215 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Flower_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81ede4ce51045540817c1efb83dbb65a9a9701f1abd78fa130def51d227162fc +size 421442 diff --git a/projects/voxelization/assets/Sponza/Textures/Flower_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Flower_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc60f6df8ee0a01601294062d585377ec14f28f3 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Flower_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d81efcb42b3a044595ca984cb46d941b159064bddae9a43ed8d9582db68d6b47 +size 280745 diff --git a/projects/voxelization/assets/Sponza/Textures/Lion_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Lion_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..51286b49a1eb9d1e52ad599f1d1a4abccc53d7f7 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Lion_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ea4f9bc86354d363ea8153df9b7593c3bd3da153c53b9624c1fd8a960cb3ebc +size 599959 diff --git a/projects/voxelization/assets/Sponza/Textures/Lion_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Lion_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..49132519e6e14b0a0964575ad92c86f805500421 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Lion_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f296e43c0d4c92953d4899ea0c38e8af992ba471acc87feb10f08e29a94aaef6 +size 512035 diff --git a/projects/voxelization/assets/Sponza/Textures/Lion_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Lion_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3058c29c325e5768bc3189a8c61548d44d88db55 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Lion_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c26ae9523cfae7a062fd074f74e0ddbb61630772f9a2130489e1d6e4724c38b +size 281980 diff --git a/projects/voxelization/assets/Sponza/Textures/Roof_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Roof_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e9b6f6f9a00a99b4563229f76997abf402bcc7f --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Roof_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dfa17f2dee2cd2d850585673d19d5cda477a63e21659b346d8730e80edd3b347 +size 822373 diff --git a/projects/voxelization/assets/Sponza/Textures/Roof_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Roof_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..88ee02405bb090eed4ea2b658d0adcb93b1a8ed6 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Roof_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5166a5fba339e44a9d197dedc168683971f8acb2156034248a80674cbba0dd5 +size 1607263 diff --git a/projects/voxelization/assets/Sponza/Textures/Roof_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Roof_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dccdb78a2b439240d6cb3d79ce1694e53f83217f --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Roof_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e90e94cadf80528a3df3496035d3c77900eba24d88b1c1d31c9dce77990fc44 +size 872255 diff --git a/projects/voxelization/assets/Sponza/Textures/Shield_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Shield_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da701b850fd9bb49714263dba7c0d2681808ad5a --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Shield_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:70d85bff3bc017177124942ae187db117ba2588e490746f440f2cef085852956 +size 357112 diff --git a/projects/voxelization/assets/Sponza/Textures/Shield_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Shield_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c918da16e61180632dae222a622bd8d479ff9031 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Shield_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9cbd61fcb8047f1c21450eff05207b29d71824edd75483267b5fbc2f7b21378c +size 329124 diff --git a/projects/voxelization/assets/Sponza/Textures/Shield_diff.jpg b/projects/voxelization/assets/Sponza/Textures/Shield_diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90aa71962d62e0370f1d3550fc35781c54a466bf --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Shield_diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73a94a12fb0a9ab4c75e40996c9a2d49ac45ac18a8c8ae81d5ceb1c8ae52d6dc +size 362437 diff --git a/projects/voxelization/assets/Sponza/Textures/Thorn_Diff.png b/projects/voxelization/assets/Sponza/Textures/Thorn_Diff.png new file mode 100644 index 0000000000000000000000000000000000000000..cd52793a05b4f0827299d86c7446315ce67c1c68 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Thorn_Diff.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fbdbf67d3501831ad3a4e2adbf3d27e4c3d13ba1d655e5861c3f39b5f899f65 +size 2427921 diff --git a/projects/voxelization/assets/Sponza/Textures/Thorn_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Thorn_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d85686c7aa40b03a94d00fa86dcd1f41681f1e1f --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Thorn_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dc5bf5f3091b548f1dfe40410b1aa1e2e55d82e2d77ba57dbd1f92955a0d8ff8 +size 413837 diff --git a/projects/voxelization/assets/Sponza/Textures/Thorn_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Thorn_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc414f0ea13433f7fe9832f4a64d0514ae9a2239 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Thorn_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da2a0b308b4d6d57ffbcf4bb7509d4afe91156d5d86d0396e83d7cac883a4857 +size 668962 diff --git a/projects/voxelization/assets/Sponza/Textures/VaseRound_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/VaseRound_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..74e6d243f6da2015398ef9a08de663c59f32c329 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/VaseRound_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:430758549ba2189d94f06abeaae092bfc0a1d8c22afc56f5caf3f99c5ee7407c +size 572245 diff --git a/projects/voxelization/assets/Sponza/Textures/VaseRound_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/VaseRound_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..154b1fa17729990b54b368febb55a259b7a47292 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/VaseRound_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8af8fb0810bf3493242491da44f7ae14f7c0bfa6cbacea4be01f6f4261db4559 +size 618837 diff --git a/projects/voxelization/assets/Sponza/Textures/VaseRound_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/VaseRound_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..97c19f41117e9a3aaec02885b236349c8ba9c6ca --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/VaseRound_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12e1f2ea3c2714a6ec455fa8e623a702e33ef95765ebd09f7874a68fa55fd104 +size 399963 diff --git a/projects/voxelization/assets/Sponza/Textures/Vase_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4d53ec1a796184e24893aff99f53652d08bd4f47 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Vase_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1da7664b7b88ae58386a6e6b5eca2829537818061d704f002356d8fc483c1a28 +size 531156 diff --git a/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Diff.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Diff.jpg new file mode 100644 index 0000000000000000000000000000000000000000..149a0f1c47e15ebfc2a9de474e8dda46ed3ab2de --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Diff.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f849f0510ca0df66ba1cba2380a6a1b481444d9e64b68da6b2ce202b54d9cec +size 302363 diff --git a/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bcdd65e4b718c0dc7e74f03aa50cac22f10febea --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b93291121303b5a24a488a3e29e0645a24bb471413cfd25c0917d306dff1d038 +size 208352 diff --git a/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Spec.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8976a203dfe463d121d48e743b3ecb662ac52d09 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Vase_Hanging_Spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c288a37ed8373d1132644873624b096a72ba81d03cda601e050a5de24ba13c39 +size 104626 diff --git a/projects/voxelization/assets/Sponza/Textures/Vase_Norm.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_Norm.jpg new file mode 100644 index 0000000000000000000000000000000000000000..de5758090ab8ab0aaa374110aa08df6c7f53d599 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Vase_Norm.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:728dd0fe3b570103e51dbfcc1eac64ad09916a7a31603c62a4090a2f2afaa6f8 +size 876227 diff --git a/projects/voxelization/assets/Sponza/Textures/Vase_spec.jpg b/projects/voxelization/assets/Sponza/Textures/Vase_spec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0cce3ebf638a3ca932bca8b28c31b8474f123349 --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/Vase_spec.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb09708202859d08764601acc9c9ea3b5254b2c4fd39dcc6b6055768c77aee9b +size 370061 diff --git a/projects/voxelization/assets/Sponza/Textures/white.png b/projects/voxelization/assets/Sponza/Textures/white.png new file mode 100644 index 0000000000000000000000000000000000000000..98e867371da9926f451d5754604bc97b3186296a --- /dev/null +++ b/projects/voxelization/assets/Sponza/Textures/white.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e2ab2939dda535ad28779e41c20c98368f630f29c5824a0a5a430f47b3b6da12 +size 951 diff --git a/projects/voxelization/assets/lensDirt.jpg b/projects/voxelization/assets/lensDirt.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f941567527fe92f23aa6955e15ba95dc46881dad --- /dev/null +++ b/projects/voxelization/assets/lensDirt.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95982351ecf3d4d129d17612b40a8092944a285c0bffdd74d4886dd8489695ca +size 107603 diff --git a/projects/voxelization/assets/shaders/brdf.inc b/projects/voxelization/assets/shaders/brdf.inc new file mode 100644 index 0000000000000000000000000000000000000000..4cf334eaceedd18815ab928aed38d5f8d3f51c1e --- /dev/null +++ b/projects/voxelization/assets/shaders/brdf.inc @@ -0,0 +1,31 @@ +#ifndef BRDF_INC +#define BRDF_INC + +const float pi = 3.1415; + +vec3 lambertBRDF(vec3 albedo){ + return albedo / pi; +} + +vec3 fresnelSchlick(float cosTheta, vec3 f0){ + return f0 + (vec3(1) - f0) * pow(1 - cosTheta, 5); +} + +float GGXDistribution(float r, float NoH){ + float r2 = r * r; + float denom = pi * pow(NoH * NoH * (r2 - 1) + 1, 2); + return r2 / max(denom, 0.00001); +} + +float GGXSmithShadowingPart(float r, float cosTheta){ + float nom = cosTheta * 2; + float r2 = r * r; + float denom = cosTheta + sqrt(r2 + (1 - r2) * cosTheta * cosTheta); + return nom / max(denom, 0.00001); +} + +float GGXSmithShadowing(float r, float NoV, float NoL){ + return GGXSmithShadowingPart(r, NoV) * GGXSmithShadowingPart(r, NoL); +} + +#endif // #ifndef BRDF_INC \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/depthPrepass.frag b/projects/voxelization/assets/shaders/depthPrepass.frag new file mode 100644 index 0000000000000000000000000000000000000000..5e2f7a092ca300af40cc039608a44d080c28730f --- /dev/null +++ b/projects/voxelization/assets/shaders/depthPrepass.frag @@ -0,0 +1,20 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "perMeshResources.inc" + +layout(location = 0) in vec2 passUV; + +layout(location = 0) out vec4 outColor; // only used for alpha to coverage, not actually written to + +// coverage to alpha techniques explained in: https://bgolus.medium.com/anti-aliased-alpha-test-the-esoteric-alpha-to-coverage-8b177335ae4f +void main() { + float alpha = texture(sampler2D(albedoTexture, textureSampler), passUV).a; + float alphaCutoff = 0.5; + + // scale alpha to one pixel width + alpha = (alpha - alphaCutoff) / max(fwidth(alpha), 0.0001) + 0.5; + + outColor.a = alpha; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shadow.vert b/projects/voxelization/assets/shaders/depthPrepass.vert similarity index 61% rename from projects/voxelization/resources/shaders/shadow.vert rename to projects/voxelization/assets/shaders/depthPrepass.vert index e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8..4bb3500eb59214e30fce84862e181fd7e24b7340 100644 --- a/projects/voxelization/resources/shaders/shadow.vert +++ b/projects/voxelization/assets/shaders/depthPrepass.vert @@ -1,7 +1,12 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + layout(location = 0) in vec3 inPosition; +layout(location = 2) in vec2 inUV; + +layout(location = 0) out vec2 passUV; layout( push_constant ) uniform constants{ mat4 mvp; @@ -9,4 +14,5 @@ layout( push_constant ) uniform constants{ void main() { gl_Position = mvp * vec4(inPosition, 1.0); + passUV = inUV; } \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/depthToMoments.comp b/projects/voxelization/assets/shaders/depthToMoments.comp new file mode 100644 index 0000000000000000000000000000000000000000..79e47cdef02143ed97d53e533f47e822db8c0f6f --- /dev/null +++ b/projects/voxelization/assets/shaders/depthToMoments.comp @@ -0,0 +1,38 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#extension GL_ARB_texture_multisample : enable + +#include "shadowMapping.inc" + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(set=0, binding=0) uniform texture2DMS srcTexture; +layout(set=0, binding=1) uniform sampler depthSampler; +layout(set=0, binding=2, rgba16) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + int msaaCount; +}; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + float z = 0; + for(int i = 0; i < msaaCount; i++){ + z += texelFetch(sampler2DMS(srcTexture, depthSampler), uv, i).r; + } + z /= msaaCount; + z = 2 * z - 1; // algorithm expects depth in range [-1:1] + + float z2 = z*z; + vec4 moments = vec4(z, z2, z2*z, z2*z2); + vec4 momentsQuantized = quantizeMoments(moments); + + imageStore(outImage, uv, momentsQuantized); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/lightInfo.inc b/projects/voxelization/assets/shaders/lightInfo.inc new file mode 100644 index 0000000000000000000000000000000000000000..a87f9ce7bebc1db1688dd20dd80608e99925755a --- /dev/null +++ b/projects/voxelization/assets/shaders/lightInfo.inc @@ -0,0 +1,12 @@ +#ifndef LIGHT_INFO_INC +#define LIGHT_INFO_INC + +struct LightInfo{ + vec3 L; + float padding; + vec3 sunColor; + float sunStrength; + mat4 lightMatrix; +}; + +#endif // #ifndef LIGHT_INFO_INC \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/luma.inc b/projects/voxelization/assets/shaders/luma.inc new file mode 100644 index 0000000000000000000000000000000000000000..17b3b282830ab155ce62e9b1394c0985ceccecd9 --- /dev/null +++ b/projects/voxelization/assets/shaders/luma.inc @@ -0,0 +1,8 @@ +#ifndef LUMA_INC +#define LUMA_INC + +float computeLuma(vec3 c){ + return dot(c, vec3(0.21, 0.72, 0.07)); +} + +#endif // #ifndef LUMA_INC \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/msaa4XResolve.comp b/projects/voxelization/assets/shaders/msaa4XResolve.comp new file mode 100644 index 0000000000000000000000000000000000000000..8bb1a946e3ba43f4e80f21f6bd730e020276f2d8 --- /dev/null +++ b/projects/voxelization/assets/shaders/msaa4XResolve.comp @@ -0,0 +1,83 @@ +#version 450 +#extension GL_ARB_texture_multisample : enable +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2DMS srcTexture; +layout(set=0, binding=1) uniform sampler MSAASampler; +layout(set=0, binding=2, r11f_g11f_b10f) uniform image2D outImage; + +#include "luma.inc" + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +vec3 tonemap(vec3 c){ + return c / (1 + computeLuma(c)); +} + +vec3 tonemapReverse(vec3 c){ + return c / (1 - computeLuma(c)); +} + +float reconstructionFilter(float d){ + // gauß filter, tuned so that distance of one has weight around 20% + float a = 1.6; + return exp(-a * d*d); +} + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + + vec2 samplePositions[4] = { + vec2(0.375, 0.125), + vec2(0.875, 0.375), + vec2(0.125, 0.625), + vec2(0.625, 0.875)}; + + vec3 color = vec3(0); + float wTotal = 0; + + // four samples from main pixel + for(int i = 0; i < 4; i++){ + vec3 msaaSample = texelFetch(sampler2DMS(srcTexture, MSAASampler), uv, i).rgb; + float d = distance(vec2(0.5), samplePositions[i]); + float w = reconstructionFilter(d); + color += tonemap(msaaSample) * w; + wTotal += w; + } + + ivec2 neighbourOffsets[4] = { + ivec2( 1, 0), // right + ivec2(-1, 0), // left + ivec2( 0, 1), // top + ivec2( 0, -1) // bot + }; + + int neighbourSampleIndices[8] = { + 0, 2, // left samples of right neighbour + 1, 3, // right samples of left neighbour + 2, 3, // bot samples of top neighbour + 0, 1 // top samples of bot neighbour + }; + + // two additional samples from each neighbour + for(int neighbour = 0; neighbour < 4; neighbour++){ + for(int i = 0; i < 2; i++){ + int sampleIndex = neighbourSampleIndices[neighbour * 2 + i]; + ivec2 pixelOffset = neighbourOffsets[neighbour]; + ivec2 pixelUV = uv + pixelOffset; + vec3 msaaSample = texelFetch(sampler2DMS(srcTexture, MSAASampler), pixelUV, sampleIndex).rgb; + float d = distance(vec2(0.5), samplePositions[sampleIndex] + pixelOffset); + float w = reconstructionFilter(d); + color += tonemap(msaaSample) * w; + wTotal += w; + } + } + color /= wTotal; + color = tonemapReverse(color); + + imageStore(outImage, uv, vec4(color, 0.f)); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/perMeshResources.inc b/projects/voxelization/assets/shaders/perMeshResources.inc new file mode 100644 index 0000000000000000000000000000000000000000..b1523713cf2040f672f74be3f47f9bf43996c614 --- /dev/null +++ b/projects/voxelization/assets/shaders/perMeshResources.inc @@ -0,0 +1,4 @@ +layout(set=1, binding=0) uniform texture2D albedoTexture; +layout(set=1, binding=1) uniform sampler textureSampler; +layout(set=1, binding=2) uniform texture2D normalTexture; +layout(set=1, binding=3) uniform texture2D specularTexture; \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/postEffects.comp b/projects/voxelization/assets/shaders/postEffects.comp new file mode 100644 index 0000000000000000000000000000000000000000..c0f9fe1a764bcdabac5501e2f82692c6f476e9e6 --- /dev/null +++ b/projects/voxelization/assets/shaders/postEffects.comp @@ -0,0 +1,149 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +#include "luma.inc" + +layout(set=0, binding=0) uniform texture2D inTexture; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rgba8) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout( push_constant ) uniform constants{ + float time; +}; + +// from: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +vec3 ACESFilm(vec3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0, 1); +} + +// From Dave Hoskins: https://www.shadertoy.com/view/4djSRW. +float hash(vec3 p3){ + p3 = fract(p3 * 0.1031); + p3 += dot(p3,p3.yzx + 19.19); + return fract((p3.x + p3.y) * p3.z); +} + +// From iq: https://www.shadertoy.com/view/4sfGzS. +float noise(vec3 x){ + vec3 i = floor(x); + vec3 f = fract(x); + f = f*f*(3.0-2.0*f); + return mix(mix(mix(hash(i+vec3(0, 0, 0)), + hash(i+vec3(1, 0, 0)),f.x), + mix(hash(i+vec3(0, 1, 0)), + hash(i+vec3(1, 1, 0)),f.x),f.y), + mix(mix(hash(i+vec3(0, 0, 1)), + hash(i+vec3(1, 0, 1)),f.x), + mix(hash(i+vec3(0, 1, 1)), + hash(i+vec3(1, 1, 1)),f.x),f.y),f.z); +} + +// From: https://www.shadertoy.com/view/3sGSWVF +// Slightly high-passed continuous value-noise. +float grainSource(vec3 x, float strength, float pitch){ + float center = noise(x); + float v1 = center - noise(vec3( 1, 0, 0)/pitch + x) + 0.5; + float v2 = center - noise(vec3( 0, 1, 0)/pitch + x) + 0.5; + float v3 = center - noise(vec3(-1, 0, 0)/pitch + x) + 0.5; + float v4 = center - noise(vec3( 0,-1, 0)/pitch + x) + 0.5; + + float total = (v1 + v2 + v3 + v4) / 4.0; + return mix(1, 0.5 + total, strength); +} + +vec3 applyGrain(ivec2 uv, vec3 c){ + float grainLift = 0.6; + float grainStrength = 0.4; + float grainTimeFactor = 0.1; + + float timeColorOffset = 1.2; + vec3 grain = vec3( + grainSource(vec3(uv, floor(grainTimeFactor*time)), grainStrength, grainLift), + grainSource(vec3(uv, floor(grainTimeFactor*time + timeColorOffset)), grainStrength, grainLift), + grainSource(vec3(uv, floor(grainTimeFactor*time - timeColorOffset)), grainStrength, grainLift)); + + return c * grain; +} + +vec2 computeDistortedUV(vec2 uv, float aspectRatio){ + uv = uv * 2 - 1; + float r2 = dot(uv, uv); + float k1 = 0.02f; + + float maxR2 = dot(vec2(1), vec2(1)); + float maxFactor = maxR2 * k1; + + // correction only needed for pincushion distortion + maxFactor = min(maxFactor, 0); + + uv /= 1 + r2*k1; + + // correction to avoid going out of [-1, 1] range when using barrel distortion + uv *= 1 + maxFactor; + + return uv * 0.5 + 0.5; +} + +float computeLocalContrast(vec2 uv){ + float lumaMin = 100; + float lumaMax = 0; + + vec2 pixelSize = vec2(1) / textureSize(sampler2D(inTexture, textureSampler), 0); + + for(int x = -1; x <= 1; x++){ + for(int y = -1; y <= 1; y++){ + vec3 c = texture(sampler2D(inTexture, textureSampler), uv + vec2(x, y) * pixelSize).rgb; + float luma = computeLuma(c); + lumaMin = min(lumaMin, luma); + lumaMax = max(lumaMax, luma); + } + } + + return lumaMax - lumaMin; +} + +vec3 computeChromaticAberrationScale(vec2 uv){ + float localContrast = computeLocalContrast(uv); + vec3 colorScales = vec3(-1, 0, 1); + float aberrationScale = 0.004; + vec3 maxScaleFactors = colorScales * aberrationScale; + float factor = clamp(localContrast, 0, 1); + return mix(vec3(0), maxScaleFactors, factor); +} + +vec3 sampleColorChromaticAberration(vec2 uv){ + vec2 toCenter = (vec2(0.5) - uv); + + vec3 scaleFactors = computeChromaticAberrationScale(uv); + + float r = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.r).r; + float g = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.g).g; + float b = texture(sampler2D(inTexture, textureSampler), uv + toCenter * scaleFactors.b).b; + return vec3(r, g, b); +} + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 textureRes = textureSize(sampler2D(inTexture, textureSampler), 0); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec2 uv = vec2(coord) / textureRes; + float aspectRatio = float(textureRes.x) / textureRes.y; + uv = computeDistortedUV(uv, aspectRatio); + + vec3 tonemapped = sampleColorChromaticAberration(uv); + tonemapped = applyGrain(coord, tonemapped); + + vec3 gammaCorrected = pow(tonemapped, vec3(1.f / 2.2f)); + imageStore(outImage, coord, vec4(gammaCorrected, 0.f)); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/shader.frag b/projects/voxelization/assets/shaders/shader.frag new file mode 100644 index 0000000000000000000000000000000000000000..25ec69acb77bace1134920bbcee56deb40bb936b --- /dev/null +++ b/projects/voxelization/assets/shaders/shader.frag @@ -0,0 +1,170 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + +#include "perMeshResources.inc" +#include "lightInfo.inc" +#include "shadowMapping.inc" +#include "brdf.inc" +#include "voxel.inc" + +layout(location = 0) in vec3 passNormal; +layout(location = 1) in vec2 passUV; +layout(location = 2) in vec3 passPos; +layout(location = 3) in vec4 passTangent; + +layout(location = 0) out vec3 outColor; + +layout(set=0, binding=0) uniform sunBuffer { + LightInfo lightInfo; +}; +layout(set=0, binding=1) uniform texture2D shadowMap; +layout(set=0, binding=2) uniform sampler shadowMapSampler; + +layout(set=0, binding=3) uniform cameraBuffer { + vec3 cameraPos; +}; + +layout(set=0, binding=4) uniform texture3D voxelTexture; +layout(set=0, binding=5) uniform sampler voxelSampler; + +layout(set=0, binding=6) uniform VoxelInfoBuffer{ + VoxelInfo voxelInfo; +}; + +layout(set=0, binding=7) uniform VolumetricSettings { + vec3 scatteringCoefficient; + float volumetricAmbientLight; + vec3 absorptionCoefficient; +}; + + +vec3 cookTorrance(vec3 f0, float r, vec3 N, vec3 V, vec3 L){ + + vec3 H = normalize(L + V); + + float NoH = clamp(dot(N, H), 0, 1); + float NoL = clamp(dot(N, L), 0, 1); + float NoV = clamp(abs(dot(N, V)), 0, 1); // abs to account for wrong visibility caused by normal mapping + + vec3 F = fresnelSchlick(NoH, f0); + float D = GGXDistribution(r, NoH); + float G = GGXSmithShadowing(r, NoV, NoL); + + return (F * D * G) / max(4 * NoV * NoL, 0.00001); +} + +float roughnessToConeAngleDegree(float r){ + return mix(degreeToRadian(3), degreeToRadian(60), r); +} + +// from: "Next Generation Post Processing in Call Of Duty Advanced Warfare" slide page 123 +float interleavedGradientNoise(vec2 uv){ + vec3 magic = vec3(0.06711056, 0.00583715, 62.9829189); + return fract(magic.z * fract(dot(uv, magic.xy))); +} + +// from: https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile +vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV ) +{ + const vec4 c0 = { -1, -0.0275, -0.572, 0.022 }; + const vec4 c1 = { 1, 0.0425, 1.04, -0.04 }; + vec4 r = Roughness * c0 + c1; + float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; + vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; + return SpecularColor * AB.x + AB.y; +} + +float isotropicPhase(){ + return 1 / (4 * pi); +} + +vec3 volumetricLighting(vec3 colorIn, vec3 V, vec3 pos, float d){ + vec3 color = colorIn; + + int sampleCount = 20; + float stepSize = d / sampleCount; + + vec3 extinctionCoefficient = scatteringCoefficient + absorptionCoefficient; + + float noise = 2 * pi * interleavedGradientNoise(gl_FragCoord.xy); + vec2 shadowOffset = 3.f * vec2(sin(noise), cos(noise)) / textureSize(sampler2D(shadowMap, shadowMapSampler), 0); + + float noiseScale = 0.1f; + pos += V * noiseScale * interleavedGradientNoise(gl_FragCoord.xy); + + for(int i = 0; i < sampleCount; i++){ + vec3 samplePoint = pos + V * i * stepSize; + float phase = isotropicPhase(); + vec3 light = lightInfo.sunColor * lightInfo.sunStrength; + float shadow = shadowTest(samplePoint, lightInfo, shadowMap, shadowMapSampler, shadowOffset); + light *= shadow; + light += volumetricAmbientLight; + + color += phase * light * scatteringCoefficient * stepSize; + color *= exp(-stepSize * extinctionCoefficient); + } + return color; +} + +void main() { + + vec3 albedoTexel = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; + vec3 normalTexel = texture(sampler2D(normalTexture, textureSampler), passUV).rgb; + vec3 specularTexel = texture(sampler2D(specularTexture, textureSampler), passUV).rgb; + + float r = specularTexel.g; + + float metal = specularTexel.b; + vec3 albedo = mix(albedoTexel, vec3(0), metal); + vec3 f0_dielectric = vec3(0.04f); + vec3 f0 = mix(f0_dielectric, albedoTexel, metal); + + vec3 T = normalize(passTangent.xyz); + vec3 N_geo = normalize(passNormal); + vec3 B = cross(N_geo, T) * passTangent.w; + mat3 TBN = mat3(T, B, N_geo); + normalTexel = normalTexel * 2 - 1; + + vec3 N = normalize(TBN * normalTexel); + vec3 L = lightInfo.L; + vec3 V = normalize(cameraPos - passPos); + + float NoL = clamp(dot(N, L), 0, 1); + float NoV = clamp(abs(dot(N, V)), 0, 1); + + vec3 sunSpecular = cookTorrance(f0, r, N, V, L); + vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * NoL; + + float noise = 2 * pi * interleavedGradientNoise(gl_FragCoord.xy); + vec2 shadowOffset = 0.05f * vec2(sin(noise), cos(noise)) / textureSize(sampler2D(shadowMap, shadowMapSampler), 0); + float shadow = shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler, shadowOffset); + sun *= shadow; + + vec3 F_in = fresnelSchlick(NoL, f0); + vec3 F_out = fresnelSchlick(NoV, f0); + vec3 diffuse = lambertBRDF(albedo) * (1 - F_in) * (1 - F_out); + + vec3 up = abs(N_geo.y) >= 0.99 ? vec3(1, 0, 0) : vec3(0, 1, 0); + vec3 right = normalize(cross(up, N)); + up = cross(N, right); + mat3 toSurface = mat3(right, up, N); + + vec3 diffuseTrace = diffuseVoxelTraceHemisphere(toSurface, passPos, voxelTexture, voxelSampler, voxelInfo); + + vec3 R = reflect(-V, N); + float reflectionConeAngle = roughnessToConeAngleDegree(r); + vec3 offsetTraceStart = passPos + N_geo * 0.1f; + offsetTraceStart += R * interleavedGradientNoise(gl_FragCoord.xy) * 0.5; + vec3 specularTrace = voxelConeTrace(R, offsetTraceStart, reflectionConeAngle, voxelTexture, voxelSampler, voxelInfo); + specularTrace *= clamp(dot(N, R), 0, 1); + vec3 reflectionBRDF = EnvBRDFApprox(f0, r, NoV); + + outColor = + (diffuse + sunSpecular) * sun + + lambertBRDF(albedo) * diffuseTrace + + reflectionBRDF * specularTrace; + + float d = distance(cameraPos, passPos); + outColor = volumetricLighting(outColor, V, passPos, d); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shader.vert b/projects/voxelization/assets/shaders/shader.vert similarity index 80% rename from projects/voxelization/resources/shaders/shader.vert rename to projects/voxelization/assets/shaders/shader.vert index 926f86af2860cb57c44d2d5ee78712b6ae155e5c..e3873f98a308347592725e794d6b7102cbbe3e5c 100644 --- a/projects/voxelization/resources/shaders/shader.vert +++ b/projects/voxelization/assets/shaders/shader.vert @@ -4,10 +4,12 @@ layout(location = 0) in vec3 inPosition; layout(location = 1) in vec3 inNormal; layout(location = 2) in vec2 inUV; +layout(location = 3) in vec4 inTangent; layout(location = 0) out vec3 passNormal; layout(location = 1) out vec2 passUV; layout(location = 2) out vec3 passPos; +layout(location = 3) out vec4 passTangent; layout( push_constant ) uniform constants{ mat4 mvp; @@ -19,4 +21,5 @@ void main() { passNormal = mat3(model) * inNormal; // assuming no weird stuff like shearing or non-uniform scaling passUV = inUV; passPos = (model * vec4(inPosition, 1)).xyz; + passTangent = vec4(mat3(model) * inTangent.xyz, inTangent.w); } \ No newline at end of file diff --git a/projects/bloom/resources/shaders/shadow.frag b/projects/voxelization/assets/shaders/shadow.frag similarity index 62% rename from projects/bloom/resources/shaders/shadow.frag rename to projects/voxelization/assets/shaders/shadow.frag index 848f853f556660b4900b5db7fb6fc98d57c1cd5b..65592d2cfe161b8522de1a0c3e68fa1d6afa80be 100644 --- a/projects/bloom/resources/shaders/shadow.frag +++ b/projects/voxelization/assets/shaders/shadow.frag @@ -1,5 +1,6 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable void main() { diff --git a/projects/bloom/resources/shaders/shadow.vert b/projects/voxelization/assets/shaders/shadow.vert similarity index 82% rename from projects/bloom/resources/shaders/shadow.vert rename to projects/voxelization/assets/shaders/shadow.vert index e0f41d42d575fa64fedbfa04adf89ac0f4aeebe8..d800c547368c4f2126c880534276a3be3cf336f5 100644 --- a/projects/bloom/resources/shaders/shadow.vert +++ b/projects/voxelization/assets/shaders/shadow.vert @@ -1,6 +1,8 @@ #version 450 #extension GL_ARB_separate_shader_objects : enable +#extension GL_GOOGLE_include_directive : enable + layout(location = 0) in vec3 inPosition; layout( push_constant ) uniform constants{ diff --git a/projects/voxelization/assets/shaders/shadowBlur.inc b/projects/voxelization/assets/shaders/shadowBlur.inc new file mode 100644 index 0000000000000000000000000000000000000000..ed4994ed1ace34afdafff15920d18a2433a3c0a4 --- /dev/null +++ b/projects/voxelization/assets/shaders/shadowBlur.inc @@ -0,0 +1,27 @@ +#ifndef SHADOW_BLUR_INC +#define SHADOW_BLUR_INC + +vec4 blurMomentShadowMap1D(ivec2 coord, ivec2 blurDirection, texture2D srcTexture, sampler depthSampler){ + + int blurRadius = 7; + int minOffset = -(blurRadius-1) / 2; + int maxOffset = -minOffset; + + vec2 pixelSize = vec2(1) / textureSize(sampler2D(srcTexture, depthSampler), 0); + + float wTotal = 0; + vec4 moments = vec4(0); + + float weights1D[4] = { 0.5, 0.25, 0.125, 0.0625 }; // gaussian + + for(int i = minOffset; i <= maxOffset; i++){ + vec2 uv = (coord + i * blurDirection) * pixelSize; + uv += 0.5 * pixelSize * blurDirection * sign(i); // half pixel shift to take advantage of bilinear filtering + float w = weights1D[abs(i)]; + moments += w * texture(sampler2D(srcTexture, depthSampler), uv); + wTotal += w; + } + return moments / wTotal; +} + +#endif // #ifndef SHADOW_BLUR_INC \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/shadowBlurX.comp b/projects/voxelization/assets/shaders/shadowBlurX.comp new file mode 100644 index 0000000000000000000000000000000000000000..41d127fdf5ce46dec883d49af4f284b5787d5d38 --- /dev/null +++ b/projects/voxelization/assets/shaders/shadowBlurX.comp @@ -0,0 +1,23 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "shadowBlur.inc" + +layout(set=0, binding=0) uniform texture2D srcTexture; +layout(set=0, binding=1) uniform sampler depthSampler; +layout(set=0, binding=2, rgba16) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec4 moments = blurMomentShadowMap1D(coord, ivec2(1, 0), srcTexture, depthSampler); + // moments = texelFetch(sampler2D(srcTexture, depthSampler), coord, 0); + imageStore(outImage, coord, moments); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/shadowBlurY.comp b/projects/voxelization/assets/shaders/shadowBlurY.comp new file mode 100644 index 0000000000000000000000000000000000000000..c1710d7d6c75ef0093fecfe708272f56f9541eaf --- /dev/null +++ b/projects/voxelization/assets/shaders/shadowBlurY.comp @@ -0,0 +1,23 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#include "shadowBlur.inc" + +layout(set=0, binding=0) uniform texture2D srcTexture; +layout(set=0, binding=1) uniform sampler depthSampler; +layout(set=0, binding=2, rgba16) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec4 moments = blurMomentShadowMap1D(coord, ivec2(0, 1), srcTexture, depthSampler); + // moments = texelFetch(sampler2D(srcTexture, depthSampler), coord, 0); + imageStore(outImage, coord, moments); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/shadowMapping.inc b/projects/voxelization/assets/shaders/shadowMapping.inc new file mode 100644 index 0000000000000000000000000000000000000000..9124a05c310c2cc16e6b02802f5adb36bde42804 --- /dev/null +++ b/projects/voxelization/assets/shaders/shadowMapping.inc @@ -0,0 +1,97 @@ +#ifndef SHADOW_MAPPING_INC +#define SHADOW_MAPPING_INC + +#include "lightInfo.inc" + +// nice math blob from the moment shadow mapping presentation +float ComputeMSMShadowIntensity(vec4 _4Moments, float FragmentDepth, float DepthBias, float MomentBias) +{ + vec4 b=mix(_4Moments, vec4(0, 0.63, 0, 0.63),MomentBias); + + vec3 z; + z[0]=FragmentDepth-DepthBias; + float L32D22=fma(-b[0], b[1], b[2]); + float D22=fma(-b[0], b[0], b[1]); + float SquaredDepthVariance=fma(-b[1], b[1], b[3]); + float D33D22=dot(vec2(SquaredDepthVariance,-L32D22), + vec2(D22, L32D22)); + + float InvD22=1.0/D22; + float L32=L32D22*InvD22; + vec3 c=vec3(1.0,z[0],z[0]*z[0]); + c[1]-=b.x; + c[2]-=b.y+L32*c[1]; + c[1]*=InvD22; + c[2]*=D22/D33D22; + c[1]-=L32*c[2]; + c[0]-=dot(c.yz,b.xy); + float p=c[1]/c[2]; + float q=c[0]/c[2]; + float r=sqrt((p*p*0.25)-q); + z[1]=-p*0.5-r; + z[2]=-p*0.5+r; + vec4 Switch= + (z[2]<z[0])?vec4(z[1],z[0],1.0,1.0):( + (z[1]<z[0])?vec4(z[0],z[1],0.0,1.0): + vec4(0.0)); + float Quotient=(Switch[0]*z[2]-b[0]*(Switch[0]+z[2])+b[1]) + /((z[2]-Switch[1])*(z[0]-z[1])); + return 1-clamp(Switch[2]+Switch[3]*Quotient, 0, 1); +} + +vec4 quantizeMoments(vec4 moments){ + vec4 quantized; + quantized.r = 1.5 * moments.r - 2 * moments.b + 0.5; + quantized.g = 4 * moments.g - 4 * moments.a; + quantized.b = sqrt(3)/2 * moments.r - sqrt(12)/9 * moments.b + 0.5; + quantized.a = 0.5 * moments.g + 0.5 * moments.a; + + return quantized; +} + +vec4 unquantizeMoments(vec4 moments){ + moments -= vec4(0.5, 0, 0.5, 0); + vec4 unquantized; + unquantized.r = -1.f / 3 * moments.r + sqrt(3) * moments.b; + unquantized.g = 0.125 * moments.g + moments.a; + unquantized.b = -0.75 * moments.r + 0.75 * sqrt(3) * moments.b; + unquantized.a = -0.125 * moments.g + moments.a; + return unquantized / 0.98; // division reduces light bleeding +} + +float rescaleRange(float a, float b, float v) +{ + return clamp((v - a) / (b - a), 0, 1); +} + +float reduceLightBleeding(float shadow, float amount) +{ + return rescaleRange(amount, 1.0f, shadow); +} + +float shadowTest(vec3 worldPos, LightInfo lightInfo, texture2D shadowMap, sampler shadowMapSampler, vec2 offset){ + vec4 lightPos = lightInfo.lightMatrix * vec4(worldPos, 1); + lightPos /= lightPos.w; + lightPos.xy = lightPos.xy * 0.5 + 0.5; + lightPos.xy += offset; + + if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){ + return 1; + } + + lightPos.z = clamp(lightPos.z, 0, 1); + lightPos.z = 2 * lightPos.z - 1; // algorithm expects depth in range [-1:1] + + vec4 shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy); + + shadowMapSample = unquantizeMoments(shadowMapSample); + + float depthBias = 0.f; + float momentBias = 0.0006; + + float shadow = ComputeMSMShadowIntensity(shadowMapSample, lightPos.z, depthBias, momentBias); + return clamp(shadow, 0, 1); + // return reduceLightBleeding(shadow, 0.1f); +} + +#endif // #ifndef SHADOW_MAPPING_INC \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/sky.frag b/projects/voxelization/assets/shaders/sky.frag new file mode 100644 index 0000000000000000000000000000000000000000..2a3b2ad03e1936641a565b2f3fbd1f19f186ff7a --- /dev/null +++ b/projects/voxelization/assets/shaders/sky.frag @@ -0,0 +1,13 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) out vec3 outColor; + +layout( push_constant ) uniform constants{ + vec3 skyColor; + float skyStrength; +}; + +void main() { + outColor = skyColor * skyStrength; +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/sky.vert b/projects/voxelization/assets/shaders/sky.vert new file mode 100644 index 0000000000000000000000000000000000000000..686e6f352e9bb1054656f58340a9cfc9b55fcff4 --- /dev/null +++ b/projects/voxelization/assets/shaders/sky.vert @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +const vec2 positions[3] = { + vec2(-1, -1), + vec2(-1, 4), + vec2(4, -1) +}; + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 1, 1); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/tonemapping.comp b/projects/voxelization/assets/shaders/tonemapping.comp new file mode 100644 index 0000000000000000000000000000000000000000..ffadc9a71e207f97fec9a8815aa1c61bc709c369 --- /dev/null +++ b/projects/voxelization/assets/shaders/tonemapping.comp @@ -0,0 +1,34 @@ +#version 440 +#extension GL_GOOGLE_include_directive : enable + +layout(set=0, binding=0) uniform texture2D inTexture; +layout(set=0, binding=1) uniform sampler textureSampler; +layout(set=0, binding=2, rgba8) uniform image2D outImage; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +// from: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +vec3 ACESFilm(vec3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return clamp((x*(a*x+b))/(x*(c*x+d)+e), 0, 1); +} + +void main(){ + + if(any(greaterThanEqual(gl_GlobalInvocationID.xy, imageSize(outImage)))){ + return; + } + ivec2 textureRes = textureSize(sampler2D(inTexture, textureSampler), 0); + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + vec2 uv = vec2(coord) / textureRes; + + vec3 linearColor = texture(sampler2D(inTexture, textureSampler), uv).rgb; + vec3 tonemapped = ACESFilm(linearColor); + + imageStore(outImage, coord, vec4(tonemapped, 0.f)); +} \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/voxel.inc b/projects/voxelization/assets/shaders/voxel.inc new file mode 100644 index 0000000000000000000000000000000000000000..6133ca7cfc52ca77cb70fb8c2cc0e83ef6da4016 --- /dev/null +++ b/projects/voxelization/assets/shaders/voxel.inc @@ -0,0 +1,179 @@ +#include "brdf.inc" + +struct VoxelInfo{ + vec3 offset; + float extent; +}; + +struct PackedVoxelData{ + uint color; + uint normal; + uint albedo; +}; + +uint flattenVoxelUVToIndex(ivec3 UV, ivec3 voxelImageSize){ + return UV.x + UV.y * voxelImageSize.x + UV.z * voxelImageSize.x* voxelImageSize.y; +} + +vec3 worldToVoxelCoordinates(vec3 world, VoxelInfo info){ + return (world - info.offset) / info.extent + 0.5f; +} + +ivec3 voxelCoordinatesToUV(vec3 voxelCoordinates, ivec3 voxelImageResolution){ + return ivec3(voxelCoordinates * voxelImageResolution); +} + +vec3 voxelCoordinatesToWorldPosition(ivec3 coord, int voxelResolution, VoxelInfo voxelInfo, float voxelHalfSize){ + return (vec3(coord) / voxelResolution - 0.5) * voxelInfo.extent + voxelHalfSize + voxelInfo.offset; +} + +// packed voxel data: +// 1 bit opacity +// 7 bit exposure +// 8 bit blue +// 8 bit green +// 8 bit red +float maxExposure = 16.f; + +uint packVoxelColor(vec3 color){ + + color = clamp(color, vec3(0), vec3(maxExposure)); + float maxComponent = max(max(max(color.r, color.g), color.b), 1.f); + color /= maxComponent; + + uint opaqueBit = 1 << 31; + uint exposureBits = (0x0000007F & uint(maxComponent / maxExposure * 127)) << 24; + uint redBits = (0x000000FF & uint(color.r * 255)) << 0; + uint greenBits = (0x000000FF & uint(color.g * 255)) << 8; + uint blueBits = (0x000000FF & uint(color.b * 255)) << 16; + return opaqueBit | exposureBits | blueBits | greenBits | redBits; +} + +vec4 unpackVoxelColor(uint packed){ + vec4 rgba; + rgba.r = (packed >> 0 & 0x000000FF) / 255.f; + rgba.g = (packed >> 8 & 0x000000FF) / 255.f; + rgba.b = (packed >> 16 & 0x000000FF) / 255.f; + rgba.a = packed >> 31; + + rgba.rgb *= (packed >> 24 & 0x0000007F) / 127.f * maxExposure; + + return rgba; +} + +uint packSNormInto9Bits(float x){ + uint lengthBits = 0x000000FF & uint(abs(x) * 255.f); + uint signBits = (x < 0 ? 1 : 0) << 8; + return lengthBits | signBits; +} + +float unpack9LowBitsIntoSNorm(uint bits){ + bits = (0x000001FF & bits); + float length = bits / 255.f; + float sign = (bits >> 8) == 0 ? 1 : -1; + return sign * length; +} + +// normals are packed with 9 bits each, 8 for length and 1 for sign +uint packVoxelNormal(vec3 N){ + N = clamp(N, vec3(0), vec3(1)); + uint xBits = packSNormInto9Bits(N.x) << 0; + uint yBits = packSNormInto9Bits(N.y) << 9; + uint zBits = packSNormInto9Bits(N.z) << 18; + return zBits | yBits | xBits; +} + +vec3 unpackVoxelNormal(uint packed){ + vec3 N; + N.x = unpack9LowBitsIntoSNorm(packed >> 0); + N.y = unpack9LowBitsIntoSNorm(packed >> 9); + N.z = unpack9LowBitsIntoSNorm(packed >> 18); + return normalize(N); +} + +uint packUNormInto8Bits(float x){ + return 0x000000FF & uint(abs(x) * 255.f); +} + +float unpack8LowBitsIntoUNorm(uint bits){ + bits = (0x000000FF & bits); + return bits / 255.f; +} + +// albedo is packed with 8 bits each +uint packVoxelAlbedo(vec3 albedo){ + albedo = clamp(albedo, vec3(0), vec3(1)); + uint rBits = packUNormInto8Bits(albedo.r) << 0; + uint gBits = packUNormInto8Bits(albedo.g) << 8; + uint bBits = packUNormInto8Bits(albedo.b) << 16; + return bBits | gBits | rBits; +} + +vec3 unpackVoxelAlbedo(uint packed){ + vec3 albedo; + albedo.r = unpack8LowBitsIntoUNorm(packed >> 0); + albedo.g = unpack8LowBitsIntoUNorm(packed >> 8); + albedo.b = unpack8LowBitsIntoUNorm(packed >> 16); + return albedo; +} + +vec3 voxelConeTrace(vec3 direction, vec3 startPosition, float coneAngleRadian, texture3D voxelTexture, sampler voxelSampler, VoxelInfo voxelInfo){ + + int voxelResolution = textureSize(sampler3D(voxelTexture, voxelSampler), 0).x; + float voxelSize = voxelInfo.extent / voxelResolution; + float maxMip = float(log2(voxelResolution)); + float maxStableMip = 4; // must be the same as in Voxelization::voxelizeMeshes + maxMip = min(maxMip, maxStableMip); + float d = 2 * sqrt(3 * pow(voxelSize, 2)); + vec3 color = vec3(0); + float a = 0; + + float coneAngleHalf = coneAngleRadian * 0.5f; + + int maxSamples = 16; + for(int i = 0; i < maxSamples; i++){ + + vec3 samplePos = startPosition + d * direction; + vec3 sampleUV = worldToVoxelCoordinates(samplePos, voxelInfo); + + if(a >= 0.95 || any(lessThan(sampleUV, vec3(0))) || any(greaterThan(sampleUV, vec3(1)))){ + break; + } + + float coneDiameter = 2 * tan(coneAngleHalf) * d; + float mip = log2(coneDiameter / voxelSize); + mip = min(mip, maxMip); + + vec4 voxelSample = textureLod(sampler3D(voxelTexture, voxelSampler), sampleUV , mip); + + color += (1 - a) * voxelSample.rgb; + a += (1 - a) * voxelSample.a; + + float minStepSize = 1.f; + d += max(coneDiameter, minStepSize); + } + return color; +} + +float degreeToRadian(float d){ + return d / 180.f * pi; +} + +vec3 diffuseVoxelTraceHemisphere(mat3 toSurface, vec3 position, texture3D voxelTexture, sampler voxelSampler, VoxelInfo voxelInfo){ + float coneAngle = degreeToRadian(60.f); + vec3 diffuseTrace = vec3(0); + { + vec3 sampleDirection = toSurface * vec3(0, 0, 1); + float weight = pi / 4.f; + diffuseTrace += weight * voxelConeTrace(sampleDirection, position, coneAngle, voxelTexture, voxelSampler, voxelInfo); + } + for(int i = 0; i < 6;i++){ + float theta = 2 * pi / i; + float phi = pi / 3; // 60 degrees + vec3 sampleDirection = toSurface * vec3(cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi)); + float weight = pi * (3.f / 4.f) / 6; + vec3 trace = voxelConeTrace(sampleDirection, position, coneAngle, voxelTexture, voxelSampler, voxelInfo); + diffuseTrace += weight * trace; + } + return diffuseTrace; +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelBufferToImage.comp b/projects/voxelization/assets/shaders/voxelBufferToImage.comp similarity index 50% rename from projects/voxelization/resources/shaders/voxelBufferToImage.comp rename to projects/voxelization/assets/shaders/voxelBufferToImage.comp index 5e8298886cb2bacbc81f981e8e90310cdc876d5d..2c2cffe856c8b0fc7db07202572aaa35e8445603 100644 --- a/projects/voxelization/resources/shaders/voxelBufferToImage.comp +++ b/projects/voxelization/assets/shaders/voxelBufferToImage.comp @@ -3,7 +3,7 @@ #include "voxel.inc" layout(set=0, binding=0, std430) buffer voxelBuffer{ - uint packedVoxelData[]; + PackedVoxelData packedVoxelData[]; }; layout(set=0, binding=1, rgba16f) uniform image3D voxelImage; @@ -19,6 +19,15 @@ void main(){ ivec3 UV = ivec3(gl_GlobalInvocationID); uint flatIndex = flattenVoxelUVToIndex(UV, voxelImageSize); - vec4 color = unpackVoxelInfo(packedVoxelData[flatIndex]); + vec4 color = unpackVoxelColor(packedVoxelData[flatIndex].color); + + // for proper visualisation voxel secondary bounce should be disabled, otherwise it adds color + + // for debugging: write normal into image, so voxel visualisation draws normal + // color = vec4(unpackVoxelNormal(packedVoxelData[flatIndex].normal), color.a); + + // for debugging: write albedo into image, so voxel visualisation draws albedo + // color = vec4(unpackVoxelAlbedo(packedVoxelData[flatIndex].albedo), color.a); + imageStore(voxelImage, UV, vec4(color)); } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelReset.comp b/projects/voxelization/assets/shaders/voxelReset.comp similarity index 50% rename from projects/voxelization/resources/shaders/voxelReset.comp rename to projects/voxelization/assets/shaders/voxelReset.comp index 14b78d6584d703be68594e3cb03ebcd47c94b6e0..79eda9ec95e703d39af57bc3b29044f0ad6c1bf9 100644 --- a/projects/voxelization/resources/shaders/voxelReset.comp +++ b/projects/voxelization/assets/shaders/voxelReset.comp @@ -1,7 +1,9 @@ #version 450 +#extension GL_GOOGLE_include_directive : enable +#include "voxel.inc" layout(set=0, binding=0) buffer voxelizationBuffer{ - uint isFilled[]; + PackedVoxelData packedVoxelData[]; }; layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; @@ -15,5 +17,7 @@ void main(){ if(gl_GlobalInvocationID.x> voxelCount){ return; } - isFilled[gl_GlobalInvocationID.x] = 0; + packedVoxelData[gl_GlobalInvocationID.x].color = 0; + packedVoxelData[gl_GlobalInvocationID.x].normal = 0; + packedVoxelData[gl_GlobalInvocationID.x].albedo = 0; } \ No newline at end of file diff --git a/projects/voxelization/assets/shaders/voxelSecondaryBounce.comp b/projects/voxelization/assets/shaders/voxelSecondaryBounce.comp new file mode 100644 index 0000000000000000000000000000000000000000..29026e7052861ab190200b23d9f860bc609d1550 --- /dev/null +++ b/projects/voxelization/assets/shaders/voxelSecondaryBounce.comp @@ -0,0 +1,46 @@ +#version 450 +#extension GL_GOOGLE_include_directive : enable +#include "voxel.inc" +#include "brdf.inc" + +layout(set=0, binding=0, std430) buffer voxelBuffer{ + PackedVoxelData packedVoxelData[]; +}; +layout(set=0, binding=1) uniform texture3D voxelImageIn; +layout(set=0, binding=2) uniform sampler voxelSampler; +layout(set=0, binding=3, rgba16f) uniform image3D voxelImageOut; +layout(set=0, binding=4) uniform voxelizationInfo{ + VoxelInfo voxelInfo; +}; + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +void main(){ + + ivec3 voxelImageSize = imageSize(voxelImageOut); + if(any(greaterThanEqual(gl_GlobalInvocationID, voxelImageSize))){ + return; + } + ivec3 UV = ivec3(gl_GlobalInvocationID); + + vec4 color = texelFetch(sampler3D(voxelImageIn, voxelSampler), UV, 0); + + if(color.a > 0){ + uint flatIndex = flattenVoxelUVToIndex(UV, voxelImageSize); + vec3 N = unpackVoxelNormal(packedVoxelData[flatIndex].normal); + + float halfVoxelSize = voxelInfo.extent / float(voxelImageSize.x) * 0.5f; + vec3 pos = voxelCoordinatesToWorldPosition(UV, voxelImageSize.x, voxelInfo, halfVoxelSize); + + vec3 up = abs(N.y) >= 0.99 ? vec3(1, 0, 0) : vec3(0, 1, 0); + vec3 right = normalize(cross(up, N)); + up = cross(N, right); + mat3 toSurface = mat3(right, up, N); + + vec3 secondaryBounce = diffuseVoxelTraceHemisphere(toSurface, pos, voxelImageIn, voxelSampler, voxelInfo); + vec3 albedo = unpackVoxelAlbedo(packedVoxelData[flatIndex].albedo); + color.rgb += lambertBRDF(albedo) * secondaryBounce; + } + + imageStore(voxelImageOut, UV, color); +} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelVisualisation.frag b/projects/voxelization/assets/shaders/voxelVisualisation.frag similarity index 100% rename from projects/voxelization/resources/shaders/voxelVisualisation.frag rename to projects/voxelization/assets/shaders/voxelVisualisation.frag diff --git a/projects/voxelization/resources/shaders/voxelVisualisation.geom b/projects/voxelization/assets/shaders/voxelVisualisation.geom similarity index 100% rename from projects/voxelization/resources/shaders/voxelVisualisation.geom rename to projects/voxelization/assets/shaders/voxelVisualisation.geom diff --git a/projects/voxelization/resources/shaders/voxelVisualisation.vert b/projects/voxelization/assets/shaders/voxelVisualisation.vert similarity index 90% rename from projects/voxelization/resources/shaders/voxelVisualisation.vert rename to projects/voxelization/assets/shaders/voxelVisualisation.vert index 8377143f4f4bbf351d3251df9724d37e1747a4dc..e26e2209ffb9bd3e62103fa9e7eeccce13d7d602 100644 --- a/projects/voxelization/resources/shaders/voxelVisualisation.vert +++ b/projects/voxelization/assets/shaders/voxelVisualisation.vert @@ -25,7 +25,7 @@ void main() { int index2D = gl_VertexIndex % slicePixelCount; int y = index2D / voxelResolution; int x = index2D % voxelResolution; - vec3 position = (vec3(x, y, z) / voxelResolution - 0.5) * voxelInfo.extent + passCubeHalf + voxelInfo.offset; + vec3 position = voxelCoordinatesToWorldPosition(ivec3(x, y, z), voxelResolution, voxelInfo, passCubeHalf); gl_Position = vec4(position, 1.0); vec4 voxelColor = imageLoad(voxelImage, ivec3(x,y,z)); diff --git a/projects/voxelization/resources/shaders/voxelization.frag b/projects/voxelization/assets/shaders/voxelization.frag similarity index 75% rename from projects/voxelization/resources/shaders/voxelization.frag rename to projects/voxelization/assets/shaders/voxelization.frag index a49b13185ec26b069661141cfdbbfbbe45d14fd3..0bbd26bff249db1390399b26f2f4b5a139195fef 100644 --- a/projects/voxelization/resources/shaders/voxelization.frag +++ b/projects/voxelization/assets/shaders/voxelization.frag @@ -6,13 +6,14 @@ #include "perMeshResources.inc" #include "lightInfo.inc" #include "shadowMapping.inc" +#include "brdf.inc" layout(location = 0) in vec3 passPos; layout(location = 1) in vec2 passUV; layout(location = 2) in vec3 passN; layout(set=0, binding=0, std430) buffer voxelizationBuffer{ - uint packedVoxelData[]; + PackedVoxelData packedVoxelData[]; }; layout(set=0, binding=1) uniform voxelizationInfo{ @@ -28,14 +29,6 @@ layout(set=0, binding=3) uniform sunBuffer { layout(set=0, binding=4) uniform texture2D shadowMap; layout(set=0, binding=5) uniform sampler shadowMapSampler; -vec3 worldToVoxelCoordinates(vec3 world, VoxelInfo info){ - return (world - info.offset) / info.extent + 0.5f; -} - -ivec3 voxelCoordinatesToUV(vec3 voxelCoordinates, ivec3 voxelImageResolution){ - return ivec3(voxelCoordinates * voxelImageResolution); -} - void main() { vec3 voxelCoordinates = worldToVoxelCoordinates(passPos, voxelInfo); ivec3 voxelImageSize = imageSize(voxelImage); @@ -49,9 +42,11 @@ void main() { vec3 N = normalize(passN); float NoL = clamp(dot(N, lightInfo.L), 0, 1); - vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * NoL * shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler); + vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * NoL * shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler, vec2(0)); vec3 color = albedo * sun; - color = albedo * sun; + color = lambertBRDF(albedo) * sun; - atomicMax(packedVoxelData[flatIndex], packVoxelInfo(color)); + atomicMax(packedVoxelData[flatIndex].color, packVoxelColor(color)); + atomicMax(packedVoxelData[flatIndex].normal, packVoxelNormal(N)); + atomicMax(packedVoxelData[flatIndex].albedo, packVoxelAlbedo(albedo)); } \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxelization.geom b/projects/voxelization/assets/shaders/voxelization.geom similarity index 100% rename from projects/voxelization/resources/shaders/voxelization.geom rename to projects/voxelization/assets/shaders/voxelization.geom diff --git a/projects/voxelization/resources/shaders/voxelization.vert b/projects/voxelization/assets/shaders/voxelization.vert similarity index 92% rename from projects/voxelization/resources/shaders/voxelization.vert rename to projects/voxelization/assets/shaders/voxelization.vert index 1302a42441b5b9c8ea7d24f97d29b684e4d64993..221d0f6d189cfe1d6fb8e9e8e2fc9c04884c40c1 100644 --- a/projects/voxelization/resources/shaders/voxelization.vert +++ b/projects/voxelization/assets/shaders/voxelization.vert @@ -18,5 +18,5 @@ void main() { gl_Position = mvp * vec4(inPosition, 1.0); passPos = (model * vec4(inPosition, 1)).xyz; passUV = inUV; - passN = inNormal; + passN = mat3(model) * inNormal; } \ No newline at end of file diff --git a/projects/voxelization/resources/Sponza/Sponza.bin b/projects/voxelization/resources/Sponza/Sponza.bin deleted file mode 100644 index cfedd26ca5a67b6d0a47d44d13a75e14a141717a..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/Sponza.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4b809f7a17687dc99e6f41ca1ea32c06eded8779bf34d16f1f565d750b0ffd68 -size 6347696 diff --git a/projects/voxelization/resources/Sponza/Sponza.gltf b/projects/voxelization/resources/Sponza/Sponza.gltf deleted file mode 100644 index 172ea07e21c94465211c860cd805355704cef230..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/Sponza.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5cc0ecad5c4694088ff820e663619c370421afc1323ac487406e8e9b4735d787 -size 713962 diff --git a/projects/voxelization/resources/Sponza/background.png b/projects/voxelization/resources/Sponza/background.png deleted file mode 100644 index b64def129da38f4e23d89e21b4af1039008a4327..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/background.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f5b5f900ff8ed83a31750ec8e428b5b91273794ddcbfc4e4b8a6a7e781f8c686 -size 1417666 diff --git a/projects/voxelization/resources/Sponza/chain_texture.png b/projects/voxelization/resources/Sponza/chain_texture.png deleted file mode 100644 index c1e1768cff78e0614ad707eca8602a4c4edab5e5..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/chain_texture.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8362cfd472880daeaea37439326a4651d1338680ae69bb2513fc6b17c8de7d4 -size 490895 diff --git a/projects/voxelization/resources/Sponza/lion.png b/projects/voxelization/resources/Sponza/lion.png deleted file mode 100644 index c49c7f0ed31e762e19284d0d3624fbc47664e56b..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/lion.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f882f746c3a9cd51a9c6eedc1189b97668721d91a3fe49232036e789912c652 -size 2088728 diff --git a/projects/voxelization/resources/Sponza/spnza_bricks_a_diff.png b/projects/voxelization/resources/Sponza/spnza_bricks_a_diff.png deleted file mode 100644 index cde4c7a6511e9a5f03c63ad996437fcdba3ce2df..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/spnza_bricks_a_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b94219c2f5f943f3f4715c74e7d1038bf0ab3b3b3216a758eaee67f875df0851 -size 1928829 diff --git a/projects/voxelization/resources/Sponza/sponza_arch_diff.png b/projects/voxelization/resources/Sponza/sponza_arch_diff.png deleted file mode 100644 index bcd9bda2918d226039f9e2d03902d377b706fab6..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_arch_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0df2c8a01b2843b1c792b494f7173cdbc4f834840fc2177af3e5d690fceda57 -size 1596151 diff --git a/projects/voxelization/resources/Sponza/sponza_ceiling_a_diff.png b/projects/voxelization/resources/Sponza/sponza_ceiling_a_diff.png deleted file mode 100644 index 59de631ffac4414cabf69b2dc794c46fc187d6cb..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_ceiling_a_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ab6c187a81aa68f4eba30119e17fce2e4882a9ec320f70c90482dbe9da82b1c6 -size 1872074 diff --git a/projects/voxelization/resources/Sponza/sponza_column_a_diff.png b/projects/voxelization/resources/Sponza/sponza_column_a_diff.png deleted file mode 100644 index 01a82432d3f9939bbefe850bdb900f1ff9a3f6db..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_column_a_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2c291507e2808bb83e160ab4b020689817df273baad3713a9ad19ac15fac6826 -size 1840992 diff --git a/projects/voxelization/resources/Sponza/sponza_column_b_diff.png b/projects/voxelization/resources/Sponza/sponza_column_b_diff.png deleted file mode 100644 index 10a660cce2a5a9b8997772c746058ce23e7d45d7..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_column_b_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2820b0267c4289c6cedbb42721792a57ef244ec2d0935941011c2a7d3fe88a9b -size 2170433 diff --git a/projects/voxelization/resources/Sponza/sponza_column_c_diff.png b/projects/voxelization/resources/Sponza/sponza_column_c_diff.png deleted file mode 100644 index bc46fd979044a938d3adca7601689e71504e48bf..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_column_c_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0bc993ff59865468ef4530798930c7dfefb07482d71db45bc2a520986b27735 -size 2066950 diff --git a/projects/voxelization/resources/Sponza/sponza_curtain_blue_diff.png b/projects/voxelization/resources/Sponza/sponza_curtain_blue_diff.png deleted file mode 100644 index 384c8c2c051160d530eb3ac8b05c9c60752a2d2b..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_curtain_blue_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b85c6bb3cd5105f48d3812ec8e7a1068521ce69e917300d79e136e19d45422fb -size 9510905 diff --git a/projects/voxelization/resources/Sponza/sponza_curtain_diff.png b/projects/voxelization/resources/Sponza/sponza_curtain_diff.png deleted file mode 100644 index af842e9f5fe18c1f609875e00899a6770fa4488b..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_curtain_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:563c56bdbbee395a6ef7f0c51c8ac9223c162e517b4cdba0d4654e8de27c98d8 -size 9189263 diff --git a/projects/voxelization/resources/Sponza/sponza_curtain_green_diff.png b/projects/voxelization/resources/Sponza/sponza_curtain_green_diff.png deleted file mode 100644 index 6c9b6391a199407637fa71033d79fb58b8b4f0d7..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_curtain_green_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:238fe1c7f481388d1c1d578c2da8d411b99e8f0030ab62060a306db333124476 -size 8785458 diff --git a/projects/voxelization/resources/Sponza/sponza_details_diff.png b/projects/voxelization/resources/Sponza/sponza_details_diff.png deleted file mode 100644 index 12656686362c3e0a297e060491f33bd7351551f9..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_details_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cb1223b3bb82f8757e7df25a6891f1239cdd7ec59990340e952fb2d6b7ea570c -size 1522643 diff --git a/projects/voxelization/resources/Sponza/sponza_fabric_blue_diff.png b/projects/voxelization/resources/Sponza/sponza_fabric_blue_diff.png deleted file mode 100644 index 879d16ef84722a4fc13e83a771778de326e4bc54..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_fabric_blue_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:467d290bf5d4b2a017da140ba9e244ed8a8a9be5418a9ac9bcb4ad572ae2d7ab -size 2229440 diff --git a/projects/voxelization/resources/Sponza/sponza_fabric_diff.png b/projects/voxelization/resources/Sponza/sponza_fabric_diff.png deleted file mode 100644 index 3311287a219d2148620b87fe428fea071688d051..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_fabric_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1594f59cc2848db26add47361f4e665e3d8afa147760ed915d839fea42b20287 -size 2267382 diff --git a/projects/voxelization/resources/Sponza/sponza_fabric_green_diff.png b/projects/voxelization/resources/Sponza/sponza_fabric_green_diff.png deleted file mode 100644 index de110f369004388dae4cd5067c63428db3a07834..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_fabric_green_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:902b87faab221173bf370cea7c74cb9060b4d870ac6316b190dafded1cb12993 -size 2258220 diff --git a/projects/voxelization/resources/Sponza/sponza_flagpole_diff.png b/projects/voxelization/resources/Sponza/sponza_flagpole_diff.png deleted file mode 100644 index 5f6e0812a0df80346318baa3cb50a6888afc58f8..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_flagpole_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bfffb62e770959c725d0f3db6dc7dbdd46a380ec55ef884dab94d44ca017b438 -size 1425673 diff --git a/projects/voxelization/resources/Sponza/sponza_floor_a_diff.png b/projects/voxelization/resources/Sponza/sponza_floor_a_diff.png deleted file mode 100644 index 788ed764f79ba724f04a2d603076a5b85013e188..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_floor_a_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a16f9230fa91f9f31dfca6216ce205f1ef132d44f3b012fbf6efc0fba69770ab -size 1996838 diff --git a/projects/voxelization/resources/Sponza/sponza_roof_diff.png b/projects/voxelization/resources/Sponza/sponza_roof_diff.png deleted file mode 100644 index c5b84261fdd1cc776a94b3ce398c7806b895f9a3..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_roof_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7fc412138c20da19f8173e53545e771f4652558dff624d4dc67143e40efe562b -size 2320533 diff --git a/projects/voxelization/resources/Sponza/sponza_thorn_diff.png b/projects/voxelization/resources/Sponza/sponza_thorn_diff.png deleted file mode 100644 index 7a9142674a7d4a6f94a48c5152cf0300743b597a..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/sponza_thorn_diff.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a73a17c883cd0d0d67cfda2dc4118400a916366c05b9a5ac465f0c8b30fd9c8e -size 635001 diff --git a/projects/voxelization/resources/Sponza/vase_dif.png b/projects/voxelization/resources/Sponza/vase_dif.png deleted file mode 100644 index 61236a81cb324af8797b05099cd264cefe189e56..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/vase_dif.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:53d06f52bf9e59df4cf00237707cca76c4f692bda61a62b06a30d321311d6dd9 -size 1842101 diff --git a/projects/voxelization/resources/Sponza/vase_hanging.png b/projects/voxelization/resources/Sponza/vase_hanging.png deleted file mode 100644 index 36a3cee71d8213225090c74f8c0dce33b9d44378..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/vase_hanging.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a9d10b4f27a3c9a78d5bac882fdd4b6a6987c262f48fa490670fe5e235951e31 -size 1432804 diff --git a/projects/voxelization/resources/Sponza/vase_plant.png b/projects/voxelization/resources/Sponza/vase_plant.png deleted file mode 100644 index 7ad95e702e229f1ebd803e5203a266d15f2c07b9..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/vase_plant.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d2087371ff02212fb7014b6daefa191cf5676d2227193fff261a5d02f554cb8e -size 998089 diff --git a/projects/voxelization/resources/Sponza/vase_round.png b/projects/voxelization/resources/Sponza/vase_round.png deleted file mode 100644 index c17953abc000c44b8991e23c136c2b67348f3d1b..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/Sponza/vase_round.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa23d48d492d5d4ada2ddb27d1ef22952b214e6eb3b301c65f9d88442723d20a -size 1871399 diff --git a/projects/voxelization/resources/cube/boards2_vcyc_jpg.jpg b/projects/voxelization/resources/cube/boards2_vcyc_jpg.jpg deleted file mode 100644 index 2636039e272289c0fba3fa2d88a060b857501248..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/cube/boards2_vcyc_jpg.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cca33a6e58ddd1b37a6e6853a9aa0e7b15ca678937119194752393dd2a0a0564 -size 1192476 diff --git a/projects/voxelization/resources/cube/cube.bin b/projects/voxelization/resources/cube/cube.bin deleted file mode 100644 index 3303cd8635848bee18e10ab8754d5e4e7218db92..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/cube/cube.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9bb9b6b8bbe50a0aaa517057f245ee844f80afa7426dacb2aed4128f71629ce4 -size 840 diff --git a/projects/voxelization/resources/cube/cube.blend b/projects/voxelization/resources/cube/cube.blend deleted file mode 100644 index 62ccb2c742094bcfb5ed194ab905bffae86bfd65..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/cube/cube.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a6c1e245f259c610528c9485db6688928faac0ab2addee9e3c2dde7740e4dd09 -size 774920 diff --git a/projects/voxelization/resources/cube/cube.blend1 b/projects/voxelization/resources/cube/cube.blend1 deleted file mode 100644 index 13f21dcca218d7bc7a07a8a9682b5e1d9e607736..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/cube/cube.blend1 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f4496f423569b8ca81f3b3a55fad00f925557e0193fb9dbe6cdce7e71fb48f7b -size 774920 diff --git a/projects/voxelization/resources/cube/cube.glb b/projects/voxelization/resources/cube/cube.glb deleted file mode 100644 index 66a42c65e71dcf375e04cc378256024dd3c7834d..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/cube/cube.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:198568b715f397d78f7c358c0f709a419e7fd677e54cdec7c19f71b5ed264897 -size 1194508 diff --git a/projects/voxelization/resources/cube/cube.gltf b/projects/voxelization/resources/cube/cube.gltf deleted file mode 100644 index 428176144843dd06c78fe1d11a6392a0ea02b22d..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/cube/cube.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f82f455647a84ca6242882ae26a79a499d3ce594f8de317ab89488c5b79721ac -size 2823 diff --git a/projects/voxelization/resources/shaders/lightInfo.inc b/projects/voxelization/resources/shaders/lightInfo.inc deleted file mode 100644 index 4345d4f1504d27df7392b34bcaf17efdcfecef33..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/shaders/lightInfo.inc +++ /dev/null @@ -1,6 +0,0 @@ -struct LightInfo{ - vec3 L; float padding; - vec3 sunColor; - float sunStrength; - mat4 lightMatrix; -}; \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/perMeshResources.inc b/projects/voxelization/resources/shaders/perMeshResources.inc deleted file mode 100644 index 95e4fb7c27009965659d14a9c72acfec950c37e3..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/shaders/perMeshResources.inc +++ /dev/null @@ -1,2 +0,0 @@ -layout(set=1, binding=0) uniform texture2D albedoTexture; -layout(set=1, binding=1) uniform sampler textureSampler; \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shader.frag b/projects/voxelization/resources/shaders/shader.frag deleted file mode 100644 index 8653ae5958ce3b42eac6b1eaa6813f85b6ed589c..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/shaders/shader.frag +++ /dev/null @@ -1,28 +0,0 @@ -#version 450 -#extension GL_ARB_separate_shader_objects : enable -#extension GL_GOOGLE_include_directive : enable - -#include "perMeshResources.inc" -#include "lightInfo.inc" -#include "shadowMapping.inc" - -layout(location = 0) in vec3 passNormal; -layout(location = 1) in vec2 passUV; -layout(location = 2) in vec3 passPos; - -layout(location = 0) out vec3 outColor; - -layout(set=0, binding=0) uniform sunBuffer { - LightInfo lightInfo; -}; -layout(set=0, binding=1) uniform texture2D shadowMap; -layout(set=0, binding=2) uniform sampler shadowMapSampler; - -void main() { - vec3 N = normalize(passNormal); - vec3 sun = lightInfo.sunStrength * lightInfo.sunColor * clamp(dot(N, lightInfo.L), 0, 1); - sun *= shadowTest(passPos, lightInfo, shadowMap, shadowMapSampler); - vec3 ambient = vec3(0.05); - vec3 albedo = texture(sampler2D(albedoTexture, textureSampler), passUV).rgb; - outColor = albedo * (sun + ambient); -} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/shadowMapping.inc b/projects/voxelization/resources/shaders/shadowMapping.inc deleted file mode 100644 index 1fa34a388c35b96a3316e972ca562d35e2c3cf90..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/shaders/shadowMapping.inc +++ /dev/null @@ -1,16 +0,0 @@ -float shadowTest(vec3 worldPos, LightInfo lightInfo, texture2D shadowMap, sampler shadowMapSampler){ - vec4 lightPos = lightInfo.lightMatrix * vec4(worldPos, 1); - lightPos /= lightPos.w; - lightPos.xy = lightPos.xy * 0.5 + 0.5; - - if(any(lessThan(lightPos.xy, vec2(0))) || any(greaterThan(lightPos.xy, vec2(1)))){ - return 1; - } - - lightPos.z = clamp(lightPos.z, 0, 1); - - float shadowMapSample = texture(sampler2D(shadowMap, shadowMapSampler), lightPos.xy).r; - float bias = 0.01f; - shadowMapSample += bias; - return shadowMapSample < lightPos.z ? 0 : 1; -} \ No newline at end of file diff --git a/projects/voxelization/resources/shaders/voxel.inc b/projects/voxelization/resources/shaders/voxel.inc deleted file mode 100644 index 25c0a82bbc887913a4d69ccdeee2b0d8934828c8..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/shaders/voxel.inc +++ /dev/null @@ -1,42 +0,0 @@ -struct VoxelInfo{ - vec3 offset; - float extent; -}; - -uint flattenVoxelUVToIndex(ivec3 UV, ivec3 voxelImageSize){ - return UV.x + UV.y * voxelImageSize.x + UV.z * voxelImageSize.x* voxelImageSize.y; -} - -// packed voxel data: -// 1 bit opacity -// 7 bit exposure -// 8 bit blue -// 8 bit green -// 8 bit red -float maxExposure = 16.f; - -uint packVoxelInfo(vec3 color){ - - color = clamp(color, vec3(0), vec3(maxExposure)); - float maxComponent = max(max(max(color.r, color.g), color.b), 1.f); - color /= maxComponent; - - uint opaqueBit = 1 << 31; - uint exposureBits = (0x0000007F & uint(maxComponent / maxExposure * 127)) << 24; - uint redBits = (0x000000FF & uint(color.r * 255)) << 0; - uint greenBits = (0x000000FF & uint(color.g * 255)) << 8; - uint blueBits = (0x000000FF & uint(color.b * 255)) << 16; - return opaqueBit | exposureBits | blueBits | greenBits | redBits; -} - -vec4 unpackVoxelInfo(uint packed){ - vec4 rgba; - rgba.r = (packed >> 0 & 0x000000FF) / 255.f; - rgba.g = (packed >> 8 & 0x000000FF) / 255.f; - rgba.b = (packed >> 16 & 0x000000FF) / 255.f; - rgba.a = packed >> 31; - - rgba.rgb *= (packed >> 24 & 0x0000007F) / 127.f * maxExposure; - - return rgba; -} \ No newline at end of file diff --git a/projects/voxelization/resources/triangle/Triangle.bin b/projects/voxelization/resources/triangle/Triangle.bin deleted file mode 100644 index 57f26ad96592b64377e6aa93823d96a94e6c5022..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/triangle/Triangle.bin +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:412ebd5f7242c266b4957e7e26be13aa331dbcb7bbb854ab334a2437ae8ed959 -size 104 diff --git a/projects/voxelization/resources/triangle/Triangle.blend b/projects/voxelization/resources/triangle/Triangle.blend deleted file mode 100644 index 2421dc5e1bb029d73a9ec09cc4530c5196851fd7..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/triangle/Triangle.blend +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:387e544df173219fbf292a64a6656d1d782bbf71a5a9e9fdef0a308f47b05477 -size 758144 diff --git a/projects/voxelization/resources/triangle/Triangle.glb b/projects/voxelization/resources/triangle/Triangle.glb deleted file mode 100644 index 4148620cd6af0dadbc791aa1c52bb5431a40884b..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/triangle/Triangle.glb +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f4be087a605212d139416b5352a018283b26b99260cbcddb7013a1beeb331227 -size 980 diff --git a/projects/voxelization/resources/triangle/Triangle.gltf b/projects/voxelization/resources/triangle/Triangle.gltf deleted file mode 100644 index a188e6ee16a5e8486cf307c7bda8cfd99bdbeea6..0000000000000000000000000000000000000000 --- a/projects/voxelization/resources/triangle/Triangle.gltf +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d5fc354e040f79cff329e919677b194c75e3a522c6406f75c1108ad9575f12ec -size 2202 diff --git a/projects/voxelization/src/ShadowMapping.cpp b/projects/voxelization/src/ShadowMapping.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73f98cd3ea35d7f2bdf99d29f803df0abbc1adac --- /dev/null +++ b/projects/voxelization/src/ShadowMapping.cpp @@ -0,0 +1,338 @@ +#include "ShadowMapping.hpp" + +#include <vkcv/Pass.hpp> +#include <vkcv/shader/GLSLCompiler.hpp> + +const vk::Format shadowMapFormat = vk::Format::eR16G16B16A16Unorm; +const vk::Format shadowMapDepthFormat = vk::Format::eD32Sfloat; +const uint32_t shadowMapResolution = 1024; +const vkcv::Multisampling msaa = vkcv::Multisampling::MSAA8X; + +vkcv::ShaderProgram loadShadowShader() { + vkcv::ShaderProgram shader; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(vkcv::ShaderStage::VERTEX, "assets/shaders/shadow.vert", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, "assets/shaders/shadow.frag", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +vkcv::ShaderProgram loadDepthToMomentsShader() { + vkcv::ShaderProgram shader; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/depthToMoments.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +vkcv::ShaderProgram loadShadowBlurXShader() { + vkcv::ShaderProgram shader; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/shadowBlurX.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +vkcv::ShaderProgram loadShadowBlurYShader() { + vkcv::ShaderProgram shader; + vkcv::shader::GLSLCompiler compiler; + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/shadowBlurY.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +glm::mat4 computeShadowViewProjectionMatrix( + const glm::vec3& lightDirection, + const vkcv::camera::Camera& camera, + float maxShadowDistance, + const glm::vec3& voxelVolumeOffset, + float voxelVolumeExtent) { + + const glm::vec3 cameraPos = camera.getPosition(); + const glm::vec3 forward = glm::normalize(camera.getFront()); + glm::vec3 up = glm::normalize(camera.getUp()); + const glm::vec3 right = glm::normalize(glm::cross(forward, up)); + up = glm::cross(right, forward); + + const float fov = camera.getFov(); + const float aspectRatio = camera.getRatio(); + + float near; + float far; + camera.getNearFar(near, far); + far = std::min(maxShadowDistance, far); + + const glm::vec3 nearCenter = cameraPos + forward * near; + const float nearUp = near * tan(fov * 0.5); + const float nearRight = nearUp * aspectRatio; + + const glm::vec3 farCenter = cameraPos + forward * far; + const float farUp = far * tan(fov * 0.5); + const float farRight = farUp * aspectRatio; + + std::array<glm::vec3, 8> viewFrustumCorners = { + nearCenter + right * nearRight + nearUp * up, + nearCenter + right * nearRight - nearUp * up, + nearCenter - right * nearRight + nearUp * up, + nearCenter - right * nearRight - nearUp * up, + + farCenter + right * farRight + farUp * up, + farCenter + right * farRight - farUp * up, + farCenter - right * farRight + farUp * up, + farCenter - right * farRight - farUp * up + }; + + std::array<glm::vec3, 8> voxelVolumeCorners = { + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(1, 1, 1), + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(1, 1, -1), + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(1, -1, 1), + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(1, -1, -1), + + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(-1, 1, 1), + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(-1, 1, -1), + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(-1, -1, 1), + voxelVolumeOffset + voxelVolumeExtent * glm::vec3(-1, -1, -1), + }; + + glm::vec3 minView(std::numeric_limits<float>::max()); + glm::vec3 maxView(std::numeric_limits<float>::lowest()); + + const glm::mat4 view = glm::lookAt(glm::vec3(0), -lightDirection, glm::vec3(0, -1, 0)); + + auto getMinMaxView = [&](std::array<glm::vec3, 8> points) { + for (const glm::vec3& p : points) { + const auto& pView = glm::vec3(view * glm::vec4(p, 1)); + minView = glm::min(minView, pView); + maxView = glm::max(maxView, pView); + } + }; + + getMinMaxView(viewFrustumCorners); + getMinMaxView(voxelVolumeCorners); + + // rotationaly invariant to avoid shadow swimming when moving camera + // could potentially be wasteful, but guarantees stability, regardless of camera and voxel volume + glm::vec3 scale = glm::vec3(1.f / glm::max(far, voxelVolumeExtent)); + + glm::vec3 offset = -0.5f * (maxView + minView) * scale; + + // snap to texel to avoid shadow swimming when moving + glm::vec2 offset2D = glm::vec2(offset); + glm::vec2 frustumExtent2D = glm::vec2(1) / glm::vec2(scale); + glm::vec2 texelSize = glm::vec2(frustumExtent2D / static_cast<float>(shadowMapResolution)); + offset2D = glm::ceil(offset2D / texelSize) * texelSize; + offset.x = offset2D.x; + offset.y = offset2D.y; + + glm::mat4 crop(1); + crop[0][0] = scale.x; + crop[1][1] = scale.y; + crop[2][2] = scale.z; + + crop[3][0] = offset.x; + crop[3][1] = offset.y; + crop[3][2] = offset.z; + + glm::mat4 vulkanCorrectionMatrix(1.f); + vulkanCorrectionMatrix[2][2] = 0.5; + vulkanCorrectionMatrix[3][2] = 0.5; + + return vulkanCorrectionMatrix * crop * view; +} + +ShadowMapping::ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vertexLayout) : + m_corePtr(corePtr), + m_shadowMap(vkcv::image(*corePtr, shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, true, true)), + m_shadowMapIntermediate(vkcv::image(*corePtr, shadowMapFormat, shadowMapResolution, shadowMapResolution, 1, false, true)), + m_shadowMapDepth(vkcv::image(*corePtr, shadowMapDepthFormat, shadowMapResolution, shadowMapResolution, 1, false, false, false, msaa)), + m_lightInfoBuffer(buffer<LightInfo>(*corePtr, vkcv::BufferType::UNIFORM, 1)){ + + vkcv::ShaderProgram shadowShader = loadShadowShader(); + + // pass + m_shadowMapPass = vkcv::passFormat(*corePtr, shadowMapDepthFormat, true, msaa); + + // pipeline + vkcv::GraphicsPipelineConfig shadowPipeConfig ( + shadowShader, + m_shadowMapPass, + vertexLayout, + {} + ); + + shadowPipeConfig.setResolution(shadowMapResolution, shadowMapResolution); + shadowPipeConfig.setDepthClampingEnabled(true); + shadowPipeConfig.setCulling(vkcv::CullMode::Front); + m_shadowMapPipe = corePtr->createGraphicsPipeline(shadowPipeConfig); + + m_shadowSampler = corePtr->createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::CLAMP_TO_EDGE + ); + + // depth to moments + vkcv::ShaderProgram depthToMomentsShader = loadDepthToMomentsShader(); + + m_depthToMomentsDescriptorSetLayout = corePtr->createDescriptorSetLayout(depthToMomentsShader.getReflectedDescriptors().at(0)); + m_depthToMomentsDescriptorSet = corePtr->createDescriptorSet(m_depthToMomentsDescriptorSetLayout); + m_depthToMomentsPipe = corePtr->createComputePipeline({ depthToMomentsShader, { m_depthToMomentsDescriptorSetLayout }}); + + vkcv::DescriptorWrites depthToMomentDescriptorWrites; + depthToMomentDescriptorWrites.writeSampledImage(0, m_shadowMapDepth.getHandle()); + depthToMomentDescriptorWrites.writeSampler(1, m_shadowSampler); + depthToMomentDescriptorWrites.writeStorageImage(2, m_shadowMap.getHandle()); + corePtr->writeDescriptorSet(m_depthToMomentsDescriptorSet, depthToMomentDescriptorWrites); + + // shadow blur X + vkcv::ShaderProgram shadowBlurXShader = loadShadowBlurXShader(); + m_shadowBlurXDescriptorSetLayout = corePtr->createDescriptorSetLayout(shadowBlurXShader.getReflectedDescriptors().at(0)); + m_shadowBlurXDescriptorSet = corePtr->createDescriptorSet(m_shadowBlurXDescriptorSetLayout); + m_shadowBlurXPipe = corePtr->createComputePipeline({ shadowBlurXShader, { m_shadowBlurXDescriptorSetLayout }}); + + vkcv::DescriptorWrites shadowBlurXDescriptorWrites; + shadowBlurXDescriptorWrites.writeSampledImage(0, m_shadowMap.getHandle()); + shadowBlurXDescriptorWrites.writeSampler(1, m_shadowSampler); + shadowBlurXDescriptorWrites.writeStorageImage(2, m_shadowMapIntermediate.getHandle()); + corePtr->writeDescriptorSet(m_shadowBlurXDescriptorSet, shadowBlurXDescriptorWrites); + + // shadow blur Y + vkcv::ShaderProgram shadowBlurYShader = loadShadowBlurYShader(); + m_shadowBlurYDescriptorSetLayout = corePtr->createDescriptorSetLayout(shadowBlurYShader.getReflectedDescriptors().at(0)); + m_shadowBlurYDescriptorSet = corePtr->createDescriptorSet(m_shadowBlurYDescriptorSetLayout); + m_shadowBlurYPipe = corePtr->createComputePipeline({ shadowBlurYShader, { m_shadowBlurYDescriptorSetLayout }}); + + vkcv::DescriptorWrites shadowBlurYDescriptorWrites; + shadowBlurYDescriptorWrites.writeSampledImage(0, m_shadowMapIntermediate.getHandle()); + shadowBlurYDescriptorWrites.writeSampler(1, m_shadowSampler); + shadowBlurYDescriptorWrites.writeStorageImage(2, m_shadowMap.getHandle()); + corePtr->writeDescriptorSet(m_shadowBlurYDescriptorSet, shadowBlurYDescriptorWrites); +} + +void ShadowMapping::recordShadowMapRendering( + const vkcv::CommandStreamHandle& cmdStream, + const glm::vec2& lightAngleRadian, + const glm::vec3& lightColor, + float lightStrength, + float maxShadowDistance, + const std::vector<vkcv::VertexData>& meshes, + const std::vector<glm::mat4>& modelMatrices, + const vkcv::camera::Camera& camera, + const glm::vec3& voxelVolumeOffset, + float voxelVolumeExtent, + const vkcv::WindowHandle& windowHandle, + vkcv::Downsampler& downsampler) { + + LightInfo lightInfo; + lightInfo.sunColor = lightColor; + lightInfo.sunStrength = lightStrength; + lightInfo.direction = glm::normalize(glm::vec3( + std::cos(lightAngleRadian.x) * std::cos(lightAngleRadian.y), + std::sin(lightAngleRadian.x), + std::cos(lightAngleRadian.x) * std::sin(lightAngleRadian.y))); + + lightInfo.lightMatrix = computeShadowViewProjectionMatrix( + lightInfo.direction, + camera, + maxShadowDistance, + voxelVolumeOffset, + voxelVolumeExtent); + m_lightInfoBuffer.fill({ lightInfo }); + + vkcv::PushConstants shadowPushConstants = vkcv::pushConstants<glm::mat4>(); + + for (const auto& m : modelMatrices) { + shadowPushConstants.appendDrawcall(lightInfo.lightMatrix * m); + } + + std::vector<vkcv::InstanceDrawcall> drawcalls; + for (const auto& mesh : meshes) { + drawcalls.push_back(vkcv::InstanceDrawcall(mesh)); + } + + m_corePtr->recordBeginDebugLabel(cmdStream, "Shadow map depth", {1, 1, 1, 1}); + m_corePtr->recordDrawcallsToCmdStream( + cmdStream, + m_shadowMapPipe, + shadowPushConstants, + drawcalls, + { m_shadowMapDepth.getHandle() }, + windowHandle + ); + m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapDepth.getHandle()); + m_corePtr->recordEndDebugLabel(cmdStream); + + // depth to moments + const auto dispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(shadowMapResolution, shadowMapResolution), + vkcv::DispatchSize(8, 8) + ); + + const uint32_t msaaSampleCount = vkcv::msaaToSampleCount(msaa); + + vkcv::PushConstants msaaPushConstants = vkcv::pushConstants<uint32_t>(); + msaaPushConstants.appendDrawcall(msaaSampleCount); + + m_corePtr->recordBeginDebugLabel(cmdStream, "Depth to moments", { 1, 1, 1, 1 }); + + m_corePtr->prepareImageForStorage(cmdStream, m_shadowMap.getHandle()); + m_corePtr->recordComputeDispatchToCmdStream( + cmdStream, + m_depthToMomentsPipe, + dispatchCount, + { vkcv::useDescriptorSet(0, m_depthToMomentsDescriptorSet) }, + msaaPushConstants + ); + m_corePtr->prepareImageForSampling(cmdStream, m_shadowMap.getHandle()); + m_corePtr->recordEndDebugLabel(cmdStream); + + m_corePtr->recordBeginDebugLabel(cmdStream, "Moment shadow map blur", { 1, 1, 1, 1 }); + + // blur X + m_corePtr->prepareImageForStorage(cmdStream, m_shadowMapIntermediate.getHandle()); + m_corePtr->recordComputeDispatchToCmdStream( + cmdStream, + m_shadowBlurXPipe, + dispatchCount, + { vkcv::useDescriptorSet(0, m_shadowBlurXDescriptorSet) }, + vkcv::PushConstants(0) + ); + m_corePtr->prepareImageForSampling(cmdStream, m_shadowMapIntermediate.getHandle()); + + // blur Y + m_corePtr->prepareImageForStorage(cmdStream, m_shadowMap.getHandle()); + m_corePtr->recordComputeDispatchToCmdStream( + cmdStream, + m_shadowBlurYPipe, + dispatchCount, + { vkcv::useDescriptorSet(0, m_shadowBlurYDescriptorSet) }, + vkcv::PushConstants(0) + ); + m_shadowMap.recordMipChainGeneration(cmdStream, downsampler); + + m_corePtr->recordEndDebugLabel(cmdStream); +} + +vkcv::ImageHandle ShadowMapping::getShadowMap() { + return m_shadowMap.getHandle(); +} + +vkcv::SamplerHandle ShadowMapping::getShadowSampler() { + return m_shadowSampler; +} + +vkcv::BufferHandle ShadowMapping::getLightInfoBuffer() { + return m_lightInfoBuffer.getHandle(); +} \ No newline at end of file diff --git a/projects/voxelization/src/ShadowMapping.hpp b/projects/voxelization/src/ShadowMapping.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a843a7de9c9e4c731b16ae0c8d5bdfd2ac263711 --- /dev/null +++ b/projects/voxelization/src/ShadowMapping.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include <vkcv/Buffer.hpp> +#include <vkcv/Core.hpp> +#include <vkcv/camera/Camera.hpp> +#include <vkcv/Downsampler.hpp> +#include <vkcv/Image.hpp> + +#include <glm/glm.hpp> +#define GLM_ENABLE_EXPERIMENTAL // use this before inclusion, else error! +#include <glm/gtx/transform.hpp> + +struct LightInfo { + glm::vec3 direction; + float padding; + glm::vec3 sunColor; + float sunStrength; + glm::mat4 lightMatrix; +}; + +class ShadowMapping { +public: + ShadowMapping(vkcv::Core* corePtr, const vkcv::VertexLayout& vertexLayout); + + void recordShadowMapRendering( + const vkcv::CommandStreamHandle& cmdStream, + const glm::vec2& lightAngleRadian, + const glm::vec3& lightColor, + float lightStrength, + float maxShadowDistance, + const std::vector<vkcv::VertexData>& meshes, + const std::vector<glm::mat4>& modelMatrices, + const vkcv::camera::Camera& camera, + const glm::vec3& voxelVolumeOffset, + float voxelVolumeExtent, + const vkcv::WindowHandle& windowHandle, + vkcv::Downsampler& downsampler); + + vkcv::ImageHandle getShadowMap(); + vkcv::SamplerHandle getShadowSampler(); + vkcv::BufferHandle getLightInfoBuffer(); + +private: + vkcv::Core* m_corePtr; + + vkcv::Image m_shadowMap; + vkcv::Image m_shadowMapIntermediate; + vkcv::Image m_shadowMapDepth; + vkcv::SamplerHandle m_shadowSampler; + vkcv::Buffer<LightInfo> m_lightInfoBuffer; + + vkcv::PassHandle m_shadowMapPass; + vkcv::GraphicsPipelineHandle m_shadowMapPipe; + + vkcv::ComputePipelineHandle m_depthToMomentsPipe; + vkcv::DescriptorSetLayoutHandle m_depthToMomentsDescriptorSetLayout; + vkcv::DescriptorSetHandle m_depthToMomentsDescriptorSet; + + vkcv::ComputePipelineHandle m_shadowBlurXPipe; + vkcv::DescriptorSetLayoutHandle m_shadowBlurXDescriptorSetLayout; + vkcv::DescriptorSetHandle m_shadowBlurXDescriptorSet; + + vkcv::ComputePipelineHandle m_shadowBlurYPipe; + vkcv::DescriptorSetLayoutHandle m_shadowBlurYDescriptorSetLayout; + vkcv::DescriptorSetHandle m_shadowBlurYDescriptorSet; +}; \ No newline at end of file diff --git a/projects/voxelization/src/Voxelization.cpp b/projects/voxelization/src/Voxelization.cpp index a04131cedfdc508e14a3dbd97da642e1af48f8da..5c70b9dd0b3fe21118c4eddc32fca53e6e21c329 100644 --- a/projects/voxelization/src/Voxelization.cpp +++ b/projects/voxelization/src/Voxelization.cpp @@ -1,4 +1,6 @@ #include "Voxelization.hpp" + +#include <vkcv/Pass.hpp> #include <vkcv/shader/GLSLCompiler.hpp> #include <glm/gtc/matrix_transform.hpp> #include <algorithm> @@ -6,15 +8,15 @@ vkcv::ShaderProgram loadVoxelizationShader() { vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram shader; - compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/voxelization.vert", + compiler.compile(vkcv::ShaderStage::VERTEX, "assets/shaders/voxelization.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::GEOMETRY, "resources/shaders/voxelization.geom", + compiler.compile(vkcv::ShaderStage::GEOMETRY, "assets/shaders/voxelization.geom", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/voxelization.frag", + compiler.compile(vkcv::ShaderStage::FRAGMENT, "assets/shaders/voxelization.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -24,15 +26,15 @@ vkcv::ShaderProgram loadVoxelizationShader() { vkcv::ShaderProgram loadVoxelVisualisationShader() { vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram shader; - compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/voxelVisualisation.vert", + compiler.compile(vkcv::ShaderStage::VERTEX, "assets/shaders/voxelVisualisation.vert", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::GEOMETRY, "resources/shaders/voxelVisualisation.geom", + compiler.compile(vkcv::ShaderStage::GEOMETRY, "assets/shaders/voxelVisualisation.geom", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/voxelVisualisation.frag", + compiler.compile(vkcv::ShaderStage::FRAGMENT, "assets/shaders/voxelVisualisation.frag", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -42,7 +44,7 @@ vkcv::ShaderProgram loadVoxelVisualisationShader() { vkcv::ShaderProgram loadVoxelResetShader() { vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram shader; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/voxelReset.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/voxelReset.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); @@ -52,152 +54,173 @@ vkcv::ShaderProgram loadVoxelResetShader() { vkcv::ShaderProgram loadVoxelBufferToImageShader() { vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram shader; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/voxelBufferToImage.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/voxelBufferToImage.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { shader.addShader(shaderStage, path); }); return shader; } -const uint32_t voxelResolution = 128; -uint32_t voxelCount = voxelResolution * voxelResolution * voxelResolution; -const vk::Format voxelizationDummyFormat = vk::Format::eR8Unorm; +vkcv::ShaderProgram loadSecondaryBounceShader() { + vkcv::shader::GLSLCompiler compiler; + vkcv::ShaderProgram shader; + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/voxelSecondaryBounce.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + shader.addShader(shaderStage, path); + }); + return shader; +} + +const uint32_t voxelResolution = 128; +uint32_t voxelCount = voxelResolution * voxelResolution * voxelResolution; +const vk::Format voxelizationDummyFormat = vk::Format::eR8Unorm; +const int maxStableMip = 4; // must be the same as in voxelConeTrace shader function Voxelization::Voxelization( vkcv::Core* corePtr, const Dependencies& dependencies, vkcv::BufferHandle lightInfoBuffer, vkcv::ImageHandle shadowMap, - vkcv::SamplerHandle shadowSampler) + vkcv::SamplerHandle shadowSampler, + vkcv::SamplerHandle voxelSampler, + vkcv::Multisampling msaa) : - m_corePtr(corePtr), - m_voxelImage(m_corePtr->createImage(vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)), - m_dummyRenderTarget(m_corePtr->createImage(voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)), - m_voxelInfoBuffer(m_corePtr->createBuffer<VoxelizationInfo>(vkcv::BufferType::UNIFORM, 1)), - m_voxelBuffer(m_corePtr->createBuffer<VoxelBufferContent>(vkcv::BufferType::STORAGE, voxelCount)){ + m_corePtr(corePtr), + m_voxelImageIntermediate(vkcv::image(*m_corePtr, vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)), + m_voxelImage(vkcv::image(*m_corePtr, vk::Format::eR16G16B16A16Sfloat, voxelResolution, voxelResolution, voxelResolution, true, true)), + m_voxelBuffer(buffer<VoxelBufferContent>(*m_corePtr, vkcv::BufferType::STORAGE, voxelCount)), + m_dummyRenderTarget(vkcv::image(*m_corePtr, voxelizationDummyFormat, voxelResolution, voxelResolution, 1, false, false, true)), + m_voxelInfoBuffer(buffer<VoxelizationInfo>(*m_corePtr, vkcv::BufferType::UNIFORM, 1)) { const vkcv::ShaderProgram voxelizationShader = loadVoxelizationShader(); - const vkcv::PassConfig voxelizationPassConfig({vkcv::AttachmentDescription( - vkcv::AttachmentOperation::DONT_CARE, - vkcv::AttachmentOperation::DONT_CARE, - voxelizationDummyFormat) }); + const vkcv::PassConfig voxelizationPassConfig {{ + vkcv::AttachmentDescription( + voxelizationDummyFormat, + vkcv::AttachmentOperation::DONT_CARE, + vkcv::AttachmentOperation::DONT_CARE + ) + }, vkcv::Multisampling::None }; m_voxelizationPass = m_corePtr->createPass(voxelizationPassConfig); - std::vector<vkcv::DescriptorBinding> voxelizationDescriptorBindings = - { voxelizationShader.getReflectedDescriptors()[0] }; - m_voxelizationDescriptorSet = m_corePtr->createDescriptorSet(voxelizationDescriptorBindings); + m_voxelizationDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelizationShader.getReflectedDescriptors().at(0)); + m_voxelizationDescriptorSet = m_corePtr->createDescriptorSet(m_voxelizationDescriptorSetLayout); - vkcv::DescriptorSetHandle dummyPerMeshDescriptorSet = - m_corePtr->createDescriptorSet({ voxelizationShader.getReflectedDescriptors()[1] }); + vkcv::DescriptorSetLayoutHandle dummyPerMeshDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelizationShader.getReflectedDescriptors().at(1)); + vkcv::DescriptorSetHandle dummyPerMeshDescriptorSet = m_corePtr->createDescriptorSet(dummyPerMeshDescriptorSetLayout); - const vkcv::PipelineConfig voxelizationPipeConfig{ + vkcv::GraphicsPipelineConfig voxelizationPipeConfig ( voxelizationShader, - voxelResolution, - voxelResolution, m_voxelizationPass, dependencies.vertexLayout, - { - m_corePtr->getDescriptorSet(m_voxelizationDescriptorSet).layout, - m_corePtr->getDescriptorSet(dummyPerMeshDescriptorSet).layout}, - false, - true }; + { m_voxelizationDescriptorSetLayout, dummyPerMeshDescriptorSetLayout } + ); + + voxelizationPipeConfig.setResolution(voxelResolution, voxelResolution); + voxelizationPipeConfig.setUsingConservativeRasterization(true); + m_voxelizationPipe = m_corePtr->createGraphicsPipeline(voxelizationPipeConfig); vkcv::DescriptorWrites voxelizationDescriptorWrites; - voxelizationDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; - voxelizationDescriptorWrites.uniformBufferWrites = { - vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()), - vkcv::UniformBufferDescriptorWrite(3, lightInfoBuffer) - }; - voxelizationDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(4, shadowMap) }; - voxelizationDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(5, shadowSampler) }; - voxelizationDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(2, m_voxelImage.getHandle()) }; + voxelizationDescriptorWrites.writeStorageBuffer(0, m_voxelBuffer.getHandle()); + voxelizationDescriptorWrites.writeUniformBuffer( + 1, m_voxelInfoBuffer.getHandle() + ).writeUniformBuffer( + 3, lightInfoBuffer + ); + + voxelizationDescriptorWrites.writeSampledImage(4, shadowMap); + voxelizationDescriptorWrites.writeSampler(5, shadowSampler); + voxelizationDescriptorWrites.writeStorageImage(2, m_voxelImageIntermediate.getHandle()); m_corePtr->writeDescriptorSet(m_voxelizationDescriptorSet, voxelizationDescriptorWrites); vkcv::ShaderProgram voxelVisualisationShader = loadVoxelVisualisationShader(); - const std::vector<vkcv::DescriptorBinding> voxelVisualisationDescriptorBindings = - { voxelVisualisationShader.getReflectedDescriptors()[0] }; - m_visualisationDescriptorSet = m_corePtr->createDescriptorSet(voxelVisualisationDescriptorBindings); - - const vkcv::AttachmentDescription voxelVisualisationColorAttachments( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::LOAD, - dependencies.colorBufferFormat + m_visualisationDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(voxelVisualisationShader.getReflectedDescriptors().at(0)); + m_visualisationDescriptorSet = m_corePtr->createDescriptorSet(m_visualisationDescriptorSetLayout); + + m_visualisationPass = vkcv::passFormats( + *m_corePtr, + { dependencies.colorBufferFormat, dependencies.depthBufferFormat }, + false, + msaa ); - const vkcv::AttachmentDescription voxelVisualisationDepthAttachments( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::LOAD, - dependencies.depthBufferFormat - ); - - vkcv::PassConfig voxelVisualisationPassDefinition( - { voxelVisualisationColorAttachments, voxelVisualisationDepthAttachments }); - m_visualisationPass = m_corePtr->createPass(voxelVisualisationPassDefinition); - - const vkcv::PipelineConfig voxelVisualisationPipeConfig{ + vkcv::GraphicsPipelineConfig voxelVisualisationPipeConfig ( voxelVisualisationShader, - 0, - 0, m_visualisationPass, {}, - { m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).layout }, - true, - false, - vkcv::PrimitiveTopology::PointList }; // points are extended to cubes in the geometry shader + { m_visualisationDescriptorSetLayout } + ); // points are extended to cubes in the geometry shader + + voxelVisualisationPipeConfig.setPrimitiveTopology(vkcv::PrimitiveTopology::PointList); m_visualisationPipe = m_corePtr->createGraphicsPipeline(voxelVisualisationPipeConfig); std::vector<uint16_t> voxelIndexData; - for (int i = 0; i < voxelCount; i++) { - voxelIndexData.push_back(i); + for (uint32_t i = 0; i < voxelCount; i++) { + voxelIndexData.push_back(static_cast<uint16_t>(i)); } - const vkcv::DescriptorSetUsage voxelizationDescriptorUsage(0, m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).vulkanHandle); + const auto voxelizationDescriptorUsage = vkcv::useDescriptorSet(0, m_visualisationDescriptorSet); vkcv::ShaderProgram resetVoxelShader = loadVoxelResetShader(); - m_voxelResetDescriptorSet = m_corePtr->createDescriptorSet(resetVoxelShader.getReflectedDescriptors()[0]); - m_voxelResetPipe = m_corePtr->createComputePipeline( + m_voxelResetDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(resetVoxelShader.getReflectedDescriptors().at(0)); + m_voxelResetDescriptorSet = m_corePtr->createDescriptorSet(m_voxelResetDescriptorSetLayout); + m_voxelResetPipe = m_corePtr->createComputePipeline({ resetVoxelShader, - { m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).layout }); + { m_voxelResetDescriptorSetLayout } + }); vkcv::DescriptorWrites resetVoxelWrites; - resetVoxelWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; + resetVoxelWrites.writeStorageBuffer(0, m_voxelBuffer.getHandle()); m_corePtr->writeDescriptorSet(m_voxelResetDescriptorSet, resetVoxelWrites); - + // buffer to image vkcv::ShaderProgram bufferToImageShader = loadVoxelBufferToImageShader(); - m_bufferToImageDescriptorSet = m_corePtr->createDescriptorSet(bufferToImageShader.getReflectedDescriptors()[0]); - m_bufferToImagePipe = m_corePtr->createComputePipeline( + m_bufferToImageDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(bufferToImageShader.getReflectedDescriptors().at(0)); + m_bufferToImageDescriptorSet = m_corePtr->createDescriptorSet(m_bufferToImageDescriptorSetLayout); + m_bufferToImagePipe = m_corePtr->createComputePipeline({ bufferToImageShader, - { m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).layout }); + { m_bufferToImageDescriptorSetLayout } + }); vkcv::DescriptorWrites bufferToImageDescriptorWrites; - bufferToImageDescriptorWrites.storageBufferWrites = { vkcv::StorageBufferDescriptorWrite(0, m_voxelBuffer.getHandle()) }; - bufferToImageDescriptorWrites.storageImageWrites = { vkcv::StorageImageDescriptorWrite(1, m_voxelImage.getHandle()) }; + bufferToImageDescriptorWrites.writeStorageBuffer(0, m_voxelBuffer.getHandle()); + bufferToImageDescriptorWrites.writeStorageImage(1, m_voxelImageIntermediate.getHandle()); m_corePtr->writeDescriptorSet(m_bufferToImageDescriptorSet, bufferToImageDescriptorWrites); + + // secondary bounce + vkcv::ShaderProgram secondaryBounceShader = loadSecondaryBounceShader(); + + m_secondaryBounceDescriptorSetLayout = m_corePtr->createDescriptorSetLayout(secondaryBounceShader.getReflectedDescriptors().at(0)); + m_secondaryBounceDescriptorSet = m_corePtr->createDescriptorSet(m_secondaryBounceDescriptorSetLayout); + m_secondaryBouncePipe = m_corePtr->createComputePipeline({ + secondaryBounceShader, + { m_secondaryBounceDescriptorSetLayout } + }); + + vkcv::DescriptorWrites secondaryBounceDescriptorWrites; + secondaryBounceDescriptorWrites.writeStorageBuffer(0, m_voxelBuffer.getHandle()); + secondaryBounceDescriptorWrites.writeSampledImage(1, m_voxelImageIntermediate.getHandle()); + secondaryBounceDescriptorWrites.writeSampler(2, voxelSampler); + secondaryBounceDescriptorWrites.writeStorageImage(3, m_voxelImage.getHandle()); + secondaryBounceDescriptorWrites.writeUniformBuffer(4, m_voxelInfoBuffer.getHandle()); + m_corePtr->writeDescriptorSet(m_secondaryBounceDescriptorSet, secondaryBounceDescriptorWrites); } void Voxelization::voxelizeMeshes( - vkcv::CommandStreamHandle cmdStream, - const glm::vec3& cameraPosition, - const std::vector<vkcv::Mesh>& meshes, + vkcv::CommandStreamHandle cmdStream, + const std::vector<vkcv::VertexData>& meshes, const std::vector<glm::mat4>& modelMatrices, - const std::vector<vkcv::DescriptorSetHandle>& perMeshDescriptorSets) { + const std::vector<vkcv::DescriptorSetHandle>& perMeshDescriptorSets, + const vkcv::WindowHandle& windowHandle, + vkcv::Downsampler& downsampler) { - VoxelizationInfo voxelizationInfo; - voxelizationInfo.extent = m_voxelExtent; + m_voxelInfoBuffer.fill({ m_voxelInfo }); - // move voxel offset with camera in voxel sized steps - const float voxelSize = m_voxelExtent / voxelResolution; - voxelizationInfo.offset = glm::floor(cameraPosition / voxelSize) * voxelSize; - - m_voxelInfoBuffer.fill({ voxelizationInfo }); - - const float voxelizationHalfExtent = 0.5f * m_voxelExtent; + const float voxelizationHalfExtent = 0.5f * m_voxelInfo.extent; const glm::mat4 voxelizationProjection = glm::ortho( -voxelizationHalfExtent, voxelizationHalfExtent, @@ -206,103 +229,160 @@ void Voxelization::voxelizeMeshes( -voxelizationHalfExtent, voxelizationHalfExtent); - const glm::mat4 voxelizationView = glm::translate(glm::mat4(1.f), -voxelizationInfo.offset); + const glm::mat4 voxelizationView = glm::translate(glm::mat4(1.f), -m_voxelInfo.offset); const glm::mat4 voxelizationViewProjection = voxelizationProjection * voxelizationView; - - std::vector<std::array<glm::mat4, 2>> voxelizationMatrices; + + vkcv::PushConstants voxelizationPushConstants (2 * sizeof(glm::mat4)); + for (const auto& m : modelMatrices) { - voxelizationMatrices.push_back({ voxelizationViewProjection * m, m }); + voxelizationPushConstants.appendDrawcall(std::array<glm::mat4, 2>{ voxelizationViewProjection * m, m }); } - const vkcv::PushConstantData voxelizationPushConstantData((void*)voxelizationMatrices.data(), 2 * sizeof(glm::mat4)); - // reset voxels const uint32_t resetVoxelGroupSize = 64; - uint32_t resetVoxelDispatchCount[3]; - resetVoxelDispatchCount[0] = glm::ceil(voxelCount / float(resetVoxelGroupSize)); - resetVoxelDispatchCount[1] = 1; - resetVoxelDispatchCount[2] = 1; + + vkcv::PushConstants voxelCountPushConstants = vkcv::pushConstants<uint32_t>(); + voxelCountPushConstants.appendDrawcall(voxelCount); - m_corePtr->prepareImageForStorage(cmdStream, m_voxelImage.getHandle()); + m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel reset", { 1, 1, 1, 1 }); m_corePtr->recordComputeDispatchToCmdStream( cmdStream, m_voxelResetPipe, - resetVoxelDispatchCount, - { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_voxelResetDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(&voxelCount, sizeof(voxelCount))); + vkcv::dispatchInvocations(voxelCount, resetVoxelGroupSize), + { vkcv::useDescriptorSet(0, m_voxelResetDescriptorSet) }, + voxelCountPushConstants + ); m_corePtr->recordBufferMemoryBarrier(cmdStream, m_voxelBuffer.getHandle()); + m_corePtr->recordEndDebugLabel(cmdStream); // voxelization - std::vector<vkcv::DrawcallInfo> drawcalls; - for (int i = 0; i < meshes.size(); i++) { - drawcalls.push_back(vkcv::DrawcallInfo( - meshes[i], - { - vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_voxelizationDescriptorSet).vulkanHandle), - vkcv::DescriptorSetUsage(1, m_corePtr->getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) - })); + std::vector<vkcv::InstanceDrawcall> drawcalls; + for (size_t i = 0; i < meshes.size(); i++) { + vkcv::InstanceDrawcall drawcall (meshes[i]); + drawcall.useDescriptorSet(0, m_voxelizationDescriptorSet); + drawcall.useDescriptorSet(1, perMeshDescriptorSets[i]); + drawcalls.push_back(drawcall); } + m_corePtr->recordBeginDebugLabel(cmdStream, "Voxelization", { 1, 1, 1, 1 }); + m_corePtr->prepareImageForStorage(cmdStream, m_voxelImageIntermediate.getHandle()); m_corePtr->recordDrawcallsToCmdStream( cmdStream, - m_voxelizationPass, m_voxelizationPipe, - voxelizationPushConstantData, + voxelizationPushConstants, drawcalls, - { m_dummyRenderTarget.getHandle() }); + { m_dummyRenderTarget.getHandle() }, + windowHandle + ); + m_corePtr->recordEndDebugLabel(cmdStream); // buffer to image - const uint32_t bufferToImageGroupSize[3] = { 4, 4, 4 }; - uint32_t bufferToImageDispatchCount[3]; - for (int i = 0; i < 3; i++) { - bufferToImageDispatchCount[i] = glm::ceil(voxelResolution / float(bufferToImageGroupSize[i])); - } - + const auto bufferToImageDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(voxelResolution, voxelResolution, voxelResolution), + vkcv::DispatchSize(4, 4, 4) + ); + + m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel buffer to image", { 1, 1, 1, 1 }); m_corePtr->recordComputeDispatchToCmdStream( cmdStream, m_bufferToImagePipe, bufferToImageDispatchCount, - { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_bufferToImageDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); + { vkcv::useDescriptorSet(0, m_bufferToImageDescriptorSet) }, + vkcv::PushConstants(0) + ); - m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImage.getHandle()); + m_corePtr->recordImageMemoryBarrier(cmdStream, m_voxelImageIntermediate.getHandle()); + m_corePtr->recordEndDebugLabel(cmdStream); - m_voxelImage.recordMipChainGeneration(cmdStream); + // intermediate image mipchain + m_corePtr->recordBeginDebugLabel(cmdStream, "Intermediate Voxel mipmap generation", { 1, 1, 1, 1 }); + m_voxelImageIntermediate.recordMipChainGeneration(cmdStream, downsampler); + m_corePtr->recordEndDebugLabel(cmdStream); + + // secondary bounce + m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel secondary bounce", { 1, 1, 1, 1 }); + m_corePtr->prepareImageForStorage(cmdStream, m_voxelImage.getHandle()); + m_corePtr->recordComputeDispatchToCmdStream( + cmdStream, + m_secondaryBouncePipe, + bufferToImageDispatchCount, + { vkcv::useDescriptorSet(0, m_secondaryBounceDescriptorSet) }, + vkcv::PushConstants(0)); + m_voxelImage.recordMipChainGeneration(cmdStream, downsampler); + m_corePtr->recordEndDebugLabel(cmdStream); + + // final image mipchain + m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel mipmap generation", { 1, 1, 1, 1 }); + m_voxelImage.recordMipChainGeneration(cmdStream, downsampler); + m_corePtr->recordEndDebugLabel(cmdStream); } void Voxelization::renderVoxelVisualisation( vkcv::CommandStreamHandle cmdStream, const glm::mat4& viewProjectin, const std::vector<vkcv::ImageHandle>& renderTargets, - uint32_t mipLevel) { + uint32_t mipLevel, + const vkcv::WindowHandle& windowHandle) { - const vkcv::PushConstantData voxelVisualisationPushConstantData((void*)&viewProjectin, sizeof(glm::mat4)); + vkcv::PushConstants voxelVisualisationPushConstants = vkcv::pushConstants<glm::mat4>(); + voxelVisualisationPushConstants.appendDrawcall(viewProjectin); - mipLevel = std::clamp(mipLevel, (uint32_t)0, m_voxelImage.getMipCount()-1); + mipLevel = std::clamp(mipLevel, (uint32_t) 0, m_voxelImage.getMipLevels() - 1); // write descriptor set vkcv::DescriptorWrites voxelVisualisationDescriptorWrite; - voxelVisualisationDescriptorWrite.storageImageWrites = - { vkcv::StorageImageDescriptorWrite(0, m_voxelImage.getHandle(), mipLevel) }; - voxelVisualisationDescriptorWrite.uniformBufferWrites = - { vkcv::UniformBufferDescriptorWrite(1, m_voxelInfoBuffer.getHandle()) }; + voxelVisualisationDescriptorWrite.writeStorageImage(0, m_voxelImage.getHandle(), mipLevel); + voxelVisualisationDescriptorWrite.writeUniformBuffer(1, m_voxelInfoBuffer.getHandle()); m_corePtr->writeDescriptorSet(m_visualisationDescriptorSet, voxelVisualisationDescriptorWrite); uint32_t drawVoxelCount = voxelCount / exp2(mipLevel); - - const auto drawcall = vkcv::DrawcallInfo( - vkcv::Mesh({}, nullptr, drawVoxelCount), - { vkcv::DescriptorSetUsage(0, m_corePtr->getDescriptorSet(m_visualisationDescriptorSet).vulkanHandle) }); - + + vkcv::VertexData voxelData; + voxelData.setCount(drawVoxelCount); + + vkcv::InstanceDrawcall drawcall (voxelData); + drawcall.useDescriptorSet(0, m_visualisationDescriptorSet); + + m_corePtr->recordBeginDebugLabel(cmdStream, "Voxel visualisation", { 1, 1, 1, 1 }); + m_corePtr->prepareImageForStorage(cmdStream, m_voxelImage.getHandle()); m_corePtr->recordDrawcallsToCmdStream( cmdStream, - m_visualisationPass, m_visualisationPipe, - voxelVisualisationPushConstantData, + voxelVisualisationPushConstants, { drawcall }, - renderTargets); + renderTargets, + windowHandle + ); + m_corePtr->recordEndDebugLabel(cmdStream); +} + +void Voxelization::updateVoxelOffset(const vkcv::camera::Camera& camera) { + + // move voxel offset with camera in voxel sized steps + const float voxelSize = m_voxelInfo.extent / voxelResolution; + const float snapSize = voxelSize * exp2(maxStableMip); + + glm::vec3 voxelVolumeCenter = camera.getPosition() + (1.f / 3.f) * m_voxelInfo.extent * glm::normalize(camera.getFront()); + voxelVolumeCenter.y = camera.getPosition().y; + m_voxelInfo.offset = glm::floor(voxelVolumeCenter / snapSize) * snapSize; } void Voxelization::setVoxelExtent(float extent) { - m_voxelExtent = extent; -} \ No newline at end of file + m_voxelInfo.extent = extent; +} + +vkcv::ImageHandle Voxelization::getVoxelImageHandle() const { + return m_voxelImage.getHandle(); +} + +vkcv::BufferHandle Voxelization::getVoxelInfoBufferHandle() const { + return m_voxelInfoBuffer.getHandle(); +} + +glm::vec3 Voxelization::getVoxelOffset() const{ + return m_voxelInfo.offset; +} + +float Voxelization::getVoxelExtent() const { + return m_voxelInfo.extent; +} diff --git a/projects/voxelization/src/Voxelization.hpp b/projects/voxelization/src/Voxelization.hpp index 25830b171edb9154e37b2d597c2bbbf2daea6b2e..f624d03503b67cc2de0632367d90742fb975473e 100644 --- a/projects/voxelization/src/Voxelization.hpp +++ b/projects/voxelization/src/Voxelization.hpp @@ -1,6 +1,11 @@ #pragma once + +#include <vkcv/Buffer.hpp> #include <vkcv/Core.hpp> +#include <vkcv/Image.hpp> #include <glm/glm.hpp> +#include <vkcv/camera/Camera.hpp> +#include <vkcv/Downsampler.hpp> class Voxelization{ public: @@ -14,48 +19,70 @@ public: const Dependencies& dependencies, vkcv::BufferHandle lightInfoBuffer, vkcv::ImageHandle shadowMap, - vkcv::SamplerHandle shadowSampler); + vkcv::SamplerHandle shadowSampler, + vkcv::SamplerHandle voxelSampler, + vkcv::Multisampling msaa); void voxelizeMeshes( - vkcv::CommandStreamHandle cmdStream, - const glm::vec3& cameraPosition, - const std::vector<vkcv::Mesh>& meshes, + vkcv::CommandStreamHandle cmdStream, + const std::vector<vkcv::VertexData>& meshes, const std::vector<glm::mat4>& modelMatrices, - const std::vector<vkcv::DescriptorSetHandle>& perMeshDescriptorSets); + const std::vector<vkcv::DescriptorSetHandle>& perMeshDescriptorSets, + const vkcv::WindowHandle& windowHandle, + vkcv::Downsampler& downsampler); void renderVoxelVisualisation( vkcv::CommandStreamHandle cmdStream, const glm::mat4& viewProjectin, const std::vector<vkcv::ImageHandle>& renderTargets, - uint32_t mipLevel); + uint32_t mipLevel, + const vkcv::WindowHandle& windowHandle); + void updateVoxelOffset(const vkcv::camera::Camera& camera); void setVoxelExtent(float extent); + vkcv::ImageHandle getVoxelImageHandle() const; + vkcv::BufferHandle getVoxelInfoBufferHandle() const; + + glm::vec3 getVoxelOffset() const; + float getVoxelExtent() const; + private: vkcv::Core* m_corePtr; struct VoxelBufferContent{ - uint32_t isFilled; + uint32_t lightEncoded; + uint32_t normalEncoded; + uint32_t albedoEncoded; }; + vkcv::Image m_voxelImageIntermediate; vkcv::Image m_voxelImage; - vkcv::Buffer<VoxelBufferContent> m_voxelBuffer; + vkcv::Buffer<VoxelBufferContent> m_voxelBuffer; + + vkcv::Image m_dummyRenderTarget; + vkcv::PassHandle m_voxelizationPass; + vkcv::GraphicsPipelineHandle m_voxelizationPipe; + vkcv::DescriptorSetLayoutHandle m_voxelizationDescriptorSetLayout; + vkcv::DescriptorSetHandle m_voxelizationDescriptorSet; - vkcv::Image m_dummyRenderTarget; - vkcv::PassHandle m_voxelizationPass; - vkcv::PipelineHandle m_voxelizationPipe; - vkcv::DescriptorSetHandle m_voxelizationDescriptorSet; + vkcv::ComputePipelineHandle m_voxelResetPipe; + vkcv::DescriptorSetLayoutHandle m_voxelResetDescriptorSetLayout; + vkcv::DescriptorSetHandle m_voxelResetDescriptorSet; - vkcv::PipelineHandle m_voxelResetPipe; - vkcv::DescriptorSetHandle m_voxelResetDescriptorSet; + vkcv::ComputePipelineHandle m_bufferToImagePipe; + vkcv::DescriptorSetLayoutHandle m_bufferToImageDescriptorSetLayout; + vkcv::DescriptorSetHandle m_bufferToImageDescriptorSet; - vkcv::PipelineHandle m_bufferToImagePipe; - vkcv::DescriptorSetHandle m_bufferToImageDescriptorSet; + vkcv::PassHandle m_visualisationPass; + vkcv::GraphicsPipelineHandle m_visualisationPipe; - vkcv::PassHandle m_visualisationPass; - vkcv::PipelineHandle m_visualisationPipe; + vkcv::ComputePipelineHandle m_secondaryBouncePipe; + vkcv::DescriptorSetLayoutHandle m_secondaryBounceDescriptorSetLayout; + vkcv::DescriptorSetHandle m_secondaryBounceDescriptorSet; - vkcv::DescriptorSetHandle m_visualisationDescriptorSet; + vkcv::DescriptorSetLayoutHandle m_visualisationDescriptorSetLayout; + vkcv::DescriptorSetHandle m_visualisationDescriptorSet; struct VoxelizationInfo { glm::vec3 offset; @@ -63,5 +90,5 @@ private: }; vkcv::Buffer<VoxelizationInfo> m_voxelInfoBuffer; - float m_voxelExtent = 20.f; + VoxelizationInfo m_voxelInfo; }; \ No newline at end of file diff --git a/projects/voxelization/src/main.cpp b/projects/voxelization/src/main.cpp index aabed2180f598c9792a76ddcdcf4a2f400d16334..9b9bf39b205cd65b8a0f5813b7f4376ee68152a9 100644 --- a/projects/voxelization/src/main.cpp +++ b/projects/voxelization/src/main.cpp @@ -1,50 +1,133 @@ #include <iostream> #include <vkcv/Core.hpp> +#include <vkcv/Pass.hpp> +#include <vkcv/Sampler.hpp> #include <GLFW/glfw3.h> #include <vkcv/camera/CameraManager.hpp> #include <chrono> #include <vkcv/asset/asset_loader.hpp> #include <vkcv/shader/GLSLCompiler.hpp> -#include <vkcv/Logger.hpp> #include "Voxelization.hpp" -#include <glm/glm.hpp> #include "vkcv/gui/GUI.hpp" +#include "ShadowMapping.hpp" +#include <vkcv/upscaling/FSRUpscaling.hpp> +#include <vkcv/upscaling/BilinearUpscaling.hpp> +#include <vkcv/upscaling/NISUpscaling.hpp> +#include <vkcv/effects/BloomAndFlaresEffect.hpp> +#include <vkcv/algorithm/SinglePassDownsampler.hpp> int main(int argc, const char** argv) { - const char* applicationName = "Voxelization"; + const std::string applicationName = "Voxelization"; - uint32_t windowWidth = 1280; - uint32_t windowHeight = 720; + const vkcv::Multisampling msaa = vkcv::Multisampling::MSAA4X; + const bool usingMsaa = msaa != vkcv::Multisampling::None; + + vkcv::Features features; + features.requireExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); + + features.requireFeature([](vk::PhysicalDeviceFeatures& features) { + features.setGeometryShader(true); + features.setDepthClamp(true); + features.setShaderInt16(true); + }); - vkcv::Window window = vkcv::Window::create( - applicationName, - windowWidth, - windowHeight, - true + features.requireExtensionFeature<vk::PhysicalDeviceDescriptorIndexingFeatures>( + VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, + [](vk::PhysicalDeviceDescriptorIndexingFeatures& features) { + features.setDescriptorBindingPartiallyBound(true); + } ); - - vkcv::camera::CameraManager cameraManager(window); - uint32_t camIndex = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - uint32_t camIndex2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); - - cameraManager.getCamera(camIndex).setPosition(glm::vec3(0.f, 0.f, 3.f)); - cameraManager.getCamera(camIndex).setNearFar(0.1f, 30.0f); - cameraManager.getCamera(camIndex).setYaw(180.0f); - cameraManager.getCamera(camIndex2).setNearFar(0.1f, 30.0f); + features.tryExtensionFeature<vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures>( + VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME, + [](vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures& features) { + features.setShaderSubgroupExtendedTypes(true); + } + ); + + features.tryExtensionFeature<vk::PhysicalDevice16BitStorageFeatures>( + VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, + [](vk::PhysicalDevice16BitStorageFeatures& features) { + features.setStorageBuffer16BitAccess(true); + } + ); + + features.tryExtensionFeature<vk::PhysicalDeviceShaderFloat16Int8Features>( + VK_KHR_16BIT_STORAGE_EXTENSION_NAME, + [](vk::PhysicalDeviceShaderFloat16Int8Features& features) { + features.setShaderFloat16(true); + } + ); + + const uint32_t windowWidth = 1280; + const uint32_t windowHeight = 720; vkcv::Core core = vkcv::Core::create( - window, - applicationName, - VK_MAKE_VERSION(0, 0, 1), - { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, - {}, - { "VK_KHR_swapchain" } + applicationName, + VK_MAKE_VERSION(0, 0, 1), + { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }, + features ); + + vkcv::WindowHandle windowHandle = core.createWindow(applicationName, windowWidth, windowHeight, true); + vkcv::Window& window = core.getWindow(windowHandle); + + bool isFullscreen = false; + uint32_t windowedWidthBackup = window.getWidth(); + uint32_t windowedHeightBackup = window.getHeight(); + int windowedPosXBackup; + int windowedPosYBackup; + glfwGetWindowPos(window.getWindow(), &windowedPosXBackup, &windowedPosYBackup); + + window.e_key.add([&](int key, int scancode, int action, int mods) { + if (key == GLFW_KEY_F11 && action == GLFW_PRESS) { + if (isFullscreen) { + glfwSetWindowMonitor( + window.getWindow(), + nullptr, + windowedPosXBackup, + windowedPosYBackup, + windowedWidthBackup, + windowedHeightBackup, + GLFW_DONT_CARE); + } + else { + windowedWidthBackup = window.getWidth(); + windowedHeightBackup = window.getHeight(); + + glfwGetWindowPos(window.getWindow(), &windowedPosXBackup, &windowedPosYBackup); + + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* videoMode = glfwGetVideoMode(monitor); + + glfwSetWindowMonitor( + window.getWindow(), + glfwGetPrimaryMonitor(), + 0, + 0, + videoMode->width, + videoMode->height, + videoMode->refreshRate); + } + isFullscreen = !isFullscreen; + } + }); + + vkcv::camera::CameraManager cameraManager(window); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle0).setPosition(glm::vec3(0.f, 0.f, 3.f)); + cameraManager.getCamera(camHandle0).setNearFar(0.1f, 30.0f); + cameraManager.getCamera(camHandle0).setYaw(180.0f); + cameraManager.getCamera(camHandle0).setFov(glm::radians(37.8)); // fov of a 35mm lens + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0.f, 0.f, 3.f)); + cameraManager.getCamera(camHandle1).setNearFar(0.1f, 30.0f); vkcv::asset::Scene mesh; - const char* path = argc > 1 ? argv[1] : "resources/Sponza/Sponza.gltf"; + const char* path = argc > 1 ? argv[1] : "assets/Sponza/Sponza.gltf"; vkcv::asset::Scene scene; int result = vkcv::asset::loadScene(path, scene); @@ -61,25 +144,17 @@ int main(int argc, const char** argv) { std::vector<std::vector<uint8_t>> vBuffers; std::vector<std::vector<uint8_t>> iBuffers; - std::vector<vkcv::VertexBufferBinding> vBufferBindings; std::vector<std::vector<vkcv::VertexBufferBinding>> vertexBufferBindings; - std::vector<vkcv::asset::VertexAttribute> vAttributes; - - for (int i = 0; i < scene.vertexGroups.size(); i++) { + for (size_t i = 0; i < scene.vertexGroups.size(); i++) { vBuffers.push_back(scene.vertexGroups[i].vertexBuffer.data); iBuffers.push_back(scene.vertexGroups[i].indexBuffer.data); - - auto& attributes = scene.vertexGroups[i].vertexBuffer.attributes; - - std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) { - return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type); - }); } std::vector<vkcv::Buffer<uint8_t>> vertexBuffers; for (const vkcv::asset::VertexGroup& group : scene.vertexGroups) { - vertexBuffers.push_back(core.createBuffer<uint8_t>( + vertexBuffers.push_back(buffer<uint8_t>( + core, vkcv::BufferType::VERTEX, group.vertexBuffer.data.size())); vertexBuffers.back().fill(group.vertexBuffer.data); @@ -87,48 +162,52 @@ int main(int argc, const char** argv) { std::vector<vkcv::Buffer<uint8_t>> indexBuffers; for (const auto& dataBuffer : iBuffers) { - indexBuffers.push_back(core.createBuffer<uint8_t>( + indexBuffers.push_back(buffer<uint8_t>( + core, vkcv::BufferType::INDEX, dataBuffer.size())); indexBuffers.back().fill(dataBuffer); } - int vertexBufferIndex = 0; - for (const auto& vertexGroup : scene.vertexGroups) { - for (const auto& attribute : vertexGroup.vertexBuffer.attributes) { - vAttributes.push_back(attribute); - vBufferBindings.push_back(vkcv::VertexBufferBinding(attribute.offset, vertexBuffers[vertexBufferIndex].getVulkanHandle())); - } - vertexBufferBindings.push_back(vBufferBindings); - vBufferBindings.clear(); - vertexBufferIndex++; + for (size_t i = 0; i < scene.vertexGroups.size(); i++) { + vertexBufferBindings.push_back(vkcv::asset::loadVertexBufferBindings( + scene.vertexGroups[i].vertexBuffer.attributes, + vertexBuffers[i].getHandle(), + { + vkcv::asset::PrimitiveType::POSITION, + vkcv::asset::PrimitiveType::NORMAL, + vkcv::asset::PrimitiveType::TEXCOORD_0, + vkcv::asset::PrimitiveType::TANGENT + } + )); } const vk::Format colorBufferFormat = vk::Format::eB10G11R11UfloatPack32; - const vkcv::AttachmentDescription color_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - colorBufferFormat + const vkcv::AttachmentDescription color_attachment ( + colorBufferFormat, + vkcv::AttachmentOperation::CLEAR, + vkcv::AttachmentOperation::STORE ); const vk::Format depthBufferFormat = vk::Format::eD32Sfloat; - const vkcv::AttachmentDescription depth_attachment( - vkcv::AttachmentOperation::STORE, - vkcv::AttachmentOperation::CLEAR, - depthBufferFormat + const vkcv::AttachmentDescription depth_attachment ( + depthBufferFormat, + vkcv::AttachmentOperation::LOAD, + vkcv::AttachmentOperation::STORE ); - - vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }); + + // forward shading config + vkcv::PassConfig forwardPassDefinition({ color_attachment, depth_attachment }, msaa); vkcv::PassHandle forwardPass = core.createPass(forwardPassDefinition); vkcv::shader::GLSLCompiler compiler; vkcv::ShaderProgram forwardProgram; - compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("resources/shaders/shader.vert"), + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { forwardProgram.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("resources/shaders/shader.frag"), + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { forwardProgram.addShader(shaderStage, path); }); @@ -137,134 +216,250 @@ int main(int argc, const char** argv) { std::vector<vkcv::VertexBinding> vertexBindings; for (size_t i = 0; i < vertexAttachments.size(); i++) { - vertexBindings.push_back(vkcv::VertexBinding(i, { vertexAttachments[i] })); + vertexBindings.push_back(vkcv::createVertexBinding(i, { vertexAttachments[i] })); } - const vkcv::VertexLayout vertexLayout (vertexBindings); - - // shadow map - vkcv::SamplerHandle shadowSampler = core.createSampler( - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerFilterType::NEAREST, - vkcv::SamplerMipmapMode::NEAREST, - vkcv::SamplerAddressMode::CLAMP_TO_EDGE - ); - const vk::Format shadowMapFormat = vk::Format::eD16Unorm; - const uint32_t shadowMapResolution = 1024; - const vkcv::Image shadowMap = core.createImage(shadowMapFormat, shadowMapResolution, shadowMapResolution); - - // light info buffer - struct LightInfo { - glm::vec3 direction; - float padding; - glm::vec3 sunColor = glm::vec3(1.f); - float sunStrength = 8.f; - glm::mat4 lightMatrix; - }; - LightInfo lightInfo; - vkcv::Buffer lightBuffer = core.createBuffer<LightInfo>(vkcv::BufferType::UNIFORM, sizeof(glm::vec3)); + const vkcv::VertexLayout vertexLayout { vertexBindings }; - vkcv::DescriptorSetHandle forwardShadingDescriptorSet = - core.createDescriptorSet({ forwardProgram.getReflectedDescriptors()[0] }); + vkcv::DescriptorSetLayoutHandle forwardShadingDescriptorSetLayout = core.createDescriptorSetLayout(forwardProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle forwardShadingDescriptorSet = core.createDescriptorSet(forwardShadingDescriptorSetLayout); - vkcv::DescriptorWrites forwardDescriptorWrites; - forwardDescriptorWrites.uniformBufferWrites = { vkcv::UniformBufferDescriptorWrite(0, lightBuffer.getHandle()) }; - forwardDescriptorWrites.sampledImageWrites = { vkcv::SampledImageDescriptorWrite(1, shadowMap.getHandle()) }; - forwardDescriptorWrites.samplerWrites = { vkcv::SamplerDescriptorWrite(2, shadowSampler) }; - core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); + // depth prepass config + vkcv::ShaderProgram depthPrepassShader; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/depthPrepass.vert"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + depthPrepassShader.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/depthPrepass.frag"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + depthPrepassShader.addShader(shaderStage, path); + }); - vkcv::SamplerHandle colorSampler = core.createSampler( - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerFilterType::LINEAR, - vkcv::SamplerMipmapMode::LINEAR, - vkcv::SamplerAddressMode::REPEAT - ); + const std::vector<vkcv::VertexAttachment> prepassVertexAttachments = depthPrepassShader.getVertexAttachments(); + + std::vector<vkcv::VertexBinding> prepassVertexBindings; + for (size_t i = 0; i < prepassVertexAttachments.size(); i++) { + prepassVertexBindings.push_back(vkcv::createVertexBinding(i, { prepassVertexAttachments[i] })); + } + const vkcv::VertexLayout prepassVertexLayout { prepassVertexBindings }; + + vkcv::PassHandle prepassPass = vkcv::passFormat(core, depthBufferFormat, true, msaa); // create descriptor sets + vkcv::SamplerHandle colorSampler = vkcv::samplerLinear(core); + + std::vector<vkcv::DescriptorSetLayoutHandle> materialDescriptorSetLayouts; std::vector<vkcv::DescriptorSetHandle> materialDescriptorSets; std::vector<vkcv::Image> sceneImages; + + vkcv::algorithm::SinglePassDownsampler spdDownsampler (core, colorSampler); + + auto mipStream = core.createCommandStream(vkcv::QueueType::Graphics); for (const auto& material : scene.materials) { - int baseColorIndex = material.baseColor; - if (baseColorIndex < 0) { - vkcv_log(vkcv::LogLevel::WARNING, "Material lacks base color"); - baseColorIndex = 0; + int albedoIndex = material.baseColor; + int normalIndex = material.normal; + int specularIndex = material.metalRough; + + if (albedoIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks albedo"); + albedoIndex = 0; + } + if (normalIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks normal"); + normalIndex = 0; } + if (specularIndex < 0) { + vkcv_log(vkcv::LogLevel::WARNING, "Material lacks specular"); + specularIndex = 0; + } + + materialDescriptorSetLayouts.push_back(core.createDescriptorSetLayout(forwardProgram.getReflectedDescriptors().at(1))); + materialDescriptorSets.push_back(core.createDescriptorSet(materialDescriptorSetLayouts.back())); + + vkcv::asset::Texture& albedoTexture = scene.textures[albedoIndex]; + vkcv::asset::Texture& normalTexture = scene.textures[normalIndex]; + vkcv::asset::Texture& specularTexture = scene.textures[specularIndex]; - materialDescriptorSets.push_back(core.createDescriptorSet(forwardProgram.getReflectedDescriptors()[1])); + // albedo texture + sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Srgb, albedoTexture.w, albedoTexture.h, 1, true)); + sceneImages.back().fill(albedoTexture.data.data()); + sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler); + const vkcv::ImageHandle albedoHandle = sceneImages.back().getHandle(); - vkcv::asset::Texture& sceneTexture = scene.textures[baseColorIndex]; + // normal texture + sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Unorm, normalTexture.w, normalTexture.h, 1, true, true)); + sceneImages.back().fill(normalTexture.data.data()); + sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler); + const vkcv::ImageHandle normalHandle = sceneImages.back().getHandle(); - sceneImages.push_back(core.createImage(vk::Format::eR8G8B8A8Srgb, sceneTexture.w, sceneTexture.h, 1, true)); - sceneImages.back().fill(sceneTexture.data.data()); - sceneImages.back().generateMipChainImmediate(); - sceneImages.back().switchLayout(vk::ImageLayout::eShaderReadOnlyOptimal); + // specular texture + sceneImages.push_back(vkcv::image(core, vk::Format::eR8G8B8A8Unorm, specularTexture.w, specularTexture.h, 1, true, true)); + sceneImages.back().fill(specularTexture.data.data()); + sceneImages.back().recordMipChainGeneration(mipStream, spdDownsampler); + const vkcv::ImageHandle specularHandle = sceneImages.back().getHandle(); vkcv::DescriptorWrites setWrites; - setWrites.sampledImageWrites = { - vkcv::SampledImageDescriptorWrite(0, sceneImages.back().getHandle()) - }; - setWrites.samplerWrites = { - vkcv::SamplerDescriptorWrite(1, colorSampler), - }; + setWrites.writeSampledImage( + 0, albedoHandle + ).writeSampledImage( + 2, normalHandle + ).writeSampledImage( + 3, specularHandle + ); + + setWrites.writeSampler(1, colorSampler); core.writeDescriptorSet(materialDescriptorSets.back(), setWrites); } + + core.submitCommandStream(mipStream, false); + std::vector<vkcv::DescriptorSetLayoutHandle> perMeshDescriptorSetLayouts; std::vector<vkcv::DescriptorSetHandle> perMeshDescriptorSets; for (const auto& vertexGroup : scene.vertexGroups) { + perMeshDescriptorSetLayouts.push_back(materialDescriptorSetLayouts[vertexGroup.materialIndex]); perMeshDescriptorSets.push_back(materialDescriptorSets[vertexGroup.materialIndex]); } - const vkcv::PipelineConfig forwardPipelineConfig { + // prepass pipeline + vkcv::DescriptorSetLayoutHandle prepassDescriptorSetLayout = core.createDescriptorSetLayout({}); + vkcv::DescriptorSetHandle prepassDescriptorSet = core.createDescriptorSet(prepassDescriptorSetLayout); + + auto swapchainExtent = core.getSwapchainExtent(window.getSwapchain()); + + vkcv::GraphicsPipelineConfig prepassPipelineConfig ( + depthPrepassShader, + prepassPass, + vertexLayout, + { prepassDescriptorSetLayout, perMeshDescriptorSetLayouts[0] } + ); + + prepassPipelineConfig.setCulling(vkcv::CullMode::Back); + prepassPipelineConfig.setDepthTest(vkcv::DepthTest::LessEqual); + prepassPipelineConfig.setWritingAlphaToCoverage(true); + + vkcv::GraphicsPipelineHandle prepassPipeline = core.createGraphicsPipeline(prepassPipelineConfig); + + // forward pipeline + vkcv::GraphicsPipelineConfig forwardPipelineConfig ( forwardProgram, - windowWidth, - windowHeight, forwardPass, vertexLayout, - { core.getDescriptorSet(forwardShadingDescriptorSet).layout, - core.getDescriptorSet(perMeshDescriptorSets[0]).layout }, - true - }; + { forwardShadingDescriptorSetLayout, perMeshDescriptorSetLayouts[0] } + ); + + forwardPipelineConfig.setCulling(vkcv::CullMode::Back); + forwardPipelineConfig.setDepthTest(vkcv::DepthTest::Equal); + forwardPipelineConfig.setWritingDepth(false); - vkcv::PipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); + vkcv::GraphicsPipelineHandle forwardPipeline = core.createGraphicsPipeline(forwardPipelineConfig); if (!forwardPipeline) { std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl; return EXIT_FAILURE; } - vkcv::ImageHandle depthBuffer = core.createImage(depthBufferFormat, windowWidth, windowHeight).getHandle(); - vkcv::ImageHandle colorBuffer = core.createImage(colorBufferFormat, windowWidth, windowHeight, 1, false, true, true).getHandle(); - - const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); + // sky + struct SkySettings { + glm::vec3 color; + float strength; + }; + SkySettings skySettings; + skySettings.color = glm::vec3(0.15, 0.65, 1); + skySettings.strength = 5; + + vkcv::PassHandle skyPass = vkcv::passFormats( + core, + { colorBufferFormat, depthBufferFormat }, + false, + msaa + ); - vkcv::ShaderProgram shadowShader; - compiler.compile(vkcv::ShaderStage::VERTEX, "resources/shaders/shadow.vert", + vkcv::ShaderProgram skyShader; + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/sky.vert"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - shadowShader.addShader(shaderStage, path); + skyShader.addShader(shaderStage, path); }); - compiler.compile(vkcv::ShaderStage::FRAGMENT, "resources/shaders/shadow.frag", + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/sky.frag"), [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { - shadowShader.addShader(shaderStage, path); + skyShader.addShader(shaderStage, path); }); - const std::vector<vkcv::AttachmentDescription> shadowAttachments = { - vkcv::AttachmentDescription(vkcv::AttachmentOperation::STORE, vkcv::AttachmentOperation::CLEAR, shadowMapFormat) - }; - const vkcv::PassConfig shadowPassConfig(shadowAttachments); - const vkcv::PassHandle shadowPass = core.createPass(shadowPassConfig); - const vkcv::PipelineConfig shadowPipeConfig{ - shadowShader, - shadowMapResolution, - shadowMapResolution, - shadowPass, - vertexLayout, - {}, - false - }; - const vkcv::PipelineHandle shadowPipe = core.createGraphicsPipeline(shadowPipeConfig); + vkcv::GraphicsPipelineConfig skyPipeConfig ( + skyShader, + skyPass, + {}, + {} + ); + + skyPipeConfig.setWritingDepth(false); + + vkcv::GraphicsPipelineHandle skyPipe = core.createGraphicsPipeline(skyPipeConfig); + + vkcv::ImageConfig depthBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + depthBufferConfig.setMultisampling(msaa); + + // render targets + vkcv::ImageHandle depthBuffer = core.createImage( + depthBufferFormat, + depthBufferConfig + ); + + const bool colorBufferRequiresStorage = !usingMsaa; + + vkcv::ImageConfig colorBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + colorBufferConfig.setSupportingStorage(colorBufferRequiresStorage); + colorBufferConfig.setSupportingColorAttachment(true); + colorBufferConfig.setMultisampling(msaa); + + vkcv::ImageHandle colorBuffer = core.createImage( + colorBufferFormat, + colorBufferConfig + ); + + vkcv::ImageConfig resolveBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + resolveBufferConfig.setSupportingStorage(true); + resolveBufferConfig.setSupportingColorAttachment(true); + + vkcv::ImageHandle resolvedColorBuffer; + if (usingMsaa) { + resolvedColorBuffer = core.createImage( + colorBufferFormat, + resolveBufferConfig + ); + } else { + resolvedColorBuffer = colorBuffer; + } + + vkcv::ImageConfig swapBufferConfig ( + swapchainExtent.width, + swapchainExtent.height + ); + + swapBufferConfig.setSupportingStorage(true); + + vkcv::ImageHandle swapBuffer = core.createImage( + colorBufferFormat, + swapBufferConfig + ); + + vkcv::ImageHandle swapBuffer2 = core.createImage( + colorBufferFormat, + swapBufferConfig + ); - std::vector<std::array<glm::mat4, 2>> mainPassMatrices; - std::vector<glm::mat4> mvpLight; + const vkcv::ImageHandle swapchainInput = vkcv::ImageHandle::createSwapchainImageHandle(); bool renderVoxelVis = false; window.e_key.add([&renderVoxelVis](int key ,int scancode, int action, int mods) { @@ -273,15 +468,59 @@ int main(int argc, const char** argv) { } }); + bool renderUI = true; + window.e_key.add([&renderUI](int key, int scancode, int action, int mods) { + if (key == GLFW_KEY_I && action == GLFW_PRESS) { + renderUI = !renderUI; + } + }); + // tonemapping compute shader vkcv::ShaderProgram tonemappingProgram; - compiler.compile(vkcv::ShaderStage::COMPUTE, "resources/shaders/tonemapping.comp", + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/tonemapping.comp", [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { tonemappingProgram.addShader(shaderStage, path); }); - vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingProgram.getReflectedDescriptors()[0]); - vkcv::PipelineHandle tonemappingPipeline = core.createComputePipeline(tonemappingProgram, - { core.getDescriptorSet(tonemappingDescriptorSet).layout }); + + vkcv::DescriptorSetLayoutHandle tonemappingDescriptorSetLayout = core.createDescriptorSetLayout( + tonemappingProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle tonemappingDescriptorSet = core.createDescriptorSet(tonemappingDescriptorSetLayout); + vkcv::ComputePipelineHandle tonemappingPipeline = core.createComputePipeline({ + tonemappingProgram, + { tonemappingDescriptorSetLayout } + }); + + // tonemapping compute shader + vkcv::ShaderProgram postEffectsProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/postEffects.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + postEffectsProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle postEffectsDescriptorSetLayout = core.createDescriptorSetLayout( + postEffectsProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle postEffectsDescriptorSet = core.createDescriptorSet(postEffectsDescriptorSetLayout); + vkcv::ComputePipelineHandle postEffectsPipeline = core.createComputePipeline({ + postEffectsProgram, + { postEffectsDescriptorSetLayout } + }); + + // resolve compute shader + vkcv::ShaderProgram resolveProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, "assets/shaders/msaa4XResolve.comp", + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + resolveProgram.addShader(shaderStage, path); + }); + + vkcv::DescriptorSetLayoutHandle resolveDescriptorSetLayout = core.createDescriptorSetLayout( + resolveProgram.getReflectedDescriptors().at(0)); + vkcv::DescriptorSetHandle resolveDescriptorSet = core.createDescriptorSet(resolveDescriptorSetLayout); + vkcv::ComputePipelineHandle resolvePipeline = core.createComputePipeline({ + resolveProgram, + { resolveDescriptorSetLayout } + }); + + vkcv::SamplerHandle resolveSampler = vkcv::samplerNearest(core, true); // model matrices per mesh std::vector<glm::mat4> modelMatrices; @@ -293,26 +532,34 @@ int main(int argc, const char** argv) { } } - // prepare drawcalls - std::vector<vkcv::Mesh> meshes; - for (int i = 0; i < scene.vertexGroups.size(); i++) { - vkcv::Mesh mesh( - vertexBufferBindings[i], - indexBuffers[i].getVulkanHandle(), - scene.vertexGroups[i].numIndices); + // prepare meshes + std::vector<vkcv::VertexData> meshes; + for (size_t i = 0; i < scene.vertexGroups.size(); i++) { + vkcv::VertexData mesh (vertexBufferBindings[i]); + mesh.setIndexBuffer(indexBuffers[i].getHandle()); + mesh.setCount(scene.vertexGroups[i].numIndices); meshes.push_back(mesh); } - std::vector<vkcv::DrawcallInfo> drawcalls; - std::vector<vkcv::DrawcallInfo> shadowDrawcalls; - for (int i = 0; i < meshes.size(); i++) { - - drawcalls.push_back(vkcv::DrawcallInfo(meshes[i], { - vkcv::DescriptorSetUsage(0, core.getDescriptorSet(forwardShadingDescriptorSet).vulkanHandle), - vkcv::DescriptorSetUsage(1, core.getDescriptorSet(perMeshDescriptorSets[i]).vulkanHandle) })); - shadowDrawcalls.push_back(vkcv::DrawcallInfo(meshes[i], {})); + std::vector<vkcv::InstanceDrawcall> drawcalls; + std::vector<vkcv::InstanceDrawcall> prepassDrawcalls; + for (size_t i = 0; i < meshes.size(); i++) { + vkcv::InstanceDrawcall drawcall (meshes[i]); + drawcall.useDescriptorSet(0, forwardShadingDescriptorSet); + drawcall.useDescriptorSet(1, perMeshDescriptorSets[i]); + + vkcv::InstanceDrawcall prepassDrawcall (meshes[i]); + prepassDrawcall.useDescriptorSet(0, prepassDescriptorSet); + prepassDrawcall.useDescriptorSet(1, perMeshDescriptorSets[i]); + + drawcalls.push_back(drawcall); + prepassDrawcalls.push_back(prepassDrawcall); } + vkcv::SamplerHandle voxelSampler = vkcv::samplerLinear(core, true); + + ShadowMapping shadowMapping(&core, vertexLayout); + Voxelization::Dependencies voxelDependencies; voxelDependencies.colorBufferFormat = colorBufferFormat; voxelDependencies.depthBufferFormat = depthBufferFormat; @@ -320,156 +567,472 @@ int main(int argc, const char** argv) { Voxelization voxelization( &core, voxelDependencies, - lightBuffer.getHandle(), - shadowMap.getHandle(), - shadowSampler); + shadowMapping.getLightInfoBuffer(), + shadowMapping.getShadowMap(), + shadowMapping.getShadowSampler(), + voxelSampler, + msaa); + + vkcv::effects::BloomAndFlaresEffect bloomFlares (core, true); + vkcv::Buffer<glm::vec3> cameraPosBuffer = buffer<glm::vec3>(core, vkcv::BufferType::UNIFORM, 1); + + struct VolumetricSettings { + glm::vec3 scatteringCoefficient; + float ambientLight; + glm::vec3 absorptionCoefficient; + }; + vkcv::Buffer<VolumetricSettings> volumetricSettingsBuffer + = buffer<VolumetricSettings>(core, vkcv::BufferType::UNIFORM ,1); - vkcv::gui::GUI gui(core, window); + // write forward pass descriptor set + vkcv::DescriptorWrites forwardDescriptorWrites; + forwardDescriptorWrites.writeUniformBuffer( + 0, shadowMapping.getLightInfoBuffer() + ).writeUniformBuffer( + 3, cameraPosBuffer.getHandle() + ).writeUniformBuffer( + 6, voxelization.getVoxelInfoBufferHandle() + ).writeUniformBuffer( + 7, volumetricSettingsBuffer.getHandle() + ); + + forwardDescriptorWrites.writeSampledImage( + 1, shadowMapping.getShadowMap() + ).writeSampledImage( + 4, voxelization.getVoxelImageHandle() + ); + + forwardDescriptorWrites.writeSampler( + 2, shadowMapping.getShadowSampler() + ).writeSampler( + 5, voxelSampler + ); + + core.writeDescriptorSet(forwardShadingDescriptorSet, forwardDescriptorWrites); - glm::vec2 lightAngles(90.f, 0.f); - int voxelVisualisationMip = 0; - float voxelizationExtent = 20.f; + vkcv::upscaling::FSRUpscaling upscaling (core); + uint32_t fsrWidth = swapchainExtent.width, fsrHeight = swapchainExtent.height; + + vkcv::upscaling::FSRQualityMode fsrMode = vkcv::upscaling::FSRQualityMode::NONE; + int fsrModeIndex = static_cast<int>(fsrMode); + + const std::vector<const char*> fsrModeNames = { + "None", + "Ultra Quality", + "Quality", + "Balanced", + "Performance" + }; + + bool fsrMipLoadBiasFlag = true; + bool fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; + + vkcv::upscaling::BilinearUpscaling upscaling1 (core); + vkcv::upscaling::NISUpscaling upscaling2 (core); + + const std::vector<const char*> modeNames = { + "Bilinear Upscaling", + "FSR Upscaling", + "NIS Upscaling" + }; + + int upscalingMode = 0; + + vkcv::gui::GUI gui(core, windowHandle); - auto start = std::chrono::system_clock::now(); - const auto appStartTime = start; - while (window.isWindowOpen()) { - vkcv::Window::pollEvents(); + glm::vec2 lightAnglesDegree = glm::vec2(90.f, 0.f); + glm::vec3 lightColor = glm::vec3(1); + float lightStrength = 25.f; + float maxShadowDistance = 30.f; - uint32_t swapchainWidth, swapchainHeight; - if (!core.beginFrame(swapchainWidth, swapchainHeight)) { - continue; - } + int voxelVisualisationMip = 0; + float voxelizationExtent = 35.f; - if ((swapchainWidth != windowWidth) || ((swapchainHeight != windowHeight))) { - depthBuffer = core.createImage(depthBufferFormat, swapchainWidth, swapchainHeight).getHandle(); - colorBuffer = core.createImage(colorBufferFormat, swapchainWidth, swapchainHeight, 1, false, true, true).getHandle(); + bool msaaCustomResolve = true; - windowWidth = swapchainWidth; - windowHeight = swapchainHeight; + glm::vec3 scatteringColor = glm::vec3(1); + float scatteringDensity = 0.005; + glm::vec3 absorptionColor = glm::vec3(1); + float absorptionDensity = 0.005; + float volumetricAmbient = 0.2; + + core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, + uint32_t swapchainWidth, uint32_t swapchainHeight) { + uint32_t width, height; + vkcv::upscaling::getFSRResolution( + fsrMode, + swapchainWidth, swapchainHeight, + width, height + ); + + if ((width != fsrWidth) || ((height != fsrHeight)) || + (fsrMipLoadBiasFlagBackup != fsrMipLoadBiasFlag)) { + fsrWidth = width; + fsrHeight = height; + fsrMipLoadBiasFlagBackup = fsrMipLoadBiasFlag; + + colorSampler = core.createSampler( + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + vkcv::SamplerAddressMode::REPEAT, + fsrMipLoadBiasFlag? vkcv::upscaling::getFSRLodBias(fsrMode) : 0.0f + ); + + for (size_t i = 0; i < scene.materials.size(); i++) { + vkcv::DescriptorWrites setWrites; + setWrites.writeSampler(1, colorSampler); + core.writeDescriptorSet(materialDescriptorSets[i], setWrites); + } + + depthBufferConfig.setWidth(fsrWidth); + depthBufferConfig.setHeight(fsrHeight); + + depthBuffer = core.createImage( + depthBufferFormat, + depthBufferConfig + ); + + colorBufferConfig.setWidth(fsrWidth); + colorBufferConfig.setHeight(fsrHeight); + + colorBuffer = core.createImage( + colorBufferFormat, + colorBufferConfig + ); + + if (usingMsaa) { + resolveBufferConfig.setWidth(fsrWidth); + resolveBufferConfig.setHeight(fsrHeight); + + resolvedColorBuffer = core.createImage( + colorBufferFormat, + resolveBufferConfig + ); + } else { + resolvedColorBuffer = colorBuffer; + } + + swapBufferConfig.setWidth(fsrWidth); + swapBufferConfig.setHeight(fsrHeight); + + swapBuffer = core.createImage( + colorBufferFormat, + swapBufferConfig + ); + + swapBufferConfig.setWidth(swapchainWidth); + swapBufferConfig.setHeight(swapchainHeight); + + swapBuffer2 = core.createImage( + colorBufferFormat, + swapBufferConfig + ); } - auto end = std::chrono::system_clock::now(); - auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); - // update descriptor sets which use swapchain image vkcv::DescriptorWrites tonemappingDescriptorWrites; - tonemappingDescriptorWrites.storageImageWrites = { - vkcv::StorageImageDescriptorWrite(0, colorBuffer), - vkcv::StorageImageDescriptorWrite(1, swapchainInput) }; - core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites); - - start = end; - cameraManager.update(0.000001 * static_cast<double>(deltatime.count())); - - glm::vec2 lightAngleRadian = glm::radians(lightAngles); - lightInfo.direction = glm::normalize(glm::vec3( - std::cos(lightAngleRadian.x) * std::cos(lightAngleRadian.y), - std::sin(lightAngleRadian.x), - std::cos(lightAngleRadian.x) * std::sin(lightAngleRadian.y))); + tonemappingDescriptorWrites.writeSampledImage(0, resolvedColorBuffer); + tonemappingDescriptorWrites.writeSampler(1, colorSampler); + tonemappingDescriptorWrites.writeStorageImage(2, swapBuffer); - const float shadowProjectionSize = 20.f; - glm::mat4 projectionLight = glm::ortho( - -shadowProjectionSize, - shadowProjectionSize, - -shadowProjectionSize, - shadowProjectionSize, - -shadowProjectionSize, - shadowProjectionSize); + core.writeDescriptorSet(tonemappingDescriptorSet, tonemappingDescriptorWrites); + + // update descriptor sets which use swapchain image + vkcv::DescriptorWrites postEffectsDescriptorWrites; + postEffectsDescriptorWrites.writeSampledImage(0, swapBuffer2); + postEffectsDescriptorWrites.writeSampler(1, colorSampler); + postEffectsDescriptorWrites.writeStorageImage(2, swapchainInput); + + core.writeDescriptorSet(postEffectsDescriptorSet, postEffectsDescriptorWrites); + + // update resolve descriptor, color images could be changed + vkcv::DescriptorWrites resolveDescriptorWrites; + resolveDescriptorWrites.writeSampledImage(0, colorBuffer); + resolveDescriptorWrites.writeSampler(1, resolveSampler); + resolveDescriptorWrites.writeStorageImage(2, resolvedColorBuffer); + core.writeDescriptorSet(resolveDescriptorSet, resolveDescriptorWrites); + + cameraManager.update(dt); + cameraPosBuffer.fill({ cameraManager.getActiveCamera().getPosition() }); - glm::mat4 vulkanCorrectionMatrix(1.f); - vulkanCorrectionMatrix[2][2] = 0.5; - vulkanCorrectionMatrix[3][2] = 0.5; - projectionLight = vulkanCorrectionMatrix * projectionLight; + auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - const glm::mat4 viewLight = glm::lookAt(glm::vec3(0), -lightInfo.direction, glm::vec3(0, -1, 0)); + voxelization.updateVoxelOffset(cameraManager.getActiveCamera()); - lightInfo.lightMatrix = projectionLight * viewLight; - lightBuffer.fill({ lightInfo }); + // shadow map + glm::vec2 lightAngleRadian = glm::radians(lightAnglesDegree); + shadowMapping.recordShadowMapRendering( + cmdStream, + lightAngleRadian, + lightColor, + lightStrength, + maxShadowDistance, + meshes, + modelMatrices, + cameraManager.getActiveCamera(), + voxelization.getVoxelOffset(), + voxelization.getVoxelExtent(), + windowHandle, + spdDownsampler + ); + + // voxelization + voxelization.setVoxelExtent(voxelizationExtent); + voxelization.voxelizeMeshes( + cmdStream, + meshes, + modelMatrices, + perMeshDescriptorSets, + windowHandle, + spdDownsampler + ); + // depth prepass const glm::mat4 viewProjectionCamera = cameraManager.getActiveCamera().getMVP(); - - mainPassMatrices.clear(); - mvpLight.clear(); + + vkcv::PushConstants prepassPushConstants = vkcv::pushConstants<glm::mat4>(); + + std::vector<glm::mat4> prepassMatrices; for (const auto& m : modelMatrices) { - mainPassMatrices.push_back({ viewProjectionCamera * m, m }); - mvpLight.push_back(lightInfo.lightMatrix * m); + prepassPushConstants.appendDrawcall(viewProjectionCamera * m); } + + const std::vector<vkcv::ImageHandle> prepassRenderTargets = { depthBuffer }; - vkcv::PushConstantData pushConstantData((void*)mainPassMatrices.data(), 2 * sizeof(glm::mat4)); - const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer }; - - const vkcv::PushConstantData shadowPushConstantData((void*)mvpLight.data(), sizeof(glm::mat4)); - - auto cmdStream = core.createCommandStream(vkcv::QueueType::Graphics); - - // shadow map + core.recordBeginDebugLabel(cmdStream, "Depth prepass", { 1, 1, 1, 1 }); core.recordDrawcallsToCmdStream( cmdStream, - shadowPass, - shadowPipe, - shadowPushConstantData, - shadowDrawcalls, - { shadowMap.getHandle() }); - core.prepareImageForSampling(cmdStream, shadowMap.getHandle()); + prepassPipeline, + prepassPushConstants, + prepassDrawcalls, + prepassRenderTargets, + windowHandle + ); + + core.recordImageMemoryBarrier(cmdStream, depthBuffer); + core.recordEndDebugLabel(cmdStream); + + vkcv::PushConstants pushConstants (2 * sizeof(glm::mat4)); + + // main pass + for (const auto& m : modelMatrices) { + pushConstants.appendDrawcall(std::array<glm::mat4, 2>{ viewProjectionCamera * m, m }); + } - voxelization.setVoxelExtent(voxelizationExtent); - voxelization.voxelizeMeshes( - cmdStream, - cameraManager.getActiveCamera().getPosition(), - meshes, - modelMatrices, - perMeshDescriptorSets); + VolumetricSettings volumeSettings; + volumeSettings.scatteringCoefficient = scatteringColor * scatteringDensity; + volumeSettings.absorptionCoefficient = absorptionColor * absorptionDensity; + volumeSettings.ambientLight = volumetricAmbient; + volumetricSettingsBuffer.fill({ volumeSettings }); + + const std::vector<vkcv::ImageHandle> renderTargets = { colorBuffer, depthBuffer }; - // main pass + core.recordBeginDebugLabel(cmdStream, "Forward rendering", { 1, 1, 1, 1 }); core.recordDrawcallsToCmdStream( cmdStream, - forwardPass, - forwardPipeline, - pushConstantData, + forwardPipeline, + pushConstants, drawcalls, - renderTargets); + renderTargets, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); if (renderVoxelVis) { - voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip); + voxelization.renderVoxelVisualisation(cmdStream, viewProjectionCamera, renderTargets, voxelVisualisationMip, windowHandle); } - - const uint32_t tonemappingLocalGroupSize = 8; - const uint32_t tonemappingDispatchCount[3] = { - static_cast<uint32_t>(glm::ceil(windowWidth / static_cast<float>(tonemappingLocalGroupSize))), - static_cast<uint32_t>(glm::ceil(windowHeight / static_cast<float>(tonemappingLocalGroupSize))), - 1 - }; - + + vkcv::PushConstants skySettingsPushConstants = vkcv::pushConstants<SkySettings>(); + skySettingsPushConstants.appendDrawcall(skySettings); + + vkcv::VertexData skyData; + skyData.setCount(3); + + // sky + core.recordBeginDebugLabel(cmdStream, "Sky", { 1, 1, 1, 1 }); + core.recordDrawcallsToCmdStream( + cmdStream, + skyPipe, + skySettingsPushConstants, + { vkcv::InstanceDrawcall(skyData) }, + renderTargets, + windowHandle + ); + core.recordEndDebugLabel(cmdStream); + + const uint32_t fullscreenLocalGroupSize = 8; + auto fullscreenDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(fsrWidth, fsrHeight), + vkcv::DispatchSize(fullscreenLocalGroupSize, fullscreenLocalGroupSize) + ); + + if (usingMsaa) { + core.recordBeginDebugLabel(cmdStream, "MSAA resolve", { 1, 1, 1, 1 }); + if (msaaCustomResolve) { + core.prepareImageForSampling(cmdStream, colorBuffer); + core.prepareImageForStorage(cmdStream, resolvedColorBuffer); + + assert(msaa == vkcv::Multisampling::MSAA4X); // shaders is written for msaa 4x + core.recordComputeDispatchToCmdStream( + cmdStream, + resolvePipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, resolveDescriptorSet) }, + vkcv::PushConstants(0) + ); + + core.recordImageMemoryBarrier(cmdStream, resolvedColorBuffer); + } + else { + core.resolveMSAAImage(cmdStream, colorBuffer, resolvedColorBuffer); + } + core.recordEndDebugLabel(cmdStream); + } + + bloomFlares.updateCameraDirection(cameraManager.getActiveCamera()); + bloomFlares.recordEffect(cmdStream, resolvedColorBuffer, resolvedColorBuffer); + + core.prepareImageForStorage(cmdStream, swapBuffer); + core.prepareImageForSampling(cmdStream, resolvedColorBuffer); + + core.recordBeginDebugLabel(cmdStream, "Tonemapping", { 1, 1, 1, 1 }); + core.recordComputeDispatchToCmdStream( + cmdStream, + tonemappingPipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, tonemappingDescriptorSet) }, + vkcv::PushConstants(0) + ); + + core.prepareImageForStorage(cmdStream, swapBuffer2); + core.prepareImageForSampling(cmdStream, swapBuffer); + core.recordEndDebugLabel(cmdStream); + + switch (upscalingMode) { + case 0: + upscaling1.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + break; + case 1: + upscaling.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + break; + case 2: + upscaling2.recordUpscaling(cmdStream, swapBuffer, swapBuffer2); + break; + default: + break; + } + core.prepareImageForStorage(cmdStream, swapchainInput); - core.prepareImageForStorage(cmdStream, colorBuffer); - + core.prepareImageForSampling(cmdStream, swapBuffer2); + + vkcv::PushConstants timePushConstants = vkcv::pushConstants<float>(); + timePushConstants.appendDrawcall(static_cast<float>(t * 100000.0)); + + fullscreenDispatchCount = vkcv::dispatchInvocations( + vkcv::DispatchSize(swapchainWidth, swapchainHeight), + vkcv::DispatchSize(fullscreenLocalGroupSize, fullscreenLocalGroupSize) + ); + + core.recordBeginDebugLabel(cmdStream, "Post Processing", { 1, 1, 1, 1 }); core.recordComputeDispatchToCmdStream( - cmdStream, - tonemappingPipeline, - tonemappingDispatchCount, - { vkcv::DescriptorSetUsage(0, core.getDescriptorSet(tonemappingDescriptorSet).vulkanHandle) }, - vkcv::PushConstantData(nullptr, 0)); + cmdStream, + postEffectsPipeline, + fullscreenDispatchCount, + { vkcv::useDescriptorSet(0, postEffectsDescriptorSet) }, + timePushConstants + ); + core.recordEndDebugLabel(cmdStream); // present and end core.prepareSwapchainImageForPresent(cmdStream); core.submitCommandStream(cmdStream); + // draw UI gui.beginGUI(); - ImGui::Begin("Settings"); - ImGui::DragFloat2("Light angles", &lightAngles.x); - ImGui::ColorEdit3("Sun color", &lightInfo.sunColor.x); - ImGui::DragFloat("Sun strength", &lightInfo.sunStrength); - ImGui::Checkbox("Draw voxel visualisation", &renderVoxelVis); - ImGui::SliderInt("Visualisation mip", &voxelVisualisationMip, 0, 7); - ImGui::DragFloat("Voxelization extent", &voxelizationExtent, 1.f, 0.f); - voxelVisualisationMip = std::max(voxelVisualisationMip, 0); - ImGui::End(); + if (renderUI) { + ImGui::Begin("Settings"); + + ImGui::Checkbox("MSAA custom resolve", &msaaCustomResolve); + + ImGui::DragFloat2("Light angles", &lightAnglesDegree.x); + ImGui::ColorEdit3("Sun color", &lightColor.x); + ImGui::DragFloat("Sun strength", &lightStrength); + ImGui::DragFloat("Max shadow distance", &maxShadowDistance); + maxShadowDistance = std::max(maxShadowDistance, 1.f); + + ImGui::ColorEdit3("Sky color", &skySettings.color.x); + ImGui::DragFloat("Sky strength", &skySettings.strength, 0.1); + + ImGui::Checkbox("Draw voxel visualisation", &renderVoxelVis); + ImGui::SliderInt("Visualisation mip", &voxelVisualisationMip, 0, 7); + ImGui::DragFloat("Voxelization extent", &voxelizationExtent, 1.f, 0.f); + voxelizationExtent = std::max(voxelizationExtent, 1.f); + voxelVisualisationMip = std::max(voxelVisualisationMip, 0); + + ImGui::ColorEdit3("Scattering color", &scatteringColor.x); + ImGui::DragFloat("Scattering density", &scatteringDensity, 0.0001); + ImGui::ColorEdit3("Absorption color", &absorptionColor.x); + ImGui::DragFloat("Absorption density", &absorptionDensity, 0.0001); + ImGui::DragFloat("Volumetric ambient", &volumetricAmbient, 0.002); + + float sharpness = upscaling.getSharpness(); + + ImGui::Combo("FSR Quality Mode", &fsrModeIndex, fsrModeNames.data(), fsrModeNames.size()); + ImGui::DragFloat("FSR Sharpness", &sharpness, 0.001, 0.0f, 1.0f); + ImGui::Checkbox("FSR Mip Lod Bias", &fsrMipLoadBiasFlag); + ImGui::Combo("Upscaling Mode", &upscalingMode, modeNames.data(), modeNames.size()); + + if ((fsrModeIndex >= 0) && (fsrModeIndex <= 4)) { + fsrMode = static_cast<vkcv::upscaling::FSRQualityMode>(fsrModeIndex); + } + + upscaling.setSharpness(sharpness); + upscaling2.setSharpness(sharpness); + + if (ImGui::Button("Reload forward pass")) { + vkcv::ShaderProgram newForwardProgram; + + compiler.compile(vkcv::ShaderStage::VERTEX, std::filesystem::path("assets/shaders/shader.vert"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + newForwardProgram.addShader(shaderStage, path); + }); + compiler.compile(vkcv::ShaderStage::FRAGMENT, std::filesystem::path("assets/shaders/shader.frag"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + newForwardProgram.addShader(shaderStage, path); + }); + + forwardPipelineConfig.setShaderProgram(newForwardProgram); + vkcv::GraphicsPipelineHandle newPipeline = core.createGraphicsPipeline(forwardPipelineConfig); + + if (newPipeline) { + forwardPipeline = newPipeline; + } + } + + if (ImGui::Button("Reload tonemapping")) { + vkcv::ShaderProgram newProgram; + compiler.compile(vkcv::ShaderStage::COMPUTE, std::filesystem::path("assets/shaders/tonemapping.comp"), + [&](vkcv::ShaderStage shaderStage, const std::filesystem::path& path) { + newProgram.addShader(shaderStage, path); + }); + + vkcv::ComputePipelineHandle newPipeline = core.createComputePipeline({ + newProgram, + { tonemappingDescriptorSetLayout } + }); + + if (newPipeline) { + tonemappingPipeline = newPipeline; + } + } + + ImGui::End(); + } gui.endGUI(); - - core.endFrame(); - } + }); return 0; } diff --git a/resources/extensions.txt b/resources/extensions.txt new file mode 100644 index 0000000000000000000000000000000000000000..db1c0ef745c97e11b18118da1874a6854f2b09fd --- /dev/null +++ b/resources/extensions.txt @@ -0,0 +1,6 @@ +GitLab.gitlab-workflow +llvm-vs-code-extensions.vscode-clangd +ms-vscode.cmake-tools +slevesque.vscode-3dviewer +twxs.cmake +webfreak.debug diff --git a/screenshots/bindless_textures.png b/screenshots/bindless_textures.png new file mode 100644 index 0000000000000000000000000000000000000000..e25ed64cdb1386d966e4fd693efb7c8c1e47ae86 --- /dev/null +++ b/screenshots/bindless_textures.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ffc3bb769bd6ed17520ed0ee5e055246b2f8769451ef79bc9bd2d08aa064f50a +size 181594 diff --git a/screenshots/fire_works.png b/screenshots/fire_works.png new file mode 100644 index 0000000000000000000000000000000000000000..79dcf38616c0e5817c73efbd7fe931167764e546 --- /dev/null +++ b/screenshots/fire_works.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31aeb780a731731c8c680e10c8d3336921881b0ae6f3cff575c71af834b8bd5b +size 282503 diff --git a/screenshots/first_mesh.png b/screenshots/first_mesh.png new file mode 100644 index 0000000000000000000000000000000000000000..980baafbbacfa1a7d29c6eada5d4396ab0cf2425 --- /dev/null +++ b/screenshots/first_mesh.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f585b69b4f5c517d5f44c5eefa5cf2324d2cd58ee0e215135b824926377bfd61 +size 190960 diff --git a/screenshots/first_scene.png b/screenshots/first_scene.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac8147bbef9a11deb64153ebd1dd0e3503b1b9c --- /dev/null +++ b/screenshots/first_scene.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8bce66f549f154b5d5ad20d6fcb633e62d0e9a1d62889d148bf6e73d46287edc +size 1185543 diff --git a/screenshots/first_triangle.png b/screenshots/first_triangle.png new file mode 100644 index 0000000000000000000000000000000000000000..7eda745af4f4e8103910d1d618b6ed3c26cbb850 --- /dev/null +++ b/screenshots/first_triangle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:82577075fbbcc774b08cccdcace4cc1ab94e10e135f176091faadcb089047c3f +size 20840 diff --git a/screenshots/head_demo.png b/screenshots/head_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..4fb51ce5d96354f647bc69b1c7b53fd43085f07b --- /dev/null +++ b/screenshots/head_demo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd4beee1709a1f4b85537b7d951b06b1cdf27ea1c00c73cea5a8b8b261bd11b6 +size 438783 diff --git a/screenshots/indirect_dispatch.png b/screenshots/indirect_dispatch.png new file mode 100644 index 0000000000000000000000000000000000000000..196a28a8fe34350ca0bccae5af02f9ba3755598a --- /dev/null +++ b/screenshots/indirect_dispatch.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6b64f1ca2fe9ab5324be8917c1a36fdf2efb83d46a83dee5c1200ca51479096 +size 795319 diff --git a/screenshots/indirect_draw.png b/screenshots/indirect_draw.png new file mode 100644 index 0000000000000000000000000000000000000000..13f0aaf3c020ddf117b3d6c29b65c31899e0743c --- /dev/null +++ b/screenshots/indirect_draw.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f37958116905dd90f57326f78e086a4b2739424e850c49439421d0986dd4454f +size 2353039 diff --git a/screenshots/mesh_shader.png b/screenshots/mesh_shader.png new file mode 100644 index 0000000000000000000000000000000000000000..bf9fe85f01518c4b9d31d46fe6f1424e214a9ce5 --- /dev/null +++ b/screenshots/mesh_shader.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37049cc8301eea437be01d6f485553b96e8961b2bd63d1ade7d0b2be3c17d5d4 +size 39343 diff --git a/screenshots/mpm.png b/screenshots/mpm.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8e172251cc31bcad91108848f32a7bcca86e9e --- /dev/null +++ b/screenshots/mpm.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3443fe0eee59cc56f0b675704b1f4c7dc94eb1ede1ce6af7866bcd85c52b816 +size 36964 diff --git a/screenshots/particle_simulation_gravity.png b/screenshots/particle_simulation_gravity.png new file mode 100644 index 0000000000000000000000000000000000000000..88e39c1191d8d4a118d00e1912e46ee7114e6416 --- /dev/null +++ b/screenshots/particle_simulation_gravity.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47e8b6d364a696fb011a9fabc888b29031a2c54f0a0e4418b09ba6c2b40557e2 +size 721671 diff --git a/screenshots/particle_simulation_space.png b/screenshots/particle_simulation_space.png new file mode 100644 index 0000000000000000000000000000000000000000..81ba7839b99810cfdbb55c4ebfb9ac24c5d25064 --- /dev/null +++ b/screenshots/particle_simulation_space.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bd48bab2359e3f819d702012a4adde9699a3a093d96f4980edd9d7eed84379d +size 541571 diff --git a/screenshots/particle_simulation_water.png b/screenshots/particle_simulation_water.png new file mode 100644 index 0000000000000000000000000000000000000000..46bc5b6a51735f237b09ded014a271c6ad02c811 --- /dev/null +++ b/screenshots/particle_simulation_water.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c8d3262556252815f922e37eae809c727eed3870c05a2aaedf0b651dc61b7f9 +size 587136 diff --git a/screenshots/path_tracer.png b/screenshots/path_tracer.png new file mode 100644 index 0000000000000000000000000000000000000000..154e7b1a7d1c84d0f5e72187ed5f5312edd75969 --- /dev/null +++ b/screenshots/path_tracer.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e331aabbd5947b7b62d6762e3087ee2c2be18e2b53005f2eca77be9d80c183e9 +size 1547130 diff --git a/screenshots/ray_tracer.png b/screenshots/ray_tracer.png new file mode 100644 index 0000000000000000000000000000000000000000..71041d9474fba0f7534e367af679dc1e76e2ecde --- /dev/null +++ b/screenshots/ray_tracer.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c39817e4eec1dd7661d76a539670699df9caaf64c077bff6b1430b57ac99bc54 +size 62317 diff --git a/screenshots/rtx_ambient_occlusion.png b/screenshots/rtx_ambient_occlusion.png new file mode 100644 index 0000000000000000000000000000000000000000..87c2471d1a55016949184e7cbfbdcf0d0702908f --- /dev/null +++ b/screenshots/rtx_ambient_occlusion.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0df2a044b9ccc69ad221a0421ae3c79ff94b72eda7773625a26a000855592545 +size 50286 diff --git a/screenshots/sph.png b/screenshots/sph.png new file mode 100644 index 0000000000000000000000000000000000000000..3a4fd8bf426474bc9db222af44bf16e992ab23e7 --- /dev/null +++ b/screenshots/sph.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d238d48ffcfe1e28b73811d4d2bfea8ca3d2c23cf12e1b3ac9d095a8f7930384 +size 312081 diff --git a/screenshots/voxelization.png b/screenshots/voxelization.png new file mode 100644 index 0000000000000000000000000000000000000000..880601199622c1bba68fa822af16d86c1dd12074 --- /dev/null +++ b/screenshots/voxelization.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:946ae03143eaeb5d202dafbaa30e560427a3e887e8c3fddb6852e5c8c5c031ac +size 3060521 diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..7e41ca8e8ad62ce6667ab4d9c549f2ea613d28aa --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# Check if release or debug build +CMAKE_BUILD_DIR="build" +CMAKE_FLAGS="" +if [ "$1" = "--debug" ]; then + CMAKE_BUILD_DIR="cmake-build-debug" + CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=Debug" +elif [ "$1" = "--release" ]; then + CMAKE_BUILD_DIR="cmake-build-release" + CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=Release" +fi + +# Navigate to the main directory of the cloned repository +cd "$(dirname "$0")" || exit +cd .. + +# Setup git lfs and the submodules +git lfs install +git submodule init +git submodule update + +# Setup build directory +mkdir $CMAKE_BUILD_DIR +cd $CMAKE_BUILD_DIR || exit +BUILD_THREADS=$(($(nproc --all) - 1)) + +if [ $BUILD_THREADS -lt 1 ]; then + BUILD_THREADS=1 +fi + +# Build process +cmake $CMAKE_FLAGS .. +make -j $BUILD_THREADS "$@" \ No newline at end of file diff --git a/scripts/generate.sh b/scripts/generate.sh new file mode 100755 index 0000000000000000000000000000000000000000..23e5b0ee54589ebfacbb08942ec9a5018d8704fa --- /dev/null +++ b/scripts/generate.sh @@ -0,0 +1,85 @@ +#!/bin/sh +if [ $# -gt 0 ]; then + CMAKE_PROJECT_DIR="$(realpath "$1")" + mkdir -p $CMAKE_PROJECT_DIR +else + CMAKE_PROJECT_DIR="$(pwd)" +fi + +CMAKE_PROJECT_NAME="$(basename "$CMAKE_PROJECT_DIR")" + +# Navigate to the main directory of the cloned repository +cd "$(dirname "$0")" || exit +cd .. + +CMAKE_FRAMEWORK_DIR="$(realpath -s --relative-to="$CMAKE_PROJECT_DIR" "$(pwd)")" + +if [ "$CMAKE_FRAMEWORK_DIR" == "../.." ]; then + IS_INTERNAL_PROJECT=true +else + IS_INTERNAL_PROJECT=false +fi + +# Navigate back to the project directory +cd "$CMAKE_PROJECT_DIR" || exit + +test -f "CMakeLists.txt" && echo "WARNING: CMakeLists.txt exists already! Project generation stops!" && exit +test -f "src/main.cpp" && echo "WARNING: src/main.cpp exists already! Project generation stops!" && exit + +generate_cmake_lists() { + echo "cmake_minimum_required(VERSION 3.16)" + echo "project($CMAKE_PROJECT_NAME)" + echo + echo "set(CMAKE_CXX_STANDARD 20)" + echo "set(CMAKE_CXX_STANDARD_REQUIRED ON)" + echo + + if test "$IS_INTERNAL_PROJECT" = false; then + echo "set(BUILD_MODULES ON CACHE INTERNAL \"\")" + echo "set(BUILD_PROJECTS OFF CACHE INTERNAL \"\")" + echo "set(BUILD_DOXYGEN_DOCS OFF CACHE INTERNAL \"\")" + echo "set(BUILD_SHARED OFF CACHE INTERNAL \"\")" + echo "add_subdirectory($CMAKE_FRAMEWORK_DIR)" + echo + fi + + echo "add_executable($CMAKE_PROJECT_NAME src/main.cpp)" + echo + + if test "$IS_INTERNAL_PROJECT" = true; then + echo "target_include_directories($CMAKE_PROJECT_NAME SYSTEM BEFORE PRIVATE \${vkcv_include} \${vkcv_includes})" + echo "target_link_libraries($CMAKE_PROJECT_NAME vkcv \${vkcv_libraries})" + else + echo "target_include_directories($CMAKE_PROJECT_NAME SYSTEM BEFORE PRIVATE \${vkcv_includes})" + echo "target_link_libraries($CMAKE_PROJECT_NAME \${vkcv_libraries})" + fi +} + +generate_main_cpp() { + echo "#include <vkcv/Core.hpp>" + echo + echo "int main(int argc, const char** argv) {" + echo " vkcv::Core core = vkcv::Core::create(" + echo " \"$CMAKE_PROJECT_NAME\"," + echo " VK_MAKE_VERSION(0, 0, 1)," + echo " { vk::QueueFlagBits::eTransfer,vk::QueueFlagBits::eGraphics, vk::QueueFlagBits::eCompute }," + echo " { VK_KHR_SWAPCHAIN_EXTENSION_NAME }" + echo " );" + echo " " + echo " vkcv::WindowHandle windowHandle = core.createWindow(" + echo " \"$CMAKE_PROJECT_NAME\"," + echo " 800," + echo " 600," + echo " true" + echo " );" + echo " " + echo " while (vkcv::Window::hasOpenWindow()) {" + echo " vkcv::Window::pollEvents();" + echo " }" + echo "}" +} + +generate_cmake_lists > "CMakeLists.txt" + +mkdir -p "src" +generate_main_cpp > "src/main.cpp" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000000000000000000000000000000000000..15468cc23703a68e235a9b22644662d6f79b4673 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,27 @@ +#!/bin/sh +VKCV_BRANCH="$(git status | awk 'NR == 1 { print $3 }')" + +if [ "$VKCV_BRANCH" != "develop" ]; then + echo "WARNING: Please switch to origin/develop branch for preparing the next release!" + exit +fi + +VKCV_VERSION="$(grep -oEi "#define.+VKCV_FRAMEWORK_VERSION.+\(VK_MAKE_VERSION\(([0-9]+,.*[0-9]+,.*[0-9]+)\)\)" include/vkcv/Core.hpp | awk 'match($0, /.*\(([0-9]+),.*([0-9]+),.*([0-9]+)\)/, a) {print a[1]"."a[2]"."a[3]}')" + +if [ $(git tag | grep "$VKCV_VERSION" | wc -l) -gt 0 ]; then + echo "WARNING: Please adjust the version of the framework before uploading a release! (Duplicate version: $VKCV_VERSION)" + exit +fi + +vim CHANGELOG.md +git add CHANGELOG.md + +git commit -S -s -m "========== VkCV-$VKCV_VERSION ==========" +git push + +git checkout master +git merge develop + +git tag -a "$VKCV_VERSION" -m "VkCV-$VKCV_VERSION release" +git push +git push --tags diff --git a/scripts/run.sh b/scripts/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..2f5a32131003b8c684adb4a4d0d9219bdc8fc3d8 --- /dev/null +++ b/scripts/run.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Navigate to the scripts directory +cd "$(dirname "$0")" || exit + +# Check if there is a project name as argument +if [ $# -lt 1 ]; then + echo "You need to specify a project name to run!">&2 + exit +fi + +RUN_WITH_HUD="no" +BUILD_FLAGS="" +while [ $# -gt 1 ]; do + case "$1" in + "--"*) + if [ "$1" = "--hud" ]; then + RUN_WITH_HUD="yes" + else + BUILD_FLAGS="$BUILD_FLAGS$1 " + fi + shift 1;; + *) break;; + esac +done + +PROJECT="$1" +PROJECT_DIR="../projects/$PROJECT" +shift 1 + +# Check if the project name is valid +if [ ! -d "$PROJECT_DIR" ]; then + echo "There is no project with the name '$PROJECT'!">&2 + exit +fi + +./build.sh $FLAGS "$PROJECT" +cd "$PROJECT_DIR" || exit + +if [ ! -f "$PROJECT" ]; then + echo "Building the project '$PROJECT' failed!">&2 + exit +fi + +if [ "$RUN_WITH_HUD" = "yes" ]; then + MANGOHUD=1 ./"$PROJECT" "$@" +else + ./"$PROJECT" "$@" +fi \ No newline at end of file diff --git a/scripts/setup-codium.sh b/scripts/setup-codium.sh new file mode 100755 index 0000000000000000000000000000000000000000..66a217d81b545cee4b6cdb2995e54350de6cdcb3 --- /dev/null +++ b/scripts/setup-codium.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +CODIUM=codium + +if [ ! -f /usr/bin/codium ]; then + if [ -f /usr/bin/code ]; then + CODIUM=code + else + echo "Please install VSCodium on your system!" + echo "More information here: https://github.com/VSCodium/vscodium" + exit + fi +fi + +cat "resources/extensions.txt" | while read EXTENSION || [[ -n $EXTENSION ]]; +do + $CODIUM --install-extension $EXTENSION --force +done + diff --git a/scripts/upload_docs.sh b/scripts/upload_docs.sh new file mode 100755 index 0000000000000000000000000000000000000000..656354f6d4996976b632fd42afed89827f956569 --- /dev/null +++ b/scripts/upload_docs.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +VKCV_VERSION="$(grep -oEi "#define.+VKCV_FRAMEWORK_VERSION.+\(VK_MAKE_VERSION\(([0-9]+,.*[0-9]+,.*[0-9]+)\)\)" include/vkcv/Core.hpp | awk 'match($0, /.*\(([0-9]+),.*([0-9]+),.*([0-9]+)\)/, a) {print a[1]"."a[2]"."a[3]}')" + +DOXYFILE_TMP=$(mktemp) +sed -r "s/(PROJECT_NUMBER.*=.*)([0-9]+\.[0-9]+\.[0-9]+)/\1$VKCV_VERSION/" Doxyfile > $DOXYFILE_TMP +mv $DOXYFILE_TMP Doxyfile + +doxygen Doxyfile +rsync -r doc/html/ vkcv@penguin2.uni-koblenz.de:/home/vkcv/public_html/doc/ diff --git a/src/vkcv/BlitDownsampler.cpp b/src/vkcv/BlitDownsampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..956c3079e683f3326b6085ca314d5c7081a74e0d --- /dev/null +++ b/src/vkcv/BlitDownsampler.cpp @@ -0,0 +1,18 @@ + +#include "vkcv/BlitDownsampler.hpp" + +#include "ImageManager.hpp" +#include "vkcv/Core.hpp" + +namespace vkcv { + + BlitDownsampler::BlitDownsampler(Core &core, ImageManager &imageManager) : + Downsampler(core), m_imageManager(imageManager) {} + + void BlitDownsampler::recordDownsampling(const CommandStreamHandle &cmdStream, + const ImageHandle &image) { + m_imageManager.recordImageMipChainGenerationToCmdStream(cmdStream, image); + m_core.prepareImageForSampling(cmdStream, image); + } + +} // namespace vkcv diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp index aec96411c5d9e07f200b24fbdcf9fa69e2af53d5..b443f8caeb9b8ca591cba6bbc7147001aa419154 100644 --- a/src/vkcv/BufferManager.cpp +++ b/src/vkcv/BufferManager.cpp @@ -3,139 +3,234 @@ * @file vkcv/BufferManager.cpp */ -#include "vkcv/BufferManager.hpp" +#include "BufferManager.hpp" #include "vkcv/Core.hpp" #include <vkcv/Logger.hpp> +#include <limits> + namespace vkcv { - - BufferManager::BufferManager() noexcept : - m_core(nullptr), m_buffers(), m_stagingBuffer(BufferHandle()) - { - } - - void BufferManager::init() { - if (!m_core) { - return; + + bool BufferManager::init(Core &core) { + if (!HandleManager<BufferEntry, BufferHandle>::init(core)) { + return false; } - m_stagingBuffer = createBuffer(BufferType::STAGING, 1024 * 1024, BufferMemoryType::HOST_VISIBLE); - } - - BufferManager::~BufferManager() noexcept { - for (uint64_t id = 0; id < m_buffers.size(); id++) { - destroyBufferById(id); + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + const auto& memoryProperties = allocator.getMemoryProperties(); + const auto& heaps = memoryProperties->memoryHeaps; + + std::vector<vk::MemoryPropertyFlags> heapMemoryFlags; + heapMemoryFlags.resize(heaps.size()); + + for (const auto& type : memoryProperties->memoryTypes) { + if (type.heapIndex >= heaps.size()) { + continue; + } + + heapMemoryFlags[type.heapIndex] |= type.propertyFlags; } + + vk::DeviceSize maxDeviceHeapSize = 0; + uint32_t deviceHeapIndex = 0; + + for (uint32_t i = 0; i < heaps.size(); i++) { + if (!(heaps[i].flags & vk::MemoryHeapFlagBits::eDeviceLocal)) { + continue; + } + + if (!(heapMemoryFlags[i] & vk::MemoryPropertyFlagBits::eDeviceLocal)) { + continue; + } + + if (heaps[i].size < maxDeviceHeapSize) { + continue; + } + + maxDeviceHeapSize = heaps[i].size; + deviceHeapIndex = i; + } + + if (heapMemoryFlags[deviceHeapIndex] & vk::MemoryPropertyFlagBits::eHostVisible) { + m_resizableBar = true; + } else { + m_resizableBar = false; + } + + m_stagingBuffer = createBuffer( + TypeGuard(1), + BufferType::STAGING, + BufferMemoryType::HOST_VISIBLE, + 1024 * 1024, + false + ); + + return true; } - - /** - * @brief searches memory type index for buffer allocation, combines requirements of buffer and application - * @param physicalMemoryProperties Memory Properties of physical device - * @param typeBits Bit field for suitable memory types - * @param requirements Property flags that are required - * @return memory type index for Buffer - */ - uint32_t searchBufferMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { - const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount; - for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) { - const uint32_t memoryTypeBits = (1 << memoryIndex); - const bool isRequiredMemoryType = typeBits & memoryTypeBits; - - const vk::MemoryPropertyFlags properties = - physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags; - const bool hasRequiredProperties = - (properties & requirements) == requirements; - - if (isRequiredMemoryType && hasRequiredProperties) - return static_cast<int32_t>(memoryIndex); + + uint64_t BufferManager::getIdFrom(const BufferHandle &handle) const { + return handle.getId(); + } + + BufferHandle BufferManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return BufferHandle(id, destroy); + } + + void BufferManager::destroyById(uint64_t id) { + auto &buffer = getById(id); + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + if (buffer.m_handle) { + if (buffer.m_mapping) { + allocator.unmapMemory(buffer.m_allocation); + } + + allocator.destroyBuffer(buffer.m_handle, buffer.m_allocation); + + buffer.m_handle = nullptr; + buffer.m_allocation = nullptr; } + } - // failed to find memory type - return -1; + BufferManager::BufferManager() noexcept : + HandleManager<BufferEntry, BufferHandle>(), + m_resizableBar(false), + m_stagingBuffer(BufferHandle()) {} + + BufferManager::~BufferManager() noexcept { + clear(); } - - BufferHandle BufferManager::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType) { + + BufferHandle BufferManager::createBuffer(const TypeGuard &typeGuard, BufferType type, + BufferMemoryType memoryType, size_t size, + bool readable) { vk::BufferCreateFlags createFlags; vk::BufferUsageFlags usageFlags; - + switch (type) { - case BufferType::VERTEX: - usageFlags = vk::BufferUsageFlagBits::eVertexBuffer; - break; - case BufferType::UNIFORM: - usageFlags = vk::BufferUsageFlagBits::eUniformBuffer; - break; - case BufferType::STORAGE: - usageFlags = vk::BufferUsageFlagBits::eStorageBuffer; - break; - case BufferType::STAGING: - usageFlags = vk::BufferUsageFlagBits::eTransferSrc; - break; - case BufferType::INDEX: - usageFlags = vk::BufferUsageFlagBits::eIndexBuffer; - break; - default: - // TODO: maybe an issue - break; + case BufferType::VERTEX: + usageFlags = vk::BufferUsageFlagBits::eVertexBuffer; + break; + case BufferType::UNIFORM: + usageFlags = vk::BufferUsageFlagBits::eUniformBuffer; + break; + case BufferType::STORAGE: + usageFlags = vk::BufferUsageFlagBits::eStorageBuffer; + break; + case BufferType::STAGING: + usageFlags = vk::BufferUsageFlagBits::eTransferSrc + | vk::BufferUsageFlagBits::eTransferDst; + break; + case BufferType::INDEX: + usageFlags = vk::BufferUsageFlagBits::eIndexBuffer; + break; + case BufferType::INDIRECT: + usageFlags = vk::BufferUsageFlagBits::eStorageBuffer + | vk::BufferUsageFlagBits::eIndirectBuffer; + break; + default: + vkcv_log(LogLevel::WARNING, "Unknown buffer type"); + break; } - + if (memoryType == BufferMemoryType::DEVICE_LOCAL) { usageFlags |= vk::BufferUsageFlagBits::eTransferDst; } - - const vk::Device& device = m_core->getContext().getDevice(); - - vk::Buffer buffer = device.createBuffer( - vk::BufferCreateInfo(createFlags, size, usageFlags) - ); - - const vk::MemoryRequirements requirements = device.getBufferMemoryRequirements(buffer); - const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); - - vk::MemoryPropertyFlags memoryTypeFlags; + + if (readable) { + usageFlags |= vk::BufferUsageFlagBits::eTransferSrc; + } + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + vma::MemoryUsage memoryUsage; bool mappable = false; - + switch (memoryType) { - case BufferMemoryType::DEVICE_LOCAL: - memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal; - break; - case BufferMemoryType::HOST_VISIBLE: - memoryTypeFlags = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent; - mappable = true; - break; - default: - // TODO: maybe an issue - break; + case BufferMemoryType::DEVICE_LOCAL: + memoryUsage = vma::MemoryUsage::eAutoPreferDevice; + mappable = false; + break; + case BufferMemoryType::HOST_VISIBLE: + memoryUsage = vma::MemoryUsage::eAutoPreferHost; + mappable = true; + break; + default: + vkcv_log(LogLevel::WARNING, "Unknown buffer memory type"); + memoryUsage = vma::MemoryUsage::eUnknown; + mappable = false; + break; } - const uint32_t memoryTypeIndex = searchBufferMemoryType( - physicalDevice.getMemoryProperties(), - requirements.memoryTypeBits, - memoryTypeFlags + vma::AllocationCreateFlags allocationCreateFlags; + + if (mappable) { + if (type == vkcv::BufferType::STAGING) { + allocationCreateFlags = vma::AllocationCreateFlagBits::eHostAccessSequentialWrite; + } else { + allocationCreateFlags = vma::AllocationCreateFlagBits::eHostAccessRandom; + } + } else + if ((m_resizableBar) && (memoryType == BufferMemoryType::DEVICE_LOCAL)) { + allocationCreateFlags = vma::AllocationCreateFlagBits::eHostAccessAllowTransferInstead + | vma::AllocationCreateFlagBits::eHostAccessSequentialWrite; + } + + auto bufferAllocation = allocator.createBuffer( + vk::BufferCreateInfo(createFlags, size, usageFlags), + vma::AllocationCreateInfo( + allocationCreateFlags, + memoryUsage, + vk::MemoryPropertyFlags(), + vk::MemoryPropertyFlags(), + 0, + vma::Pool(), + nullptr + ) ); + + vk::Buffer buffer = bufferAllocation.first; + vma::Allocation allocation = bufferAllocation.second; - vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); - - device.bindBufferMemory(buffer, memory, 0); + const vk::MemoryPropertyFlags finalMemoryFlags = allocator.getAllocationMemoryProperties( + allocation + ); - const uint64_t id = m_buffers.size(); - m_buffers.push_back({ buffer, memory, size, nullptr, mappable }); - return BufferHandle(id, [&](uint64_t id) { destroyBufferById(id); }); + if (vk::MemoryPropertyFlagBits::eHostVisible & finalMemoryFlags) { + mappable = true; + } + + return add({ + typeGuard, + type, + memoryType, + size, + buffer, + allocation, + readable, + mappable, + nullptr, + 0 + }); } - - struct StagingStepInfo { + + /** + * @brief Structure to store details required for a write staging process. + */ + struct StagingWriteInfo { const void* data; size_t size; size_t offset; - + vk::Buffer buffer; vk::Buffer stagingBuffer; - vk::DeviceMemory stagingMemory; - + vma::Allocation stagingAllocation; + size_t stagingLimit; size_t stagingPosition; }; - + /** * Copies data from CPU to a staging buffer and submits the commands to copy * each part one after another into the actual target buffer. @@ -146,223 +241,291 @@ namespace vkcv { * @param core Core instance * @param info Staging-info structure */ - void copyFromStagingBuffer(Core* core, StagingStepInfo& info) { + static void fillFromStagingBuffer(Core &core, StagingWriteInfo &info) { const size_t remaining = info.size - info.stagingPosition; const size_t mapped_size = std::min(remaining, info.stagingLimit); - - const vk::Device& device = core->getContext().getDevice(); - - void* mapped = device.mapMemory(info.stagingMemory, 0, mapped_size); - memcpy(mapped, reinterpret_cast<const char*>(info.data) + info.stagingPosition, mapped_size); - device.unmapMemory(info.stagingMemory); - - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Transfer; - - core->recordAndSubmitCommandsImmediate( - submitInfo, - [&info, &mapped_size](const vk::CommandBuffer& commandBuffer) { - const vk::BufferCopy region ( - 0, - info.offset + info.stagingPosition, - mapped_size - ); - - commandBuffer.copyBuffer(info.stagingBuffer, info.buffer, 1, ®ion); - }, - [&core, &info, &mapped_size, &remaining]() { - if (mapped_size < remaining) { - info.stagingPosition += mapped_size; - - copyFromStagingBuffer( - core, - info - ); - } + + const vma::Allocator &allocator = core.getContext().getAllocator(); + + void* mapped = allocator.mapMemory(info.stagingAllocation); + memcpy(mapped, reinterpret_cast<const char*>(info.data) + info.stagingPosition, + mapped_size); + allocator.unmapMemory(info.stagingAllocation); + + auto stream = core.createCommandStream(QueueType::Transfer); + + core.recordCommandsToStream( + stream, + [&info, &mapped_size](const vk::CommandBuffer &commandBuffer) { + const vk::BufferCopy region(0, info.offset + info.stagingPosition, mapped_size); + + commandBuffer.copyBuffer(info.stagingBuffer, info.buffer, 1, ®ion); + }, + [&core, &info, &mapped_size, &remaining]() { + if (mapped_size < remaining) { + info.stagingPosition += mapped_size; + + fillFromStagingBuffer(core, info); } - ); + }); + + core.submitCommandStream(stream, false); } - - vk::Buffer BufferManager::getBuffer(const BufferHandle& handle) const { - const uint64_t id = handle.getId(); - - if (id >= m_buffers.size()) { - return nullptr; - } - - auto& buffer = m_buffers[id]; - + + /** + * @brief Structure to store details required for a read staging process. + */ + struct StagingReadInfo { + void* data; + size_t size; + size_t offset; + + vk::Buffer buffer; + vk::Buffer stagingBuffer; + vma::Allocation stagingAllocation; + + size_t stagingLimit; + size_t stagingPosition; + }; + + /** + * Copies data from a staging buffer to CPU and submits the commands to copy + * each part one after another into the actual target data pointer. + * + * The function can be used fully asynchronously! + * Just be careful to not use the staging buffer in parallel! + * + * @param core Core instance + * @param info Staging-info structure + */ + static void readToStagingBuffer(Core &core, StagingReadInfo &info) { + const size_t remaining = info.size - info.stagingPosition; + const size_t mapped_size = std::min(remaining, info.stagingLimit); + + auto stream = core.createCommandStream(QueueType::Transfer); + + core.recordCommandsToStream( + stream, + [&info, &mapped_size](const vk::CommandBuffer &commandBuffer) { + const vk::BufferCopy region(info.offset + info.stagingPosition, 0, mapped_size); + + commandBuffer.copyBuffer(info.buffer, info.stagingBuffer, 1, ®ion); + }, + [&core, &info, &mapped_size, &remaining]() { + const vma::Allocator &allocator = core.getContext().getAllocator(); + + const void* mapped = allocator.mapMemory(info.stagingAllocation); + memcpy(reinterpret_cast<char*>(info.data) + info.stagingPosition, mapped, + mapped_size); + allocator.unmapMemory(info.stagingAllocation); + + if (mapped_size < remaining) { + info.stagingPosition += mapped_size; + + readToStagingBuffer(core, info); + } + }); + + core.submitCommandStream(stream, false); + } + + vk::Buffer BufferManager::getBuffer(const BufferHandle &handle) const { + auto &buffer = (*this) [handle]; + return buffer.m_handle; } - + + TypeGuard BufferManager::getTypeGuard(const BufferHandle &handle) const { + auto &buffer = (*this) [handle]; + + return buffer.m_typeGuard; + } + + BufferType BufferManager::getBufferType(const BufferHandle &handle) const { + auto &buffer = (*this) [handle]; + + return buffer.m_type; + } + + BufferMemoryType BufferManager::getBufferMemoryType(const BufferHandle &handle) const { + auto &buffer = (*this) [handle]; + + return buffer.m_memoryType; + } + size_t BufferManager::getBufferSize(const BufferHandle &handle) const { - const uint64_t id = handle.getId(); - - if (id >= m_buffers.size()) { - return 0; - } - - auto& buffer = m_buffers[id]; - + auto &buffer = (*this) [handle]; + return buffer.m_size; } - - vk::DeviceMemory BufferManager::getDeviceMemory(const BufferHandle& handle) const { - const uint64_t id = handle.getId(); - - if (id >= m_buffers.size()) { - return nullptr; - } - - auto& buffer = m_buffers[id]; - - return buffer.m_memory; + + vk::DeviceMemory BufferManager::getDeviceMemory(const BufferHandle &handle) const { + auto &buffer = (*this) [handle]; + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + auto info = allocator.getAllocationInfo(buffer.m_allocation); + + return info.deviceMemory; } - - void BufferManager::fillBuffer(const BufferHandle& handle, const void *data, size_t size, size_t offset) { - const uint64_t id = handle.getId(); - + + void BufferManager::fillBuffer(const BufferHandle &handle, const void* data, size_t size, + size_t offset) { + auto &buffer = (*this) [handle]; + if (size == 0) { - size = SIZE_MAX; + size = std::numeric_limits<size_t>::max(); } - - if (id >= m_buffers.size()) { + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + if (offset > buffer.m_size) { return; } - - auto& buffer = m_buffers[id]; - - if (buffer.m_mapped) { - return; + + const size_t max_size = std::min(size, buffer.m_size - offset); + + if (buffer.m_mappable) { + void* mapped = allocator.mapMemory(buffer.m_allocation); + memcpy(reinterpret_cast<char*>(mapped) + offset, data, max_size); + allocator.unmapMemory(buffer.m_allocation); + } else { + auto &stagingBuffer = (*this) [m_stagingBuffer]; + + StagingWriteInfo info; + info.data = data; + info.size = max_size; + info.offset = offset; + + info.buffer = buffer.m_handle; + info.stagingBuffer = stagingBuffer.m_handle; + info.stagingAllocation = stagingBuffer.m_allocation; + + info.stagingLimit = stagingBuffer.m_size; + info.stagingPosition = 0; + + fillFromStagingBuffer(getCore(), info); } - - const vk::Device& device = m_core->getContext().getDevice(); - + } + + void BufferManager::readBuffer(const BufferHandle &handle, void* data, size_t size, + size_t offset) { + auto &buffer = (*this) [handle]; + + if (size == 0) { + size = std::numeric_limits<size_t>::max(); + } + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + if (offset > buffer.m_size) { return; } - + const size_t max_size = std::min(size, buffer.m_size - offset); - + if (buffer.m_mappable) { - void* mapped = device.mapMemory(buffer.m_memory, offset, max_size); - memcpy(mapped, data, max_size); - device.unmapMemory(buffer.m_memory); + const void* mapped = allocator.mapMemory(buffer.m_allocation); + memcpy(data, reinterpret_cast<const char*>(mapped) + offset, max_size); + allocator.unmapMemory(buffer.m_allocation); } else { - auto& stagingBuffer = m_buffers[ m_stagingBuffer.getId() ]; - - StagingStepInfo info; + auto &stagingBuffer = (*this) [m_stagingBuffer]; + + StagingReadInfo info; info.data = data; - info.size = std::min(size, max_size - offset); + info.size = max_size; info.offset = offset; - + info.buffer = buffer.m_handle; info.stagingBuffer = stagingBuffer.m_handle; - info.stagingMemory = stagingBuffer.m_memory; - - const vk::MemoryRequirements stagingRequirements = device.getBufferMemoryRequirements(stagingBuffer.m_handle); - - info.stagingLimit = stagingRequirements.size; + info.stagingAllocation = stagingBuffer.m_allocation; + + info.stagingLimit = stagingBuffer.m_size; info.stagingPosition = 0; - - copyFromStagingBuffer(m_core, info); + + readToStagingBuffer(getCore(), info); } } - - void* BufferManager::mapBuffer(const BufferHandle& handle, size_t offset, size_t size) { - const uint64_t id = handle.getId(); - + + void* BufferManager::mapBuffer(const BufferHandle &handle, size_t offset, size_t size) { + auto &buffer = (*this) [handle]; + if (size == 0) { - size = SIZE_MAX; + size = std::numeric_limits<size_t>::max(); } - - if (id >= m_buffers.size()) { + + if (offset > buffer.m_size) { return nullptr; } - auto& buffer = m_buffers[id]; - - if (buffer.m_mapped) { - return nullptr; + if (buffer.m_mapping) { + ++buffer.m_mapCounter; + + vkcv_log(LogLevel::WARNING, + "Mapping a buffer multiple times (%lu) is not recommended", + buffer.m_mapCounter); + + return buffer.m_mapping + offset; } - const vk::Device& device = m_core->getContext().getDevice(); - - if (offset > buffer.m_size) { - return nullptr; + if (buffer.m_mappable) { + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + buffer.m_mapping = reinterpret_cast<char*>(allocator.mapMemory(buffer.m_allocation)); + } else { + buffer.m_mapping = m_allocator.allocate(buffer.m_size); + + if (buffer.m_readable) { + readBuffer(handle, buffer.m_mapping, buffer.m_size, 0); + } } - const size_t max_size = std::min(size, buffer.m_size - offset); - buffer.m_mapped = device.mapMemory(buffer.m_memory, offset, max_size); - return buffer.m_mapped; + buffer.m_mapCounter = 1; + return buffer.m_mapping + offset; } - - void BufferManager::unmapBuffer(const BufferHandle& handle) { - const uint64_t id = handle.getId(); + + void BufferManager::unmapBuffer(const BufferHandle &handle) { + auto &buffer = (*this) [handle]; - if (id >= m_buffers.size()) { + if (buffer.m_mapCounter > 1) { + --buffer.m_mapCounter; return; } - auto& buffer = m_buffers[id]; - - if (buffer.m_mapped == nullptr) { - return; + if (buffer.m_mapCounter == 0) { + vkcv_log(LogLevel::WARNING, + "It seems like the buffer is not mapped to memory"); } - const vk::Device& device = m_core->getContext().getDevice(); - - device.unmapMemory(buffer.m_memory); - buffer.m_mapped = nullptr; - } - - void BufferManager::destroyBufferById(uint64_t id) { - if (id >= m_buffers.size()) { - return; + if (!buffer.m_mapping) { + vkcv_log(LogLevel::ERROR, + "Buffer is not mapped to memory"); } - auto& buffer = m_buffers[id]; - - const vk::Device& device = m_core->getContext().getDevice(); - - if (buffer.m_memory) { - device.freeMemory(buffer.m_memory); - buffer.m_memory = nullptr; + if (buffer.m_mappable) { + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + allocator.unmapMemory(buffer.m_allocation); + } else { + fillBuffer(handle, buffer.m_mapping, buffer.m_size, 0); + m_allocator.deallocate(buffer.m_mapping, buffer.m_size); } - if (buffer.m_handle) { - device.destroyBuffer(buffer.m_handle); - buffer.m_handle = nullptr; - } + buffer.m_mapping = nullptr; + buffer.m_mapCounter = 0; } - void BufferManager ::recordBufferMemoryBarrier(const BufferHandle& handle, vk::CommandBuffer cmdBuffer) { + void BufferManager ::recordBufferMemoryBarrier(const BufferHandle &handle, + vk::CommandBuffer cmdBuffer) { + auto &buffer = (*this) [handle]; - const uint64_t id = handle.getId(); + vk::BufferMemoryBarrier memoryBarrier(vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, 0, 0, + buffer.m_handle, 0, buffer.m_size); - if (id >= m_buffers.size()) { - vkcv_log(vkcv::LogLevel::ERROR, "Invalid buffer handle"); - return; - } - - auto& buffer = m_buffers[id]; - - vk::BufferMemoryBarrier memoryBarrier( - vk::AccessFlagBits::eMemoryWrite, - vk::AccessFlagBits::eMemoryRead, - 0, - 0, - buffer.m_handle, - 0, - buffer.m_size); - - cmdBuffer.pipelineBarrier( - vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eBottomOfPipe, - {}, - nullptr, - memoryBarrier, - nullptr); + cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, {}, nullptr, + memoryBarrier, nullptr); } -} +} // namespace vkcv diff --git a/src/vkcv/BufferManager.hpp b/src/vkcv/BufferManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f28269725bd2025fe8d5fced57c47a87358bd45b --- /dev/null +++ b/src/vkcv/BufferManager.hpp @@ -0,0 +1,191 @@ +#pragma once +/** + * @authors Tobias Frisch, Alexander Gauggel, Artur Wasmut, Lars Hoerttrich, Sebastian Gaida + * @file vkcv/BufferManager.hpp + * @brief Manager to handle buffer operations. + */ + +#include <memory> +#include <vector> + +#include <vk_mem_alloc.hpp> +#include <vulkan/vulkan.hpp> + +#include "vkcv/BufferTypes.hpp" +#include "vkcv/TypeGuard.hpp" + +#include "HandleManager.hpp" + +namespace vkcv { + + struct BufferEntry { + TypeGuard m_typeGuard; + + BufferType m_type; + BufferMemoryType m_memoryType; + size_t m_size; + + vk::Buffer m_handle; + vma::Allocation m_allocation; + + bool m_readable; + bool m_mappable; + char *m_mapping; + size_t m_mapCounter; + }; + + /** + * @brief Class to manage the creation, destruction, allocation + * and filling of buffers. + */ + class BufferManager : public HandleManager<BufferEntry, BufferHandle> { + friend class Core; + + private: + std::allocator<char> m_allocator; + + bool m_resizableBar; + BufferHandle m_stagingBuffer; + + bool init(Core &core) override; + + [[nodiscard]] uint64_t getIdFrom(const BufferHandle &handle) const override; + + [[nodiscard]] BufferHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + /** + * Destroys and deallocates buffer represented by a given + * buffer handle id. + * + * @param id Buffer handle id + */ + void destroyById(uint64_t id) override; + + public: + BufferManager() noexcept; + + ~BufferManager() noexcept override; + + /** + * @brief Creates and allocates a new buffer and returns its + * unique buffer handle. + * + * @param[in] typeGuard Type guard + * @param[in] type Type of buffer + * @param[in] memoryType Type of buffers memory + * @param[in] size Size of buffer in bytes + * @param[in] supportIndirect Support of indirect usage + * @param[in] readable Support read functionality + * @return New buffer handle + */ + [[nodiscard]] BufferHandle createBuffer(const TypeGuard &typeGuard, BufferType type, + BufferMemoryType memoryType, size_t size, + bool readable); + + /** + * @brief Returns the Vulkan buffer handle of a buffer + * represented by a given buffer handle. + * + * @param[in] handle Buffer handle + * @return Vulkan buffer handle + */ + [[nodiscard]] vk::Buffer getBuffer(const BufferHandle &handle) const; + + /** + * @brief Returns the type guard of a buffer represented + * by a given buffer handle. + * + * @param[in] handle Buffer handle + * @return Type guard + */ + [[nodiscard]] TypeGuard getTypeGuard(const BufferHandle &handle) const; + + /** + * @brief Returns the buffer type of a buffer represented + * by a given buffer handle. + * + * @param[in] handle Buffer handle + * @return Buffer type + */ + [[nodiscard]] BufferType getBufferType(const BufferHandle &handle) const; + + /** + * @brief Returns the buffer memory type of a buffer + * represented by a given buffer handle. + * + * @param[in] handle Buffer handle + * @return Buffer memory type + */ + [[nodiscard]] BufferMemoryType getBufferMemoryType(const BufferHandle &handle) const; + + /** + * @brief Returns the size of a buffer represented + * by a given buffer handle. + * + * @param[in] handle Buffer handle + * @return Size of the buffer + */ + [[nodiscard]] size_t getBufferSize(const BufferHandle &handle) const; + + /** + * @brief Returns the Vulkan device memory handle of a buffer + * represented by a given buffer handle id. + * + * @param[in] handle Buffer handle + * @return Vulkan device memory handle + */ + [[nodiscard]] vk::DeviceMemory getDeviceMemory(const BufferHandle &handle) const; + + /** + * @brief Fills a buffer represented by a given buffer + * handle with custom data. + * + * @param[in] handle Buffer handle + * @param[in] data Pointer to data + * @param[in] size Size of data in bytes + * @param[in] offset Offset to fill in data in bytes + */ + void fillBuffer(const BufferHandle &handle, const void* data, size_t size, size_t offset); + + /** + * @brief Reads from a buffer represented by a given + * buffer handle to some data pointer. + * + * @param[in] handle Buffer handle + * @param[in] data Pointer to data + * @param[in] size Size of data to read in bytes + * @param[in] offset Offset to read from buffer in bytes + */ + void readBuffer(const BufferHandle &handle, void* data, size_t size, size_t offset); + + /** + * @brief Maps memory to a buffer represented by a given + * buffer handle and returns it. + * + * @param[in] handle Buffer handle + * @param[in] offset Offset of mapping in bytes + * @param[in] size Size of mapping in bytes + * @return Pointer to mapped memory + */ + void* mapBuffer(const BufferHandle &handle, size_t offset, size_t size); + + /** + * @brief Unmaps memory from a buffer represented by a given + * buffer handle. + * + * @param[in] handle Buffer handle + */ + void unmapBuffer(const BufferHandle &handle); + + /** + * @brief Records a memory barrier for a buffer, + * synchronizing subsequent accesses to buffer data + * + * @param[in] handle BufferHandle of the buffer + * @param[in] cmdBuffer Vulkan command buffer to record the barrier into + */ + void recordBufferMemoryBarrier(const BufferHandle &handle, vk::CommandBuffer cmdBuffer); + }; + +} // namespace vkcv diff --git a/src/vkcv/CommandResources.cpp b/src/vkcv/CommandResources.cpp deleted file mode 100644 index a31e6967d85bd099fe5cbbc865b0e062212ca16e..0000000000000000000000000000000000000000 --- a/src/vkcv/CommandResources.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "vkcv/CommandResources.hpp" -#include <iostream> - -#include "vkcv/Logger.hpp" - -namespace vkcv { - - std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager) { - std::unordered_set<int> indexSet; - for (const auto& queue : queueManager.getGraphicsQueues()) { - indexSet.insert(queue.familyIndex); - } - for (const auto& queue : queueManager.getComputeQueues()) { - indexSet.insert(queue.familyIndex); - } - for (const auto& queue : queueManager.getTransferQueues()) { - indexSet.insert(queue.familyIndex); - } - indexSet.insert(queueManager.getPresentQueue().familyIndex); - return indexSet; - } - - CommandResources createCommandResources(const vk::Device& device, const std::unordered_set<int>& familyIndexSet) { - CommandResources resources; - const size_t queueFamiliesCount = familyIndexSet.size(); - resources.cmdPoolPerQueueFamily.resize(queueFamiliesCount); - - const vk::CommandPoolCreateFlags poolFlags = vk::CommandPoolCreateFlagBits::eTransient; - for (const int familyIndex : familyIndexSet) { - const vk::CommandPoolCreateInfo poolCreateInfo(poolFlags, familyIndex); - resources.cmdPoolPerQueueFamily[familyIndex] = device.createCommandPool(poolCreateInfo, nullptr, {}); - } - - return resources; - } - - void destroyCommandResources(const vk::Device& device, const CommandResources& resources) { - for (const vk::CommandPool &pool : resources.cmdPoolPerQueueFamily) { - device.destroyCommandPool(pool); - } - } - - vk::CommandBuffer allocateCommandBuffer(const vk::Device& device, const vk::CommandPool cmdPool) { - const vk::CommandBufferAllocateInfo info(cmdPool, vk::CommandBufferLevel::ePrimary, 1); - return device.allocateCommandBuffers(info).front(); - } - - vk::CommandPool chooseCmdPool(const Queue& queue, const CommandResources& cmdResources) { - return cmdResources.cmdPoolPerQueueFamily[queue.familyIndex]; - } - - Queue getQueueForSubmit(const QueueType type, const QueueManager& queueManager) { - if (type == QueueType::Graphics) { - return queueManager.getGraphicsQueues().front(); - } - else if (type == QueueType::Compute) { - return queueManager.getComputeQueues().front(); - } - else if (type == QueueType::Transfer) { - return queueManager.getTransferQueues().front(); - } - else if (type == QueueType::Present) { - return queueManager.getPresentQueue(); - } - else { - vkcv_log(LogLevel::ERROR, "Unknown queue type"); - return queueManager.getGraphicsQueues().front(); // graphics is the most general queue - } - } - - void beginCommandBuffer(const vk::CommandBuffer cmdBuffer, const vk::CommandBufferUsageFlags flags) { - const vk::CommandBufferBeginInfo beginInfo(flags); - cmdBuffer.begin(beginInfo); - } - - void submitCommandBufferToQueue( - const vk::Queue queue, - const vk::CommandBuffer cmdBuffer, - const vk::Fence fence, - const std::vector<vk::Semaphore>& waitSemaphores, - const std::vector<vk::Semaphore>& signalSemaphores) { - - const std::vector<vk::PipelineStageFlags> waitDstStageMasks(waitSemaphores.size(), vk::PipelineStageFlagBits::eAllCommands); - const vk::SubmitInfo queueSubmitInfo(waitSemaphores, waitDstStageMasks, cmdBuffer, signalSemaphores); - queue.submit(queueSubmitInfo, fence); - } -} \ No newline at end of file diff --git a/src/vkcv/CommandStreamManager.cpp b/src/vkcv/CommandStreamManager.cpp index 5a5b359b912d9cef36e0b03379d7f0f6f0951381..b93cf0a812933ef3f1ea3f62689a5c03062e6e80 100644 --- a/src/vkcv/CommandStreamManager.cpp +++ b/src/vkcv/CommandStreamManager.cpp @@ -1,121 +1,112 @@ -#include "vkcv/CommandStreamManager.hpp" +#include "CommandStreamManager.hpp" #include "vkcv/Core.hpp" #include "vkcv/Logger.hpp" +#include <limits> + namespace vkcv { - CommandStreamManager::CommandStreamManager() noexcept : m_core(nullptr){} - CommandStreamManager::~CommandStreamManager() noexcept { - for (const auto& stream : m_commandStreams) { - if (stream.cmdBuffer && stream.cmdBuffer) { - m_core->getContext().getDevice().freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); - } - } + uint64_t CommandStreamManager::getIdFrom(const CommandStreamHandle &handle) const { + return handle.getId(); } - void CommandStreamManager::init(Core* core) { - if (!core) { - vkcv_log(LogLevel::ERROR, "Requires valid core pointer"); + CommandStreamHandle CommandStreamManager::createById(uint64_t id, + const HandleDestroyFunction &destroy) { + return CommandStreamHandle(id, destroy); + } + + void CommandStreamManager::destroyById(uint64_t id) { + auto &stream = getById(id); + + if (stream.cmdBuffer) { + getCore().getContext().getDevice().freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); + stream.cmdBuffer = nullptr; + stream.callbacks.clear(); } - m_core = core; } - CommandStreamHandle CommandStreamManager::createCommandStream( - const vk::Queue queue, - vk::CommandPool cmdPool) { + CommandStreamManager::CommandStreamManager() noexcept : + HandleManager<CommandStreamEntry, CommandStreamHandle>() {} - const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(m_core->getContext().getDevice(), cmdPool); + CommandStreamManager::~CommandStreamManager() noexcept { + clear(); + } - CommandStream stream(cmdBuffer, queue, cmdPool); - beginCommandBuffer(stream.cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + CommandStreamHandle CommandStreamManager::createCommandStream(const vk::Queue &queue, + vk::CommandPool cmdPool) { + const vk::CommandBufferAllocateInfo info(cmdPool, vk::CommandBufferLevel::ePrimary, 1); + auto &device = getCore().getContext().getDevice(); - // find unused stream - int unusedStreamIndex = -1; - for (int i = 0; i < m_commandStreams.size(); i++) { - if (m_commandStreams[i].cmdBuffer) { - // still in use - } - else { - unusedStreamIndex = i; - break; + const vk::CommandBuffer cmdBuffer = device.allocateCommandBuffers(info).front(); + + const vk::CommandBufferBeginInfo beginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + cmdBuffer.begin(beginInfo); + + for (uint64_t id = 0; id < getCount(); id++) { + auto &stream = getById(id); + + if (!(stream.cmdBuffer)) { + stream.cmdBuffer = cmdBuffer; + stream.cmdPool = cmdPool; + stream.queue = queue; + + return createById(id, [&](uint64_t id) { + destroyById(id); + }); } } - const bool foundUnusedStream = unusedStreamIndex >= 0; - if (foundUnusedStream) { - m_commandStreams[unusedStreamIndex] = stream; - return CommandStreamHandle(unusedStreamIndex); - } - - CommandStreamHandle handle(m_commandStreams.size()); - m_commandStreams.push_back(stream); - return handle; - } - - void CommandStreamManager::recordCommandsToStream( - const CommandStreamHandle handle, - const RecordCommandFunction record) { - - const size_t id = handle.getId(); - if (id >= m_commandStreams.size()) { - vkcv_log(LogLevel::ERROR, "Requires valid handle"); - return; - } + return add({ cmdBuffer, cmdPool, queue, {} }); + } - CommandStream& stream = m_commandStreams[id]; + void CommandStreamManager::recordCommandsToStream(const CommandStreamHandle &handle, + const RecordCommandFunction &record) { + auto &stream = (*this) [handle]; record(stream.cmdBuffer); } - void CommandStreamManager::addFinishCallbackToStream( - const CommandStreamHandle handle, - const FinishCommandFunction finish) { - - const size_t id = handle.getId(); - if (id >= m_commandStreams.size()) { - vkcv_log(LogLevel::ERROR, "Requires valid handle"); - return; - } - - CommandStream& stream = m_commandStreams[id]; + void CommandStreamManager::addFinishCallbackToStream(const CommandStreamHandle &handle, + const FinishCommandFunction &finish) { + auto &stream = (*this) [handle]; stream.callbacks.push_back(finish); } void CommandStreamManager::submitCommandStreamSynchronous( - const CommandStreamHandle handle, - std::vector<vk::Semaphore> &waitSemaphores, - std::vector<vk::Semaphore> &signalSemaphores) { - - const size_t id = handle.getId(); - if (id >= m_commandStreams.size()) { - vkcv_log(LogLevel::ERROR, "Requires valid handle"); - return; - } - CommandStream& stream = m_commandStreams[id]; + const CommandStreamHandle &handle, std::vector<vk::Semaphore> &waitSemaphores, + std::vector<vk::Semaphore> &signalSemaphores) { + auto &stream = (*this) [handle]; stream.cmdBuffer.end(); - const auto device = m_core->getContext().getDevice(); - const vk::Fence waitFence = createFence(device); - submitCommandBufferToQueue(stream.queue, stream.cmdBuffer, waitFence, waitSemaphores, signalSemaphores); - waitForFence(device, waitFence); - device.destroyFence(waitFence); + const auto device = getCore().getContext().getDevice(); + const vk::Fence waitFence = device.createFence({}); - device.freeCommandBuffers(stream.cmdPool, stream.cmdBuffer); - stream.cmdBuffer = nullptr; - stream.cmdPool = nullptr; - stream.queue = nullptr; + const std::vector<vk::PipelineStageFlags> waitDstStageMasks( + waitSemaphores.size(), vk::PipelineStageFlagBits::eAllCommands); - for (const auto& finishCallback : stream.callbacks) { + const vk::SubmitInfo queueSubmitInfo(waitSemaphores, waitDstStageMasks, stream.cmdBuffer, + signalSemaphores); + + stream.queue.submit(queueSubmitInfo, waitFence); + + const auto result = device.waitForFences(waitFence, true, std::numeric_limits<uint64_t>::max()); + + if (result == vk::Result::eTimeout) { + device.waitIdle(); + } + + device.destroyFence(waitFence); + stream.queue = nullptr; + + for (const auto &finishCallback : stream.callbacks) { finishCallback(); } } - vk::CommandBuffer CommandStreamManager::getStreamCommandBuffer(const CommandStreamHandle handle) { - const size_t id = handle.getId(); - if (id >= m_commandStreams.size()) { - vkcv_log(LogLevel::ERROR, "Requires valid handle"); - return nullptr; - } - return m_commandStreams[id].cmdBuffer; + vk::CommandBuffer + CommandStreamManager::getStreamCommandBuffer(const CommandStreamHandle &handle) { + auto &stream = (*this) [handle]; + return stream.cmdBuffer; } -} \ No newline at end of file + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/CommandStreamManager.hpp b/src/vkcv/CommandStreamManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..898f170c08494b941f58632443261afee6134f75 --- /dev/null +++ b/src/vkcv/CommandStreamManager.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "vkcv/Event.hpp" +#include "vkcv/EventFunctionTypes.hpp" + +#include "HandleManager.hpp" + +namespace vkcv { + + /** + * @brief Represents one command stream, into which commands can be recorded into. + * Consists of a command buffer, the command buffer's command pool and a queue, as well as some + * callbacks. + */ + struct CommandStreamEntry { + vk::CommandBuffer cmdBuffer; + vk::CommandPool cmdPool; + vk::Queue queue; + std::vector<FinishCommandFunction> callbacks; + }; + + /** + * @brief Responsible for creation, deletion, callbacks and recording of command streams + */ + class CommandStreamManager : public HandleManager<CommandStreamEntry, CommandStreamHandle> { + friend class Core; + + private: + [[nodiscard]] uint64_t getIdFrom(const CommandStreamHandle &handle) const override; + + [[nodiscard]] CommandStreamHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + void destroyById(uint64_t id) override; + + public: + CommandStreamManager() noexcept; + + ~CommandStreamManager() noexcept override; + + /** + * @brief Creates a new command stream + * + * @param queue Queue the command buffer will be submitted to + * @param cmdPool Command pool the command buffer will be allocated from + * @return Handle that represents the #CommandStream + */ + CommandStreamHandle createCommandStream(const vk::Queue &queue, vk::CommandPool cmdPool); + + /** + * @brief Record vulkan commands to a #CommandStream, using a record function + * + * @param handle Command stream handle + * @param record Function that records the vulkan commands + */ + void recordCommandsToStream(const CommandStreamHandle &handle, + const RecordCommandFunction &record); + + /** + * @brief Add a callback to a #CommandStream that is called + * every time the command stream is submitted and finished + * + * @param handle Command stream handle + * @param finish Callback that is called when a command stream submission is finished + */ + void addFinishCallbackToStream(const CommandStreamHandle &handle, + const FinishCommandFunction &finish); + + /** + * @brief Submits a #CommandStream to it's queue and returns after execution is finished + * + * @param handle Command stream handle + * @param waitSemaphores Semaphores that are waited upon before executing the recorded + * commands + * @param signalSemaphores Semaphores that are signaled when execution of the recorded + * commands is finished + */ + void submitCommandStreamSynchronous(const CommandStreamHandle &handle, + std::vector<vk::Semaphore> &waitSemaphores, + std::vector<vk::Semaphore> &signalSemaphores); + + /** + * @brief Returns the underlying vulkan handle of a #CommandStream to be used for manual + * command recording + * + * @param handle Command stream handle + * @return Vulkan handle of the #CommandStream + */ + vk::CommandBuffer getStreamCommandBuffer(const CommandStreamHandle &handle); + }; + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/ComputePipelineManager.cpp b/src/vkcv/ComputePipelineManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..453dddd1baf93337280fe381b7f77e88bd2e3517 --- /dev/null +++ b/src/vkcv/ComputePipelineManager.cpp @@ -0,0 +1,102 @@ +#include "ComputePipelineManager.hpp" + +#include "vkcv/Core.hpp" + +namespace vkcv { + + uint64_t ComputePipelineManager::getIdFrom(const ComputePipelineHandle &handle) const { + return handle.getId(); + } + + ComputePipelineHandle ComputePipelineManager::createById(uint64_t id, + const HandleDestroyFunction &destroy) { + return ComputePipelineHandle(id, destroy); + } + + void ComputePipelineManager::destroyById(uint64_t id) { + auto &pipeline = getById(id); + + if (pipeline.m_handle) { + getCore().getContext().getDevice().destroy(pipeline.m_handle); + pipeline.m_handle = nullptr; + } + + if (pipeline.m_layout) { + getCore().getContext().getDevice().destroy(pipeline.m_layout); + pipeline.m_layout = nullptr; + } + } + + ComputePipelineManager::ComputePipelineManager() noexcept : + HandleManager<ComputePipelineEntry, ComputePipelineHandle>() {} + + vk::Result ComputePipelineManager::createShaderModule(vk::ShaderModule &module, + const ShaderProgram &shaderProgram, + const ShaderStage stage) { + std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage); + vk::ShaderModuleCreateInfo moduleInfo({}, code.size() * sizeof(uint32_t), code.data()); + return getCore().getContext().getDevice().createShaderModule(&moduleInfo, nullptr, &module); + } + + ComputePipelineManager::~ComputePipelineManager() noexcept { + clear(); + } + + vk::Pipeline ComputePipelineManager::getVkPipeline(const ComputePipelineHandle &handle) const { + auto &pipeline = (*this) [handle]; + return pipeline.m_handle; + } + + vk::PipelineLayout + ComputePipelineManager::getVkPipelineLayout(const ComputePipelineHandle &handle) const { + auto &pipeline = (*this) [handle]; + return pipeline.m_layout; + } + + ComputePipelineHandle ComputePipelineManager::createComputePipeline( + const ShaderProgram &shaderProgram, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) { + // Temporally handing over the Shader Program instead of a pipeline config + vk::ShaderModule computeModule {}; + if (createShaderModule(computeModule, shaderProgram, ShaderStage::COMPUTE) + != vk::Result::eSuccess) + return {}; + + vk::PipelineShaderStageCreateInfo pipelineComputeShaderStageInfo( + {}, vk::ShaderStageFlagBits::eCompute, computeModule, "main", nullptr); + + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, descriptorSetLayouts); + + const size_t pushConstantsSize = shaderProgram.getPushConstantsSize(); + vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eCompute, 0, + pushConstantsSize); + if (pushConstantsSize > 0) { + pipelineLayoutCreateInfo.setPushConstantRangeCount(1); + pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRange); + } + + vk::PipelineLayout vkPipelineLayout {}; + if (getCore().getContext().getDevice().createPipelineLayout(&pipelineLayoutCreateInfo, + nullptr, &vkPipelineLayout) + != vk::Result::eSuccess) { + getCore().getContext().getDevice().destroy(computeModule); + return {}; + } + + vk::ComputePipelineCreateInfo computePipelineCreateInfo {}; + computePipelineCreateInfo.stage = pipelineComputeShaderStageInfo; + computePipelineCreateInfo.layout = vkPipelineLayout; + + vk::Pipeline vkPipeline; + if (getCore().getContext().getDevice().createComputePipelines( + nullptr, 1, &computePipelineCreateInfo, nullptr, &vkPipeline) + != vk::Result::eSuccess) { + getCore().getContext().getDevice().destroy(computeModule); + return ComputePipelineHandle(); + } + + getCore().getContext().getDevice().destroy(computeModule); + return add({ vkPipeline, vkPipelineLayout }); + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/ComputePipelineManager.hpp b/src/vkcv/ComputePipelineManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..32fdc57943bff8d6b8194b9167a067de9a61f8b8 --- /dev/null +++ b/src/vkcv/ComputePipelineManager.hpp @@ -0,0 +1,80 @@ +#pragma once + +/** + * @authors Mark Mints, Tobias Frisch + * @file src/vkcv/ComputePipelineManager.hpp + * @brief Creation and handling of Compute Pipelines + */ + +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "HandleManager.hpp" + +#include "vkcv/ComputePipelineConfig.hpp" +#include "vkcv/ShaderProgram.hpp" + +namespace vkcv { + + struct ComputePipelineEntry { + vk::Pipeline m_handle; + vk::PipelineLayout m_layout; + }; + + /** + * @brief Class to manage compute pipelines. + */ + class ComputePipelineManager : + public HandleManager<ComputePipelineEntry, ComputePipelineHandle> { + private: + [[nodiscard]] uint64_t getIdFrom(const ComputePipelineHandle &handle) const override; + + [[nodiscard]] ComputePipelineHandle + createById(uint64_t id, const HandleDestroyFunction &destroy) override; + + /** + * Destroys and deallocates compute pipeline represented by a given + * compute pipeline handle id. + * + * @param id Compute pipeline handle id + */ + void destroyById(uint64_t id) override; + + vk::Result createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, + ShaderStage stage); + + public: + ComputePipelineManager() noexcept; + + ~ComputePipelineManager() noexcept override; // dtor + + /** + * Returns a vk::Pipeline object by handle. + * @param handle Directing to the requested pipeline. + * @return vk::Pipeline. + */ + [[nodiscard]] vk::Pipeline getVkPipeline(const ComputePipelineHandle &handle) const; + + /** + * Returns a vk::PipelineLayout object by handle. + * @param handle Directing to the requested pipeline. + * @return vk::PipelineLayout. + */ + [[nodiscard]] vk::PipelineLayout + getVkPipelineLayout(const ComputePipelineHandle &handle) const; + + /** + * Creates a Compute Pipeline based on the set shader stages in the Config Struct. + * This function is wrapped in /src/vkcv/Core.cpp by Core::createComputePipeline(const + * ComputePipelineConfig &config). On application level it is necessary first to fill a + * ComputePipelineConfig Struct. + * @param shaderProgram Hands over all needed information for pipeline creation. + * @param descriptorSetLayouts Hands over all needed information for pipeline creation. + * @return A Handler to the created Compute Pipeline Object. + */ + ComputePipelineHandle + createComputePipeline(const ShaderProgram &shaderProgram, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts); + }; + +} // namespace vkcv diff --git a/src/vkcv/Context.cpp b/src/vkcv/Context.cpp index ac133d1affc81702ee1a19b3f66810e606bec58d..1d92268e244d62e6efc71bc2c9a3a6387bb5e7f0 100644 --- a/src/vkcv/Context.cpp +++ b/src/vkcv/Context.cpp @@ -1,147 +1,136 @@ -#include <GLFW/glfw3.h> - #include "vkcv/Context.hpp" +#include "vkcv/Core.hpp" +#include "vkcv/Window.hpp" -namespace vkcv -{ - Context::Context(Context &&other) noexcept: - m_Instance(other.m_Instance), - m_PhysicalDevice(other.m_PhysicalDevice), - m_Device(other.m_Device), - m_QueueManager(other.m_QueueManager) - { - other.m_Instance = nullptr; - other.m_PhysicalDevice = nullptr; - other.m_Device = nullptr; - } - - Context & Context::operator=(Context &&other) noexcept - { - m_Instance = other.m_Instance; - m_PhysicalDevice = other.m_PhysicalDevice; - m_Device = other.m_Device; - m_QueueManager = other.m_QueueManager; - - other.m_Instance = nullptr; - other.m_PhysicalDevice = nullptr; - other.m_Device = nullptr; - - return *this; - } - - Context::Context(vk::Instance instance, - vk::PhysicalDevice physicalDevice, - vk::Device device, - QueueManager&& queueManager) noexcept : - m_Instance{instance}, - m_PhysicalDevice{physicalDevice}, - m_Device{device}, - m_QueueManager{queueManager} - {} - - Context::~Context() noexcept - { - m_Device.destroy(); - m_Instance.destroy(); - } - - const vk::Instance &Context::getInstance() const - { - return m_Instance; - } - - const vk::PhysicalDevice &Context::getPhysicalDevice() const - { - return m_PhysicalDevice; - } - - const vk::Device &Context::getDevice() const - { - return m_Device; - } - - const QueueManager& Context::getQueueManager() const { - return m_QueueManager; - } +namespace vkcv { - /** - * @brief The physical device is evaluated by three categories: - * discrete GPU vs. integrated GPU, amount of queues and its abilities, and VRAM.physicalDevice. - * @param physicalDevice The physical device - * @return Device score as integer - */ - int deviceScore(const vk::PhysicalDevice& physicalDevice) - { - int score = 0; - vk::PhysicalDeviceProperties properties = physicalDevice.getProperties(); - std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties(); - - // for every queue family compute queue flag bits and the amount of queues - for (const auto& qFamily : qFamilyProperties) { - uint32_t qCount = qFamily.queueCount; - uint32_t bitCount = (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0) - + (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0) - + (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0) - + (static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eSparseBinding) != 0); - score += static_cast<int>(qCount * bitCount); - } - - // compute the VRAM of the physical device - vk::PhysicalDeviceMemoryProperties memoryProperties = physicalDevice.getMemoryProperties(); - auto vram = static_cast<int>(memoryProperties.memoryHeaps[0].size / static_cast<uint32_t>(1E9)); - score *= vram; - - if (properties.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) { - score *= 2; - } - else if (properties.deviceType != vk::PhysicalDeviceType::eIntegratedGpu) { - score = -1; - } - - return score; + Context::Context(Context &&other) noexcept : + m_Instance(other.m_Instance), m_PhysicalDevice(other.m_PhysicalDevice), + m_Device(other.m_Device), m_FeatureManager(std::move(other.m_FeatureManager)), + m_QueueManager(std::move(other.m_QueueManager)), m_Allocator(other.m_Allocator) { + other.m_Instance = nullptr; + other.m_PhysicalDevice = nullptr; + other.m_Device = nullptr; + other.m_Allocator = nullptr; } - + + Context &Context::operator=(Context &&other) noexcept { + m_Instance = other.m_Instance; + m_PhysicalDevice = other.m_PhysicalDevice; + m_Device = other.m_Device; + m_FeatureManager = std::move(other.m_FeatureManager); + m_QueueManager = std::move(other.m_QueueManager); + m_Allocator = other.m_Allocator; + + other.m_Instance = nullptr; + other.m_PhysicalDevice = nullptr; + other.m_Device = nullptr; + other.m_Allocator = nullptr; + + return *this; + } + + Context::Context(vk::Instance instance, vk::PhysicalDevice physicalDevice, vk::Device device, + FeatureManager &&featureManager, QueueManager &&queueManager, + vma::Allocator &&allocator) noexcept : + m_Instance(instance), + m_PhysicalDevice(physicalDevice), m_Device(device), + m_FeatureManager(std::move(featureManager)), m_QueueManager(std::move(queueManager)), + m_Allocator(allocator) {} + + Context::~Context() noexcept { + m_Allocator.destroy(); + m_Device.destroy(); + m_Instance.destroy(); + } + + const vk::Instance &Context::getInstance() const { + return m_Instance; + } + + const vk::PhysicalDevice &Context::getPhysicalDevice() const { + return m_PhysicalDevice; + } + + const vk::Device &Context::getDevice() const { + return m_Device; + } + + const FeatureManager &Context::getFeatureManager() const { + return m_FeatureManager; + } + + const QueueManager &Context::getQueueManager() const { + return m_QueueManager; + } + + const vma::Allocator &Context::getAllocator() const { + return m_Allocator; + } + /** - * @brief All existing physical devices will be evaluated by deviceScore. + * @brief All existing physical devices will be evaluated. * @param instance The instance - * @return The optimal physical device - * @see Context.deviceScore - */ - vk::PhysicalDevice pickPhysicalDevice(vk::Instance& instance) - { - vk::PhysicalDevice phyDevice; - std::vector<vk::PhysicalDevice> devices = instance.enumeratePhysicalDevices(); - + * @param physicalDevice The optimal physical device + * @return Returns if a suitable GPU is found as physical device + */ + static bool pickPhysicalDevice(const vk::Instance &instance, + vk::PhysicalDevice &physicalDevice) { + const std::vector<vk::PhysicalDevice> &devices = instance.enumeratePhysicalDevices(); + if (devices.empty()) { - throw std::runtime_error("failed to find GPUs with Vulkan support!"); + vkcv_log(LogLevel::ERROR, "Failed to find GPUs with Vulkan support"); + return false; } - - int max_score = -1; - for (const auto& device : devices) { - int score = deviceScore(device); - if (score > max_score) { - max_score = score; - phyDevice = device; + + vk::PhysicalDeviceType type = vk::PhysicalDeviceType::eOther; + vk::DeviceSize vramSize = 0; + + for (const auto &device : devices) { + const auto &properties = device.getProperties(); + + if (vk::PhysicalDeviceType::eOther == type) { + type = properties.deviceType; + physicalDevice = device; + continue; + } + + if (vk::PhysicalDeviceType::eDiscreteGpu != properties.deviceType) + continue; + + if (vk::PhysicalDeviceType::eDiscreteGpu != type) { + type = properties.deviceType; + physicalDevice = device; + continue; + } + + vk::PhysicalDeviceMemoryProperties memoryProperties = + physicalDevice.getMemoryProperties(); + vk::DeviceSize maxSize = 0; + + for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; i++) + if (memoryProperties.memoryHeaps [i].size > maxSize) + maxSize = memoryProperties.memoryHeaps [i].size; + + if (maxSize > vramSize) { + type = properties.deviceType; + physicalDevice = device; + vramSize = maxSize; } } - - if (max_score == -1) { - throw std::runtime_error("failed to find a suitable GPU!"); - } - - return phyDevice; + + return true; } - + /** - * @brief With the help of the reference "supported" all elements in "check" checked, - * if they are supported by the physical device. - * @param supported The reference that can be used to check "check" - * @param check The elements to be checked - * @return True, if all elements in "check" are supported - */ - bool checkSupport(std::vector<const char*>& supported, std::vector<const char*>& check) - { + * @brief Check whether all string occurrences in "check" are contained in "supported" + * @param supported The const vector const char* reference used to compare entries in "check" + * @param check The const vector const char* reference elements to be checked by "supported" + * @return True, if all elements in "check" are supported (contained in supported) + */ + bool checkSupport(const std::vector<const char*> &supported, + const std::vector<const char*> &check) { for (auto checkElem : check) { bool found = false; for (auto supportedElem : supported) { @@ -155,143 +144,332 @@ namespace vkcv } return true; } - - - std::vector<const char*> getRequiredExtensions() { - uint32_t glfwExtensionCount = 0; - const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); -#ifndef NDEBUG - extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + std::vector<std::string> getRequiredExtensions() { + std::vector<std::string> extensions = Window::getExtensions(); + +#ifdef VULKAN_DEBUG_LABELS + extensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); #endif - + return extensions; } - - Context Context::create(const char *applicationName, - uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions) { + + /** + * Given the @p physicalDevice and the @p queuePriorities, the @p queueCreateInfos are computed. + * First, the requested queues are sorted by priority depending on the availability of queues in + * the queue families of the given + * @p physicalDevice. Then check, if all requested queues are creatable. If so, the @p + * queueCreateInfos will be computed. Furthermore, lists of index pairs (queueFamilyIndex, + * queueIndex) for later referencing of the separate queues will be computed. + * @param[in] physicalDevice The physical device + * @param[in] queuePriorities The queue priorities used for the computation of @p + * queueCreateInfos + * @param[in] queueFlags The queue flags requesting the queues + * @param[in,out] queueCreateInfos The queue create info structures to be created + * @param[in,out] queuePairsGraphics The list of index pairs (queueFamilyIndex, queueIndex) of + * queues of type vk::QueueFlagBits::eGraphics + * @param[in,out] queuePairsCompute The list of index pairs (queueFamilyIndex, queueIndex) of + * queues of type vk::QueueFlagBits::eCompute + * @param[in,out] queuePairsTransfer The list of index pairs (queueFamilyIndex, queueIndex) of + * queues of type vk::QueueFlagBits::eTransfer + * @throws std::runtime_error If the requested queues from @p queueFlags are not creatable due + * to insufficient availability. + */ + static void + queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice, + const std::vector<float> &queuePriorities, + const std::vector<vk::QueueFlagBits> &queueFlags, + std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos, + std::vector<std::pair<int, int>> &queuePairsGraphics, + std::vector<std::pair<int, int>> &queuePairsCompute, + std::vector<std::pair<int, int>> &queuePairsTransfer) { + queueCreateInfos = {}; + queuePairsGraphics = {}; + queuePairsCompute = {}; + queuePairsTransfer = {}; + std::vector<vk::QueueFamilyProperties> qFamilyProperties = + physicalDevice.getQueueFamilyProperties(); + + // check priorities of flags -> the lower prioCount the higher the priority + std::vector<int> prios; + for (auto flag : queueFlags) { + int prioCount = 0; + for (size_t i = 0; i < qFamilyProperties.size(); i++) { + prioCount += (static_cast<uint32_t>(flag & qFamilyProperties [i].queueFlags) != 0) + * qFamilyProperties [i].queueCount; + } + prios.push_back(prioCount); + } + // resort flags with heighest priority before allocating the queues + std::vector<vk::QueueFlagBits> newFlags; + for (size_t i = 0; i < prios.size(); i++) { + auto minElem = std::min_element(prios.begin(), prios.end()); + int index = minElem - prios.begin(); + newFlags.push_back(queueFlags [index]); + prios [index] = std::numeric_limits<int>::max(); + } + + // create requested queues and check if more requested queues are supported + // herefore: create vector that updates available queues in each queue family + // structure: [qFamily_0, ..., qFamily_n] where + // - qFamily_i = [GraphicsCount, ComputeCount, TransferCount], 0 <= i <= n + std::vector<std::vector<int>> queueFamilyStatus, initialQueueFamilyStatus; + + for (auto qFamily : qFamilyProperties) { + auto graphicsCount = + static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0 ? + qFamily.queueCount : + 0; + auto computeCount = + static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0 ? + qFamily.queueCount : + 0; + auto transferCount = + static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0 ? + qFamily.queueCount : + 0; + queueFamilyStatus.push_back({ static_cast<int>(graphicsCount), + static_cast<int>(computeCount), + static_cast<int>(transferCount) }); + } + + initialQueueFamilyStatus = queueFamilyStatus; + // check if every queue with the specified queue flag can be created + // this automatically checks for queue flag support! + for (auto qFlag : newFlags) { + bool found; + switch (qFlag) { + case vk::QueueFlagBits::eGraphics: + found = false; + for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (queueFamilyStatus [i][0] > 0) { + queuePairsGraphics.emplace_back(std::pair( + i, initialQueueFamilyStatus [i][0] - queueFamilyStatus [i][0])); + queueFamilyStatus [i][0]--; + queueFamilyStatus [i][1]--; + queueFamilyStatus [i][2]--; + found = true; + } + } + if (!found) { + for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (initialQueueFamilyStatus [i][0] > 0) { + queuePairsGraphics.emplace_back(std::pair(i, 0)); + found = true; + } + } + + vkcv_log(LogLevel::WARNING, "Not enough %s queues", + vk::to_string(qFlag).c_str()); + } + break; + case vk::QueueFlagBits::eCompute: + found = false; + for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (queueFamilyStatus [i][1] > 0) { + queuePairsCompute.emplace_back(std::pair( + i, initialQueueFamilyStatus [i][1] - queueFamilyStatus [i][1])); + queueFamilyStatus [i][0]--; + queueFamilyStatus [i][1]--; + queueFamilyStatus [i][2]--; + found = true; + } + } + if (!found) { + for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (initialQueueFamilyStatus [i][1] > 0) { + queuePairsCompute.emplace_back(std::pair(i, 0)); + found = true; + } + } + + vkcv_log(LogLevel::WARNING, "Not enough %s queues", + vk::to_string(qFlag).c_str()); + } + break; + case vk::QueueFlagBits::eTransfer: + found = false; + for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (queueFamilyStatus [i][2] > 0) { + queuePairsTransfer.emplace_back(std::pair( + i, initialQueueFamilyStatus [i][2] - queueFamilyStatus [i][2])); + queueFamilyStatus [i][0]--; + queueFamilyStatus [i][1]--; + queueFamilyStatus [i][2]--; + found = true; + } + } + if (!found) { + for (size_t i = 0; i < queueFamilyStatus.size() && !found; i++) { + if (initialQueueFamilyStatus [i][2] > 0) { + queuePairsTransfer.emplace_back(std::pair(i, 0)); + found = true; + } + } + + vkcv_log(LogLevel::WARNING, "Not enough %s queues", + vk::to_string(qFlag).c_str()); + } + break; + default: + vkcv_log(LogLevel::ERROR, "Invalid input for queue flag bits: %s", + vk::to_string(qFlag).c_str()); + break; + } + } + + // create all requested queues + for (size_t i = 0; i < qFamilyProperties.size(); i++) { + uint32_t create = std::abs(initialQueueFamilyStatus [i][0] - queueFamilyStatus [i][0]); + if (create > 0) { + vk::DeviceQueueCreateInfo qCreateInfo(vk::DeviceQueueCreateFlags(), i, create, + queuePriorities.data()); + queueCreateInfos.push_back(qCreateInfo); + } + } + } + + Context Context::create(const std::string &applicationName, uint32_t applicationVersion, + const std::vector<vk::QueueFlagBits> &queueFlags, + const Features &features, + const std::vector<const char*> &instanceExtensions) { // check for layer support - - const std::vector<vk::LayerProperties>& layerProperties = vk::enumerateInstanceLayerProperties(); - + + const std::vector<vk::LayerProperties> &layerProperties = + vk::enumerateInstanceLayerProperties(); + std::vector<const char*> supportedLayers; supportedLayers.reserve(layerProperties.size()); - - for (auto& elem : layerProperties) { + + for (auto &elem : layerProperties) { supportedLayers.push_back(elem.layerName); } // if in debug mode, check if validation layers are supported. Enable them if supported -#ifndef NDEBUG - std::vector<const char*> validationLayers = { - "VK_LAYER_KHRONOS_validation" - }; - +#ifdef VULKAN_VALIDATION_LAYERS + std::vector<const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" }; + if (!checkSupport(supportedLayers, validationLayers)) { - throw std::runtime_error("Validation layers requested but not available!"); + vkcv_log_throw_error("Validation layers requested but not available!"); } #endif - - // check for extension support - std::vector<vk::ExtensionProperties> instanceExtensionProperties = vk::enumerateInstanceExtensionProperties(); - + + // check for instance extension support + std::vector<vk::ExtensionProperties> instanceExtensionProperties = + vk::enumerateInstanceExtensionProperties(); + std::vector<const char*> supportedExtensions; supportedExtensions.reserve(instanceExtensionProperties.size()); - - for (auto& elem : instanceExtensionProperties) { + + for (auto &elem : instanceExtensionProperties) { supportedExtensions.push_back(elem.extensionName); } - - if (!checkSupport(supportedExtensions, instanceExtensions)) { - throw std::runtime_error("The requested instance extensions are not supported!"); - } - + // for GLFW: get all required extensions - std::vector<const char*> requiredExtensions = getRequiredExtensions(); - instanceExtensions.insert(instanceExtensions.end(), requiredExtensions.begin(), requiredExtensions.end()); - - const vk::ApplicationInfo applicationInfo( - applicationName, - applicationVersion, - "vkCV", - VK_MAKE_VERSION(0, 0, 1), - VK_HEADER_VERSION_COMPLETE - ); - + auto requiredStrings = getRequiredExtensions(); + std::vector<const char*> requiredExtensions; + + for (const auto &extension : requiredStrings) { + requiredExtensions.push_back(extension.c_str()); + } + + requiredExtensions.insert(requiredExtensions.end(), instanceExtensions.begin(), + instanceExtensions.end()); + + if (!checkSupport(supportedExtensions, requiredExtensions)) { + vkcv_log_throw_error("The requested instance extensions are not supported!"); + } + + const vk::ApplicationInfo applicationInfo(applicationName.c_str(), applicationVersion, + VKCV_FRAMEWORK_NAME, VKCV_FRAMEWORK_VERSION, + VK_HEADER_VERSION_COMPLETE); + vk::InstanceCreateInfo instanceCreateInfo( - vk::InstanceCreateFlags(), - &applicationInfo, - 0, - nullptr, - static_cast<uint32_t>(instanceExtensions.size()), - instanceExtensions.data() - ); + vk::InstanceCreateFlags(), &applicationInfo, 0, nullptr, + static_cast<uint32_t>(requiredExtensions.size()), requiredExtensions.data()); -#ifndef NDEBUG +#ifdef VULKAN_VALIDATION_LAYERS instanceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); instanceCreateInfo.ppEnabledLayerNames = validationLayers.data(); #endif - + vk::Instance instance = vk::createInstance(instanceCreateInfo); - + std::vector<vk::PhysicalDevice> physicalDevices = instance.enumeratePhysicalDevices(); - vk::PhysicalDevice physicalDevice = pickPhysicalDevice(instance); - - // check for physical device extension support - std::vector<vk::ExtensionProperties> deviceExtensionProperties = physicalDevice.enumerateDeviceExtensionProperties(); - supportedExtensions.clear(); - for (auto& elem : deviceExtensionProperties) { - supportedExtensions.push_back(elem.extensionName); + vk::PhysicalDevice physicalDevice; + + if (!pickPhysicalDevice(instance, physicalDevice)) { + vkcv_log_throw_error("Picking suitable GPU as physical device failed!"); } - if (!checkSupport(supportedExtensions, deviceExtensions)) { - throw std::runtime_error("The requested device extensions are not supported by the physical device!"); + + FeatureManager featureManager(physicalDevice); + +#ifdef __APPLE__ + featureManager.useExtension("VK_KHR_portability_subset", true); +#endif + + for (const auto &feature : features.getList()) { + feature(featureManager); } - + + const auto &extensions = featureManager.getActiveExtensions(); + std::vector<vk::DeviceQueueCreateInfo> qCreateInfos; - - // create required queues std::vector<float> qPriorities; qPriorities.resize(queueFlags.size(), 1.f); std::vector<std::pair<int, int>> queuePairsGraphics, queuePairsCompute, queuePairsTransfer; - - QueueManager::queueCreateInfosQueueHandles(physicalDevice, qPriorities, queueFlags, qCreateInfos, queuePairsGraphics, queuePairsCompute, queuePairsTransfer); - + + queueCreateInfosQueueHandles(physicalDevice, qPriorities, queueFlags, qCreateInfos, + queuePairsGraphics, queuePairsCompute, queuePairsTransfer); + vk::DeviceCreateInfo deviceCreateInfo( - vk::DeviceCreateFlags(), - qCreateInfos.size(), - qCreateInfos.data(), - 0, - nullptr, - deviceExtensions.size(), - deviceExtensions.data(), - nullptr // Should our device use some features??? If yes: TODO - ); + vk::DeviceCreateFlags(), qCreateInfos.size(), qCreateInfos.data(), 0, nullptr, + extensions.size(), extensions.data(), nullptr, &(featureManager.getFeatures())); -#ifndef NDEBUG +#ifdef VULKAN_VALIDATION_LAYERS deviceCreateInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size()); deviceCreateInfo.ppEnabledLayerNames = validationLayers.data(); #endif - // FIXME: check if device feature is supported - vk::PhysicalDeviceFeatures deviceFeatures; - deviceFeatures.fragmentStoresAndAtomics = true; - deviceFeatures.geometryShader = true; - deviceCreateInfo.pEnabledFeatures = &deviceFeatures; - - // Ablauf - // qCreateInfos erstellen --> braucht das Device - // device erstellen - // jetzt koennen wir mit dem device die queues erstellen - vk::Device device = physicalDevice.createDevice(deviceCreateInfo); + + QueueManager queueManager = QueueManager::create( + device, + queuePairsGraphics, + queuePairsCompute, + queuePairsTransfer + ); - QueueManager queueManager = QueueManager::create(device, queuePairsGraphics, queuePairsCompute, queuePairsTransfer); + const bool coherentDeviceMemory = featureManager.checkFeatures<vk::PhysicalDeviceCoherentMemoryFeaturesAMD>( + vk::StructureType::ePhysicalDeviceCoherentMemoryFeaturesAMD, + [](const vk::PhysicalDeviceCoherentMemoryFeaturesAMD &features) { + return features.deviceCoherentMemory; + } + ); + + vma::AllocatorCreateFlags vmaFlags; + if (coherentDeviceMemory) { + vmaFlags |= vma::AllocatorCreateFlagBits::eAmdDeviceCoherentMemory; + } - return Context(instance, physicalDevice, device, std::move(queueManager)); + const vma::AllocatorCreateInfo allocatorCreateInfo( + vmaFlags, + physicalDevice, + device, + 0, + nullptr, + nullptr, + nullptr, + nullptr, + instance, + VK_HEADER_VERSION_COMPLETE + ); + + vma::Allocator allocator = vma::createAllocator(allocatorCreateInfo); + + return Context(instance, physicalDevice, device, std::move(featureManager), + std::move(queueManager), std::move(allocator)); } - -} + +} // namespace vkcv diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp index 1492b1afa563543e6a9eef380295bcb71fef58b8..7fde9196d3187f3aafa7134325ee7d166d6cd23c 100644 --- a/src/vkcv/Core.cpp +++ b/src/vkcv/Core.cpp @@ -1,546 +1,1303 @@ /** - * @authors Artur Wasmut + * @authors Artur Wasmut, Alexander Gauggel, Tobias Frisch * @file src/vkcv/Core.cpp * @brief Handling of global states regarding dependencies */ #include <GLFW/glfw3.h> +#include <cmath> -#include "vkcv/Core.hpp" +#include "BufferManager.hpp" +#include "CommandStreamManager.hpp" +#include "ComputePipelineManager.hpp" +#include "DescriptorSetLayoutManager.hpp" +#include "DescriptorSetManager.hpp" +#include "GraphicsPipelineManager.hpp" +#include "ImageManager.hpp" #include "PassManager.hpp" -#include "PipelineManager.hpp" -#include "vkcv/BufferManager.hpp" #include "SamplerManager.hpp" -#include "ImageManager.hpp" -#include "DescriptorManager.hpp" -#include "ImageLayoutTransitions.hpp" -#include "vkcv/CommandStreamManager.hpp" -#include <cmath> +#include "WindowManager.hpp" +#include "vkcv/BlitDownsampler.hpp" +#include "vkcv/Core.hpp" +#include "vkcv/Image.hpp" #include "vkcv/Logger.hpp" -namespace vkcv -{ - - Core Core::create(Window &window, - const char *applicationName, - uint32_t applicationVersion, - std::vector<vk::QueueFlagBits> queueFlags, - std::vector<const char *> instanceExtensions, - std::vector<const char *> deviceExtensions) - { - Context context = Context::create( - applicationName, applicationVersion, - queueFlags, - instanceExtensions, - deviceExtensions - ); +namespace vkcv { + + /** + * @brief Generates a set of the family indices for all different kinds of + * queues a given queue manager provides. + * + * @param[in] queueManager Queue manager + * @return Set of queue family indices + */ + static std::unordered_set<int> generateQueueFamilyIndexSet(const QueueManager &queueManager) { + std::unordered_set<int> indexSet; + + for (const auto &queue : queueManager.getGraphicsQueues()) { + indexSet.insert(queue.familyIndex); + } + + for (const auto &queue : queueManager.getComputeQueues()) { + indexSet.insert(queue.familyIndex); + } - Swapchain swapChain = Swapchain::create(window, context); - - std::vector<vk::ImageView> swapchainImageViews = createSwapchainImageViews( context, swapChain); - - const auto& queueManager = context.getQueueManager(); - - const int graphicQueueFamilyIndex = queueManager.getGraphicsQueues()[0].familyIndex; - const std::unordered_set<int> queueFamilySet = generateQueueFamilyIndexSet(queueManager); - const auto commandResources = createCommandResources(context.getDevice(), queueFamilySet); - const auto defaultSyncResources = createSyncResources(context.getDevice()); - - return Core(std::move(context) , window, swapChain, swapchainImageViews, commandResources, defaultSyncResources); - } - - const Context &Core::getContext() const - { - return m_Context; - } - - const Swapchain& Core::getSwapchain() const { - return m_swapchain; - } - - Core::Core(Context &&context, Window &window, const Swapchain& swapChain, std::vector<vk::ImageView> swapchainImageViews, - const CommandResources& commandResources, const SyncResources& syncResources) noexcept : - m_Context(std::move(context)), - m_window(window), - m_swapchain(swapChain), - m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)}, - m_PipelineManager{std::make_unique<PipelineManager>(m_Context.m_Device)}, - m_DescriptorManager(std::make_unique<DescriptorManager>(m_Context.m_Device)), - m_BufferManager{std::unique_ptr<BufferManager>(new BufferManager())}, - m_SamplerManager(std::unique_ptr<SamplerManager>(new SamplerManager(m_Context.m_Device))), - m_ImageManager{std::unique_ptr<ImageManager>(new ImageManager(*m_BufferManager))}, - m_CommandStreamManager{std::unique_ptr<CommandStreamManager>(new CommandStreamManager)}, - m_CommandResources(commandResources), - m_SyncResources(syncResources) - { - m_BufferManager->m_core = this; - m_BufferManager->init(); - m_CommandStreamManager->init(this); - - m_ImageManager->m_core = this; - - e_resizeHandle = m_window.e_resize.add( [&](int width, int height) { - m_swapchain.signalSwapchainRecreation(); - }); - - const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); - m_ImageManager->setSwapchainImages( - swapchainImages, - swapchainImageViews, - swapChain.getExtent().width, - swapChain.getExtent().height, - swapChain.getFormat()); + for (const auto &queue : queueManager.getTransferQueues()) { + indexSet.insert(queue.familyIndex); + } + + indexSet.insert(queueManager.getPresentQueue().familyIndex); + return indexSet; + } + + /** + * @brief Creates and returns a vector of newly allocated command pools + * for each different queue family index in a given set. + * + * @param[in,out] device Vulkan device + * @param[in] familyIndexSet Set of queue family indices + * @return New command pools + */ + static std::vector<vk::CommandPool> + createCommandPools(const vk::Device &device, const std::unordered_set<int> &familyIndexSet) { + std::vector<vk::CommandPool> commandPoolsPerQueueFamily; + commandPoolsPerQueueFamily.resize(familyIndexSet.size()); + + const vk::CommandPoolCreateFlags poolFlags = vk::CommandPoolCreateFlagBits::eTransient; + for (const int familyIndex : familyIndexSet) { + const vk::CommandPoolCreateInfo poolCreateInfo(poolFlags, familyIndex); + commandPoolsPerQueueFamily [familyIndex] = + device.createCommandPool(poolCreateInfo, nullptr, {}); + } + + return commandPoolsPerQueueFamily; + } + + Core Core::create(const std::string &applicationName, uint32_t applicationVersion, + const std::vector<vk::QueueFlagBits> &queueFlags, const Features &features, + const std::vector<const char*> &instanceExtensions) { + Context context = Context::create(applicationName, applicationVersion, queueFlags, features, + instanceExtensions); + + return Core(std::move(context)); + } + + const Context &Core::getContext() const { + return m_Context; + } + + Core::Core(Context &&context) noexcept : + m_Context(std::move(context)), m_PassManager(std::make_unique<PassManager>()), + m_GraphicsPipelineManager(std::make_unique<GraphicsPipelineManager>()), + m_ComputePipelineManager(std::make_unique<ComputePipelineManager>()), + m_DescriptorSetLayoutManager(std::make_unique<DescriptorSetLayoutManager>()), + m_DescriptorSetManager(std::make_unique<DescriptorSetManager>()), + m_BufferManager(std::make_unique<BufferManager>()), + m_SamplerManager(std::make_unique<SamplerManager>()), + m_ImageManager(std::make_unique<ImageManager>()), + m_CommandStreamManager { std::make_unique<CommandStreamManager>() }, + m_WindowManager(std::make_unique<WindowManager>()), + m_SwapchainManager(std::make_unique<SwapchainManager>()), m_CommandPools(), + m_RenderFinished(), m_SwapchainImageAcquired(), m_downsampler(nullptr) { + m_CommandPools = createCommandPools( + m_Context.getDevice(), generateQueueFamilyIndexSet(m_Context.getQueueManager())); + + m_RenderFinished = m_Context.getDevice().createSemaphore({}); + m_SwapchainImageAcquired = m_Context.getDevice().createSemaphore({}); + + m_PassManager->init(*this); + m_GraphicsPipelineManager->init(*this); + m_ComputePipelineManager->init(*this); + m_DescriptorSetLayoutManager->init(*this); + m_DescriptorSetManager->init(*this, *m_DescriptorSetLayoutManager); + m_BufferManager->init(*this); + m_SamplerManager->init(*this); + m_ImageManager->init(*this, *m_BufferManager); + m_CommandStreamManager->init(*this); + m_SwapchainManager->init(*this); + m_downsampler = std::unique_ptr<Downsampler>(new BlitDownsampler(*this, *m_ImageManager)); } Core::~Core() noexcept { - m_window.e_resize.remove(e_resizeHandle); - m_Context.getDevice().waitIdle(); - destroyCommandResources(m_Context.getDevice(), m_CommandResources); - destroySyncResources(m_Context.getDevice(), m_SyncResources); + for (const vk::CommandPool &pool : m_CommandPools) { + m_Context.getDevice().destroyCommandPool(pool); + } + + m_Context.getDevice().destroySemaphore(m_RenderFinished); + m_Context.getDevice().destroySemaphore(m_SwapchainImageAcquired); + } + + GraphicsPipelineHandle Core::createGraphicsPipeline(const GraphicsPipelineConfig &config) { + return m_GraphicsPipelineManager->createPipeline(config, *m_PassManager, + *m_DescriptorSetLayoutManager); + } + + ComputePipelineHandle Core::createComputePipeline(const ComputePipelineConfig &config) { + std::vector<vk::DescriptorSetLayout> layouts; + layouts.resize(config.getDescriptorSetLayouts().size()); + + for (size_t i = 0; i < layouts.size(); i++) { + layouts [i] = m_DescriptorSetLayoutManager + ->getDescriptorSetLayout(config.getDescriptorSetLayouts() [i]) + .vulkanHandle; + } - m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain()); - m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface()); + return m_ComputePipelineManager->createComputePipeline(config.getShaderProgram(), layouts); } - PipelineHandle Core::createGraphicsPipeline(const PipelineConfig &config) - { - return m_PipelineManager->createPipeline(config, *m_PassManager); - } + PassHandle Core::createPass(const PassConfig &config) { + return m_PassManager->createPass(config); + } - PipelineHandle Core::createComputePipeline( - const ShaderProgram &shaderProgram, - const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts) - { - return m_PipelineManager->createComputePipeline(shaderProgram, descriptorSetLayouts); - } + const PassConfig &Core::getPassConfiguration(const vkcv::PassHandle &pass) { + return m_PassManager->getPassConfig(pass); + } + BufferHandle Core::createBuffer(BufferType type, const TypeGuard &typeGuard, size_t count, + BufferMemoryType memoryType, bool readable) { + return m_BufferManager->createBuffer(typeGuard, type, memoryType, + count * typeGuard.typeSize(), readable); + } - PassHandle Core::createPass(const PassConfig &config) - { - return m_PassManager->createPass(config); - } + BufferHandle Core::createBuffer(BufferType type, size_t size, BufferMemoryType memoryType, + bool readable) { + return m_BufferManager->createBuffer(TypeGuard(1), type, memoryType, size, readable); + } + + vk::Buffer Core::getBuffer(const BufferHandle &buffer) const { + return m_BufferManager->getBuffer(buffer); + } + + BufferType Core::getBufferType(const BufferHandle &handle) const { + return m_BufferManager->getBufferType(handle); + } + + BufferMemoryType Core::getBufferMemoryType(const BufferHandle &handle) const { + return m_BufferManager->getBufferMemoryType(handle); + } + + size_t Core::getBufferSize(const BufferHandle &handle) const { + return m_BufferManager->getBufferSize(handle); + } + + void Core::fillBuffer(const BufferHandle &handle, const void* data, size_t size, + size_t offset) { + m_BufferManager->fillBuffer(handle, data, size, offset); + } + + void Core::readBuffer(const BufferHandle &handle, void* data, size_t size, size_t offset) { + m_BufferManager->readBuffer(handle, data, size, offset); + } + + void* Core::mapBuffer(const BufferHandle &handle, size_t offset, size_t size) { + return m_BufferManager->mapBuffer(handle, offset, size); + } + + void Core::unmapBuffer(const BufferHandle &handle) { + m_BufferManager->unmapBuffer(handle); + } + + Result Core::acquireSwapchainImage(const SwapchainHandle &swapchainHandle) { + uint32_t imageIndex; + vk::Result result; - Result Core::acquireSwapchainImage() { - uint32_t imageIndex; - - vk::Result result; - try { result = m_Context.getDevice().acquireNextImageKHR( - m_swapchain.getSwapchain(), - std::numeric_limits<uint64_t>::max(), - m_SyncResources.swapchainImageAcquired, - nullptr, - &imageIndex, {} - ); - } catch (vk::OutOfDateKHRError e) { + m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain, + std::numeric_limits<uint64_t>::max(), m_SwapchainImageAcquired, nullptr, + &imageIndex, {}); + } catch (const vk::OutOfDateKHRError &e) { result = vk::Result::eErrorOutOfDateKHR; + } catch (const vk::DeviceLostError &e) { + result = vk::Result::eErrorDeviceLost; } - - if (result != vk::Result::eSuccess) { + + if ((result != vk::Result::eSuccess) && (result != vk::Result::eSuboptimalKHR)) { vkcv_log(LogLevel::ERROR, "%s", vk::to_string(result).c_str()); return Result::ERROR; + } else if (result == vk::Result::eSuboptimalKHR) { + vkcv_log(LogLevel::WARNING, "Acquired image is suboptimal"); + m_SwapchainManager->signalRecreation(swapchainHandle); } - + m_currentSwapchainImageIndex = imageIndex; return Result::SUCCESS; } - bool Core::beginFrame(uint32_t& width, uint32_t& height) { - if (m_swapchain.shouldUpdateSwapchain()) { + bool Core::beginFrame(uint32_t &width, uint32_t &height, const WindowHandle &windowHandle) { + const Window &window = m_WindowManager->getWindow(windowHandle); + const SwapchainHandle swapchainHandle = window.getSwapchain(); + + if (m_SwapchainManager->shouldUpdateSwapchain(swapchainHandle)) { m_Context.getDevice().waitIdle(); - m_swapchain.updateSwapchain(m_Context, m_window); - const auto swapchainViews = createSwapchainImageViews(m_Context, m_swapchain); - const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); + m_SwapchainManager->updateSwapchain(swapchainHandle, window); - m_ImageManager->setSwapchainImages(swapchainImages, swapchainViews, width, height, m_swapchain.getFormat()); + if (!m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain) { + return false; + } + + setSwapchainImages(swapchainHandle); } - - if (acquireSwapchainImage() != Result::SUCCESS) { - vkcv_log(LogLevel::ERROR, "Acquire failed"); - - m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max(); - } - - m_Context.getDevice().waitIdle(); // TODO: this is a sin against graphics programming, but its getting late - Alex - - const auto& extent = m_swapchain.getExtent(); - + + const auto &extent = m_SwapchainManager->getExtent(swapchainHandle); + width = extent.width; height = extent.height; - + + if ((width < MIN_SURFACE_SIZE) || (height < MIN_SURFACE_SIZE)) { + return false; + } + + if (acquireSwapchainImage(swapchainHandle) != Result::SUCCESS) { + vkcv_log(LogLevel::ERROR, "Acquire failed"); + + m_currentSwapchainImageIndex = std::numeric_limits<uint32_t>::max(); + } + + m_Context.getDevice().waitIdle(); // TODO: this is a sin against graphics programming, but + // its getting late - Alex + m_ImageManager->setCurrentSwapchainImageIndex(m_currentSwapchainImageIndex); return (m_currentSwapchainImageIndex != std::numeric_limits<uint32_t>::max()); } - void Core::recordDrawcallsToCmdStream( - const CommandStreamHandle cmdStreamHandle, - const PassHandle renderpassHandle, - const PipelineHandle pipelineHandle, - const PushConstantData &pushConstantData, - const std::vector<DrawcallInfo> &drawcalls, - const std::vector<ImageHandle> &renderTargets) { + static std::array<uint32_t, 2> + getWidthHeightFromRenderTargets(const std::vector<ImageHandle> &renderTargets, + const vk::Extent2D &swapchainExtent, + const ImageManager &imageManager) { - if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { - return; - } + std::array<uint32_t, 2> widthHeight; - uint32_t width; - uint32_t height; if (renderTargets.size() > 0) { - const vkcv::ImageHandle firstImage = renderTargets[0]; + const vkcv::ImageHandle firstImage = renderTargets [0]; if (firstImage.isSwapchainImage()) { - const auto& swapchainExtent = m_swapchain.getExtent(); - width = swapchainExtent.width; - height = swapchainExtent.height; + widthHeight [0] = swapchainExtent.width; + widthHeight [1] = swapchainExtent.height; + } else { + widthHeight [0] = imageManager.getImageWidth(firstImage); + widthHeight [1] = imageManager.getImageHeight(firstImage); } - else { - width = m_ImageManager->getImageWidth(firstImage); - height = m_ImageManager->getImageHeight(firstImage); + } else { + widthHeight [0] = 1; + widthHeight [1] = 1; + } + // TODO: validate that width/height match for all attachments + return widthHeight; + } + + static vk::Framebuffer createFramebuffer(const std::vector<ImageHandle> &renderTargets, + const ImageManager &imageManager, + const vk::Extent2D &renderExtent, + const vk::RenderPass &renderpass, + const vk::Device &device) { + + std::vector<vk::ImageView> attachmentsViews; + for (const ImageHandle &handle : renderTargets) { + attachmentsViews.push_back(imageManager.getVulkanImageView(handle)); + } + + const vk::FramebufferCreateInfo createInfo( + {}, renderpass, static_cast<uint32_t>(attachmentsViews.size()), attachmentsViews.data(), + renderExtent.width, renderExtent.height, 1); + + return device.createFramebuffer(createInfo); + } + + void transitionRendertargetsToAttachmentLayout(const std::vector<ImageHandle> &renderTargets, + ImageManager &imageManager, + const vk::CommandBuffer cmdBuffer) { + + for (const ImageHandle &handle : renderTargets) { + const bool isDepthImage = isDepthFormat(imageManager.getImageFormat(handle)); + const vk::ImageLayout targetLayout = + isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : + vk::ImageLayout::eColorAttachmentOptimal; + imageManager.recordImageLayoutTransition(handle, 0, 0, targetLayout, cmdBuffer); + } + } + + std::vector<vk::ClearValue> + createAttachmentClearValues(const std::vector<AttachmentDescription> &attachments) { + std::vector<vk::ClearValue> clearValues; + for (const auto &attachment : attachments) { + if (attachment.getLoadOperation() == AttachmentOperation::CLEAR) { + clearValues.push_back(attachment.getClearValue()); } } - else { - width = 1; - height = 1; + return clearValues; + } + + void recordDynamicViewport(vk::CommandBuffer cmdBuffer, uint32_t width, uint32_t height) { + vk::Viewport dynamicViewport(0.0f, 0.0f, static_cast<float>(width), + static_cast<float>(height), 0.0f, 1.0f); + + vk::Rect2D dynamicScissor({ 0, 0 }, { width, height }); + + cmdBuffer.setViewport(0, 1, &dynamicViewport); + cmdBuffer.setScissor(0, 1, &dynamicScissor); + } + + vk::IndexType getIndexType(IndexBitCount indexByteCount) { + switch (indexByteCount) { + case IndexBitCount::Bit8: + return vk::IndexType::eUint8EXT; + case IndexBitCount::Bit16: + return vk::IndexType::eUint16; + case IndexBitCount::Bit32: + return vk::IndexType::eUint32; + default: + vkcv_log(LogLevel::ERROR, "unknown Enum"); + return vk::IndexType::eNoneKHR; + } + } + + static void recordDrawcall(const DescriptorSetManager &descriptorSetManager, + const BufferManager &bufferManager, const InstanceDrawcall &drawcall, + vk::CommandBuffer cmdBuffer, vk::PipelineLayout pipelineLayout, + const PushConstants &pushConstants, size_t drawcallIndex) { + + const auto &vertexData = drawcall.getVertexData(); + + for (uint32_t i = 0; i < vertexData.getVertexBufferBindings().size(); i++) { + const auto &vertexBinding = vertexData.getVertexBufferBindings() [i]; + + cmdBuffer.bindVertexBuffers(i, bufferManager.getBuffer(vertexBinding.buffer), + vertexBinding.offset); + } + + for (const auto &usage : drawcall.getDescriptorSetUsages()) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, pipelineLayout, usage.location, + descriptorSetManager.getDescriptorSet(usage.descriptorSet).vulkanHandle, + usage.dynamicOffsets); } - // TODO: validate that width/height match for all attachments - const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle); - const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle); + if (pushConstants.getSizePerDrawcall() > 0) { + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, + pushConstants.getSizePerDrawcall(), + pushConstants.getDrawcallData(drawcallIndex)); + } + + if (vertexData.getIndexBuffer()) { + cmdBuffer.bindIndexBuffer(bufferManager.getBuffer(vertexData.getIndexBuffer()), 0, + getIndexType(vertexData.getIndexBitCount())); + + cmdBuffer.drawIndexed(vertexData.getCount(), drawcall.getInstanceCount(), 0, 0, {}); + } else { + cmdBuffer.draw(vertexData.getCount(), drawcall.getInstanceCount(), 0, 0, {}); + } + } + + static void recordGraphicsPipeline(Core &core, CommandStreamManager &cmdStreamManager, + GraphicsPipelineManager &pipelineManager, + PassManager &passManager, ImageManager &imageManager, + const CommandStreamHandle &cmdStreamHandle, + const GraphicsPipelineHandle &pipelineHandle, + const PushConstants &pushConstants, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle, + const RecordCommandFunction &record) { + + const SwapchainHandle swapchainHandle = core.getWindow(windowHandle).getSwapchain(); + + const std::array<uint32_t, 2> extent = getWidthHeightFromRenderTargets( + renderTargets, core.getSwapchainExtent(swapchainHandle), imageManager); + + const auto width = extent [0]; + const auto height = extent [1]; + + const PassHandle &passHandle = pipelineManager.getPipelineConfig(pipelineHandle).getPass(); + + const vk::RenderPass renderPass = passManager.getVkPass(passHandle); + const PassConfig passConfig = passManager.getPassConfig(passHandle); + + const auto &attachments = passConfig.getAttachments(); + const auto &layouts = passManager.getLayouts(passHandle); - const vk::Pipeline pipeline = m_PipelineManager->getVkPipeline(pipelineHandle); - const vk::PipelineLayout pipelineLayout = m_PipelineManager->getVkPipelineLayout(pipelineHandle); + if (renderTargets.size() != layouts.size()) { + vkcv_log(LogLevel::ERROR, "Amount of render targets does not match specified pipeline"); + return; + } + + const vk::Pipeline pipeline = pipelineManager.getVkPipeline(pipelineHandle); const vk::Rect2D renderArea(vk::Offset2D(0, 0), vk::Extent2D(width, height)); - std::vector<vk::ImageView> attachmentsViews; - for (const ImageHandle handle : renderTargets) { - vk::ImageView targetHandle; - const auto cmdBuffer = m_CommandStreamManager->getStreamCommandBuffer(cmdStreamHandle); - - targetHandle = m_ImageManager->getVulkanImageView(handle); - const bool isDepthImage = isDepthFormat(m_ImageManager->getImageFormat(handle)); - const vk::ImageLayout targetLayout = - isDepthImage ? vk::ImageLayout::eDepthStencilAttachmentOptimal : vk::ImageLayout::eColorAttachmentOptimal; - m_ImageManager->recordImageLayoutTransition(handle, targetLayout, cmdBuffer); - attachmentsViews.push_back(targetHandle); - } - - const vk::FramebufferCreateInfo createInfo( - {}, - renderpass, - static_cast<uint32_t>(attachmentsViews.size()), - attachmentsViews.data(), - width, - height, - 1 - ); - - vk::Framebuffer framebuffer = m_Context.m_Device.createFramebuffer(createInfo); - - if (!framebuffer) { + vk::CommandBuffer cmdBuffer = cmdStreamManager.getStreamCommandBuffer(cmdStreamHandle); + transitionRendertargetsToAttachmentLayout(renderTargets, imageManager, cmdBuffer); + + for (size_t i = 0; i < layouts.size(); i++) { + imageManager.recordImageLayoutTransition(renderTargets [i], 0, 0, layouts [i], + cmdBuffer); + } + + const vk::Framebuffer framebuffer = + createFramebuffer(renderTargets, imageManager, renderArea.extent, renderPass, + core.getContext().getDevice()); + + if (!framebuffer) { vkcv_log(LogLevel::ERROR, "Failed to create temporary framebuffer"); - return; - } + return; + } - vk::Viewport dynamicViewport( - 0.0f, 0.0f, - static_cast<float>(width), static_cast<float>(height), - 0.0f, 1.0f - ); + auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) { + const std::vector<vk::ClearValue> clearValues = + createAttachmentClearValues(attachments); - vk::Rect2D dynamicScissor({0, 0}, {width, height}); + const vk::RenderPassBeginInfo beginInfo(renderPass, framebuffer, renderArea, + clearValues.size(), clearValues.data()); - auto &bufferManager = m_BufferManager; + cmdBuffer.beginRenderPass(beginInfo, {}, {}); + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Graphics; - submitInfo.signalSemaphores = { m_SyncResources.renderFinished }; + const GraphicsPipelineConfig &pipeConfig = + pipelineManager.getPipelineConfig(pipelineHandle); - auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { - std::vector<vk::ClearValue> clearValues; + if (pipeConfig.isViewportDynamic()) { + recordDynamicViewport(cmdBuffer, width, height); + } - for (const auto& attachment : passConfig.attachments) { - if (attachment.load_operation == AttachmentOperation::CLEAR) { - float clear = 0.0f; + if (record) { + record(cmdBuffer); + } - if (isDepthFormat(attachment.format)) { - clear = 1.0f; - } + cmdBuffer.endRenderPass(); + }; - clearValues.emplace_back(std::array<float, 4>{ - clear, - clear, - clear, - 1.f - }); - } - } + auto finishFunction = [framebuffer, &core]() { + core.getContext().getDevice().destroy(framebuffer); + }; - const vk::RenderPassBeginInfo beginInfo(renderpass, framebuffer, renderArea, clearValues.size(), clearValues.data()); - const vk::SubpassContents subpassContents = {}; - cmdBuffer.beginRenderPass(beginInfo, subpassContents, {}); + core.recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); + } - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline, {}); + void Core::recordDrawcallsToCmdStream(const CommandStreamHandle &cmdStreamHandle, + const GraphicsPipelineHandle &pipelineHandle, + const PushConstants &pushConstantData, + const std::vector<InstanceDrawcall> &drawcalls, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle) { - const PipelineConfig &pipeConfig = m_PipelineManager->getPipelineConfig(pipelineHandle); - if(pipeConfig.m_UseDynamicViewport) - { - cmdBuffer.setViewport(0, 1, &dynamicViewport); - cmdBuffer.setScissor(0, 1, &dynamicScissor); - } + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + return; + } - for (int i = 0; i < drawcalls.size(); i++) { - recordDrawcall(drawcalls[i], cmdBuffer, pipelineLayout, pushConstantData, i); - } + const vk::PipelineLayout pipelineLayout = + m_GraphicsPipelineManager->getVkPipelineLayout(pipelineHandle); - cmdBuffer.endRenderPass(); - }; + auto recordFunction = [&](const vk::CommandBuffer &cmdBuffer) { + for (size_t i = 0; i < drawcalls.size(); i++) { + recordDrawcall(*m_DescriptorSetManager, *m_BufferManager, drawcalls [i], cmdBuffer, + pipelineLayout, pushConstantData, i); + } + }; - auto finishFunction = [framebuffer, this]() - { - m_Context.m_Device.destroy(framebuffer); - }; + recordGraphicsPipeline(*this, *m_CommandStreamManager, *m_GraphicsPipelineManager, + *m_PassManager, *m_ImageManager, cmdStreamHandle, pipelineHandle, + pushConstantData, renderTargets, windowHandle, recordFunction); + } - recordCommandsToStream(cmdStreamHandle, submitFunction, finishFunction); + static void + recordIndirectDrawcall(const Core &core, const DescriptorSetManager &descriptorSetManager, + const BufferManager &bufferManager, vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, const PushConstants &pushConstantData, + size_t drawcallIndex, const IndirectDrawcall &drawcall) { + for (const auto &usage : drawcall.getDescriptorSetUsages()) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, pipelineLayout, usage.location, + descriptorSetManager.getDescriptorSet(usage.descriptorSet).vulkanHandle, + usage.dynamicOffsets); + } + + const auto &vertexData = drawcall.getVertexData(); + + for (uint32_t i = 0; i < vertexData.getVertexBufferBindings().size(); i++) { + const auto &vertexBinding = vertexData.getVertexBufferBindings() [i]; + + cmdBuffer.bindVertexBuffers(i, bufferManager.getBuffer(vertexBinding.buffer), + vertexBinding.offset); + } + + if (pushConstantData.getSizePerDrawcall() > 0) { + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, + pushConstantData.getSizePerDrawcall(), + pushConstantData.getDrawcallData(0)); + } + + if (vertexData.getIndexBuffer()) { + cmdBuffer.bindIndexBuffer(bufferManager.getBuffer(vertexData.getIndexBuffer()), 0, + getIndexType(vertexData.getIndexBitCount())); + + cmdBuffer.drawIndexedIndirect(bufferManager.getBuffer(drawcall.getIndirectDrawBuffer()), + 0, drawcall.getDrawCount(), + sizeof(vk::DrawIndexedIndirectCommand)); + } else { + cmdBuffer.drawIndirect(bufferManager.getBuffer(drawcall.getIndirectDrawBuffer()), 0, + drawcall.getDrawCount(), sizeof(vk::DrawIndirectCommand)); + } } - void Core::recordComputeDispatchToCmdStream( - CommandStreamHandle cmdStreamHandle, - PipelineHandle computePipeline, - const uint32_t dispatchCount[3], - const std::vector<DescriptorSetUsage>& descriptorSetUsages, - const PushConstantData& pushConstantData) { + void Core::recordIndirectDrawcallsToCmdStream( + const vkcv::CommandStreamHandle cmdStreamHandle, + const vkcv::GraphicsPipelineHandle &pipelineHandle, + const vkcv::PushConstants &pushConstantData, const std::vector<IndirectDrawcall> &drawcalls, + const std::vector<ImageHandle> &renderTargets, const vkcv::WindowHandle &windowHandle) { + + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + return; + } + + const vk::PipelineLayout pipelineLayout = + m_GraphicsPipelineManager->getVkPipelineLayout(pipelineHandle); + + auto recordFunction = [&](const vk::CommandBuffer &cmdBuffer) { + for (size_t i = 0; i < drawcalls.size(); i++) { + recordIndirectDrawcall(*this, *m_DescriptorSetManager, *m_BufferManager, cmdBuffer, + pipelineLayout, pushConstantData, i, drawcalls [i]); + } + }; + + recordGraphicsPipeline(*this, *m_CommandStreamManager, *m_GraphicsPipelineManager, + *m_PassManager, *m_ImageManager, cmdStreamHandle, pipelineHandle, + pushConstantData, renderTargets, windowHandle, recordFunction); + } + + static void recordMeshShaderDrawcall(const Core &core, + const DescriptorSetManager &descriptorSetManager, + vk::CommandBuffer cmdBuffer, + vk::PipelineLayout pipelineLayout, + const PushConstants &pushConstantData, + size_t drawcallIndex, const TaskDrawcall &drawcall) { + + static PFN_vkCmdDrawMeshTasksNV cmdDrawMeshTasks = + reinterpret_cast<PFN_vkCmdDrawMeshTasksNV>( + core.getContext().getDevice().getProcAddr("vkCmdDrawMeshTasksNV")); - auto submitFunction = [&](const vk::CommandBuffer& cmdBuffer) { + if (!cmdDrawMeshTasks) { + vkcv_log(LogLevel::ERROR, "Mesh shader drawcalls are not supported"); + return; + } + + for (const auto &descriptorUsage : drawcall.getDescriptorSetUsages()) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eGraphics, pipelineLayout, descriptorUsage.location, + descriptorSetManager.getDescriptorSet(descriptorUsage.descriptorSet).vulkanHandle, + descriptorUsage.dynamicOffsets); + } + + if (pushConstantData.getData()) { + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, + pushConstantData.getSizePerDrawcall(), + pushConstantData.getDrawcallData(drawcallIndex)); + } - const auto pipelineLayout = m_PipelineManager->getVkPipelineLayout(computePipeline); + cmdDrawMeshTasks(VkCommandBuffer(cmdBuffer), drawcall.getTaskCount(), 0); + } + + void Core::recordMeshShaderDrawcalls(const CommandStreamHandle &cmdStreamHandle, + const GraphicsPipelineHandle &pipelineHandle, + const PushConstants &pushConstantData, + const std::vector<TaskDrawcall> &drawcalls, + const std::vector<ImageHandle> &renderTargets, + const WindowHandle &windowHandle) { + + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + return; + } - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, m_PipelineManager->getVkPipeline(computePipeline)); - for (const auto& usage : descriptorSetUsages) { + const vk::PipelineLayout pipelineLayout = + m_GraphicsPipelineManager->getVkPipelineLayout(pipelineHandle); + + auto recordFunction = [&](const vk::CommandBuffer &cmdBuffer) { + for (size_t i = 0; i < drawcalls.size(); i++) { + recordMeshShaderDrawcall(*this, *m_DescriptorSetManager, cmdBuffer, pipelineLayout, + pushConstantData, i, drawcalls [i]); + } + }; + + recordGraphicsPipeline(*this, *m_CommandStreamManager, *m_GraphicsPipelineManager, + *m_PassManager, *m_ImageManager, cmdStreamHandle, pipelineHandle, + pushConstantData, renderTargets, windowHandle, recordFunction); + } + + void Core::recordRayGenerationToCmdStream( + CommandStreamHandle cmdStreamHandle, vk::Pipeline rtxPipeline, + vk::PipelineLayout rtxPipelineLayout, vk::StridedDeviceAddressRegionKHR rgenRegion, + vk::StridedDeviceAddressRegionKHR rmissRegion, + vk::StridedDeviceAddressRegionKHR rchitRegion, + vk::StridedDeviceAddressRegionKHR rcallRegion, + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstants &pushConstants, const WindowHandle &windowHandle) { + + auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) { + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eRayTracingKHR, rtxPipeline); + for (const auto &usage : descriptorSetUsages) { cmdBuffer.bindDescriptorSets( - vk::PipelineBindPoint::eCompute, - pipelineLayout, - usage.setLocation, - { usage.vulkanHandle }, - {}); + vk::PipelineBindPoint::eRayTracingKHR, rtxPipelineLayout, usage.location, + { m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle }, + usage.dynamicOffsets); } - if (pushConstantData.sizePerDrawcall > 0) { + + if (pushConstants.getSizePerDrawcall() > 0) { cmdBuffer.pushConstants( - pipelineLayout, - vk::ShaderStageFlagBits::eCompute, - 0, - pushConstantData.sizePerDrawcall, - pushConstantData.data); + rtxPipelineLayout, + (vk::ShaderStageFlagBits::eClosestHitKHR | vk::ShaderStageFlagBits::eMissKHR + | vk::ShaderStageFlagBits::eRaygenKHR), // TODO: add Support for eAnyHitKHR, + // eCallableKHR, eIntersectionKHR + 0, pushConstants.getSizePerDrawcall(), pushConstants.getData()); + } + + auto m_rtxDispatcher = vk::DispatchLoaderDynamic( + (PFN_vkGetInstanceProcAddr)m_Context.getInstance().getProcAddr( + "vkGetInstanceProcAddr")); + m_rtxDispatcher.init(m_Context.getInstance()); + + cmdBuffer.traceRaysKHR(&rgenRegion, &rmissRegion, &rchitRegion, &rcallRegion, + getWindow(windowHandle).getWidth(), + getWindow(windowHandle).getHeight(), 1, m_rtxDispatcher); + }; + recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr); + } + + void Core::recordComputeDispatchToCmdStream( + const CommandStreamHandle &cmdStreamHandle, const ComputePipelineHandle &computePipeline, + const DispatchSize &dispatchSize, + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstants &pushConstants) { + auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) { + const auto pipelineLayout = + m_ComputePipelineManager->getVkPipelineLayout(computePipeline); + + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, + m_ComputePipelineManager->getVkPipeline(computePipeline)); + for (const auto &usage : descriptorSetUsages) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eCompute, pipelineLayout, usage.location, + { m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle }, + usage.dynamicOffsets); + } + if (pushConstants.getSizePerDrawcall() > 0) { + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, + pushConstants.getSizePerDrawcall(), + pushConstants.getData()); } - cmdBuffer.dispatch(dispatchCount[0], dispatchCount[1], dispatchCount[2]); + + cmdBuffer.dispatch(dispatchSize.x(), dispatchSize.y(), dispatchSize.z()); }; recordCommandsToStream(cmdStreamHandle, submitFunction, nullptr); } - void Core::endFrame() { - if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + void Core::recordBeginDebugLabel(const CommandStreamHandle &cmdStream, const std::string &label, + const std::array<float, 4> &color) { +#ifdef VULKAN_DEBUG_LABELS + static PFN_vkCmdBeginDebugUtilsLabelEXT beginDebugLabel = + reinterpret_cast<PFN_vkCmdBeginDebugUtilsLabelEXT>( + m_Context.getDevice().getProcAddr("vkCmdBeginDebugUtilsLabelEXT")); + + if (!beginDebugLabel) { return; } - - const auto swapchainImages = m_Context.getDevice().getSwapchainImagesKHR(m_swapchain.getSwapchain()); - const auto& queueManager = m_Context.getQueueManager(); - std::array<vk::Semaphore, 2> waitSemaphores{ - m_SyncResources.renderFinished, - m_SyncResources.swapchainImageAcquired + auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) { + const vk::DebugUtilsLabelEXT debug(label.c_str(), color); + + beginDebugLabel(static_cast<VkCommandBuffer>(cmdBuffer), + &(static_cast<const VkDebugUtilsLabelEXT &>(debug))); }; - const vk::SwapchainKHR& swapchain = m_swapchain.getSwapchain(); - const vk::PresentInfoKHR presentInfo( - waitSemaphores, - swapchain, - m_currentSwapchainImageIndex - ); - + recordCommandsToStream(cmdStream, submitFunction, nullptr); +#endif + } + + void Core::recordEndDebugLabel(const CommandStreamHandle &cmdStream) { +#ifdef VULKAN_DEBUG_LABELS + static PFN_vkCmdEndDebugUtilsLabelEXT endDebugLabel = + reinterpret_cast<PFN_vkCmdEndDebugUtilsLabelEXT>( + m_Context.getDevice().getProcAddr("vkCmdEndDebugUtilsLabelEXT")); + + if (!endDebugLabel) { + return; + } + + auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) { + endDebugLabel(static_cast<VkCommandBuffer>(cmdBuffer)); + }; + + recordCommandsToStream(cmdStream, submitFunction, nullptr); +#endif + } + + void Core::recordComputeIndirectDispatchToCmdStream( + const CommandStreamHandle cmdStream, const ComputePipelineHandle computePipeline, + const vkcv::BufferHandle buffer, const size_t bufferArgOffset, + const std::vector<DescriptorSetUsage> &descriptorSetUsages, + const PushConstants &pushConstants) { + + auto submitFunction = [&](const vk::CommandBuffer &cmdBuffer) { + const auto pipelineLayout = + m_ComputePipelineManager->getVkPipelineLayout(computePipeline); + + cmdBuffer.bindPipeline(vk::PipelineBindPoint::eCompute, + m_ComputePipelineManager->getVkPipeline(computePipeline)); + for (const auto &usage : descriptorSetUsages) { + cmdBuffer.bindDescriptorSets( + vk::PipelineBindPoint::eCompute, pipelineLayout, usage.location, + { m_DescriptorSetManager->getDescriptorSet(usage.descriptorSet).vulkanHandle }, + usage.dynamicOffsets); + } + if (pushConstants.getSizePerDrawcall() > 0) { + cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, + pushConstants.getSizePerDrawcall(), + pushConstants.getData()); + } + cmdBuffer.dispatchIndirect(m_BufferManager->getBuffer(buffer), bufferArgOffset); + }; + + recordCommandsToStream(cmdStream, submitFunction, nullptr); + } + + void Core::endFrame(const WindowHandle &windowHandle) { + SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchain(); + + if (m_currentSwapchainImageIndex == std::numeric_limits<uint32_t>::max()) { + return; + } + + const std::array<vk::Semaphore, 2> waitSemaphores { m_RenderFinished, + m_SwapchainImageAcquired }; + + const vk::SwapchainKHR &swapchain = + m_SwapchainManager->getSwapchain(swapchainHandle).m_Swapchain; + const vk::PresentInfoKHR presentInfo(waitSemaphores, swapchain, + m_currentSwapchainImageIndex); + vk::Result result; - + try { - result = queueManager.getPresentQueue().handle.presentKHR(presentInfo); - } catch (vk::OutOfDateKHRError e) { + result = m_Context.getDevice() + .getQueue(m_SwapchainManager->getPresentQueueIndex(swapchainHandle), 0) + .presentKHR(presentInfo); + } catch (const vk::OutOfDateKHRError &e) { result = vk::Result::eErrorOutOfDateKHR; + } catch (const vk::DeviceLostError &e) { + result = vk::Result::eErrorDeviceLost; } - - if (result != vk::Result::eSuccess) { - vkcv_log(LogLevel::ERROR, "Swapchain present failed (%s)", vk::to_string(result).c_str()); - } - } - - void Core::recordAndSubmitCommandsImmediate( - const SubmitInfo &submitInfo, - const RecordCommandFunction &record, - const FinishCommandFunction &finish) - { - const vk::Device& device = m_Context.getDevice(); - - const vkcv::Queue queue = getQueueForSubmit(submitInfo.queueType, m_Context.getQueueManager()); - const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources); - const vk::CommandBuffer cmdBuffer = allocateCommandBuffer(device, cmdPool); - - beginCommandBuffer(cmdBuffer, vk::CommandBufferUsageFlagBits::eOneTimeSubmit); - record(cmdBuffer); - cmdBuffer.end(); - - vk::Fence waitFence = createFence(device); - - submitCommandBufferToQueue(queue.handle, cmdBuffer, waitFence, submitInfo.waitSemaphores, submitInfo.signalSemaphores); - waitForFence(device, waitFence); - - device.destroyFence(waitFence); - - device.freeCommandBuffers(cmdPool, cmdBuffer); - - if (finish) { - finish(); + + if ((result != vk::Result::eSuccess) && (result != vk::Result::eSuboptimalKHR)) { + vkcv_log(LogLevel::ERROR, "Swapchain presentation failed (%s)", + vk::to_string(result).c_str()); + } else if (result == vk::Result::eSuboptimalKHR) { + vkcv_log(LogLevel::WARNING, "Swapchain presentation is suboptimal"); + m_SwapchainManager->signalRecreation(swapchainHandle); } } - CommandStreamHandle Core::createCommandStream(QueueType queueType) { + /** + * @brief Returns a queue of a given type from a queue manager. + * + * @param[in] type Type of queue + * @param[in] queueManager Queue manager + * @return Queue of a given type + */ + static Queue getQueueForSubmit(QueueType type, const QueueManager &queueManager) { + switch (type) { + case QueueType::Graphics: + return queueManager.getGraphicsQueues().front(); + case QueueType::Compute: + return queueManager.getComputeQueues().front(); + case QueueType::Transfer: + return queueManager.getTransferQueues().front(); + case QueueType::Present: + return queueManager.getPresentQueue(); + default: { + vkcv_log(LogLevel::ERROR, "Unknown queue type"); + return queueManager.getGraphicsQueues().front(); // graphics is the most general queue + } + } + } - const vk::Device& device = m_Context.getDevice(); - const vkcv::Queue queue = getQueueForSubmit(queueType, m_Context.getQueueManager()); - const vk::CommandPool cmdPool = chooseCmdPool(queue, m_CommandResources); + CommandStreamHandle Core::createCommandStream(QueueType queueType) { + const vkcv::Queue queue = getQueueForSubmit(queueType, m_Context.getQueueManager()); + const vk::CommandPool cmdPool = m_CommandPools [queue.familyIndex]; return m_CommandStreamManager->createCommandStream(queue.handle, cmdPool); } - void Core::recordCommandsToStream( - const CommandStreamHandle cmdStreamHandle, - const RecordCommandFunction &record, - const FinishCommandFunction &finish) { + void Core::recordCommandsToStream(const CommandStreamHandle &stream, + const RecordCommandFunction &record, + const FinishCommandFunction &finish) { + if (record) { + m_CommandStreamManager->recordCommandsToStream(stream, record); + } - m_CommandStreamManager->recordCommandsToStream(cmdStreamHandle, record); if (finish) { - m_CommandStreamManager->addFinishCallbackToStream(cmdStreamHandle, finish); + m_CommandStreamManager->addFinishCallbackToStream(stream, finish); } } - void Core::submitCommandStream(const CommandStreamHandle handle) { + void Core::submitCommandStream(const CommandStreamHandle &stream, bool signalRendering) { std::vector<vk::Semaphore> waitSemaphores; + // FIXME: add proper user controllable sync - std::vector<vk::Semaphore> signalSemaphores = { m_SyncResources.renderFinished }; - m_CommandStreamManager->submitCommandStreamSynchronous(handle, waitSemaphores, signalSemaphores); + std::vector<vk::Semaphore> signalSemaphores; + if (signalRendering) { + signalSemaphores.push_back(m_RenderFinished); + } + + m_CommandStreamManager->submitCommandStreamSynchronous(stream, waitSemaphores, + signalSemaphores); } SamplerHandle Core::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, - SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode) { - return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode); + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode, + float mipLodBias, SamplerBorderColor borderColor) { + return m_SamplerManager->createSampler(magFilter, minFilter, mipmapMode, addressMode, + mipLodBias, borderColor); } - Image Core::createImage( - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth, - bool createMipChain, - bool supportStorage, - bool supportColorAttachment) - { - + ImageHandle Core::createImage(vk::Format format, + const ImageConfig& config, + bool createMipChain) { uint32_t mipCount = 1; if (createMipChain) { - mipCount = 1 + (uint32_t)std::floor(std::log2(std::max(width, std::max(height, depth)))); + mipCount = 1 + (uint32_t) std::floor( + std::log2(std::max( + config.getWidth(), + std::max(config.getHeight(), config.getDepth())) + ) + ); } - return Image::create( - m_ImageManager.get(), - format, - width, - height, - depth, - mipCount, - supportStorage, - supportColorAttachment); + return m_ImageManager->createImage( + format, + mipCount, + config + ); + } + + void Core::fillImage(const ImageHandle &image, + const void* data, + size_t size, + uint32_t firstLayer, + uint32_t layerCount) { + m_ImageManager->fillImage(image, data, size, firstLayer, layerCount); + } + + void Core::switchImageLayout(const ImageHandle &image, vk::ImageLayout layout) { + m_ImageManager->switchImageLayoutImmediate(image, layout); + } + + Downsampler &Core::getDownsampler() { + return *m_downsampler; + } + + WindowHandle Core::createWindow(const std::string &applicationName, uint32_t windowWidth, + uint32_t windowHeight, bool resizeable) { + WindowHandle windowHandle = m_WindowManager->createWindow( + *m_SwapchainManager, applicationName, windowWidth, windowHeight, resizeable); + + SwapchainHandle swapchainHandle = m_WindowManager->getWindow(windowHandle).getSwapchain(); + setSwapchainImages(swapchainHandle); + return windowHandle; + } + + Window &Core::getWindow(const WindowHandle &handle) { + return m_WindowManager->getWindow(handle); + } + + vk::Format Core::getSwapchainFormat(const SwapchainHandle &swapchain) const { + return m_SwapchainManager->getFormat(swapchain); + } + + uint32_t Core::getSwapchainImageCount(const SwapchainHandle &swapchain) const { + return m_SwapchainManager->getImageCount(swapchain); + } + + vk::Extent2D Core::getSwapchainExtent(const SwapchainHandle &swapchain) const { + return m_SwapchainManager->getExtent(swapchain); + } + + uint32_t Core::getImageWidth(const ImageHandle &image) { + return m_ImageManager->getImageWidth(image); + } + + uint32_t Core::getImageHeight(const ImageHandle &image) { + return m_ImageManager->getImageHeight(image); + } + + uint32_t Core::getImageDepth(const ImageHandle &image) { + return m_ImageManager->getImageDepth(image); + } + + vk::Format Core::getImageFormat(const ImageHandle &image) { + return m_ImageManager->getImageFormat(image); + } + + bool Core::isImageSupportingStorage(const ImageHandle &image) { + return m_ImageManager->isImageSupportingStorage(image); } - DescriptorSetHandle Core::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) - { - return m_DescriptorManager->createDescriptorSet(bindings); - } + uint32_t Core::getImageMipLevels(const ImageHandle &image) { + return m_ImageManager->getImageMipCount(image); + } + + uint32_t Core::getImageArrayLayers(const ImageHandle &image) { + return m_ImageManager->getImageArrayLayers(image); + } + + DescriptorSetLayoutHandle Core::createDescriptorSetLayout(const DescriptorBindings &bindings) { + return m_DescriptorSetLayoutManager->createDescriptorSetLayout(bindings); + } + + DescriptorSetHandle Core::createDescriptorSet(const DescriptorSetLayoutHandle &layout) { + return m_DescriptorSetManager->createDescriptorSet(layout); + } void Core::writeDescriptorSet(DescriptorSetHandle handle, const DescriptorWrites &writes) { - m_DescriptorManager->writeDescriptorSet( - handle, - writes, - *m_ImageManager, - *m_BufferManager, - *m_SamplerManager); - } - - DescriptorSet Core::getDescriptorSet(const DescriptorSetHandle handle) const { - return m_DescriptorManager->getDescriptorSet(handle); - } - - std::vector<vk::ImageView> Core::createSwapchainImageViews( Context &context, Swapchain& swapChain){ - std::vector<vk::ImageView> imageViews; - std::vector<vk::Image> swapChainImages = context.getDevice().getSwapchainImagesKHR(swapChain.getSwapchain()); - imageViews.reserve( swapChainImages.size() ); - //here can be swizzled with vk::ComponentSwizzle if needed - vk::ComponentMapping componentMapping( - vk::ComponentSwizzle::eR, - vk::ComponentSwizzle::eG, - vk::ComponentSwizzle::eB, - vk::ComponentSwizzle::eA ); - - vk::ImageSubresourceRange subResourceRange( vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1 ); - - for ( auto image : swapChainImages ) - { - vk::ImageViewCreateInfo imageViewCreateInfo( - vk::ImageViewCreateFlags(), - image, - vk::ImageViewType::e2D, - swapChain.getFormat(), - componentMapping, - subResourceRange); - - imageViews.push_back(context.getDevice().createImageView(imageViewCreateInfo)); - } - return imageViews; - } - - void Core::prepareSwapchainImageForPresent(const CommandStreamHandle cmdStream) { + m_DescriptorSetManager->writeDescriptorSet(handle, writes, *m_ImageManager, + *m_BufferManager, *m_SamplerManager); + } + + void Core::prepareSwapchainImageForPresent(const CommandStreamHandle &cmdStream) { auto swapchainHandle = ImageHandle::createSwapchainImageHandle(); - recordCommandsToStream(cmdStream, [swapchainHandle, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageLayoutTransition(swapchainHandle, vk::ImageLayout::ePresentSrcKHR, cmdBuffer); - }, nullptr); + recordCommandsToStream( + cmdStream, + [swapchainHandle, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition( + swapchainHandle, 0, 0, vk::ImageLayout::ePresentSrcKHR, cmdBuffer); + }, + nullptr); + } + + void Core::prepareImageForSampling(const CommandStreamHandle &cmdStream, + const ImageHandle &image, uint32_t mipLevelCount, + uint32_t mipLevelOffset) { + recordCommandsToStream( + cmdStream, + [image, mipLevelCount, mipLevelOffset, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition(image, mipLevelCount, mipLevelOffset, + vk::ImageLayout::eShaderReadOnlyOptimal, + cmdBuffer); + }, + nullptr); + } + + void Core::prepareImageForStorage(const CommandStreamHandle &cmdStream, + const ImageHandle &image, uint32_t mipLevelCount, + uint32_t mipLevelOffset) { + recordCommandsToStream( + cmdStream, + [image, mipLevelCount, mipLevelOffset, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition(image, mipLevelCount, mipLevelOffset, + vk::ImageLayout::eGeneral, cmdBuffer); + }, + nullptr); + } + + void Core::prepareImageForAttachmentManually(const vk::CommandBuffer &cmdBuffer, + const ImageHandle &image) { + transitionRendertargetsToAttachmentLayout({ image }, *m_ImageManager, cmdBuffer); } - void Core::prepareImageForSampling(const CommandStreamHandle cmdStream, const ImageHandle image) { - recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eShaderReadOnlyOptimal, cmdBuffer); - }, nullptr); + void Core::updateImageLayoutManual(const vkcv::ImageHandle &image, + const vk::ImageLayout layout) { + m_ImageManager->updateImageLayoutManual(image, layout); } - void Core::prepareImageForStorage(const CommandStreamHandle cmdStream, const ImageHandle image) { - recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageLayoutTransition(image, vk::ImageLayout::eGeneral, cmdBuffer); - }, nullptr); + void Core::recordImageMemoryBarrier(const CommandStreamHandle &cmdStream, + const ImageHandle &image) { + recordCommandsToStream( + cmdStream, + [image, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer); + }, + nullptr); } - void Core::recordImageMemoryBarrier(const CommandStreamHandle cmdStream, const ImageHandle image) { - recordCommandsToStream(cmdStream, [image, this](const vk::CommandBuffer cmdBuffer) { - m_ImageManager->recordImageMemoryBarrier(image, cmdBuffer); - }, nullptr); + void Core::recordBufferMemoryBarrier(const CommandStreamHandle &cmdStream, + const BufferHandle &buffer) { + recordCommandsToStream( + cmdStream, + [buffer, this](const vk::CommandBuffer cmdBuffer) { + m_BufferManager->recordBufferMemoryBarrier(buffer, cmdBuffer); + }, + nullptr); } - void Core::recordBufferMemoryBarrier(const CommandStreamHandle cmdStream, const BufferHandle buffer) { - recordCommandsToStream(cmdStream, [buffer, this](const vk::CommandBuffer cmdBuffer) { - m_BufferManager->recordBufferMemoryBarrier(buffer, cmdBuffer); - }, nullptr); + void Core::resolveMSAAImage(const CommandStreamHandle &cmdStream, const ImageHandle &src, + const ImageHandle &dst) { + recordCommandsToStream( + cmdStream, + [src, dst, this](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordMSAAResolve(cmdBuffer, src, dst); + }, + nullptr); } - + vk::ImageView Core::getSwapchainImageView() const { - return m_ImageManager->getVulkanImageView(vkcv::ImageHandle::createSwapchainImageHandle()); - } - -} + return m_ImageManager->getVulkanImageView(vkcv::ImageHandle::createSwapchainImageHandle()); + } + + void Core::recordMemoryBarrier(const CommandStreamHandle &cmdStream) { + recordCommandsToStream( + cmdStream, + [](const vk::CommandBuffer cmdBuffer) { + vk::MemoryBarrier barrier( + vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead, + vk::AccessFlagBits::eMemoryWrite | vk::AccessFlagBits::eMemoryRead); + + cmdBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, + vk::DependencyFlags(), 1, &barrier, 0, nullptr, 0, + nullptr); + }, + nullptr); + } + + void Core::recordBlitImage(const CommandStreamHandle &cmdStream, const ImageHandle &src, + const ImageHandle &dst, SamplerFilterType filterType) { + recordCommandsToStream( + cmdStream, + [&](const vk::CommandBuffer cmdBuffer) { + m_ImageManager->recordImageLayoutTransition( + src, 0, 0, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer); + + m_ImageManager->recordImageLayoutTransition( + dst, 0, 0, vk::ImageLayout::eTransferDstOptimal, cmdBuffer); + + const std::array<vk::Offset3D, 2> srcOffsets = { + vk::Offset3D(0, 0, 0), vk::Offset3D(m_ImageManager->getImageWidth(src), + m_ImageManager->getImageHeight(src), 1) + }; + + const std::array<vk::Offset3D, 2> dstOffsets = { + vk::Offset3D(0, 0, 0), vk::Offset3D(m_ImageManager->getImageWidth(dst), + m_ImageManager->getImageHeight(dst), 1) + }; + + const bool srcDepth = isDepthFormat(m_ImageManager->getImageFormat(src)); + const bool dstDepth = isDepthFormat(m_ImageManager->getImageFormat(dst)); + + const vk::ImageBlit blit = vk::ImageBlit( + vk::ImageSubresourceLayers(srcDepth ? vk::ImageAspectFlagBits::eDepth : + vk::ImageAspectFlagBits::eColor, + 0, 0, 1), + srcOffsets, + vk::ImageSubresourceLayers(dstDepth ? vk::ImageAspectFlagBits::eDepth : + vk::ImageAspectFlagBits::eColor, + 0, 0, 1), + dstOffsets); + + cmdBuffer.blitImage(m_ImageManager->getVulkanImage(src), + vk::ImageLayout::eTransferSrcOptimal, + m_ImageManager->getVulkanImage(dst), + vk::ImageLayout::eTransferDstOptimal, 1, &blit, + filterType == SamplerFilterType::LINEAR ? vk::Filter::eLinear : + vk::Filter::eNearest); + }, + nullptr); + } + + void Core::setSwapchainImages(SwapchainHandle handle) { + const auto &swapchain = m_SwapchainManager->getSwapchain(handle); + const auto swapchainImages = m_SwapchainManager->getSwapchainImages(handle); + const auto swapchainImageViews = m_SwapchainManager->createSwapchainImageViews(handle); + + m_ImageManager->setSwapchainImages(swapchainImages, swapchainImageViews, + swapchain.m_Extent.width, swapchain.m_Extent.height, + swapchain.m_Format); + } + + static void setDebugObjectLabel(const vk::Device &device, const vk::ObjectType &type, + uint64_t handle, const std::string &label) { +#ifdef VULKAN_DEBUG_LABELS + static PFN_vkSetDebugUtilsObjectNameEXT setDebugLabel = + reinterpret_cast<PFN_vkSetDebugUtilsObjectNameEXT>( + device.getProcAddr("vkSetDebugUtilsObjectNameEXT")); + + if (!setDebugLabel) { + return; + } + + const vk::DebugUtilsObjectNameInfoEXT debug(type, handle, label.c_str()); + + setDebugLabel(static_cast<VkDevice>(device), + &(static_cast<const VkDebugUtilsObjectNameInfoEXT &>(debug))); +#endif + } + + void Core::setDebugLabel(const BufferHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel(m_Context.getDevice(), vk::ObjectType::eBuffer, + uint64_t(static_cast<VkBuffer>(m_BufferManager->getBuffer(handle))), + label); + } + + void Core::setDebugLabel(const PassHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel(m_Context.getDevice(), vk::ObjectType::eRenderPass, + uint64_t(static_cast<VkRenderPass>(m_PassManager->getVkPass(handle))), + label); + } + + void Core::setDebugLabel(const GraphicsPipelineHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel( + m_Context.getDevice(), vk::ObjectType::ePipeline, + uint64_t(static_cast<VkPipeline>(m_GraphicsPipelineManager->getVkPipeline(handle))), + label); + } + + void Core::setDebugLabel(const ComputePipelineHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel( + m_Context.getDevice(), vk::ObjectType::ePipeline, + uint64_t(static_cast<VkPipeline>(m_ComputePipelineManager->getVkPipeline(handle))), + label); + } + + void Core::setDebugLabel(const DescriptorSetHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel(m_Context.getDevice(), vk::ObjectType::eDescriptorSet, + uint64_t(static_cast<VkDescriptorSet>( + m_DescriptorSetManager->getDescriptorSet(handle).vulkanHandle)), + label); + } + + void Core::setDebugLabel(const SamplerHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel( + m_Context.getDevice(), vk::ObjectType::eSampler, + uint64_t(static_cast<VkSampler>(m_SamplerManager->getVulkanSampler(handle))), label); + } + + void Core::setDebugLabel(const ImageHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } else if (handle.isSwapchainImage()) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to swapchain image"); + return; + } + + setDebugObjectLabel(m_Context.getDevice(), vk::ObjectType::eImage, + uint64_t(static_cast<VkImage>(m_ImageManager->getVulkanImage(handle))), + label); + } + + void Core::setDebugLabel(const CommandStreamHandle &handle, const std::string &label) { + if (!handle) { + vkcv_log(LogLevel::WARNING, "Can't set debug label to invalid handle"); + return; + } + + setDebugObjectLabel(m_Context.getDevice(), vk::ObjectType::eCommandBuffer, + uint64_t(static_cast<VkCommandBuffer>( + m_CommandStreamManager->getStreamCommandBuffer(handle))), + label); + } + + void Core::run(const vkcv::WindowFrameFunction &frame) { + auto start = std::chrono::system_clock::now(); + double t = 0.0; + + if (!frame) + return; + + while (Window::hasOpenWindow()) { + vkcv::Window::pollEvents(); + + auto end = std::chrono::system_clock::now(); + auto deltatime = std::chrono::duration_cast<std::chrono::microseconds>(end - start); + start = end; + + double dt = 0.000001 * static_cast<double>(deltatime.count()); + + for (const auto &window : m_WindowManager->getWindowHandles()) { + uint32_t swapchainWidth, swapchainHeight; + if (!beginFrame(swapchainWidth, swapchainHeight, window)) { + continue; + } + + frame(window, t, dt, swapchainWidth, swapchainHeight); + endFrame(window); + } + + t += dt; + } + } + + vk::RenderPass Core::getVulkanRenderPass(const PassHandle &handle) const { + return m_PassManager->getVkPass(handle); + } + + vk::Pipeline Core::getVulkanPipeline(const GraphicsPipelineHandle &handle) const { + return m_GraphicsPipelineManager->getVkPipeline(handle); + } + + vk::Pipeline Core::getVulkanPipeline(const ComputePipelineHandle &handle) const { + return m_ComputePipelineManager->getVkPipeline(handle); + } + + vk::DescriptorSetLayout + Core::getVulkanDescriptorSetLayout(const DescriptorSetLayoutHandle &handle) const { + return m_DescriptorSetLayoutManager->getDescriptorSetLayout(handle).vulkanHandle; + } + + vk::DescriptorSet Core::getVulkanDescriptorSet(const DescriptorSetHandle &handle) const { + return m_DescriptorSetManager->getDescriptorSet(handle).vulkanHandle; + } + + vk::Buffer Core::getVulkanBuffer(const BufferHandle &handle) const { + return m_BufferManager->getBuffer(handle); + } + + vk::Sampler Core::getVulkanSampler(const SamplerHandle &handle) const { + return m_SamplerManager->getVulkanSampler(handle); + } + + vk::Image Core::getVulkanImage(const ImageHandle &handle) const { + return m_ImageManager->getVulkanImage(handle); + } + + vk::ImageView Core::getVulkanImageView(const vkcv::ImageHandle &handle) const { + return m_ImageManager->getVulkanImageView(handle); + } + + vk::DeviceMemory Core::getVulkanDeviceMemory(const BufferHandle &handle) const { + return m_BufferManager->getDeviceMemory(handle); + } + + vk::DeviceMemory Core::getVulkanDeviceMemory(const ImageHandle &handle) const { + return m_ImageManager->getVulkanDeviceMemory(handle); + } + +} // namespace vkcv diff --git a/src/vkcv/DescriptorBinding.cpp b/src/vkcv/DescriptorBinding.cpp new file mode 100644 index 0000000000000000000000000000000000000000..251fe45c80e66ac04745621bba79feed8a14eb97 --- /dev/null +++ b/src/vkcv/DescriptorBinding.cpp @@ -0,0 +1,13 @@ +#include "vkcv/DescriptorBinding.hpp" + +namespace vkcv { + + bool DescriptorBinding::operator==(const DescriptorBinding &other) const { + return (this->bindingID == other.bindingID) + && (this->descriptorType == other.descriptorType) + && (this->descriptorCount == other.descriptorCount) + && (this->shaderStages == other.shaderStages) + && (this->variableCount == other.variableCount); + } + +} // namespace vkcv diff --git a/src/vkcv/DescriptorConfig.cpp b/src/vkcv/DescriptorConfig.cpp deleted file mode 100644 index 54e879ac7e6ec7825a4c003899e3c264454c547f..0000000000000000000000000000000000000000 --- a/src/vkcv/DescriptorConfig.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "vkcv/DescriptorConfig.hpp" - -namespace vkcv { - DescriptorBinding::DescriptorBinding( - uint32_t bindingID, - DescriptorType descriptorType, - uint32_t descriptorCount, - ShaderStage shaderStage) noexcept - : - bindingID(bindingID), - descriptorType(descriptorType), - descriptorCount(descriptorCount), - shaderStage(shaderStage) {} - -} diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp deleted file mode 100644 index 8e565a766cd407dc33c0291d3d07b01d6d3066e7..0000000000000000000000000000000000000000 --- a/src/vkcv/DescriptorManager.cpp +++ /dev/null @@ -1,294 +0,0 @@ -#include "DescriptorManager.hpp" - -#include "vkcv/Logger.hpp" - -namespace vkcv -{ - DescriptorManager::DescriptorManager(vk::Device device) noexcept: - m_Device{ device } - { - /** - * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type below. - * Finally, create an initial pool. - */ - m_PoolSizes = { vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), - vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), - vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), - vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000) }; - - m_PoolInfo = vk::DescriptorPoolCreateInfo({}, - 1000, - static_cast<uint32_t>(m_PoolSizes.size()), - m_PoolSizes.data()); - - allocateDescriptorPool(); - } - - DescriptorManager::~DescriptorManager() noexcept - { - for (uint64_t id = 0; id < m_DescriptorSets.size(); id++) { - destroyDescriptorSetById(id); - } - m_DescriptorSets.clear(); - for (const auto &pool : m_Pools) { - m_Device.destroy(pool); - } - } - - DescriptorSetHandle DescriptorManager::createDescriptorSet(const std::vector<DescriptorBinding>& bindings) - { - std::vector<vk::DescriptorSetLayoutBinding> setBindings = {}; - - //create each set's binding - for (uint32_t i = 0; i < bindings.size(); i++) { - vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding( - bindings[i].bindingID, - convertDescriptorTypeFlag(bindings[i].descriptorType), - bindings[i].descriptorCount, - convertShaderStageFlag(bindings[i].shaderStage)); - setBindings.push_back(descriptorSetLayoutBinding); - } - - DescriptorSet set; - - //create the descriptor set's layout from the bindings gathered above - vk::DescriptorSetLayoutCreateInfo layoutInfo({}, setBindings); - if(m_Device.createDescriptorSetLayout(&layoutInfo, nullptr, &set.layout) != vk::Result::eSuccess) - { - vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout"); - return DescriptorSetHandle(); - }; - - //create and allocate the set based on the layout that have been gathered above - vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &set.layout); - auto result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle); - if(result != vk::Result::eSuccess) - { - //create a new descriptor pool if the previous one ran out of memory - if (result == vk::Result::eErrorOutOfPoolMemory) { - allocateDescriptorPool(); - allocInfo.setDescriptorPool(m_Pools.back()); - result = m_Device.allocateDescriptorSets(&allocInfo, &set.vulkanHandle); - } - if (result != vk::Result::eSuccess) { - vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)", - vk::to_string(result).c_str()); - - m_Device.destroy(set.layout); - return DescriptorSetHandle(); - } - }; - - const uint64_t id = m_DescriptorSets.size(); - - m_DescriptorSets.push_back(set); - return DescriptorSetHandle(id, [&](uint64_t id) { destroyDescriptorSetById(id); }); - } - - struct WriteDescriptorSetInfo { - size_t imageInfoIndex; - size_t bufferInfoIndex; - uint32_t binding; - vk::DescriptorType type; - }; - - void DescriptorManager::writeDescriptorSet( - const DescriptorSetHandle &handle, - const DescriptorWrites &writes, - const ImageManager &imageManager, - const BufferManager &bufferManager, - const SamplerManager &samplerManager) { - - vk::DescriptorSet set = m_DescriptorSets[handle.getId()].vulkanHandle; - - std::vector<vk::DescriptorImageInfo> imageInfos; - std::vector<vk::DescriptorBufferInfo> bufferInfos; - - std::vector<WriteDescriptorSetInfo> writeInfos; - - for (const auto& write : writes.sampledImageWrites) { - vk::ImageLayout layout = write.useGeneralLayout ? vk::ImageLayout::eGeneral : vk::ImageLayout::eShaderReadOnlyOptimal; - const vk::DescriptorImageInfo imageInfo( - nullptr, - imageManager.getVulkanImageView(write.image, write.mipLevel), - layout - ); - - imageInfos.push_back(imageInfo); - - WriteDescriptorSetInfo vulkanWrite = { - imageInfos.size(), - 0, - write.binding, - vk::DescriptorType::eSampledImage, - }; - - writeInfos.push_back(vulkanWrite); - } - - for (const auto& write : writes.storageImageWrites) { - const vk::DescriptorImageInfo imageInfo( - nullptr, - imageManager.getVulkanImageView(write.image, write.mipLevel), - vk::ImageLayout::eGeneral - ); - - imageInfos.push_back(imageInfo); - - WriteDescriptorSetInfo vulkanWrite = { - imageInfos.size(), - 0, - write.binding, - vk::DescriptorType::eStorageImage - }; - - writeInfos.push_back(vulkanWrite); - } - - for (const auto& write : writes.uniformBufferWrites) { - const vk::DescriptorBufferInfo bufferInfo( - bufferManager.getBuffer(write.buffer), - static_cast<uint32_t>(0), - bufferManager.getBufferSize(write.buffer) - ); - - bufferInfos.push_back(bufferInfo); - - WriteDescriptorSetInfo vulkanWrite = { - 0, - bufferInfos.size(), - write.binding, - vk::DescriptorType::eUniformBuffer - }; - - writeInfos.push_back(vulkanWrite); - } - - for (const auto& write : writes.storageBufferWrites) { - const vk::DescriptorBufferInfo bufferInfo( - bufferManager.getBuffer(write.buffer), - static_cast<uint32_t>(0), - bufferManager.getBufferSize(write.buffer) - ); - - bufferInfos.push_back(bufferInfo); - - WriteDescriptorSetInfo vulkanWrite = { - 0, - bufferInfos.size(), - write.binding, - vk::DescriptorType::eStorageBuffer - }; - - writeInfos.push_back(vulkanWrite); - } - - for (const auto& write : writes.samplerWrites) { - const vk::Sampler& sampler = samplerManager.getVulkanSampler(write.sampler); - - const vk::DescriptorImageInfo imageInfo( - sampler, - nullptr, - vk::ImageLayout::eGeneral - ); - - imageInfos.push_back(imageInfo); - - WriteDescriptorSetInfo vulkanWrite = { - imageInfos.size(), - 0, - write.binding, - vk::DescriptorType::eSampler - }; - - writeInfos.push_back(vulkanWrite); - } - - std::vector<vk::WriteDescriptorSet> vulkanWrites; - - for (const auto& write : writeInfos) { - vk::WriteDescriptorSet vulkanWrite( - set, - write.binding, - static_cast<uint32_t>(0), - 1, - write.type, - (write.imageInfoIndex > 0? &(imageInfos[write.imageInfoIndex - 1]) : nullptr), - (write.bufferInfoIndex > 0? &(bufferInfos[write.bufferInfoIndex - 1]) : nullptr) - ); - - vulkanWrites.push_back(vulkanWrite); - } - - m_Device.updateDescriptorSets(vulkanWrites, nullptr); - } - - DescriptorSet DescriptorManager::getDescriptorSet(const DescriptorSetHandle handle) const { - return m_DescriptorSets[handle.getId()]; - } - - vk::DescriptorType DescriptorManager::convertDescriptorTypeFlag(DescriptorType type) { - switch (type) - { - case DescriptorType::UNIFORM_BUFFER: - return vk::DescriptorType::eUniformBuffer; - case DescriptorType::STORAGE_BUFFER: - return vk::DescriptorType::eStorageBuffer; - case DescriptorType::SAMPLER: - return vk::DescriptorType::eSampler; - case DescriptorType::IMAGE_SAMPLED: - return vk::DescriptorType::eSampledImage; - case DescriptorType::IMAGE_STORAGE: - return vk::DescriptorType::eStorageImage; - default: - vkcv_log(LogLevel::ERROR, "Unknown DescriptorType"); - return vk::DescriptorType::eUniformBuffer; - } - } - - vk::ShaderStageFlagBits DescriptorManager::convertShaderStageFlag(ShaderStage stage) { - switch (stage) - { - case ShaderStage::VERTEX: - return vk::ShaderStageFlagBits::eVertex; - case ShaderStage::FRAGMENT: - return vk::ShaderStageFlagBits::eFragment; - case ShaderStage::TESS_CONTROL: - return vk::ShaderStageFlagBits::eTessellationControl; - case ShaderStage::TESS_EVAL: - return vk::ShaderStageFlagBits::eTessellationEvaluation; - case ShaderStage::GEOMETRY: - return vk::ShaderStageFlagBits::eGeometry; - case ShaderStage::COMPUTE: - return vk::ShaderStageFlagBits::eCompute; - default: - return vk::ShaderStageFlagBits::eAll; - } - } - - void DescriptorManager::destroyDescriptorSetById(uint64_t id) { - if (id >= m_DescriptorSets.size()) { - vkcv_log(LogLevel::ERROR, "Invalid id"); - return; - } - - auto& set = m_DescriptorSets[id]; - if (set.layout) { - m_Device.destroyDescriptorSetLayout(set.layout); - set.layout = nullptr; - } - // FIXME: descriptor set itself not destroyed - } - - vk::DescriptorPool DescriptorManager::allocateDescriptorPool() { - vk::DescriptorPool pool; - if (m_Device.createDescriptorPool(&m_PoolInfo, nullptr, &pool) != vk::Result::eSuccess) - { - vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool"); - pool = nullptr; - }; - m_Pools.push_back(pool); - return pool; - } - -} \ No newline at end of file diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp deleted file mode 100644 index d18be64f3b069af68cecce68f6fa623c81f8dfa4..0000000000000000000000000000000000000000 --- a/src/vkcv/DescriptorManager.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/** - * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann - * @file src/vkcv/DescriptorManager.cpp - * @brief Creation and handling of descriptor sets and the respective descriptor pools - */ -#include <vulkan/vulkan.hpp> - -#include "vkcv/Handles.hpp" -#include "vkcv/DescriptorConfig.hpp" -#include "vkcv/DescriptorWrites.hpp" - -#include "ImageManager.hpp" -#include "vkcv/BufferManager.hpp" -#include "SamplerManager.hpp" - -namespace vkcv -{ - class DescriptorManager - { - public: - explicit DescriptorManager(vk::Device device) noexcept; - ~DescriptorManager() noexcept; - - DescriptorSetHandle createDescriptorSet(const std::vector<DescriptorBinding> &descriptorBindings); - - void writeDescriptorSet( - const DescriptorSetHandle &handle, - const DescriptorWrites &writes, - const ImageManager &imageManager, - const BufferManager &bufferManager, - const SamplerManager &samplerManager); - - [[nodiscard]] - DescriptorSet getDescriptorSet(const DescriptorSetHandle handle) const; - - private: - vk::Device m_Device; - std::vector<vk::DescriptorPool> m_Pools; - std::vector<vk::DescriptorPoolSize> m_PoolSizes; - vk::DescriptorPoolCreateInfo m_PoolInfo; - - - /** - * Contains all the resource descriptions that were requested by the user in calls of createResourceDescription. - */ - std::vector<DescriptorSet> m_DescriptorSets; - - /** - * Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk). - * @param[in] vkcv flag of the DescriptorType (see DescriptorConfig.hpp) - * @return vk flag of the DescriptorType - */ - static vk::DescriptorType convertDescriptorTypeFlag(DescriptorType type); - /** - * Converts the flags of the shader stages from VulkanCV (vkcv) to Vulkan (vk). - * @param[in] vkcv flag of the ShaderStage (see ShaderProgram.hpp) - * @return vk flag of the ShaderStage - */ - static vk::ShaderStageFlagBits convertShaderStageFlag(ShaderStage stage); - - /** - * Destroys a specific resource description - * @param[in] the handle id of the respective resource description - */ - void destroyDescriptorSetById(uint64_t id); - - /** - * creates a descriptor pool based on the poolSizes and poolInfo defined in the constructor - * is called initially in the constructor and then every time the pool runs out memory - * @return a DescriptorPool object - */ - vk::DescriptorPool allocateDescriptorPool(); - - }; -} \ No newline at end of file diff --git a/src/vkcv/DescriptorSetLayoutManager.cpp b/src/vkcv/DescriptorSetLayoutManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..14fe4d519f0587d6ef3febf256de83f9f5428f42 --- /dev/null +++ b/src/vkcv/DescriptorSetLayoutManager.cpp @@ -0,0 +1,107 @@ +#include "DescriptorSetLayoutManager.hpp" + +#include "vkcv/Core.hpp" + +namespace vkcv { + + uint64_t DescriptorSetLayoutManager::getIdFrom(const DescriptorSetLayoutHandle &handle) const { + return handle.getId(); + } + + DescriptorSetLayoutHandle + DescriptorSetLayoutManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return DescriptorSetLayoutHandle(id, destroy); + } + + void DescriptorSetLayoutManager::destroyById(uint64_t id) { + auto &layout = getById(id); + + if (layout.layoutUsageCount > 1) { + layout.layoutUsageCount--; + return; + } else { + layout.layoutUsageCount = 0; + } + + if (layout.vulkanHandle) { + getCore().getContext().getDevice().destroy(layout.vulkanHandle); + layout.vulkanHandle = nullptr; + } + } + + DescriptorSetLayoutManager::DescriptorSetLayoutManager() noexcept : + HandleManager<DescriptorSetLayoutEntry, DescriptorSetLayoutHandle>() {} + + DescriptorSetLayoutManager::~DescriptorSetLayoutManager() noexcept { + for (uint64_t id = 0; id < getCount(); id++) { + // Resets the usage count to zero for destruction. + getById(id).layoutUsageCount = 0; + } + + clear(); + } + + DescriptorSetLayoutHandle + DescriptorSetLayoutManager::createDescriptorSetLayout(const DescriptorBindings &bindings) { + for (uint64_t id = 0; id < getCount(); id++) { + auto &layout = getById(id); + + if (layout.descriptorBindings.size() != bindings.size()) + continue; + + if (layout.descriptorBindings == bindings) { + layout.layoutUsageCount++; + return createById(id, [&](uint64_t id) { + destroyById(id); + }); + } + } + + // create the descriptor set's layout and binding flags by iterating over its bindings + std::vector<vk::DescriptorSetLayoutBinding> bindingsVector = {}; + std::vector<vk::DescriptorBindingFlags> bindingsFlags = {}; + + for (auto bindingElem : bindings) { + DescriptorBinding binding = bindingElem.second; + uint32_t bindingID = bindingElem.first; + + bindingsVector.emplace_back(bindingID, getVkDescriptorType(binding.descriptorType), + binding.descriptorCount, + getShaderStageFlags(binding.shaderStages), nullptr); + + vk::DescriptorBindingFlags flags; + + if (binding.variableCount) + flags |= vk::DescriptorBindingFlagBits::eVariableDescriptorCount; + + if (binding.partialBinding) + flags |= vk::DescriptorBindingFlagBits::ePartiallyBound; + + bindingsFlags.push_back(flags); + } + + vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsInfo(bindingsFlags.size(), + bindingsFlags.data()); + + // create the descriptor set's layout from the binding data gathered above + vk::DescriptorSetLayout vulkanHandle; + vk::DescriptorSetLayoutCreateInfo layoutInfo(vk::DescriptorSetLayoutCreateFlags(), + bindingsVector); + layoutInfo.setPNext(&bindingFlagsInfo); + + auto result = getCore().getContext().getDevice().createDescriptorSetLayout( + &layoutInfo, nullptr, &vulkanHandle); + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "Failed to create descriptor set layout"); + return DescriptorSetLayoutHandle(); + }; + + return add({ vulkanHandle, bindings, 1 }); + } + + const DescriptorSetLayoutEntry &DescriptorSetLayoutManager::getDescriptorSetLayout( + const DescriptorSetLayoutHandle &handle) const { + return (*this) [handle]; + } + +} // namespace vkcv diff --git a/src/vkcv/DescriptorSetLayoutManager.hpp b/src/vkcv/DescriptorSetLayoutManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3e540e9daa74c533eef0f1fe9788abb1496fa008 --- /dev/null +++ b/src/vkcv/DescriptorSetLayoutManager.hpp @@ -0,0 +1,70 @@ +#pragma once +/** + * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann, Tobias Frisch + * @file src/vkcv/DescriptorManager.cpp + * @brief Creation and handling of descriptor set layouts. + */ +#include <vulkan/vulkan.hpp> + +#include "vkcv/DescriptorBinding.hpp" + +#include "HandleManager.hpp" + +namespace vkcv { + + /** + * @brief Structure to store details about a descriptor set layout. + */ + struct DescriptorSetLayoutEntry { + vk::DescriptorSetLayout vulkanHandle; + DescriptorBindings descriptorBindings; + size_t layoutUsageCount; + }; + + /** + * @brief Class to manage descriptor set layouts. + */ + class DescriptorSetLayoutManager : + public HandleManager<DescriptorSetLayoutEntry, DescriptorSetLayoutHandle> { + friend class Core; + + private: + [[nodiscard]] uint64_t getIdFrom(const DescriptorSetLayoutHandle &handle) const override; + + [[nodiscard]] DescriptorSetLayoutHandle + createById(uint64_t id, const HandleDestroyFunction &destroy) override; + + /** + * Destroys and deallocates descriptor set layout represented by a given + * descriptor set layout handle id. + * + * @param id Descriptor set layout handle id + */ + void destroyById(uint64_t id) override; + + public: + /** + * @brief Constructor of the descriptor set layout manager + */ + DescriptorSetLayoutManager() noexcept; + + /** + * @brief Destructor of the descriptor set layout manager + */ + ~DescriptorSetLayoutManager() noexcept override; + + /** + * @brief Creates a descriptor set layout with given descriptor bindings + * or returns a matching handle. + * + * @param[in] bindings Descriptor bindings + * @return Handle of descriptor set layout + */ + [[nodiscard]] DescriptorSetLayoutHandle + createDescriptorSetLayout(const DescriptorBindings &bindings); + + [[nodiscard]] const DescriptorSetLayoutEntry & + getDescriptorSetLayout(const DescriptorSetLayoutHandle &handle) const; + }; + +} // namespace vkcv diff --git a/src/vkcv/DescriptorSetManager.cpp b/src/vkcv/DescriptorSetManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6ac1c22c76373c7773a7c242805e41f628f66c7 --- /dev/null +++ b/src/vkcv/DescriptorSetManager.cpp @@ -0,0 +1,302 @@ +#include "DescriptorSetManager.hpp" + +#include "vkcv/Core.hpp" + +namespace vkcv { + + bool DescriptorSetManager::init(Core &core, + DescriptorSetLayoutManager &descriptorSetLayoutManager) { + if (!HandleManager<DescriptorSetEntry, DescriptorSetHandle>::init(core)) { + return false; + } + + m_DescriptorSetLayoutManager = &descriptorSetLayoutManager; + + /** + * Allocate the set size for the descriptor pools, namely 1000 units of each descriptor type + * below. Finally, create an initial pool. + */ + m_PoolSizes = { + vk::DescriptorPoolSize(vk::DescriptorType::eSampler, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eSampledImage, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBuffer, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eUniformBufferDynamic, 1000), + vk::DescriptorPoolSize(vk::DescriptorType::eStorageBufferDynamic, 1000), // for RTX + vk::DescriptorPoolSize(vk::DescriptorType::eAccelerationStructureKHR, 1000) // for RTX + }; + + m_PoolInfo = vk::DescriptorPoolCreateInfo( + vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet, 1000, + static_cast<uint32_t>(m_PoolSizes.size()), m_PoolSizes.data()); + + return allocateDescriptorPool(); + } + + uint64_t DescriptorSetManager::getIdFrom(const DescriptorSetHandle &handle) const { + return handle.getId(); + } + + DescriptorSetHandle DescriptorSetManager::createById(uint64_t id, + const HandleDestroyFunction &destroy) { + return DescriptorSetHandle(id, destroy); + } + + void DescriptorSetManager::destroyById(uint64_t id) { + auto &set = getById(id); + + if (set.vulkanHandle) { + getCore().getContext().getDevice().freeDescriptorSets(m_Pools [set.poolIndex], 1, + &(set.vulkanHandle)); + set.setLayoutHandle = DescriptorSetLayoutHandle(); + set.vulkanHandle = nullptr; + } + } + + vk::DescriptorPool DescriptorSetManager::allocateDescriptorPool() { + vk::DescriptorPool pool; + if (getCore().getContext().getDevice().createDescriptorPool(&m_PoolInfo, nullptr, &pool) + != vk::Result::eSuccess) { + vkcv_log(LogLevel::WARNING, "Failed to allocate descriptor pool"); + pool = nullptr; + } else { + m_Pools.push_back(pool); + } + + return pool; + } + + DescriptorSetManager::DescriptorSetManager() noexcept : + HandleManager<DescriptorSetEntry, DescriptorSetHandle>() {} + + DescriptorSetManager::~DescriptorSetManager() noexcept { + clear(); + + for (const auto &pool : m_Pools) { + if (pool) { + getCore().getContext().getDevice().destroy(pool); + } + } + } + + DescriptorSetHandle + DescriptorSetManager::createDescriptorSet(const DescriptorSetLayoutHandle &layout) { + // create and allocate the set based on the layout provided + const auto &setLayout = m_DescriptorSetLayoutManager->getDescriptorSetLayout(layout); + + vk::DescriptorSet vulkanHandle; + vk::DescriptorSetAllocateInfo allocInfo(m_Pools.back(), 1, &setLayout.vulkanHandle); + + uint32_t sumVariableDescriptorCounts = 0; + for (auto bindingElem : setLayout.descriptorBindings) { + auto binding = bindingElem.second; + + if (binding.variableCount) + sumVariableDescriptorCounts += binding.descriptorCount; + } + + vk::DescriptorSetVariableDescriptorCountAllocateInfo variableAllocInfo( + 1, &sumVariableDescriptorCounts); + + if (sumVariableDescriptorCounts > 0) { + allocInfo.setPNext(&variableAllocInfo); + } + + auto result = + getCore().getContext().getDevice().allocateDescriptorSets(&allocInfo, &vulkanHandle); + if (result != vk::Result::eSuccess) { + // create a new descriptor pool if the previous one ran out of memory + if (result == vk::Result::eErrorOutOfPoolMemory) { + allocateDescriptorPool(); + allocInfo.setDescriptorPool(m_Pools.back()); + result = getCore().getContext().getDevice().allocateDescriptorSets(&allocInfo, + &vulkanHandle); + } + + if (result != vk::Result::eSuccess) { + vkcv_log(LogLevel::ERROR, "Failed to create descriptor set (%s)", + vk::to_string(result).c_str()); + return {}; + } + }; + + size_t poolIndex = (m_Pools.size() - 1); + return add({ vulkanHandle, layout, poolIndex }); + } + + /** + * @brief Structure to store details to write to a descriptor set. + */ + struct WriteDescriptorSetInfo { + size_t imageInfoIndex; + size_t bufferInfoIndex; + size_t structureIndex; + uint32_t binding; + uint32_t arrayElementIndex; + uint32_t descriptorCount; + vk::DescriptorType type; + }; + + void DescriptorSetManager::writeDescriptorSet(const DescriptorSetHandle &handle, + const DescriptorWrites &writes, + const ImageManager &imageManager, + const BufferManager &bufferManager, + const SamplerManager &samplerManager) { + auto &set = (*this) [handle]; + + std::vector<vk::DescriptorImageInfo> imageInfos; + std::vector<vk::DescriptorBufferInfo> bufferInfos; + + std::vector<vk::WriteDescriptorSetAccelerationStructureKHR> writeStructures; + + std::vector<WriteDescriptorSetInfo> writeInfos; + + for (const auto &write : writes.getSampledImageWrites()) { + const vk::ImageLayout layout = + (write.useGeneralLayout ? vk::ImageLayout::eGeneral : + vk::ImageLayout::eShaderReadOnlyOptimal); + + for (uint32_t i = 0; i < write.mipCount; i++) { + const vk::DescriptorImageInfo imageInfo( + nullptr, + imageManager.getVulkanImageView(write.image, write.mipLevel + i, + write.arrayView), + layout); + + imageInfos.push_back(imageInfo); + } + + WriteDescriptorSetInfo vulkanWrite = { + imageInfos.size() + 1 - write.mipCount, + 0, + 0, + write.binding, + write.arrayIndex, + write.mipCount, + vk::DescriptorType::eSampledImage, + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto &write : writes.getStorageImageWrites()) { + for (uint32_t i = 0; i < write.mipCount; i++) { + const vk::DescriptorImageInfo imageInfo( + nullptr, + imageManager.getVulkanImageView(write.image, write.mipLevel + i, + write.arrayView), + vk::ImageLayout::eGeneral); + + imageInfos.push_back(imageInfo); + } + + WriteDescriptorSetInfo vulkanWrite = { + imageInfos.size() + 1 - write.mipCount, 0, 0, write.binding, 0, write.mipCount, + vk::DescriptorType::eStorageImage + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto &write : writes.getUniformBufferWrites()) { + const size_t size = bufferManager.getBufferSize(write.buffer); + const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size); + + const vk::DescriptorBufferInfo bufferInfo( + bufferManager.getBuffer(write.buffer), offset, + write.size == 0 ? size : std::min<uint32_t>(write.size, size - offset)); + + bufferInfos.push_back(bufferInfo); + + WriteDescriptorSetInfo vulkanWrite = { 0, + bufferInfos.size(), + 0, + write.binding, + 0, + 1, + write.dynamic ? + vk::DescriptorType::eUniformBufferDynamic : + vk::DescriptorType::eUniformBuffer }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto &write : writes.getStorageBufferWrites()) { + const size_t size = bufferManager.getBufferSize(write.buffer); + const uint32_t offset = std::clamp<uint32_t>(write.offset, 0, size); + + const vk::DescriptorBufferInfo bufferInfo( + bufferManager.getBuffer(write.buffer), offset, + write.size == 0 ? size : std::min<uint32_t>(write.size, size - offset)); + + bufferInfos.push_back(bufferInfo); + + WriteDescriptorSetInfo vulkanWrite = { 0, + bufferInfos.size(), + 0, + write.binding, + 0, + 1, + write.dynamic ? + vk::DescriptorType::eStorageBufferDynamic : + vk::DescriptorType::eStorageBuffer }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto &write : writes.getSamplerWrites()) { + const vk::Sampler &sampler = samplerManager.getVulkanSampler(write.sampler); + + const vk::DescriptorImageInfo imageInfo(sampler, nullptr, vk::ImageLayout::eGeneral); + + imageInfos.push_back(imageInfo); + + WriteDescriptorSetInfo vulkanWrite = { + imageInfos.size(), 0, 0, write.binding, 0, 1, vk::DescriptorType::eSampler + }; + + writeInfos.push_back(vulkanWrite); + } + + for (const auto &write : writes.getAccelerationWrites()) { + const vk::WriteDescriptorSetAccelerationStructureKHR structureWrite( + write.structures.size(), write.structures.data()); + + writeStructures.push_back(structureWrite); + + WriteDescriptorSetInfo vulkanWrite = { 0, + 0, + writeStructures.size(), + write.binding, + 0, + 1, + vk::DescriptorType::eAccelerationStructureKHR }; + + writeInfos.push_back(vulkanWrite); + } + + std::vector<vk::WriteDescriptorSet> vulkanWrites; + + for (const auto &write : writeInfos) { + vk::WriteDescriptorSet vulkanWrite( + set.vulkanHandle, write.binding, write.arrayElementIndex, write.descriptorCount, + write.type, + (write.imageInfoIndex > 0 ? &(imageInfos [write.imageInfoIndex - 1]) : nullptr), + (write.bufferInfoIndex > 0 ? &(bufferInfos [write.bufferInfoIndex - 1]) : nullptr)); + + if (write.structureIndex > 0) { + vulkanWrite.setPNext(&(writeStructures [write.structureIndex - 1])); + } + + vulkanWrites.push_back(vulkanWrite); + } + + getCore().getContext().getDevice().updateDescriptorSets(vulkanWrites, nullptr); + } + + const DescriptorSetEntry & + DescriptorSetManager::getDescriptorSet(const DescriptorSetHandle &handle) const { + return (*this) [handle]; + } + +} // namespace vkcv diff --git a/src/vkcv/DescriptorSetManager.hpp b/src/vkcv/DescriptorSetManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ae3f879ce4ef9d3b16f687bc237060c98591bd65 --- /dev/null +++ b/src/vkcv/DescriptorSetManager.hpp @@ -0,0 +1,104 @@ +#pragma once +/** + * @authors Artur Wasmut, Susanne D�tsch, Simeon Hermann, Tobias Frisch + * @file src/vkcv/DescriptorManager.cpp + * @brief Creation and handling of descriptor sets and the respective descriptor pools. + */ +#include <vulkan/vulkan.hpp> + +#include "vkcv/DescriptorBinding.hpp" +#include "vkcv/DescriptorWrites.hpp" + +#include "BufferManager.hpp" +#include "DescriptorSetLayoutManager.hpp" +#include "HandleManager.hpp" +#include "ImageManager.hpp" +#include "SamplerManager.hpp" + +namespace vkcv { + + /** + * @brief Structure to store details about a descriptor set. + */ + struct DescriptorSetEntry { + vk::DescriptorSet vulkanHandle; + DescriptorSetLayoutHandle setLayoutHandle; + size_t poolIndex; + }; + + /** + * @brief Class to manage descriptor sets. + */ + class DescriptorSetManager : public HandleManager<DescriptorSetEntry, DescriptorSetHandle> { + friend class Core; + + private: + DescriptorSetLayoutManager* m_DescriptorSetLayoutManager; + + std::vector<vk::DescriptorPool> m_Pools; + std::vector<vk::DescriptorPoolSize> m_PoolSizes; + vk::DescriptorPoolCreateInfo m_PoolInfo; + + bool init(Core &core, DescriptorSetLayoutManager &descriptorSetLayoutManager); + + [[nodiscard]] uint64_t getIdFrom(const DescriptorSetHandle &handle) const override; + + [[nodiscard]] DescriptorSetHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + /** + * Destroys and deallocates descriptor set represented by a given + * descriptor set handle id. + * + * @param id Descriptor set handle id + */ + void destroyById(uint64_t id) override; + + /** + * @brief Creates a descriptor pool based on the poolSizes and poolInfo defined in the + * constructor is called initially in the constructor and then every time the pool runs + * out memory. + * + * @return a DescriptorPool object + */ + vk::DescriptorPool allocateDescriptorPool(); + + public: + /** + * @brief Constructor of the descriptor set manager + */ + DescriptorSetManager() noexcept; + + /** + * @brief Destructor of the descriptor set manager + */ + ~DescriptorSetManager() noexcept override; + + /** + * @brief Creates a descriptor set using a given descriptor set layout. + * + * @param[in] layout Handle of descriptor set layout + * @return Handle of descriptor set + */ + [[nodiscard]] DescriptorSetHandle + createDescriptorSet(const DescriptorSetLayoutHandle &layout); + + /** + * @brief Writes to a descriptor set using writes and all required managers. + * + * @param[in] handle Handle of descriptor set + * @param[in] writes Descriptor set writes + * @param[in] imageManager Image manager + * @param[in] bufferManager Buffer manager + * @param[in] samplerManager Sampler manager + */ + void writeDescriptorSet(const DescriptorSetHandle &handle, const DescriptorWrites &writes, + const ImageManager &imageManager, + const BufferManager &bufferManager, + const SamplerManager &samplerManager); + + [[nodiscard]] const DescriptorSetEntry & + getDescriptorSet(const DescriptorSetHandle &handle) const; + }; + +} // namespace vkcv diff --git a/src/vkcv/DescriptorSetUsage.cpp b/src/vkcv/DescriptorSetUsage.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfa2ba1998e124cec0f1fc851356d28009288e1b --- /dev/null +++ b/src/vkcv/DescriptorSetUsage.cpp @@ -0,0 +1,15 @@ + +#include "vkcv/DescriptorSetUsage.hpp" + +namespace vkcv { + + DescriptorSetUsage useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet, + const std::vector<uint32_t> &dynamicOffsets) { + DescriptorSetUsage usage; + usage.location = location; + usage.descriptorSet = descriptorSet; + usage.dynamicOffsets = dynamicOffsets; + return usage; + } + +} // namespace vkcv diff --git a/src/vkcv/DescriptorWrites.cpp b/src/vkcv/DescriptorWrites.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3b4c5f4fbc4212da078ec0a00a8a93a6c021d02 --- /dev/null +++ b/src/vkcv/DescriptorWrites.cpp @@ -0,0 +1,115 @@ + +#include "vkcv/DescriptorWrites.hpp" + +namespace vkcv { + + DescriptorWrites &DescriptorWrites::writeSampledImage(uint32_t binding, + const ImageHandle& image, + uint32_t mipLevel, + bool useGeneralLayout, + uint32_t arrayIndex, + uint32_t mipCount, + bool arrayView) { + SampledImageDescriptorWrite write; + write.binding = binding; + write.image = image; + write.mipLevel = mipLevel; + write.useGeneralLayout = useGeneralLayout; + write.arrayIndex = arrayIndex; + write.mipCount = mipCount; + write.arrayView = arrayView; + m_sampledImageWrites.push_back(write); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeStorageImage(uint32_t binding, + const ImageHandle& image, + uint32_t mipLevel, + uint32_t mipCount, + bool arrayView) { + StorageImageDescriptorWrite write; + write.binding = binding; + write.image = image; + write.mipLevel = mipLevel; + write.mipCount = mipCount; + write.arrayView = arrayView; + m_storageImageWrites.push_back(write); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeUniformBuffer(uint32_t binding, + const BufferHandle& buffer, + bool dynamic, + uint32_t offset, + uint32_t size) { + BufferDescriptorWrite write; + write.binding = binding; + write.buffer = buffer; + write.dynamic = dynamic; + write.offset = offset; + write.size = size; + m_uniformBufferWrites.push_back(write); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeStorageBuffer(uint32_t binding, + const BufferHandle& buffer, + bool dynamic, + uint32_t offset, + uint32_t size) { + BufferDescriptorWrite write; + write.binding = binding; + write.buffer = buffer; + write.dynamic = dynamic; + write.offset = offset; + write.size = size; + m_storageBufferWrites.push_back(write); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeSampler(uint32_t binding, + const SamplerHandle& sampler) { + SamplerDescriptorWrite write; + write.binding = binding; + write.sampler = sampler; + m_samplerWrites.push_back(write); + return *this; + } + + DescriptorWrites &DescriptorWrites::writeAcceleration( + uint32_t binding, const std::vector<vk::AccelerationStructureKHR> &structures) { + AccelerationDescriptorWrite write; + write.binding = binding; + write.structures = structures; + m_accelerationWrites.push_back(write); + return *this; + } + + const std::vector<SampledImageDescriptorWrite> & + DescriptorWrites::getSampledImageWrites() const { + return m_sampledImageWrites; + } + + const std::vector<StorageImageDescriptorWrite> & + DescriptorWrites::getStorageImageWrites() const { + return m_storageImageWrites; + } + + const std::vector<BufferDescriptorWrite> &DescriptorWrites::getUniformBufferWrites() const { + return m_uniformBufferWrites; + } + + const std::vector<BufferDescriptorWrite> &DescriptorWrites::getStorageBufferWrites() const { + return m_storageBufferWrites; + } + + const std::vector<SamplerDescriptorWrite> &DescriptorWrites::getSamplerWrites() const { + return m_samplerWrites; + } + + const std::vector<AccelerationDescriptorWrite> & + DescriptorWrites::getAccelerationWrites() const { + return m_accelerationWrites; + } + +} // namespace vkcv diff --git a/src/vkcv/DispatchSize.cpp b/src/vkcv/DispatchSize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb58f2477ac6dfa9fa8db15a3d134698f596ff32 --- /dev/null +++ b/src/vkcv/DispatchSize.cpp @@ -0,0 +1,59 @@ + +#include "vkcv/DispatchSize.hpp" +#include "vkcv/Logger.hpp" + +#include <cmath> + +namespace vkcv { + + DispatchSize::DispatchSize(uint32_t count) : DispatchSize(count, 1, 1) {} + + DispatchSize::DispatchSize(uint32_t dimensionX, uint32_t dimentionY, uint32_t dimensionZ) : + m_Dispatch({ dimensionX, dimentionY, dimensionZ }) { + check(); + } + + const uint32_t* DispatchSize::data() const { + return m_Dispatch.data(); + } + + uint32_t DispatchSize::operator[](size_t index) const { + return m_Dispatch.at(index); + } + + uint32_t DispatchSize::x() const { + return m_Dispatch [0]; + } + + uint32_t DispatchSize::y() const { + return m_Dispatch [1]; + } + + uint32_t DispatchSize::z() const { + return m_Dispatch [2]; + } + + bool DispatchSize::check() const { + const uint32_t dimensionX = x(); + const uint32_t dimensionY = y(); + const uint32_t dimensionZ = z(); + + if ((dimensionX <= 0) || (dimensionY <= 0) || (dimensionZ <= 0)) { + vkcv_log(LogLevel::WARNING, "Dispatch size invalid: x = %u, y = %u, z = %u", dimensionX, + dimensionY, dimensionZ); + + return false; + } else { + return true; + } + } + + DispatchSize dispatchInvocations(DispatchSize globalInvocations, DispatchSize groupSize) { + const uint32_t dimensionX = std::ceil(1.0f * globalInvocations.x() / groupSize.x()); + const uint32_t dimensionY = std::ceil(1.0f * globalInvocations.y() / groupSize.y()); + const uint32_t dimensionZ = std::ceil(1.0f * globalInvocations.z() / groupSize.z()); + + return DispatchSize(dimensionX, dimensionY, dimensionZ); + } + +} // namespace vkcv diff --git a/src/vkcv/Downsampler.cpp b/src/vkcv/Downsampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..961ade2944618cd1c28f214a61eb4487a2915cf3 --- /dev/null +++ b/src/vkcv/Downsampler.cpp @@ -0,0 +1,8 @@ + +#include "vkcv/Downsampler.hpp" + +namespace vkcv { + + Downsampler::Downsampler(Core &core) : m_core(core) {} + +} // namespace vkcv diff --git a/src/vkcv/Drawcall.cpp b/src/vkcv/Drawcall.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b45cb6ecc8a31d098d65791ede15badb3dc24bec --- /dev/null +++ b/src/vkcv/Drawcall.cpp @@ -0,0 +1,64 @@ +/** + * @authors Tobias Frisch + * @file src/vkcv/Drawcall.cpp + */ + +#include "vkcv/Drawcall.hpp" + +namespace vkcv { + + const std::vector<DescriptorSetUsage> &Drawcall::getDescriptorSetUsages() const { + return m_usages; + } + + void Drawcall::useDescriptorSet(uint32_t location, const DescriptorSetHandle &descriptorSet, + const std::vector<uint32_t> &dynamicOffsets) { + DescriptorSetUsage usage; + usage.location = location; + usage.descriptorSet = descriptorSet; + usage.dynamicOffsets = dynamicOffsets; + m_usages.push_back(usage); + } + + InstanceDrawcall::InstanceDrawcall(const VertexData &vertexData, + uint32_t instanceCount) : + Drawcall(), + m_vertexData(vertexData), + m_instanceCount(instanceCount) {} + + const VertexData &InstanceDrawcall::getVertexData() const { + return m_vertexData; + } + + uint32_t InstanceDrawcall::getInstanceCount() const { + return m_instanceCount; + } + + IndirectDrawcall::IndirectDrawcall(const BufferHandle &indirectDrawBuffer, + const VertexData &vertexData, + uint32_t drawCount) : + Drawcall(), + m_indirectDrawBuffer(indirectDrawBuffer), + m_vertexData(vertexData), + m_drawCount(drawCount) { + } + + BufferHandle IndirectDrawcall::getIndirectDrawBuffer() const { + return m_indirectDrawBuffer; + } + + const VertexData &IndirectDrawcall::getVertexData() const { + return m_vertexData; + } + + uint32_t IndirectDrawcall::getDrawCount() const { + return m_drawCount; + } + + TaskDrawcall::TaskDrawcall(uint32_t taskCount) : Drawcall(), m_taskCount(taskCount) {} + + uint32_t TaskDrawcall::getTaskCount() const { + return m_taskCount; + } + +} // namespace vkcv diff --git a/src/vkcv/DrawcallRecording.cpp b/src/vkcv/DrawcallRecording.cpp deleted file mode 100644 index df7b7bbcb3fe278622cd160593eb750db00ec7b1..0000000000000000000000000000000000000000 --- a/src/vkcv/DrawcallRecording.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include <vkcv/DrawcallRecording.hpp> - -namespace vkcv { - - void recordDrawcall( - const DrawcallInfo &drawcall, - vk::CommandBuffer cmdBuffer, - vk::PipelineLayout pipelineLayout, - const PushConstantData &pushConstantData, - const size_t drawcallIndex) { - - for (uint32_t i = 0; i < drawcall.mesh.vertexBufferBindings.size(); i++) { - const auto& vertexBinding = drawcall.mesh.vertexBufferBindings[i]; - cmdBuffer.bindVertexBuffers(i, vertexBinding.buffer, vertexBinding.offset); - } - - for (const auto& descriptorUsage : drawcall.descriptorSets) { - cmdBuffer.bindDescriptorSets( - vk::PipelineBindPoint::eGraphics, - pipelineLayout, - descriptorUsage.setLocation, - descriptorUsage.vulkanHandle, - nullptr); - } - - const size_t drawcallPushConstantOffset = drawcallIndex * pushConstantData.sizePerDrawcall; - // char* cast because void* does not support pointer arithmetic - const void* drawcallPushConstantData = drawcallPushConstantOffset + (char*)pushConstantData.data; - - cmdBuffer.pushConstants( - pipelineLayout, - vk::ShaderStageFlagBits::eAll, - 0, - pushConstantData.sizePerDrawcall, - drawcallPushConstantData); - - if (drawcall.mesh.indexBuffer) { - cmdBuffer.bindIndexBuffer(drawcall.mesh.indexBuffer, 0, vk::IndexType::eUint16); //FIXME: choose proper size - cmdBuffer.drawIndexed(drawcall.mesh.indexCount, 1, 0, 0, {}); - } - else { - cmdBuffer.draw(drawcall.mesh.indexCount, 1, 0, 0, {}); - } - } -} \ No newline at end of file diff --git a/src/vkcv/FeatureManager.cpp b/src/vkcv/FeatureManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab8e20dd35b27d19655fb34174bc86fba6455857 --- /dev/null +++ b/src/vkcv/FeatureManager.cpp @@ -0,0 +1,660 @@ + +#include "vkcv/FeatureManager.hpp" + +#include <cstddef> +#include <cstring> + +#ifdef _MSVC_LANG +#include <type_traits> +#endif + +namespace vkcv { + +#ifdef _MSVC_LANG +#define typeof(var) std::decay<decltype((var))>::type +#endif + +#define vkcv_check_init_features2(type) \ + type supported; \ + vk::PhysicalDeviceFeatures2 query; \ + query.setPNext(&supported); \ + m_physicalDevice.getFeatures2(&query) + +#define vkcv_check_feature(attribute) \ + { \ + const char* f = reinterpret_cast<const char*>(&(features)); \ + const char* s = reinterpret_cast<const char*>(&(supported)); \ + const vk::Bool32* fb = \ + reinterpret_cast<const vk::Bool32*>(f + offsetof(typeof((features)), attribute)); \ + const vk::Bool32* sb = \ + reinterpret_cast<const vk::Bool32*>(s + offsetof(typeof((features)), attribute)); \ + if ((*fb) && (!*sb)) { \ + vkcv_log(((required) ? LogLevel::ERROR : LogLevel::WARNING), \ + "Feature '" #attribute "' is not supported"); \ + return false; \ + } \ + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceFeatures &features, + bool required) const { + const auto &supported = m_physicalDevice.getFeatures(); + + vkcv_check_feature(alphaToOne); + vkcv_check_feature(depthBiasClamp); + vkcv_check_feature(depthBounds); + vkcv_check_feature(depthClamp); + vkcv_check_feature(drawIndirectFirstInstance); + vkcv_check_feature(dualSrcBlend); + vkcv_check_feature(fillModeNonSolid); + vkcv_check_feature(fragmentStoresAndAtomics); + vkcv_check_feature(fullDrawIndexUint32); + vkcv_check_feature(geometryShader); + vkcv_check_feature(imageCubeArray); + vkcv_check_feature(independentBlend); + vkcv_check_feature(inheritedQueries); + vkcv_check_feature(largePoints); + vkcv_check_feature(logicOp); + vkcv_check_feature(multiDrawIndirect); + vkcv_check_feature(multiViewport); + vkcv_check_feature(occlusionQueryPrecise); + vkcv_check_feature(pipelineStatisticsQuery); + vkcv_check_feature(robustBufferAccess); + vkcv_check_feature(sampleRateShading); + vkcv_check_feature(samplerAnisotropy); + vkcv_check_feature(shaderClipDistance); + vkcv_check_feature(shaderCullDistance); + vkcv_check_feature(shaderFloat64); + vkcv_check_feature(shaderImageGatherExtended); + vkcv_check_feature(shaderInt16); + vkcv_check_feature(shaderInt64); + vkcv_check_feature(shaderResourceMinLod); + vkcv_check_feature(shaderResourceResidency); + vkcv_check_feature(shaderSampledImageArrayDynamicIndexing); + vkcv_check_feature(shaderStorageBufferArrayDynamicIndexing); + vkcv_check_feature(shaderStorageImageArrayDynamicIndexing); + vkcv_check_feature(shaderStorageImageExtendedFormats); + vkcv_check_feature(shaderStorageImageMultisample); + vkcv_check_feature(shaderStorageImageReadWithoutFormat); + vkcv_check_feature(shaderStorageImageWriteWithoutFormat); + vkcv_check_feature(shaderTessellationAndGeometryPointSize); + vkcv_check_feature(shaderUniformBufferArrayDynamicIndexing); + vkcv_check_feature(sparseBinding); + vkcv_check_feature(sparseResidency2Samples); + vkcv_check_feature(sparseResidency4Samples); + vkcv_check_feature(sparseResidency8Samples); + vkcv_check_feature(sparseResidency16Samples); + vkcv_check_feature(sparseResidencyAliased); + vkcv_check_feature(sparseResidencyBuffer); + vkcv_check_feature(sparseResidencyImage2D); + vkcv_check_feature(sparseResidencyImage3D); + vkcv_check_feature(tessellationShader); + vkcv_check_feature(textureCompressionASTC_LDR); + vkcv_check_feature(textureCompressionBC); + vkcv_check_feature(textureCompressionETC2); + vkcv_check_feature(variableMultisampleRate); + vkcv_check_feature(vertexPipelineStoresAndAtomics); + vkcv_check_feature(wideLines); + vkcv_check_feature(multiDrawIndirect); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDevice16BitStorageFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDevice16BitStorageFeatures); + + vkcv_check_feature(storageBuffer16BitAccess); + vkcv_check_feature(storageInputOutput16); + vkcv_check_feature(storagePushConstant16); + vkcv_check_feature(uniformAndStorageBuffer16BitAccess); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDevice8BitStorageFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDevice8BitStorageFeatures); + + vkcv_check_feature(storageBuffer8BitAccess); + vkcv_check_feature(storagePushConstant8); + vkcv_check_feature(uniformAndStorageBuffer8BitAccess); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceBufferDeviceAddressFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceBufferDeviceAddressFeatures); + + vkcv_check_feature(bufferDeviceAddress); + vkcv_check_feature(bufferDeviceAddressCaptureReplay); + vkcv_check_feature(bufferDeviceAddressMultiDevice); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceDescriptorIndexingFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceDescriptorIndexingFeatures); + + vkcv_check_feature(shaderInputAttachmentArrayDynamicIndexing); + vkcv_check_feature(shaderInputAttachmentArrayNonUniformIndexing); + vkcv_check_feature(shaderSampledImageArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageImageArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageTexelBufferArrayDynamicIndexing); + vkcv_check_feature(shaderStorageTexelBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderUniformBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderUniformTexelBufferArrayDynamicIndexing); + vkcv_check_feature(shaderUniformTexelBufferArrayNonUniformIndexing); + vkcv_check_feature(descriptorBindingPartiallyBound); + vkcv_check_feature(descriptorBindingSampledImageUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageImageUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageTexelBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUniformBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUniformTexelBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUpdateUnusedWhilePending); + vkcv_check_feature(descriptorBindingVariableDescriptorCount); + vkcv_check_feature(runtimeDescriptorArray); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceHostQueryResetFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceHostQueryResetFeatures); + + vkcv_check_feature(hostQueryReset); + + return true; + } + + bool + FeatureManager::checkSupport(const vk::PhysicalDeviceImagelessFramebufferFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceImagelessFramebufferFeatures); + + vkcv_check_feature(imagelessFramebuffer); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceMultiviewFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceMultiviewFeatures); + + vkcv_check_feature(multiview); + vkcv_check_feature(multiviewGeometryShader); + vkcv_check_feature(multiviewTessellationShader); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceProtectedMemoryFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceProtectedMemoryFeatures); + + vkcv_check_feature(protectedMemory); + + return true; + } + + bool + FeatureManager::checkSupport(const vk::PhysicalDeviceSamplerYcbcrConversionFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceSamplerYcbcrConversionFeatures); + + vkcv_check_feature(samplerYcbcrConversion); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceScalarBlockLayoutFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceScalarBlockLayoutFeatures); + + vkcv_check_feature(scalarBlockLayout); + + return true; + } + + bool FeatureManager::checkSupport( + const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceSeparateDepthStencilLayoutsFeatures); + + vkcv_check_feature(separateDepthStencilLayouts); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderAtomicInt64Features &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderAtomicInt64Features); + + vkcv_check_feature(shaderBufferInt64Atomics); + vkcv_check_feature(shaderSharedInt64Atomics); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderFloat16Int8Features &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderFloat16Int8Features); + + vkcv_check_feature(shaderFloat16); + vkcv_check_feature(shaderInt8); + + return true; + } + + bool FeatureManager::checkSupport( + const vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderSubgroupExtendedTypesFeatures); + + vkcv_check_feature(shaderSubgroupExtendedTypes); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceTimelineSemaphoreFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceTimelineSemaphoreFeatures); + + vkcv_check_feature(timelineSemaphore); + + return true; + } + + bool FeatureManager::checkSupport( + const vk::PhysicalDeviceUniformBufferStandardLayoutFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceUniformBufferStandardLayoutFeatures); + + vkcv_check_feature(uniformBufferStandardLayout); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVariablePointersFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVariablePointersFeatures); + + vkcv_check_feature(variablePointers); + vkcv_check_feature(variablePointersStorageBuffer); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkanMemoryModelFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVulkanMemoryModelFeatures); + + vkcv_check_feature(vulkanMemoryModel); + vkcv_check_feature(vulkanMemoryModelDeviceScope); + vkcv_check_feature(vulkanMemoryModelAvailabilityVisibilityChains); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceMeshShaderFeaturesNV &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceMeshShaderFeaturesNV); + + vkcv_check_feature(taskShader); + vkcv_check_feature(meshShader); + + return true; + } + + bool + FeatureManager::checkSupport(const vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderAtomicFloatFeaturesEXT); + + vkcv_check_feature(shaderBufferFloat32Atomics); + vkcv_check_feature(shaderBufferFloat32AtomicAdd); + vkcv_check_feature(shaderBufferFloat64Atomics); + vkcv_check_feature(shaderBufferFloat64AtomicAdd); + vkcv_check_feature(shaderSharedFloat32Atomics); + vkcv_check_feature(shaderSharedFloat32AtomicAdd); + vkcv_check_feature(shaderSharedFloat64Atomics); + vkcv_check_feature(shaderSharedFloat64AtomicAdd); + vkcv_check_feature(shaderImageFloat32Atomics); + vkcv_check_feature(shaderImageFloat32AtomicAdd); + vkcv_check_feature(sparseImageFloat32Atomics); + vkcv_check_feature(sparseImageFloat32AtomicAdd); + + return true; + } + + bool + FeatureManager::checkSupport(const vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT); + + vkcv_check_feature(shaderBufferFloat16Atomics); + vkcv_check_feature(shaderBufferFloat16AtomicAdd); + vkcv_check_feature(shaderBufferFloat16AtomicMinMax); + vkcv_check_feature(shaderBufferFloat32AtomicMinMax); + vkcv_check_feature(shaderBufferFloat64AtomicMinMax); + vkcv_check_feature(shaderSharedFloat16Atomics); + vkcv_check_feature(shaderSharedFloat16AtomicAdd); + vkcv_check_feature(shaderSharedFloat16AtomicMinMax); + vkcv_check_feature(shaderSharedFloat32AtomicMinMax); + vkcv_check_feature(shaderSharedFloat64AtomicMinMax); + vkcv_check_feature(shaderImageFloat32AtomicMinMax); + vkcv_check_feature(sparseImageFloat32AtomicMinMax); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkan12Features &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVulkan12Features); + + vkcv_check_feature(samplerMirrorClampToEdge); + vkcv_check_feature(drawIndirectCount); + vkcv_check_feature(storageBuffer8BitAccess); + vkcv_check_feature(uniformAndStorageBuffer8BitAccess); + vkcv_check_feature(storagePushConstant8); + vkcv_check_feature(shaderBufferInt64Atomics); + vkcv_check_feature(shaderSharedInt64Atomics); + vkcv_check_feature(shaderFloat16); + vkcv_check_feature(shaderInt8); + vkcv_check_feature(descriptorIndexing); + vkcv_check_feature(shaderInputAttachmentArrayDynamicIndexing); + vkcv_check_feature(shaderUniformTexelBufferArrayDynamicIndexing); + vkcv_check_feature(shaderStorageTexelBufferArrayDynamicIndexing); + vkcv_check_feature(shaderUniformBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderSampledImageArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageImageArrayNonUniformIndexing); + vkcv_check_feature(shaderInputAttachmentArrayNonUniformIndexing); + vkcv_check_feature(shaderUniformTexelBufferArrayNonUniformIndexing); + vkcv_check_feature(shaderStorageTexelBufferArrayNonUniformIndexing); + vkcv_check_feature(descriptorBindingUniformBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingSampledImageUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageImageUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUniformTexelBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingStorageTexelBufferUpdateAfterBind); + vkcv_check_feature(descriptorBindingUpdateUnusedWhilePending); + vkcv_check_feature(descriptorBindingPartiallyBound); + vkcv_check_feature(descriptorBindingVariableDescriptorCount); + vkcv_check_feature(runtimeDescriptorArray); + vkcv_check_feature(samplerFilterMinmax); + vkcv_check_feature(scalarBlockLayout); + vkcv_check_feature(imagelessFramebuffer); + vkcv_check_feature(uniformBufferStandardLayout); + vkcv_check_feature(shaderSubgroupExtendedTypes); + vkcv_check_feature(separateDepthStencilLayouts); + vkcv_check_feature(hostQueryReset); + vkcv_check_feature(timelineSemaphore); + vkcv_check_feature(bufferDeviceAddress); + vkcv_check_feature(bufferDeviceAddressCaptureReplay); + vkcv_check_feature(bufferDeviceAddressMultiDevice); + vkcv_check_feature(vulkanMemoryModel); + vkcv_check_feature(vulkanMemoryModelDeviceScope); + vkcv_check_feature(vulkanMemoryModelAvailabilityVisibilityChains); + vkcv_check_feature(shaderOutputViewportIndex); + vkcv_check_feature(shaderOutputLayer); + vkcv_check_feature(subgroupBroadcastDynamicId); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkan11Features &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVulkan11Features); + + vkcv_check_feature(multiview); + vkcv_check_feature(multiviewGeometryShader); + vkcv_check_feature(multiviewTessellationShader); + vkcv_check_feature(protectedMemory); + vkcv_check_feature(samplerYcbcrConversion); + vkcv_check_feature(shaderDrawParameters); + vkcv_check_feature(storageBuffer16BitAccess); + vkcv_check_feature(storageInputOutput16); + vkcv_check_feature(storagePushConstant16); + vkcv_check_feature(uniformAndStorageBuffer16BitAccess); + vkcv_check_feature(variablePointers); + vkcv_check_feature(variablePointersStorageBuffer); + + return true; + } + + bool + FeatureManager::checkSupport(const vk::PhysicalDeviceAccelerationStructureFeaturesKHR &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceAccelerationStructureFeaturesKHR); + + vkcv_check_feature(accelerationStructure); + vkcv_check_feature(accelerationStructureCaptureReplay); + vkcv_check_feature(accelerationStructureIndirectBuild); + vkcv_check_feature(accelerationStructureHostCommands); + vkcv_check_feature(descriptorBindingAccelerationStructureUpdateAfterBind); + + return true; + } + + bool + FeatureManager::checkSupport(const vk::PhysicalDeviceRayTracingPipelineFeaturesKHR &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceRayTracingPipelineFeaturesKHR); + + vkcv_check_feature(rayTracingPipeline); + vkcv_check_feature(rayTracingPipelineShaderGroupHandleCaptureReplay); + vkcv_check_feature(rayTracingPipelineShaderGroupHandleCaptureReplayMixed); + vkcv_check_feature(rayTracingPipelineTraceRaysIndirect); + vkcv_check_feature(rayTraversalPrimitiveCulling); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceVulkan13Features &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceVulkan13Features); + + vkcv_check_feature(robustImageAccess); + vkcv_check_feature(inlineUniformBlock); + vkcv_check_feature(descriptorBindingInlineUniformBlockUpdateAfterBind); + vkcv_check_feature(pipelineCreationCacheControl); + vkcv_check_feature(privateData); + vkcv_check_feature(shaderDemoteToHelperInvocation); + vkcv_check_feature(shaderTerminateInvocation); + vkcv_check_feature(subgroupSizeControl); + vkcv_check_feature(computeFullSubgroups); + vkcv_check_feature(synchronization2); + vkcv_check_feature(textureCompressionASTC_HDR); + vkcv_check_feature(shaderZeroInitializeWorkgroupMemory); + vkcv_check_feature(dynamicRendering); + vkcv_check_feature(shaderIntegerDotProduct); + vkcv_check_feature(maintenance4); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceCoherentMemoryFeaturesAMD &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceCoherentMemoryFeaturesAMD); + + vkcv_check_feature(deviceCoherentMemory); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceSubgroupSizeControlFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceSubgroupSizeControlFeatures); + + vkcv_check_feature(subgroupSizeControl); + vkcv_check_feature(computeFullSubgroups); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceIndexTypeUint8FeaturesEXT &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceIndexTypeUint8FeaturesEXT); + + vkcv_check_feature(indexTypeUint8); + + return true; + } + + bool FeatureManager::checkSupport(const vk::PhysicalDeviceShaderTerminateInvocationFeatures &features, + bool required) const { + vkcv_check_init_features2(vk::PhysicalDeviceShaderTerminateInvocationFeatures); + + vkcv_check_feature(shaderTerminateInvocation); + + return true; + } + + vk::BaseOutStructure* FeatureManager::findFeatureStructure(vk::StructureType type) const { + for (auto &base : m_featuresExtensions) { + if (base->sType == type) { + return base; + } + } + + return nullptr; + } + + const char* strclone(const char* str) { + if (!str) { + return nullptr; + } + + const size_t length = strlen(str) + 1; + + if (length <= 1) { + return nullptr; + } + + char* clone = new char [length]; + strcpy(clone, str); + return clone; + } + + FeatureManager::FeatureManager(vk::PhysicalDevice &physicalDevice) : + m_physicalDevice(physicalDevice), m_supportedExtensions(), m_activeExtensions(), + m_featuresBase(), m_featuresExtensions() { + for (const auto &extension : m_physicalDevice.enumerateDeviceExtensionProperties()) { + const char* clone = strclone(extension.extensionName); + + if (clone) { + m_supportedExtensions.push_back(clone); + } + } + } + + FeatureManager::FeatureManager(FeatureManager &&other) noexcept : + m_physicalDevice(other.m_physicalDevice), + m_supportedExtensions(std::move(other.m_supportedExtensions)), + m_activeExtensions(std::move(other.m_activeExtensions)), + m_featuresBase(other.m_featuresBase), + m_featuresExtensions(std::move(other.m_featuresExtensions)) { + other.m_featuresExtensions.clear(); + other.m_activeExtensions.clear(); + other.m_supportedExtensions.clear(); + } + + FeatureManager::~FeatureManager() { + for (auto &features : m_featuresExtensions) { + delete features; + } + + for (auto &extension : m_activeExtensions) { + delete [] extension; + } + + for (auto &extension : m_supportedExtensions) { + delete [] extension; + } + } + + FeatureManager &FeatureManager::operator=(FeatureManager &&other) noexcept { + m_physicalDevice = other.m_physicalDevice; + m_supportedExtensions = std::move(other.m_supportedExtensions); + m_activeExtensions = std::move(other.m_activeExtensions); + m_featuresBase = other.m_featuresBase; + m_featuresExtensions = std::move(other.m_featuresExtensions); + + other.m_featuresExtensions.clear(); + other.m_activeExtensions.clear(); + other.m_supportedExtensions.clear(); + + return *this; + } + + bool FeatureManager::isExtensionSupported(const std::string &extension) const { + for (const auto &supported : m_supportedExtensions) { + if (0 == strcmp(supported, extension.c_str())) { + return true; + } + } + + return false; + } + + bool FeatureManager::useExtension(const std::string &extension, bool required) { + const char* clone = strclone(extension.c_str()); + + if (!clone) { + vkcv_log(LogLevel::WARNING, "Extension '%s' is not valid", extension.c_str()); + return false; + } + + if (!isExtensionSupported(extension)) { + vkcv_log((required ? LogLevel::ERROR : LogLevel::WARNING), + "Extension '%s' is not supported", extension.c_str()); + + delete [] clone; + if (required) { + vkcv_log_throw_error("Required extension is not supported!"); + } + + return false; + } + + m_activeExtensions.push_back(clone); + return true; + } + + bool FeatureManager::isExtensionActive(const std::string &extension) const { + for (const auto &supported : m_activeExtensions) { + if (0 == strcmp(supported, extension.c_str())) { + return true; + } + } + + return false; + } + + const std::vector<const char*> &FeatureManager::getActiveExtensions() const { + return m_activeExtensions; + } + + bool FeatureManager::useFeatures( + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction, bool required) { + vk::PhysicalDeviceFeatures features = m_featuresBase.features; + + featureFunction(features); + + if (!checkSupport(features, required)) { + return false; + } + + m_featuresBase.features = features; + return true; + } + + const vk::PhysicalDeviceFeatures2 &FeatureManager::getFeatures() const { + return m_featuresBase; + } + +} // namespace vkcv diff --git a/src/vkcv/Features.cpp b/src/vkcv/Features.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fcfdd7b9435b7f9ab9ad230fa8d77a9a0d66b80e --- /dev/null +++ b/src/vkcv/Features.cpp @@ -0,0 +1,66 @@ + +#include "vkcv/Features.hpp" + +namespace vkcv { + + Features::Features(const std::initializer_list<std::string> &list) : m_features() { + for (const auto &extension : list) { + requireExtension(extension); + } + } + + void Features::requireExtension(const std::string &extension) { + m_features.emplace_back([extension](FeatureManager &featureManager) { + return featureManager.useExtension(extension, true); + }); + } + + void Features::requireExtensionFeature( + const std::string &extension, + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager &featureManager) { + if (featureManager.useExtension(extension, true)) { + return featureManager.useFeatures(featureFunction, true); + } else { + return false; + } + }); + } + + void Features::requireFeature( + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager &featureManager) { + return featureManager.useFeatures(featureFunction, true); + }); + } + + void Features::tryExtension(const std::string &extension) { + m_features.emplace_back([extension](FeatureManager &featureManager) { + return featureManager.useExtension(extension, false); + }); + } + + void Features::tryExtensionFeature( + const std::string &extension, + const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([extension, featureFunction](FeatureManager &featureManager) { + if (featureManager.useExtension(extension, false)) { + return featureManager.useFeatures(featureFunction, false); + } else { + return false; + } + }); + } + + void + Features::tryFeature(const std::function<void(vk::PhysicalDeviceFeatures &)> &featureFunction) { + m_features.emplace_back([featureFunction](FeatureManager &featureManager) { + return featureManager.useFeatures(featureFunction, false); + }); + } + + const std::vector<Feature> &Features::getList() const { + return m_features; + } + +} // namespace vkcv diff --git a/src/vkcv/File.cpp b/src/vkcv/File.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f840fdb06850d6943864ef119883996a461a13fe --- /dev/null +++ b/src/vkcv/File.cpp @@ -0,0 +1,58 @@ + +#include "vkcv/File.hpp" + +#include <cstdlib> + +#ifdef _WIN32 +#include <io.h> +#else +#include <unistd.h> +#endif + +#include "vkcv/Logger.hpp" + +namespace vkcv { + + std::filesystem::path generateTemporaryFilePath() { + std::filesystem::path tmp = generateTemporaryDirectoryPath(); + + if (std::filesystem::is_directory(tmp)) { + return std::filesystem::path(tmp.string() + "W"); // add W for Wambo + } else { + return tmp; + } + } + + std::filesystem::path generateTemporaryDirectoryPath() { + std::error_code code; + auto tmp = std::filesystem::temp_directory_path(code); + + if (tmp.empty()) { + tmp = std::filesystem::current_path(); + } + + char name [16] = "vkcv_tmp_XXXXXX"; + +#ifdef _WIN32 + int err = _mktemp_s(name, 16); + + if (err != 0) { + vkcv_log(LogLevel::ERROR, "Temporary file path could not be generated"); + return ""; + } +#else + int fd = mkstemp(name); // creates a file locally + + if (fd == -1) { + vkcv_log(LogLevel::ERROR, "Temporary file path could not be generated"); + return ""; + } + + close(fd); + remove(name); // removes the local file again +#endif + + return tmp / name; + } + +} // namespace vkcv diff --git a/src/vkcv/GraphicsPipelineConfig.cpp b/src/vkcv/GraphicsPipelineConfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..987cd3a74492a2b4f1428fa75e546ce7ffcba8b9 --- /dev/null +++ b/src/vkcv/GraphicsPipelineConfig.cpp @@ -0,0 +1,117 @@ + +#include "vkcv/GraphicsPipelineConfig.hpp" + +namespace vkcv { + + GraphicsPipelineConfig::GraphicsPipelineConfig() : + PipelineConfig(), m_PassHandle(), m_VertexLayout(), + m_Width(std::numeric_limits<uint32_t>::max()), + m_Height(std::numeric_limits<uint32_t>::max()) {} + + GraphicsPipelineConfig::GraphicsPipelineConfig( + const ShaderProgram &program, const PassHandle &pass, const VertexLayout &vertexLayout, + const std::vector<DescriptorSetLayoutHandle> &layouts) : + PipelineConfig(program, layouts), + m_PassHandle(pass), m_VertexLayout(vertexLayout), + m_Width(std::numeric_limits<uint32_t>::max()), + m_Height(std::numeric_limits<uint32_t>::max()) {} + + const PassHandle &GraphicsPipelineConfig::getPass() const { + return m_PassHandle; + } + + const VertexLayout &GraphicsPipelineConfig::getVertexLayout() const { + return m_VertexLayout; + } + + uint32_t GraphicsPipelineConfig::getWidth() const { + return m_Width; + } + + uint32_t GraphicsPipelineConfig::getHeight() const { + return m_Height; + } + + void GraphicsPipelineConfig::setResolution(uint32_t width, uint32_t height) { + m_Width = width; + m_Height = height; + } + + bool GraphicsPipelineConfig::isViewportDynamic() const { + return ((m_Width == std::numeric_limits<uint32_t>::max()) + && (m_Height == std::numeric_limits<uint32_t>::max())); + } + + bool GraphicsPipelineConfig::isUsingConservativeRasterization() const { + return m_UseConservativeRasterization; + } + + void GraphicsPipelineConfig::setUsingConservativeRasterization(bool conservativeRasterization) { + m_UseConservativeRasterization = conservativeRasterization; + } + + PrimitiveTopology GraphicsPipelineConfig::getPrimitiveTopology() const { + return m_PrimitiveTopology; + } + + void GraphicsPipelineConfig::setPrimitiveTopology(PrimitiveTopology primitiveTopology) { + m_PrimitiveTopology = primitiveTopology; + } + + BlendMode GraphicsPipelineConfig::getBlendMode() const { + return m_blendMode; + } + + void GraphicsPipelineConfig::setBlendMode(BlendMode blendMode) { + m_blendMode = blendMode; + } + + bool GraphicsPipelineConfig::isDepthClampingEnabled() const { + return m_EnableDepthClamping; + } + + void GraphicsPipelineConfig::setDepthClampingEnabled(bool depthClamping) { + m_EnableDepthClamping = depthClamping; + } + + CullMode GraphicsPipelineConfig::getCulling() const { + return m_Culling; + } + + void GraphicsPipelineConfig::setCulling(CullMode cullMode) { + m_Culling = cullMode; + } + + DepthTest GraphicsPipelineConfig::getDepthTest() const { + return m_DepthTest; + } + + void GraphicsPipelineConfig::setDepthTest(DepthTest depthTest) { + m_DepthTest = depthTest; + } + + bool GraphicsPipelineConfig::isWritingDepth() const { + return m_DepthWrite; + } + + void GraphicsPipelineConfig::setWritingDepth(bool writingDepth) { + m_DepthWrite = writingDepth; + } + + bool GraphicsPipelineConfig::isWritingAlphaToCoverage() const { + return m_AlphaToCoverage; + } + + void GraphicsPipelineConfig::setWritingAlphaToCoverage(bool alphaToCoverage) { + m_AlphaToCoverage = alphaToCoverage; + } + + uint32_t GraphicsPipelineConfig::getTesselationControlPoints() const { + return m_TessellationControlPoints; + } + + void GraphicsPipelineConfig::setTesselationControlPoints(uint32_t tessellationControlPoints) { + m_TessellationControlPoints = tessellationControlPoints; + } + +} // namespace vkcv diff --git a/src/vkcv/GraphicsPipelineManager.cpp b/src/vkcv/GraphicsPipelineManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e1af7f7b044a1e24a27644bc988a2ab0c2bf4e8 --- /dev/null +++ b/src/vkcv/GraphicsPipelineManager.cpp @@ -0,0 +1,683 @@ +#include "GraphicsPipelineManager.hpp" + +#include "vkcv/Core.hpp" +#include "vkcv/Image.hpp" +#include "vkcv/Logger.hpp" +#include "vkcv/Multisampling.hpp" + +namespace vkcv { + + uint64_t GraphicsPipelineManager::getIdFrom(const GraphicsPipelineHandle &handle) const { + return handle.getId(); + } + + GraphicsPipelineHandle + GraphicsPipelineManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return GraphicsPipelineHandle(id, destroy); + } + + void GraphicsPipelineManager::destroyById(uint64_t id) { + auto &pipeline = getById(id); + + if (pipeline.m_handle) { + getCore().getContext().getDevice().destroy(pipeline.m_handle); + pipeline.m_handle = nullptr; + } + + if (pipeline.m_layout) { + getCore().getContext().getDevice().destroy(pipeline.m_layout); + pipeline.m_layout = nullptr; + } + } + + GraphicsPipelineManager::GraphicsPipelineManager() noexcept : + HandleManager<GraphicsPipelineEntry, GraphicsPipelineHandle>() {} + + GraphicsPipelineManager::~GraphicsPipelineManager() noexcept { + clear(); + } + + // currently assuming default 32 bit formats, no lower precision or normalized variants + // supported + vk::Format vertexFormatToVulkanFormat(const VertexAttachmentFormat format) { + switch (format) { + case VertexAttachmentFormat::FLOAT: + return vk::Format::eR32Sfloat; + case VertexAttachmentFormat::FLOAT2: + return vk::Format::eR32G32Sfloat; + case VertexAttachmentFormat::FLOAT3: + return vk::Format::eR32G32B32Sfloat; + case VertexAttachmentFormat::FLOAT4: + return vk::Format::eR32G32B32A32Sfloat; + case VertexAttachmentFormat::INT: + return vk::Format::eR32Sint; + case VertexAttachmentFormat::INT2: + return vk::Format::eR32G32Sint; + case VertexAttachmentFormat::INT3: + return vk::Format::eR32G32B32Sint; + case VertexAttachmentFormat::INT4: + return vk::Format::eR32G32B32A32Sint; + default: + vkcv_log(LogLevel::WARNING, "Unknown vertex format"); + return vk::Format::eUndefined; + } + } + + vk::PrimitiveTopology + primitiveTopologyToVulkanPrimitiveTopology(const PrimitiveTopology topology) { + switch (topology) { + case (PrimitiveTopology::PointList): + return vk::PrimitiveTopology::ePointList; + case (PrimitiveTopology::LineList): + return vk::PrimitiveTopology::eLineList; + case (PrimitiveTopology::TriangleList): + return vk::PrimitiveTopology::eTriangleList; + case (PrimitiveTopology::PatchList): + return vk::PrimitiveTopology::ePatchList; + default: + vkcv_log(LogLevel::ERROR, "Unknown primitive topology type"); + return vk::PrimitiveTopology::eTriangleList; + } + } + + vk::CompareOp depthTestToVkCompareOp(DepthTest depthTest) { + switch (depthTest) { + case (DepthTest::None): + return vk::CompareOp::eAlways; + case (DepthTest::Less): + return vk::CompareOp::eLess; + case (DepthTest::LessEqual): + return vk::CompareOp::eLessOrEqual; + case (DepthTest::Greater): + return vk::CompareOp::eGreater; + case (DepthTest::GreatherEqual): + return vk::CompareOp::eGreaterOrEqual; + case (DepthTest::Equal): + return vk::CompareOp::eEqual; + default: + vkcv_log(LogLevel::ERROR, "Unknown depth test enum"); + return vk::CompareOp::eAlways; + } + } + + vk::ShaderStageFlagBits shaderStageToVkShaderStage(ShaderStage stage) { + switch (stage) { + case ShaderStage::VERTEX: + return vk::ShaderStageFlagBits::eVertex; + case ShaderStage::FRAGMENT: + return vk::ShaderStageFlagBits::eFragment; + case ShaderStage::GEOMETRY: + return vk::ShaderStageFlagBits::eGeometry; + case ShaderStage::TESS_CONTROL: + return vk::ShaderStageFlagBits::eTessellationControl; + case ShaderStage::TESS_EVAL: + return vk::ShaderStageFlagBits::eTessellationEvaluation; + case ShaderStage::COMPUTE: + return vk::ShaderStageFlagBits::eCompute; + case ShaderStage::TASK: + return vk::ShaderStageFlagBits::eTaskNV; + case ShaderStage::MESH: + return vk::ShaderStageFlagBits::eMeshNV; + default: + vkcv_log(LogLevel::ERROR, "Unknown shader stage"); + return vk::ShaderStageFlagBits::eAll; + } + } + + bool createPipelineShaderStageCreateInfo(const ShaderProgram &shaderProgram, ShaderStage stage, + vk::Device device, + vk::PipelineShaderStageCreateInfo* outCreateInfo) { + + assert(outCreateInfo); + std::vector<uint32_t> code = shaderProgram.getShaderBinary(stage); + vk::ShaderModuleCreateInfo vertexModuleInfo({}, code.size() * sizeof(uint32_t), + code.data()); + vk::ShaderModule shaderModule; + if (device.createShaderModule(&vertexModuleInfo, nullptr, &shaderModule) + != vk::Result::eSuccess) + return false; + + const static auto entryName = "main"; + + *outCreateInfo = vk::PipelineShaderStageCreateInfo({}, shaderStageToVkShaderStage(stage), + shaderModule, entryName, nullptr); + return true; + } + + /** + * Fills Vertex Attribute and Binding Description with the corresponding objects form the Vertex + * Layout. + * @param vertexAttributeDescriptions + * @param vertexBindingDescriptions + * @param existsVertexShader + * @param config + */ + void fillVertexInputDescription( + std::vector<vk::VertexInputAttributeDescription> &vertexAttributeDescriptions, + std::vector<vk::VertexInputBindingDescription> &vertexBindingDescriptions, + const bool existsVertexShader, const GraphicsPipelineConfig &config) { + + if (existsVertexShader) { + const VertexLayout &layout = config.getVertexLayout(); + + // iterate over the layout's specified, mutually exclusive buffer bindings that make up + // a vertex buffer + for (const auto &vertexBinding : layout.vertexBindings) { + vertexBindingDescriptions.emplace_back(vertexBinding.bindingLocation, + vertexBinding.stride, + vk::VertexInputRate::eVertex); + + // iterate over the bindings' specified, mutually exclusive vertex input attachments + // that make up a vertex + for (const auto &vertexAttachment : vertexBinding.vertexAttachments) { + vertexAttributeDescriptions.emplace_back( + vertexAttachment.inputLocation, vertexBinding.bindingLocation, + vertexFormatToVulkanFormat(vertexAttachment.format), + vertexAttachment.offset % vertexBinding.stride); + } + } + } + } + + /** + * Creates a Pipeline Vertex Input State Create Info Struct and fills it with Attribute and + * Binding data. + * @param vertexAttributeDescriptions + * @param vertexBindingDescriptions + * @return Pipeline Vertex Input State Create Info Struct + */ + vk::PipelineVertexInputStateCreateInfo createPipelineVertexInputStateCreateInfo( + std::vector<vk::VertexInputAttributeDescription> &vertexAttributeDescriptions, + std::vector<vk::VertexInputBindingDescription> &vertexBindingDescriptions) { + + vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo( + {}, vertexBindingDescriptions.size(), vertexBindingDescriptions.data(), + vertexAttributeDescriptions.size(), vertexAttributeDescriptions.data()); + return pipelineVertexInputStateCreateInfo; + } + + /** + * Creates a Pipeline Input Assembly State Create Info Struct with 'Primitive Restart' disabled. + * @param config provides data for primitive topology. + * @return Pipeline Input Assembly State Create Info Struct + */ + vk::PipelineInputAssemblyStateCreateInfo + createPipelineInputAssemblyStateCreateInfo(const GraphicsPipelineConfig &config) { + vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo( + {}, primitiveTopologyToVulkanPrimitiveTopology(config.getPrimitiveTopology()), false); + + return pipelineInputAssemblyStateCreateInfo; + } + + vk::PipelineTessellationStateCreateInfo + createPipelineTessellationStateCreateInfo(const GraphicsPipelineConfig &config) { + vk::PipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo( + {}, config.getTesselationControlPoints()); + + return pipelineTessellationStateCreateInfo; + } + + /** + * Creates a Pipeline Viewport State Create Info Struct with default set viewport and scissor + * settings. + * @param config provides with and height of the output window + * @return Pipeline Viewport State Create Info Struct + */ + vk::PipelineViewportStateCreateInfo + createPipelineViewportStateCreateInfo(const GraphicsPipelineConfig &config) { + static vk::Viewport viewport; + static vk::Rect2D scissor; + + viewport = vk::Viewport(0.f, 0.f, static_cast<float>(config.getWidth()), + static_cast<float>(config.getHeight()), 0.f, 1.f); + + scissor = vk::Rect2D({ 0, 0 }, { config.getWidth(), config.getHeight() }); + + vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo({}, 1, &viewport, 1, + &scissor); + + return pipelineViewportStateCreateInfo; + } + + /** + * Creates a Pipeline Rasterization State Create Info Struct with default values set to: + * Rasterizer Discard: Disabled + * Polygon Mode: Fill + * Front Face: Counter Clockwise + * Depth Bias: Disabled + * Line Width: 1.0 + * Depth Clamping and Culling Mode ist set by the Pipeline Config + * @param config sets Depth Clamping and Culling Mode + * @return Pipeline Rasterization State Create Info Struct + */ + vk::PipelineRasterizationStateCreateInfo createPipelineRasterizationStateCreateInfo( + const GraphicsPipelineConfig &config, + const vk::PhysicalDeviceConservativeRasterizationPropertiesEXT + &conservativeRasterProperties) { + vk::CullModeFlags cullMode; + switch (config.getCulling()) { + case CullMode::None: + cullMode = vk::CullModeFlagBits::eNone; + break; + case CullMode::Front: + cullMode = vk::CullModeFlagBits::eFront; + break; + case CullMode::Back: + cullMode = vk::CullModeFlagBits::eBack; + break; + case CullMode::Both: + cullMode = vk::CullModeFlagBits::eFrontAndBack; + break; + default: + vkcv_log(LogLevel::ERROR, "Unknown CullMode"); + cullMode = vk::CullModeFlagBits::eNone; + } + + vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo( + {}, config.isDepthClampingEnabled(), false, vk::PolygonMode::eFill, cullMode, + vk::FrontFace::eCounterClockwise, false, 0.f, 0.f, 0.f, 1.f); + + static vk::PipelineRasterizationConservativeStateCreateInfoEXT conservativeRasterization; + + if (config.isUsingConservativeRasterization()) { + const float overestimationSize = + 1.0f - conservativeRasterProperties.primitiveOverestimationSize; + const float maxOverestimationSize = + conservativeRasterProperties.maxExtraPrimitiveOverestimationSize; + + conservativeRasterization = vk::PipelineRasterizationConservativeStateCreateInfoEXT( + {}, vk::ConservativeRasterizationModeEXT::eOverestimate, + std::min(std::max(overestimationSize, 0.f), maxOverestimationSize)); + + pipelineRasterizationStateCreateInfo.pNext = &conservativeRasterization; + } + + return pipelineRasterizationStateCreateInfo; + } + + /** + * Creates a Pipeline Multisample State Create Info Struct. + * @param config set MSAA Sample Count Flag + * @return Pipeline Multisample State Create Info Struct + */ + vk::PipelineMultisampleStateCreateInfo + createPipelineMultisampleStateCreateInfo(const GraphicsPipelineConfig &config, + const PassConfig &passConfig) { + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo( + {}, msaaToSampleCountFlagBits(passConfig.getMultisampling()), false, 0.f, nullptr, + config.isWritingAlphaToCoverage(), false); + + return pipelineMultisampleStateCreateInfo; + } + + /** + * Creates a Pipeline Color Blend State Create Info Struct. + * Currently only one blend mode is supported! There for, blending is set to additive. + * @param config sets blend mode + * @return + */ + vk::PipelineColorBlendStateCreateInfo + createPipelineColorBlendStateCreateInfo(const GraphicsPipelineConfig &config, + const PassConfig &passConfig) { + static std::vector<vk::PipelineColorBlendAttachmentState> colorBlendAttachmentStates; + + colorBlendAttachmentStates.clear(); + colorBlendAttachmentStates.reserve(passConfig.getAttachments().size()); + + for (const auto& attachment : passConfig.getAttachments()) { + if ((isDepthFormat(attachment.getFormat())) || + (isStencilFormat(attachment.getFormat()))) { + continue; + } + + // currently set to additive, if not disabled + // BlendFactors must be set as soon as additional BlendModes are added + vk::PipelineColorBlendAttachmentState colorBlendAttachmentState ( + config.getBlendMode() != BlendMode::None, + vk::BlendFactor::eOne, + vk::BlendFactor::eOne, + vk::BlendOp::eAdd, + vk::BlendFactor::eOne, + vk::BlendFactor::eOne, + vk::BlendOp::eAdd, + vk::ColorComponentFlags( + VK_COLOR_COMPONENT_R_BIT | + VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | + VK_COLOR_COMPONENT_A_BIT + ) + ); + + colorBlendAttachmentStates.push_back(colorBlendAttachmentState); + } + + vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo( + {}, + false, + vk::LogicOp::eClear, + colorBlendAttachmentStates.size(), + colorBlendAttachmentStates.data(), + { 1.f, 1.f, 1.f, 1.f } + ); + + return pipelineColorBlendStateCreateInfo; + } + + /** + * Creates a Pipeline Layout Create Info Struct. + * @param config sets Push Constant Size and Descriptor Layouts. + * @return Pipeline Layout Create Info Struct + */ + vk::PipelineLayoutCreateInfo createPipelineLayoutCreateInfo( + const GraphicsPipelineConfig &config, + const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) { + static vk::PushConstantRange pushConstantRange; + + const size_t pushConstantsSize = config.getShaderProgram().getPushConstantsSize(); + pushConstantRange = + vk::PushConstantRange(vk::ShaderStageFlagBits::eAll, 0, pushConstantsSize); + + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, (descriptorSetLayouts), + (pushConstantRange)); + + if (pushConstantsSize == 0) { + pipelineLayoutCreateInfo.pushConstantRangeCount = 0; + } + + return pipelineLayoutCreateInfo; + } + + /** + * Creates a Pipeline Depth Stencil State Create Info Struct. + * @param config sets if depth test in enabled or not. + * @return Pipeline Layout Create Info Struct + */ + vk::PipelineDepthStencilStateCreateInfo + createPipelineDepthStencilStateCreateInfo(const GraphicsPipelineConfig &config) { + const vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilCreateInfo( + vk::PipelineDepthStencilStateCreateFlags(), config.getDepthTest() != DepthTest::None, + config.isWritingDepth(), depthTestToVkCompareOp(config.getDepthTest()), false, false, + {}, {}, 0.0f, 1.0f); + + return pipelineDepthStencilCreateInfo; + } + + /** + * Creates a Pipeline Dynamic State Create Info Struct. + * @param config sets whenever a dynamic viewport is used or not. + * @return Pipeline Dynamic State Create Info Struct + */ + vk::PipelineDynamicStateCreateInfo + createPipelineDynamicStateCreateInfo(const GraphicsPipelineConfig &config) { + static std::vector<vk::DynamicState> dynamicStates; + dynamicStates.clear(); + + if (config.isViewportDynamic()) { + dynamicStates.push_back(vk::DynamicState::eViewport); + dynamicStates.push_back(vk::DynamicState::eScissor); + } + + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo( + {}, static_cast<uint32_t>(dynamicStates.size()), dynamicStates.data()); + + return dynamicStateCreateInfo; + } + + GraphicsPipelineHandle + GraphicsPipelineManager::createPipeline(const GraphicsPipelineConfig &config, + const PassManager &passManager, + const DescriptorSetLayoutManager &descriptorManager) { + const vk::RenderPass &pass = passManager.getVkPass(config.getPass()); + + const auto &program = config.getShaderProgram(); + + const bool existsTaskShader = program.existsShader(ShaderStage::TASK); + const bool existsMeshShader = program.existsShader(ShaderStage::MESH); + const bool existsVertexShader = program.existsShader(ShaderStage::VERTEX); + const bool existsFragmentShader = program.existsShader(ShaderStage::FRAGMENT); + const bool existsGeometryShader = program.existsShader(ShaderStage::GEOMETRY); + const bool existsTessellationControlShader = + program.existsShader(ShaderStage::TESS_CONTROL); + const bool existsTessellationEvaluationShader = + program.existsShader(ShaderStage::TESS_EVAL); + + const bool validGeometryStages = + ((existsVertexShader + && (existsTessellationControlShader == existsTessellationEvaluationShader)) + || (existsTaskShader && existsMeshShader)); + + if (!validGeometryStages) { + vkcv_log(LogLevel::ERROR, "Requires vertex or task and mesh shader"); + return {}; + } + + if (!existsFragmentShader) { + vkcv_log(LogLevel::ERROR, "Requires fragment shader code"); + return {}; + } + + std::vector<vk::PipelineShaderStageCreateInfo> shaderStages; + auto destroyShaderModules = [&shaderStages, this] { + for (auto stage : shaderStages) { + getCore().getContext().getDevice().destroyShaderModule(stage.module); + } + shaderStages.clear(); + }; + + if (existsVertexShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::VERTEX, getCore().getContext().getDevice(), &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + if (existsTaskShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::TASK, getCore().getContext().getDevice(), &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + if (existsMeshShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::MESH, getCore().getContext().getDevice(), &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::FRAGMENT, getCore().getContext().getDevice(), &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + if (existsGeometryShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::GEOMETRY, getCore().getContext().getDevice(), &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + if (existsTessellationControlShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::TESS_CONTROL, getCore().getContext().getDevice(), + &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + if (existsTessellationEvaluationShader) { + vk::PipelineShaderStageCreateInfo createInfo; + const bool success = createPipelineShaderStageCreateInfo( + program, ShaderStage::TESS_EVAL, getCore().getContext().getDevice(), &createInfo); + + if (success) { + shaderStages.push_back(createInfo); + } else { + destroyShaderModules(); + return {}; + } + } + + const PassConfig &passConfig = passManager.getPassConfig(config.getPass()); + + // vertex input state + // Fill up VertexInputBindingDescription and VertexInputAttributeDescription Containers + std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; + std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; + fillVertexInputDescription(vertexAttributeDescriptions, vertexBindingDescriptions, + existsVertexShader, config); + + // Handover Containers to PipelineVertexInputStateCreateIngo Struct + vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = + createPipelineVertexInputStateCreateInfo(vertexAttributeDescriptions, + vertexBindingDescriptions); + + // input assembly state + vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo = + createPipelineInputAssemblyStateCreateInfo(config); + + // tesselation state + vk::PipelineTessellationStateCreateInfo pipelineTessellationStateCreateInfo = + createPipelineTessellationStateCreateInfo(config); + + // viewport state + vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo = + createPipelineViewportStateCreateInfo(config); + + // rasterization state + vk::PhysicalDeviceConservativeRasterizationPropertiesEXT conservativeRasterProperties; + vk::PhysicalDeviceProperties deviceProperties; + vk::PhysicalDeviceProperties2 deviceProperties2(deviceProperties); + deviceProperties2.pNext = &conservativeRasterProperties; + getCore().getContext().getPhysicalDevice().getProperties2(&deviceProperties2); + vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo = + createPipelineRasterizationStateCreateInfo(config, conservativeRasterProperties); + + // multisample state + vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = + createPipelineMultisampleStateCreateInfo(config, passConfig); + + // color blend state + vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo = + createPipelineColorBlendStateCreateInfo(config, passConfig); + + // Dynamic State + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = + createPipelineDynamicStateCreateInfo(config); + + std::vector<vk::DescriptorSetLayout> descriptorSetLayouts; + descriptorSetLayouts.reserve(config.getDescriptorSetLayouts().size()); + for (const auto &handle : config.getDescriptorSetLayouts()) { + descriptorSetLayouts.push_back( + descriptorManager.getDescriptorSetLayout(handle).vulkanHandle); + } + + // pipeline layout + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = + createPipelineLayoutCreateInfo(config, descriptorSetLayouts); + + vk::PipelineLayout vkPipelineLayout {}; + if (getCore().getContext().getDevice().createPipelineLayout(&pipelineLayoutCreateInfo, + nullptr, &vkPipelineLayout) + != vk::Result::eSuccess) { + destroyShaderModules(); + return {}; + } + + // Depth Stencil + const vk::PipelineDepthStencilStateCreateInfo depthStencilCreateInfo = + createPipelineDepthStencilStateCreateInfo(config); + + const vk::PipelineDepthStencilStateCreateInfo* p_depthStencilCreateInfo = nullptr; + + for (const auto &attachment : passConfig.getAttachments()) { + if ((isDepthFormat(attachment.getFormat())) + || (isStencilFormat(attachment.getFormat()))) { + p_depthStencilCreateInfo = &depthStencilCreateInfo; + break; + } + } + + // Get all setting structs together and create the Pipeline + const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( + {}, static_cast<uint32_t>(shaderStages.size()), shaderStages.data(), + &pipelineVertexInputStateCreateInfo, &pipelineInputAssemblyStateCreateInfo, + &pipelineTessellationStateCreateInfo, &pipelineViewportStateCreateInfo, + &pipelineRasterizationStateCreateInfo, &pipelineMultisampleStateCreateInfo, + p_depthStencilCreateInfo, &pipelineColorBlendStateCreateInfo, &dynamicStateCreateInfo, + vkPipelineLayout, pass, 0, {}, 0); + + vk::Pipeline vkPipeline {}; + if (getCore().getContext().getDevice().createGraphicsPipelines( + nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline) + != vk::Result::eSuccess) { + // Catch runtime error if the creation of the pipeline fails. + // Destroy everything to keep the memory clean. + destroyShaderModules(); + return {}; + } + + // Clean Up + destroyShaderModules(); + + // Hand over Handler to main Application + return add({ vkPipeline, vkPipelineLayout, config }); + } + + vk::Pipeline + GraphicsPipelineManager::getVkPipeline(const GraphicsPipelineHandle &handle) const { + auto &pipeline = (*this) [handle]; + return pipeline.m_handle; + } + + vk::PipelineLayout + GraphicsPipelineManager::getVkPipelineLayout(const GraphicsPipelineHandle &handle) const { + auto &pipeline = (*this) [handle]; + return pipeline.m_layout; + } + + const GraphicsPipelineConfig & + GraphicsPipelineManager::getPipelineConfig(const GraphicsPipelineHandle &handle) const { + auto &pipeline = (*this) [handle]; + return pipeline.m_config; + } + +} // namespace vkcv diff --git a/src/vkcv/GraphicsPipelineManager.hpp b/src/vkcv/GraphicsPipelineManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..602e1a96d29a4b78a3784c215074b9541bf5de47 --- /dev/null +++ b/src/vkcv/GraphicsPipelineManager.hpp @@ -0,0 +1,98 @@ +#pragma once + +/** + * @authors Mark Mints + * @file src/vkcv/PipelineManager.hpp + * @brief Creation and handling of Graphic Pipelines + */ +// TODO: Edit @brief: add graphics pipeline, but only then when the compute part is in an own class. +// TODO: More authors? Do we need authors (general question)? + +#include "DescriptorSetLayoutManager.hpp" +#include "HandleManager.hpp" +#include "PassManager.hpp" +#include "vkcv/GraphicsPipelineConfig.hpp" +#include <vector> +#include <vulkan/vulkan.hpp> + +namespace vkcv { + + struct GraphicsPipelineEntry { + vk::Pipeline m_handle; + vk::PipelineLayout m_layout; + GraphicsPipelineConfig m_config; + }; + + /** + * @brief Class to manage graphics pipelines. + */ + class GraphicsPipelineManager : + public HandleManager<GraphicsPipelineEntry, GraphicsPipelineHandle> { + private: + [[nodiscard]] uint64_t getIdFrom(const GraphicsPipelineHandle &handle) const override; + + [[nodiscard]] GraphicsPipelineHandle + createById(uint64_t id, const HandleDestroyFunction &destroy) override; + + /** + * Destroys and deallocates graphics pipeline represented by a given + * graphics pipeline handle id. + * + * @param id Graphics pipeline handle id + */ + void destroyById(uint64_t id) override; + + public: + GraphicsPipelineManager() noexcept; + + ~GraphicsPipelineManager() noexcept override; // dtor + + GraphicsPipelineManager(const GraphicsPipelineManager &other) = delete; // copy-ctor + GraphicsPipelineManager(GraphicsPipelineManager &&other) = delete; // move-ctor; + + GraphicsPipelineManager & + operator=(const GraphicsPipelineManager &other) = delete; // copy-assign op + GraphicsPipelineManager & + operator=(GraphicsPipelineManager &&other) = delete; // move-assign op + + /** + * Creates a Graphics Pipeline based on the set shader stages in the Config Struct. + * This function is wrapped in /src/vkcv/Core.cpp by Core::createGraphicsPipeline(const + * PipelineConfig &config). Therefore the passManager is filled already by the overall + * context of an application. On application level it is necessary first to fill a + * PipelineConfig Struct. + * @param config Hands over all needed information for pipeline creation. + * @param passManager Hands over the corresponding render pass. + * @param descriptorManager Hands over the corresponding descriptor set layouts + * @return A Handler to the created Graphics Pipeline Object. + */ + GraphicsPipelineHandle createPipeline(const GraphicsPipelineConfig &config, + const PassManager &passManager, + const DescriptorSetLayoutManager &descriptorManager); + + /** + * Returns a vk::Pipeline object by handle. + * @param handle Directing to the requested pipeline. + * @return vk::Pipeline. + */ + [[nodiscard]] vk::Pipeline getVkPipeline(const GraphicsPipelineHandle &handle) const; + + /** + * Returns a vk::PipelineLayout object by handle. + * @param handle Directing to the requested pipeline. + * @return vk::PipelineLayout. + */ + [[nodiscard]] vk::PipelineLayout + getVkPipelineLayout(const GraphicsPipelineHandle &handle) const; + + /** + * Returns the corresponding Pipeline Config Struct of a pipeline object directed by the + * given Handler. + * @param handle Directing to the requested pipeline. + * @return Pipeline Config Struct + */ + [[nodiscard]] const GraphicsPipelineConfig & + getPipelineConfig(const GraphicsPipelineHandle &handle) const; + }; + +} // namespace vkcv diff --git a/src/vkcv/HandleManager.hpp b/src/vkcv/HandleManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a7b4b7aab30f4ce29d1f91c04336e733aba17b1b --- /dev/null +++ b/src/vkcv/HandleManager.hpp @@ -0,0 +1,112 @@ +#pragma once + +#include <optional> +#include <vector> + +#include "vkcv/Handles.hpp" +#include "vkcv/Logger.hpp" + +namespace vkcv { + + class Core; + + template <typename T, typename H = Handle> + class HandleManager { + friend class Core; + + private: + Core* m_core; + std::vector<T> m_entries; + + protected: + HandleManager() noexcept : m_core(nullptr), m_entries() {} + + virtual bool init(Core &core) { + if (m_core) { + vkcv_log(vkcv::LogLevel::ERROR, "Manager is already initialized"); + return false; + } + + if (!m_entries.empty()) { + vkcv_log(vkcv::LogLevel::WARNING, + "Entries added before initialization will be erased"); + } + + m_core = &core; + m_entries.clear(); + return true; + } + + [[nodiscard]] const Core &getCore() const { + return *m_core; + } + + [[nodiscard]] Core &getCore() { + return *m_core; + } + + [[nodiscard]] size_t getCount() const { + return m_entries.size(); + } + + [[nodiscard]] const T &getById(uint64_t id) const { + if (id >= m_entries.size()) { + static T invalid; + vkcv_log(vkcv::LogLevel::ERROR, "Invalid handle id"); + return invalid; + } + + return m_entries [id]; + } + + [[nodiscard]] T &getById(uint64_t id) { + if (id >= m_entries.size()) { + static T invalid; + vkcv_log(vkcv::LogLevel::ERROR, "Invalid handle id"); + return invalid; + } + + return m_entries [id]; + } + + virtual uint64_t getIdFrom(const H &handle) const = 0; + + [[nodiscard]] virtual const T &operator[](const H &handle) const { + const Handle &_handle = handle; + return getById(getIdFrom(static_cast<const H &>(_handle))); + } + + [[nodiscard]] virtual T &operator[](const H &handle) { + const Handle &_handle = handle; + return getById(getIdFrom(static_cast<const H &>(_handle))); + } + + H add(const T &entry) { + const uint64_t id = m_entries.size(); + m_entries.push_back(entry); + return createById(id, [&](uint64_t id) { + destroyById(id); + }); + } + + virtual H createById(uint64_t id, const HandleDestroyFunction &destroy) = 0; + + virtual void destroyById(uint64_t id) = 0; + + void clear() { + for (uint64_t id = 0; id < m_entries.size(); id++) { + destroyById(id); + } + } + + public: + HandleManager(HandleManager &&other) = delete; + HandleManager(const HandleManager &other) = delete; + + HandleManager &operator=(HandleManager &&other) = delete; + HandleManager &operator=(const HandleManager &other) = delete; + + virtual ~HandleManager() noexcept = default; + }; + +} // namespace vkcv diff --git a/src/vkcv/Handles.cpp b/src/vkcv/Handles.cpp index 020489418c8e2db6ce2062d6fd20f06f90a05c37..64ab3080f5e6a649fa4fb0d8f772872713cb0140 100644 --- a/src/vkcv/Handles.cpp +++ b/src/vkcv/Handles.cpp @@ -1,99 +1,96 @@ #include "vkcv/Handles.hpp" +#include <iostream> +#include <limits> + namespace vkcv { - - Handle::Handle() : - m_id(UINT64_MAX), m_rc(nullptr), m_destroy(nullptr) - {} - - Handle::Handle(uint64_t id, const HandleDestroyFunction& destroy) : - m_id(id), m_rc(new uint64_t(1)), m_destroy(destroy) - {} - + + Handle::Handle() : m_id(std::numeric_limits<uint64_t>::max()), m_rc(nullptr), m_destroy(nullptr) {} + + Handle::Handle(uint64_t id, const HandleDestroyFunction &destroy) : + m_id(id), m_rc(new uint64_t(1)), m_destroy(destroy) {} + Handle::~Handle() { - if ((m_rc) && (--(*m_rc) == 0)) { + if ((m_rc) && (*m_rc > 0) && (--(*m_rc) == 0)) { if (m_destroy) { m_destroy(m_id); } - + delete m_rc; } } - + Handle::Handle(const Handle &other) : - m_id(other.m_id), - m_rc(other.m_rc), - m_destroy(other.m_destroy) - { + m_id(other.m_id), m_rc(other.m_rc), m_destroy(other.m_destroy) { if (m_rc) { ++(*m_rc); } } - + Handle::Handle(Handle &&other) noexcept : - m_id(other.m_id), - m_rc(other.m_rc), - m_destroy(other.m_destroy) - { + m_id(other.m_id), m_rc(other.m_rc), m_destroy(other.m_destroy) { other.m_rc = nullptr; + other.m_destroy = nullptr; } - + Handle &Handle::operator=(const Handle &other) { if (&other == this) { return *this; } - + m_id = other.m_id; m_rc = other.m_rc; m_destroy = other.m_destroy; - + if (m_rc) { ++(*m_rc); } - + return *this; } - + Handle &Handle::operator=(Handle &&other) noexcept { m_id = other.m_id; m_rc = other.m_rc; m_destroy = other.m_destroy; - + other.m_rc = nullptr; - + other.m_destroy = nullptr; + return *this; } - + uint64_t Handle::getId() const { return m_id; } - + uint64_t Handle::getRC() const { - return m_rc? *m_rc : 0; + return m_rc ? *m_rc : 0; } - + Handle::operator bool() const { - return (m_id < UINT64_MAX); + return (m_id < std::numeric_limits<uint64_t>::max()); } - + bool Handle::operator!() const { - return (m_id == UINT64_MAX); + return (m_id == std::numeric_limits<uint64_t>::max()); } - - std::ostream& operator << (std::ostream& out, const Handle& handle) { + + std::ostream &operator<<(std::ostream &out, const Handle &handle) { if (handle) { - return out << "[Handle: " << handle.getId() << ":" << handle.getRC() << "]"; + return out << "[" << typeid(handle).name() << ": " << handle.getId() << ":" + << handle.getRC() << "]"; } else { - return out << "[Handle: none]"; + return out << "[" << typeid(handle).name() << ": none]"; } } - + bool ImageHandle::isSwapchainImage() const { - return (getId() == UINT64_MAX - 1); + return (getId() == std::numeric_limits<uint64_t>::max() - 1); } - + ImageHandle ImageHandle::createSwapchainImageHandle(const HandleDestroyFunction &destroy) { - return ImageHandle(uint64_t(UINT64_MAX - 1), destroy); + return ImageHandle(uint64_t(std::numeric_limits<uint64_t>::max() - 1), destroy); } - -} + +} // namespace vkcv diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp index c48b015335e00f23a892bb96d3e89a2c0877ae61..fc6140fe4664096145aa08551422d919bcb4dc25 100644 --- a/src/vkcv/Image.cpp +++ b/src/vkcv/Image.cpp @@ -4,78 +4,97 @@ * @brief class for image handles */ #include "vkcv/Image.hpp" + #include "ImageManager.hpp" +#include "vkcv/Downsampler.hpp" + +namespace vkcv { -namespace vkcv{ - bool isDepthFormat(const vk::Format format) { switch (format) { - case(vk::Format::eD16Unorm): return true; - case(vk::Format::eD16UnormS8Uint): return true; - case(vk::Format::eD24UnormS8Uint): return true; - case(vk::Format::eD32Sfloat): return true; - case(vk::Format::eD32SfloatS8Uint): return true; - default: return false; + case (vk::Format::eD16Unorm): + case (vk::Format::eD16UnormS8Uint): + case (vk::Format::eD24UnormS8Uint): + case (vk::Format::eD32Sfloat): + case (vk::Format::eD32SfloatS8Uint): + return true; + default: + return false; } } - Image Image::create( - ImageManager* manager, - vk::Format format, - uint32_t width, - uint32_t height, - uint32_t depth, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment) - { - return Image(manager, manager->createImage(width, height, depth, format, mipCount, supportStorage, supportColorAttachment)); + bool isStencilFormat(const vk::Format format) { + switch (format) { + case (vk::Format::eS8Uint): + case (vk::Format::eD16UnormS8Uint): + case (vk::Format::eD24UnormS8Uint): + case (vk::Format::eD32SfloatS8Uint): + return true; + default: + return false; + } } - + vk::Format Image::getFormat() const { - return m_manager->getImageFormat(m_handle); + return m_core->getImageFormat(m_handle); } - + uint32_t Image::getWidth() const { - return m_manager->getImageWidth(m_handle); + return m_core->getImageWidth(m_handle); } - + uint32_t Image::getHeight() const { - return m_manager->getImageHeight(m_handle); + return m_core->getImageHeight(m_handle); } - + uint32_t Image::getDepth() const { - return m_manager->getImageDepth(m_handle); + return m_core->getImageDepth(m_handle); } - void Image::switchLayout(vk::ImageLayout newLayout) - { - m_manager->switchImageLayoutImmediate(m_handle, newLayout); + void Image::switchLayout(vk::ImageLayout newLayout) { + m_core->switchImageLayout(m_handle, newLayout); } - vkcv::ImageHandle Image::getHandle() const { + const vkcv::ImageHandle &Image::getHandle() const { return m_handle; } - uint32_t Image::getMipCount() const { - return m_manager->getImageMipCount(m_handle); + uint32_t Image::getMipLevels() const { + return m_core->getImageMipLevels(m_handle); } - void Image::fill(void *data, size_t size) { - m_manager->fillImage(m_handle, data, size); + void Image::fill(const void* data, size_t size) { + m_core->fillImage(m_handle, data, size, 0, 0); + } + + void Image::fillLayer(uint32_t layer, const void* data, size_t size) { + m_core->fillImage(m_handle, data, size, layer, 1); } - void Image::generateMipChainImmediate() { - m_manager->generateImageMipChainImmediate(m_handle); + void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle &cmdStream, + Downsampler &downsampler) { + downsampler.recordDownsampling(cmdStream, m_handle); } - void Image::recordMipChainGeneration(const vkcv::CommandStreamHandle& cmdStream) { - m_manager->recordImageMipChainGenerationToCmdStream(cmdStream, m_handle); + Image image(Core &core, vk::Format format, uint32_t width, uint32_t height, uint32_t depth, + bool createMipChain, bool supportStorage, bool supportColorAttachment, + Multisampling multisampling) { + ImageConfig config (width, height, depth); + config.setSupportingStorage(supportStorage); + config.setSupportingColorAttachment(supportColorAttachment); + config.setMultisampling(multisampling); + return image(core, format, config, createMipChain); } - Image::Image(ImageManager* manager, const ImageHandle& handle) : - m_manager(manager), - m_handle(handle) - {} + Image image(Core &core, vk::Format format, const ImageConfig &config, bool createMipChain) { + return Image( + &core, + core.createImage( + format, + config, + createMipChain + ) + ); + } -} +} // namespace vkcv diff --git a/src/vkcv/ImageConfig.cpp b/src/vkcv/ImageConfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d95a7ff6bb843b4405f119d6bec0e50a06658e0e --- /dev/null +++ b/src/vkcv/ImageConfig.cpp @@ -0,0 +1,80 @@ +/** + * @authors Tobias Frisch + * @file vkcv/Image.cpp + * @brief Structure for image configuration + */ +#include "vkcv/ImageConfig.hpp" + +namespace vkcv { + + ImageConfig::ImageConfig(uint32_t width, + uint32_t height, + uint32_t depth) + : m_width(width), + m_height(height), + m_depth(depth), + + m_supportStorage(false), + m_supportColorAttachment(false), + m_cubeMapImage(false), + + m_msaa(Multisampling::None) + {} + + uint32_t ImageConfig::getWidth() const { + return m_width; + } + + void ImageConfig::setWidth(uint32_t width) { + m_width = width; + } + + uint32_t ImageConfig::getHeight() const { + return m_height; + } + + void ImageConfig::setHeight(uint32_t height) { + m_height = height; + } + + uint32_t ImageConfig::getDepth() const { + return m_depth; + } + + void ImageConfig::setDepth(uint32_t depth) { + m_depth = depth; + } + + bool ImageConfig::isSupportingStorage() const { + return m_supportStorage; + } + + void ImageConfig::setSupportingStorage(bool supportStorage) { + m_supportStorage = supportStorage; + } + + bool ImageConfig::isSupportingColorAttachment() const { + return m_supportColorAttachment; + } + + void ImageConfig::setSupportingColorAttachment(bool supportColorAttachment) { + m_supportColorAttachment = supportColorAttachment; + } + + bool ImageConfig::isCubeMapImage() const { + return m_cubeMapImage; + } + + void ImageConfig::setCubeMapImage(bool cubeMapImage) { + m_cubeMapImage = cubeMapImage; + } + + Multisampling ImageConfig::getMultisampling() const { + return m_msaa; + } + + void ImageConfig::setMultisampling(Multisampling msaa) { + m_msaa = msaa; + } + +} \ No newline at end of file diff --git a/src/vkcv/ImageLayoutTransitions.cpp b/src/vkcv/ImageLayoutTransitions.cpp deleted file mode 100644 index 8d31c64ccbcbf33e259714f8c581c920738190b4..0000000000000000000000000000000000000000 --- a/src/vkcv/ImageLayoutTransitions.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "ImageLayoutTransitions.hpp" -#include "vkcv/Image.hpp" - -namespace vkcv { - vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image &image, vk::ImageLayout newLayout) { - - vk::ImageAspectFlags aspectFlags; - if (isDepthFormat(image.m_format)) { - aspectFlags = vk::ImageAspectFlagBits::eDepth; - } - else { - aspectFlags = vk::ImageAspectFlagBits::eColor; - } - - vk::ImageSubresourceRange imageSubresourceRange( - aspectFlags, - 0, - image.m_viewPerMip.size(), - 0, - image.m_layers - ); - - // TODO: precise AccessFlagBits, will require a lot of context - return vk::ImageMemoryBarrier( - vk::AccessFlagBits::eMemoryWrite, - vk::AccessFlagBits::eMemoryRead, - image.m_layout, - newLayout, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - image.m_handle, - imageSubresourceRange); - } - - vk::ImageMemoryBarrier createSwapchainImageLayoutTransitionBarrier( - vk::Image vulkanHandle, - vk::ImageLayout oldLayout, - vk::ImageLayout newLayout) { - - vk::ImageSubresourceRange imageSubresourceRange( - vk::ImageAspectFlagBits::eColor, - 0, - 1, - 0, - 1); - - // TODO: precise AccessFlagBits, will require a lot of context - return vk::ImageMemoryBarrier( - vk::AccessFlagBits::eMemoryWrite, - vk::AccessFlagBits::eMemoryRead, - oldLayout, - newLayout, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - vulkanHandle, - imageSubresourceRange); - } - - void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier) { - cmdBuffer.pipelineBarrier( - vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eBottomOfPipe, - {}, - nullptr, - nullptr, - barrier); - } -} \ No newline at end of file diff --git a/src/vkcv/ImageLayoutTransitions.hpp b/src/vkcv/ImageLayoutTransitions.hpp deleted file mode 100644 index 5c147f133a6492746ad410367e5e627be000d7be..0000000000000000000000000000000000000000 --- a/src/vkcv/ImageLayoutTransitions.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include <vulkan/vulkan.hpp> -#include "ImageManager.hpp" - -namespace vkcv { - vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageManager::Image& image, vk::ImageLayout newLayout); - vk::ImageMemoryBarrier createSwapchainImageLayoutTransitionBarrier( - vk::Image vulkanHandle, - vk::ImageLayout oldLayout, - vk::ImageLayout newLayout); - - void recordImageBarrier(vk::CommandBuffer cmdBuffer, vk::ImageMemoryBarrier barrier); -} \ No newline at end of file diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp index a3364ce0dfd6f59dc78c85b43570eea25cfc052d..4e65cc45f83915861ef4b67c388e5ae35ef9d35c 100644 --- a/src/vkcv/ImageManager.cpp +++ b/src/vkcv/ImageManager.cpp @@ -1,110 +1,187 @@ /** - * @authors Lars Hoerttrich + * @authors Lars Hoerttrich, Tobias Frisch * @file vkcv/ImageManager.cpp * @brief class creating and managing images */ #include "ImageManager.hpp" #include "vkcv/Core.hpp" -#include "ImageLayoutTransitions.hpp" +#include "vkcv/Image.hpp" #include "vkcv/Logger.hpp" +#include "vkcv/Multisampling.hpp" +#include "vkcv/TypeGuard.hpp" #include <algorithm> namespace vkcv { - - ImageManager::Image::Image( - vk::Image handle, - vk::DeviceMemory memory, - std::vector<vk::ImageView> views, - uint32_t width, - uint32_t height, - uint32_t depth, - vk::Format format, - uint32_t layers) - : - m_handle(handle), - m_memory(memory), - m_viewPerMip(views), - m_width(width), - m_height(height), - m_depth(depth), - m_format(format), - m_layers(layers) - {} - - /** - * @brief searches memory type index for image allocation, combines requirements of image and application - * @param physicalMemoryProperties Memory Properties of physical device - * @param typeBits Bit field for suitable memory types - * @param requirements Property flags that are required - * @return memory type index for image - */ - uint32_t searchImageMemoryType(const vk::PhysicalDeviceMemoryProperties& physicalMemoryProperties, uint32_t typeBits, vk::MemoryPropertyFlags requirements) { - const uint32_t memoryCount = physicalMemoryProperties.memoryTypeCount; - for (uint32_t memoryIndex = 0; memoryIndex < memoryCount; ++memoryIndex) { - const uint32_t memoryTypeBits = (1 << memoryIndex); - const bool isRequiredMemoryType = typeBits & memoryTypeBits; - - const vk::MemoryPropertyFlags properties = - physicalMemoryProperties.memoryTypes[memoryIndex].propertyFlags; - const bool hasRequiredProperties = - (properties & requirements) == requirements; - - if (isRequiredMemoryType && hasRequiredProperties) - return static_cast<int32_t>(memoryIndex); + + bool ImageManager::init(Core &core, BufferManager &bufferManager) { + if (!HandleManager<ImageEntry, ImageHandle>::init(core)) { + return false; } - - // failed to find memory type - return -1; + + m_bufferManager = &bufferManager; + m_swapchainImages.clear(); + return true; } - - ImageManager::ImageManager(BufferManager& bufferManager) noexcept : - m_core(nullptr), m_bufferManager(bufferManager), m_images() - { + + uint64_t ImageManager::getIdFrom(const ImageHandle &handle) const { + return handle.getId(); } - - ImageManager::~ImageManager() noexcept { - for (uint64_t id = 0; id < m_images.size(); id++) { - destroyImageById(id); + + ImageHandle ImageManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return ImageHandle(id, destroy); + } + + void ImageManager::destroyById(uint64_t id) { + auto &image = getById(id); + + const vk::Device &device = getCore().getContext().getDevice(); + + for (auto &view : image.m_viewPerMip) { + if (view) { + device.destroyImageView(view); + view = nullptr; + } + } + + for (auto &view : image.m_arrayViewPerMip) { + if (view) { + device.destroyImageView(view); + view = nullptr; + } + } + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + if (image.m_handle) { + allocator.destroyImage(image.m_handle, image.m_allocation); + + image.m_handle = nullptr; + image.m_allocation = nullptr; + } + } + + const BufferManager &ImageManager::getBufferManager() const { + return *m_bufferManager; + } + + BufferManager &ImageManager::getBufferManager() { + return *m_bufferManager; + } + + void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, + const ImageHandle &handle) { + auto &image = (*this) [handle]; + recordImageLayoutTransition(handle, 0, 0, vk::ImageLayout::eGeneral, cmdBuffer); + + vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ? + vk::ImageAspectFlagBits::eDepth : + vk::ImageAspectFlagBits::eColor; + + uint32_t srcWidth = image.m_width; + uint32_t srcHeight = image.m_height; + uint32_t srcDepth = image.m_depth; + + auto half = [](uint32_t in) { + return std::max<uint32_t>(in / 2, 1); + }; + + uint32_t dstWidth = half(srcWidth); + uint32_t dstHeight = half(srcHeight); + uint32_t dstDepth = half(srcDepth); + + for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) { + uint32_t dstMip = srcMip + 1; + vk::ImageBlit region( + vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1), + { vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) }, + vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1), + { vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) }); + + cmdBuffer.blitImage(image.m_handle, vk::ImageLayout::eGeneral, image.m_handle, + vk::ImageLayout::eGeneral, region, vk::Filter::eLinear); + + srcWidth = dstWidth; + srcHeight = dstHeight; + srcDepth = dstDepth; + + dstWidth = half(dstWidth); + dstHeight = half(dstHeight); + dstDepth = half(dstDepth); + + recordImageMemoryBarrier(handle, cmdBuffer); + } + } + + const ImageEntry &ImageManager::operator[](const ImageHandle &handle) const { + if (handle.isSwapchainImage()) { + return m_swapchainImages [m_currentSwapchainInputImage]; } - for (const auto swapchainImage : m_swapchainImages) { + + return HandleManager<ImageEntry, ImageHandle>::operator[](handle); + } + + ImageEntry &ImageManager::operator[](const ImageHandle &handle) { + if (handle.isSwapchainImage()) { + return m_swapchainImages [m_currentSwapchainInputImage]; + } + + return HandleManager<ImageEntry, ImageHandle>::operator[](handle); + } + + ImageManager::ImageManager() noexcept : + HandleManager<ImageEntry, ImageHandle>(), m_bufferManager(nullptr), m_swapchainImages(), + m_currentSwapchainInputImage(0) {} + + ImageManager::~ImageManager() noexcept { + clear(); + + for (const auto &swapchainImage : m_swapchainImages) { for (const auto view : swapchainImage.m_viewPerMip) { - m_core->getContext().getDevice().destroy(view); + getCore().getContext().getDevice().destroy(view); } } } bool isDepthImageFormat(vk::Format format) { - if ((format == vk::Format::eD16Unorm) || (format == vk::Format::eD16UnormS8Uint) || - (format == vk::Format::eD24UnormS8Uint) || (format == vk::Format::eD32Sfloat) || - (format == vk::Format::eD32SfloatS8Uint)) { + if ((format == vk::Format::eD16Unorm) || (format == vk::Format::eD16UnormS8Uint) + || (format == vk::Format::eD24UnormS8Uint) || (format == vk::Format::eD32Sfloat) + || (format == vk::Format::eD32SfloatS8Uint)) { return true; } else { return false; } } - - ImageHandle ImageManager::createImage( - uint32_t width, - uint32_t height, - uint32_t depth, - vk::Format format, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment) - { - const vk::PhysicalDevice& physicalDevice = m_core->getContext().getPhysicalDevice(); - + + ImageHandle ImageManager::createImage(vk::Format format, + uint32_t mipCount, + const ImageConfig& config) { + const vk::PhysicalDevice &physicalDevice = getCore().getContext().getPhysicalDevice(); const vk::FormatProperties formatProperties = physicalDevice.getFormatProperties(format); vk::ImageCreateFlags createFlags; vk::ImageUsageFlags imageUsageFlags = ( - vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc + vk::ImageUsageFlagBits::eSampled | + vk::ImageUsageFlagBits::eTransferDst | + vk::ImageUsageFlagBits::eTransferSrc ); - if (supportStorage) { + + vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; + + if (config.isSupportingStorage()) { imageUsageFlags |= vk::ImageUsageFlagBits::eStorage; + + if (!(formatProperties.optimalTilingFeatures + & vk::FormatFeatureFlagBits::eStorageImage)) { + imageTiling = vk::ImageTiling::eLinear; + + if (!(formatProperties.linearTilingFeatures + & vk::FormatFeatureFlagBits::eStorageImage)) + return {}; + } } - if (supportColorAttachment) { + + if (config.isSupportingColorAttachment()) { imageUsageFlags |= vk::ImageUsageFlagBits::eColorAttachment; } @@ -113,14 +190,15 @@ namespace vkcv { if (isDepthFormat) { imageUsageFlags |= vk::ImageUsageFlagBits::eDepthStencilAttachment; } - - const vk::Device& device = m_core->getContext().getDevice(); - + + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + uint32_t requiredArrayLayers = 1; + vk::ImageType imageType = vk::ImageType::e3D; vk::ImageViewType imageViewType = vk::ImageViewType::e3D; - if (depth <= 1) { - if (height <= 1) { + if (config.getDepth() <= 1) { + if (config.getHeight() <= 1) { imageType = vk::ImageType::e1D; imageViewType = vk::ImageViewType::e1D; } else { @@ -134,50 +212,73 @@ namespace vkcv { imageViewType = vk::ImageViewType::e2D; } - vk::ImageTiling imageTiling = vk::ImageTiling::eOptimal; + if (config.isCubeMapImage()) { + requiredArrayLayers = 6; + + imageViewType = vk::ImageViewType::eCube; + createFlags |= vk::ImageCreateFlagBits::eCubeCompatible; + } else + if (vk::ImageType::e3D == imageType) { + createFlags |= vk::ImageCreateFlagBits::e2DArrayCompatible; + } if (!formatProperties.optimalTilingFeatures) { if (!formatProperties.linearTilingFeatures) - return ImageHandle(); + return {}; imageTiling = vk::ImageTiling::eLinear; } - const vk::ImageFormatProperties imageFormatProperties = - physicalDevice.getImageFormatProperties(format, imageType, imageTiling, imageUsageFlags); + const vk::ImageFormatProperties imageFormatProperties = ( + physicalDevice.getImageFormatProperties( + format, + imageType, + imageTiling, + imageUsageFlags + ) + ); - const uint32_t arrayLayers = std::min<uint32_t>(1, imageFormatProperties.maxArrayLayers); + const uint32_t arrayLayers = std::min<uint32_t>( + requiredArrayLayers, + imageFormatProperties.maxArrayLayers + ); const vk::ImageCreateInfo imageCreateInfo( - createFlags, - imageType, - format, - vk::Extent3D(width, height, depth), - mipCount, - arrayLayers, - vk::SampleCountFlagBits::e1, - imageTiling, - imageUsageFlags, - vk::SharingMode::eExclusive, - {}, - vk::ImageLayout::eUndefined + createFlags, + imageType, + format, + vk::Extent3D( + config.getWidth(), + config.getHeight(), + config.getDepth() + ), + mipCount, + arrayLayers, + msaaToSampleCountFlagBits( + config.getMultisampling() + ), + imageTiling, + imageUsageFlags, + vk::SharingMode::eExclusive, + {}, + vk::ImageLayout::eUndefined ); - - vk::Image image = device.createImage(imageCreateInfo); - const vk::MemoryRequirements requirements = device.getImageMemoryRequirements(image); - - vk::MemoryPropertyFlags memoryTypeFlags = vk::MemoryPropertyFlagBits::eDeviceLocal; - - const uint32_t memoryTypeIndex = searchImageMemoryType( - physicalDevice.getMemoryProperties(), - requirements.memoryTypeBits, - memoryTypeFlags + auto imageAllocation = allocator.createImage( + imageCreateInfo, + vma::AllocationCreateInfo( + vma::AllocationCreateFlags(), + vma::MemoryUsage::eGpuOnly, + vk::MemoryPropertyFlagBits::eDeviceLocal, + vk::MemoryPropertyFlagBits::eDeviceLocal, + 0, + vma::Pool(), + nullptr + ) ); - - vk::DeviceMemory memory = device.allocateMemory(vk::MemoryAllocateInfo(requirements.size, memoryTypeIndex)); - device.bindImageMemory(image, memory, 0); - + + vk::Image image = imageAllocation.first; + vma::Allocation allocation = imageAllocation.second; vk::ImageAspectFlags aspectFlags; if (isDepthFormat) { @@ -186,219 +287,303 @@ namespace vkcv { aspectFlags = vk::ImageAspectFlagBits::eColor; } + const vk::Device &device = getCore().getContext().getDevice(); + std::vector<vk::ImageView> views; - for (int mip = 0; mip < mipCount; mip++) { + std::vector<vk::ImageView> arrayViews; + + for (uint32_t mip = 0; mip < mipCount; mip++) { const vk::ImageViewCreateInfo imageViewCreateInfo( - {}, - image, - imageViewType, - format, - vk::ComponentMapping( - vk::ComponentSwizzle::eIdentity, - vk::ComponentSwizzle::eIdentity, - vk::ComponentSwizzle::eIdentity, - vk::ComponentSwizzle::eIdentity - ), - vk::ImageSubresourceRange( - aspectFlags, - mip, - mipCount - mip, - 0, - arrayLayers - ) + {}, + image, + imageViewType, + format, + vk::ComponentMapping( + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity + ), + vk::ImageSubresourceRange( + aspectFlags, + mip, + mipCount - mip, + 0, + arrayLayers + ) ); - + views.push_back(device.createImageView(imageViewCreateInfo)); } - const uint64_t id = m_images.size(); - m_images.push_back(Image(image, memory, views, width, height, depth, format, arrayLayers)); - return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); }); - } - - ImageHandle ImageManager::createSwapchainImage() { - return ImageHandle::createSwapchainImageHandle(); - } - - vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { - - if (handle.isSwapchainImage()) { - m_swapchainImages[m_currentSwapchainInputImage].m_handle; - } - - const uint64_t id = handle.getId(); - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return nullptr; + for (uint32_t mip = 0; mip < mipCount; mip++) { + const vk::ImageViewCreateInfo imageViewCreateInfo( + {}, + image, + vk::ImageViewType::e2DArray, + format, + vk::ComponentMapping( + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity, + vk::ComponentSwizzle::eIdentity + ), + vk::ImageSubresourceRange( + aspectFlags, + mip, + 1, + 0, + arrayLayers + ) + ); + + arrayViews.push_back(device.createImageView(imageViewCreateInfo)); } - auto& image = m_images[id]; + std::vector<vk::ImageLayout> layers; + layers.resize(arrayLayers, vk::ImageLayout::eUndefined); + return add({ + image, + allocation, + views, + arrayViews, + config.getWidth(), + config.getHeight(), + config.getDepth(), + format, + layers, + config.isSupportingStorage() + }); + } + + vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const { + auto &image = (*this) [handle]; return image.m_handle; } vk::DeviceMemory ImageManager::getVulkanDeviceMemory(const ImageHandle &handle) const { - if (handle.isSwapchainImage()) { vkcv_log(LogLevel::ERROR, "Swapchain image has no memory"); return nullptr; } - - const uint64_t id = handle.getId(); - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return nullptr; - } - auto& image = m_images[id]; + auto &image = (*this) [handle]; + const vma::Allocator &allocator = getCore().getContext().getAllocator(); + + auto info = allocator.getAllocationInfo(image.m_allocation); - return image.m_memory; + return info.deviceMemory; } - vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, const size_t mipLevel) const { - + vk::ImageView ImageManager::getVulkanImageView(const ImageHandle &handle, size_t mipLevel, + bool arrayView) const { if (handle.isSwapchainImage()) { - return m_swapchainImages[m_currentSwapchainInputImage].m_viewPerMip[0]; - } - - const uint64_t id = handle.getId(); - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return nullptr; + return m_swapchainImages [m_currentSwapchainInputImage].m_viewPerMip [0]; } - const auto& image = m_images[id]; - - if (mipLevel >= m_images.size()) { + const auto &image = (*this) [handle]; + const auto &views = arrayView ? image.m_arrayViewPerMip : image.m_viewPerMip; + + if (mipLevel >= views.size()) { vkcv_log(LogLevel::ERROR, "Image does not have requested mipLevel"); return nullptr; } - - return image.m_viewPerMip[mipLevel]; + + return views [mipLevel]; } - void ImageManager::switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout) { - uint64_t id = handle.getId(); + static vk::ImageMemoryBarrier createImageLayoutTransitionBarrier(const ImageEntry &image, + uint32_t mipLevelCount, + uint32_t mipLevelOffset, + vk::ImageLayout newLayout) { + vk::ImageAspectFlags aspectFlags; + if (isDepthFormat(image.m_format)) { + aspectFlags = vk::ImageAspectFlagBits::eDepth; + } else { + aspectFlags = vk::ImageAspectFlagBits::eColor; + } - const bool isSwapchainImage = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; + const uint32_t mipLevelsMax = image.m_viewPerMip.size(); + + if (mipLevelOffset > mipLevelsMax) { + mipLevelOffset = mipLevelsMax; + } + + if ((!mipLevelCount) || (mipLevelOffset + mipLevelCount > mipLevelsMax)) { + mipLevelCount = mipLevelsMax - mipLevelOffset; } - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); + vk::ImageSubresourceRange imageSubresourceRange( + aspectFlags, + mipLevelOffset, + mipLevelCount, + 0, + static_cast<uint32_t>(image.m_layers.size()) + ); - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Graphics; + // TODO: precise AccessFlagBits, will require a lot of context + vk::ImageMemoryBarrier barrier ( + vk::AccessFlagBits::eMemoryWrite, + vk::AccessFlagBits::eMemoryRead, + image.m_layers[0], + newLayout, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + image.m_handle, + imageSubresourceRange + ); - m_core->recordAndSubmitCommandsImmediate( - submitInfo, - [transitionBarrier](const vk::CommandBuffer& commandBuffer) { - // TODO: precise PipelineStageFlagBits, will require a lot of context - commandBuffer.pipelineBarrier( - vk::PipelineStageFlagBits::eTopOfPipe, - vk::PipelineStageFlagBits::eBottomOfPipe, + return barrier; + } + + void ImageManager::switchImageLayoutImmediate(const ImageHandle &handle, + vk::ImageLayout newLayout) { + auto &image = (*this) [handle]; + const auto transitionBarrier = createImageLayoutTransitionBarrier(image, 0, 0, newLayout); + + auto &core = getCore(); + auto stream = core.createCommandStream(QueueType::Graphics); + + core.recordCommandsToStream( + stream, + [transitionBarrier](const vk::CommandBuffer &commandBuffer) { + // TODO: precise PipelineStageFlagBits, will require a lot of context + commandBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTopOfPipe, + vk::PipelineStageFlagBits::eBottomOfPipe, + {}, + nullptr, + nullptr, + transitionBarrier + ); + }, + nullptr + ); + + core.submitCommandStream(stream, false); + + for (auto& layer : image.m_layers) { + layer = newLayout; + } + } + + void ImageManager::recordImageLayoutTransition(const ImageHandle &handle, + uint32_t mipLevelCount, uint32_t mipLevelOffset, + vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer) { + auto &image = (*this) [handle]; + const auto transitionBarrier = createImageLayoutTransitionBarrier( + image, + mipLevelCount, + mipLevelOffset, + newLayout + ); + + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, {}, nullptr, nullptr, transitionBarrier - ); - }, - nullptr); - image.m_layout = newLayout; - } - - void ImageManager::recordImageLayoutTransition( - const ImageHandle& handle, - vk::ImageLayout newLayout, - vk::CommandBuffer cmdBuffer) { - - const uint64_t id = handle.getId(); - const bool isSwapchainImage = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; + ); + + for (auto& layer : image.m_layers) { + layer = newLayout; } - - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - const auto transitionBarrier = createImageLayoutTransitionBarrier(image, newLayout); - recordImageBarrier(cmdBuffer, transitionBarrier); - image.m_layout = newLayout; } - - void ImageManager::recordImageMemoryBarrier( - const ImageHandle& handle, - vk::CommandBuffer cmdBuffer) { - - const uint64_t id = handle.getId(); - const bool isSwapchainImage = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainImage) { - std::cerr << "Error: ImageManager::recordImageMemoryBarrier invalid handle" << std::endl; - return; - } - - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - const auto transitionBarrier = createImageLayoutTransitionBarrier(image, image.m_layout); - recordImageBarrier(cmdBuffer, transitionBarrier); + + void ImageManager::recordImageMemoryBarrier(const ImageHandle &handle, + vk::CommandBuffer cmdBuffer) { + auto &image = (*this) [handle]; + const auto transitionBarrier = createImageLayoutTransitionBarrier( + image, + 0, + 0, + image.m_layers[0] + ); + + cmdBuffer.pipelineBarrier( + vk::PipelineStageFlagBits::eAllCommands, + vk::PipelineStageFlagBits::eAllCommands, + {}, + nullptr, + nullptr, + transitionBarrier + ); } - constexpr uint32_t getChannelsByFormat(vk::Format format) { + constexpr uint32_t getBytesPerPixel(vk::Format format) { switch (format) { case vk::Format::eR8Unorm: return 1; + case vk::Format::eR16Unorm: + return 2; + case vk::Format::eR32Uint: case vk::Format::eR8G8B8A8Srgb: + case vk::Format::eR8G8B8A8Unorm: return 4; + case vk::Format::eR16G16B16A16Sfloat: + return 8; + case vk::Format::eR32G32B32A32Sfloat: + return 16; default: - std::cerr << "Check format instead of guessing, please!" << std::endl; + std::cerr << "Unknown image format" << std::endl; return 4; } } - void ImageManager::fillImage(const ImageHandle& handle, void* data, size_t size) - { - const uint64_t id = handle.getId(); - + void ImageManager::fillImage(const ImageHandle &handle, + const void* data, + size_t size, + uint32_t firstLayer, + uint32_t layerCount) { if (handle.isSwapchainImage()) { vkcv_log(LogLevel::ERROR, "Swapchain image cannot be filled"); return; } - - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); + + auto &image = (*this) [handle]; + + const auto imageLayerCount = static_cast<uint32_t>(image.m_layers.size()); + const uint32_t baseArrayLayer = std::min<uint32_t>(firstLayer, imageLayerCount); + + if (baseArrayLayer >= image.m_layers.size()) { return; } - auto& image = m_images[id]; + uint32_t arrayLayerCount; + + if (layerCount > 0) { + arrayLayerCount = std::min<uint32_t>(layerCount, imageLayerCount - baseArrayLayer); + } else { + arrayLayerCount = imageLayerCount - baseArrayLayer; + } - switchImageLayoutImmediate( - handle, - vk::ImageLayout::eTransferDstOptimal); + switchImageLayoutImmediate(handle, vk::ImageLayout::eTransferDstOptimal); - uint32_t channels = getChannelsByFormat(image.m_format); const size_t image_size = ( - image.m_width * image.m_height * image.m_depth * channels + image.m_width * image.m_height * image.m_depth * getBytesPerPixel(image.m_format) ); const size_t max_size = std::min(size, image_size); - BufferHandle bufferHandle = m_bufferManager.createBuffer( - BufferType::STAGING, max_size, BufferMemoryType::HOST_VISIBLE + BufferHandle bufferHandle = getBufferManager().createBuffer( + TypeGuard(1), BufferType::STAGING, BufferMemoryType::DEVICE_LOCAL, max_size, false ); - m_bufferManager.fillBuffer(bufferHandle, data, max_size, 0); + getBufferManager().fillBuffer(bufferHandle, data, max_size, 0); - vk::Buffer stagingBuffer = m_bufferManager.getBuffer(bufferHandle); + vk::Buffer stagingBuffer = getBufferManager().getBuffer(bufferHandle); - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Transfer; + auto &core = getCore(); + auto stream = core.createCommandStream(QueueType::Transfer); - m_core->recordAndSubmitCommandsImmediate( - submitInfo, - [&image, &stagingBuffer](const vk::CommandBuffer& commandBuffer) { + core.recordCommandsToStream( + stream, + [&image, &stagingBuffer, &baseArrayLayer, &arrayLayerCount] + (const vk::CommandBuffer &commandBuffer) { vk::ImageAspectFlags aspectFlags; if (isDepthImageFormat(image.m_format)) { @@ -407,16 +592,11 @@ namespace vkcv { aspectFlags = vk::ImageAspectFlagBits::eColor; } - const vk::BufferImageCopy region ( + const vk::BufferImageCopy region( 0, 0, 0, - vk::ImageSubresourceLayers( - aspectFlags, - 0, - 0, - image.m_layers - ), + vk::ImageSubresourceLayers(aspectFlags, 0, baseArrayLayer, arrayLayerCount), vk::Offset3D(0, 0, 0), vk::Extent3D(image.m_width, image.m_height, image.m_depth) ); @@ -430,216 +610,132 @@ namespace vkcv { ); }, [&]() { - switchImageLayoutImmediate( - handle, - vk::ImageLayout::eShaderReadOnlyOptimal - ); + switchImageLayoutImmediate(handle, vk::ImageLayout::eShaderReadOnlyOptimal); } ); + + core.submitCommandStream(stream, false); } - - void ImageManager::recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle) { - - const auto id = handle.getId(); - if (id >= m_images.size()) { - vkcv_log(vkcv::LogLevel::ERROR, "Invalid image handle"); - return; - } - - auto& image = m_images[id]; - recordImageLayoutTransition(handle, vk::ImageLayout::eGeneral, cmdBuffer); - - vk::ImageAspectFlags aspectMask = isDepthImageFormat(image.m_format) ? - vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor; - - uint32_t srcWidth = image.m_width; - uint32_t srcHeight = image.m_height; - uint32_t srcDepth = image.m_depth; - - auto half = [](uint32_t in) { - return std::max<uint32_t>(in / 2, 1); - }; - - uint32_t dstWidth = half(srcWidth); - uint32_t dstHeight = half(srcHeight); - uint32_t dstDepth = half(srcDepth); - - for (uint32_t srcMip = 0; srcMip < image.m_viewPerMip.size() - 1; srcMip++) { - uint32_t dstMip = srcMip + 1; - vk::ImageBlit region( - vk::ImageSubresourceLayers(aspectMask, srcMip, 0, 1), - { vk::Offset3D(0, 0, 0), vk::Offset3D(srcWidth, srcHeight, srcDepth) }, - vk::ImageSubresourceLayers(aspectMask, dstMip, 0, 1), - { vk::Offset3D(0, 0, 0), vk::Offset3D(dstWidth, dstHeight, dstDepth) }); - - cmdBuffer.blitImage( - image.m_handle, - vk::ImageLayout::eGeneral, - image.m_handle, - vk::ImageLayout::eGeneral, - region, - vk::Filter::eLinear); - - srcWidth = dstWidth; - srcHeight = dstHeight; - srcDepth = dstDepth; - - dstWidth = half(dstWidth); - dstHeight = half(dstHeight); - dstDepth = half(dstDepth); - - recordImageMemoryBarrier(handle, cmdBuffer); - } - } - - void ImageManager::generateImageMipChainImmediate(const ImageHandle& handle) { - - const auto& device = m_core->getContext().getDevice(); - - SubmitInfo submitInfo; - submitInfo.queueType = QueueType::Graphics; - - if (handle.isSwapchainImage()) { - vkcv_log(vkcv::LogLevel::ERROR, "You cannot generate a mip chain for the swapchain, what are you smoking?"); - return; - } - - const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { - recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); - }; - - m_core->recordAndSubmitCommandsImmediate(submitInfo, record, nullptr); - } - + void ImageManager::recordImageMipChainGenerationToCmdStream( - const vkcv::CommandStreamHandle& cmdStream, - const ImageHandle& handle) { - + const vkcv::CommandStreamHandle &cmdStream, const ImageHandle &handle) { const auto record = [this, handle](const vk::CommandBuffer cmdBuffer) { recordImageMipGenerationToCmdBuffer(cmdBuffer, handle); }; - m_core->recordCommandsToStream(cmdStream, record, nullptr); + + getCore().recordCommandsToStream(cmdStream, record, nullptr); } - - uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const { - const uint64_t id = handle.getId(); - const bool isSwapchainImage = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return 0; - } + + void ImageManager::recordMSAAResolve(vk::CommandBuffer cmdBuffer, const ImageHandle &src, + const ImageHandle &dst) { + auto &srcImage = (*this) [src]; + auto &dstImage = (*this) [dst]; + + const auto srcLayerCount = static_cast<uint32_t>(srcImage.m_layers.size()); + const auto dstLayerCount = static_cast<uint32_t>(dstImage.m_layers.size()); + + vk::ImageResolve region( + vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, srcLayerCount), + vk::Offset3D(0, 0, 0), + vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, dstLayerCount), + vk::Offset3D(0, 0, 0), + vk::Extent3D(dstImage.m_width, dstImage.m_height, dstImage.m_depth) + ); - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; + recordImageLayoutTransition(src, 0, 0, vk::ImageLayout::eTransferSrcOptimal, cmdBuffer); + recordImageLayoutTransition(dst, 0, 0, vk::ImageLayout::eTransferDstOptimal, cmdBuffer); + cmdBuffer.resolveImage( + srcImage.m_handle, + srcImage.m_layers[0], + dstImage.m_handle, + dstImage.m_layers[0], + region + ); + } + + uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const { + auto &image = (*this) [handle]; return image.m_width; } uint32_t ImageManager::getImageHeight(const ImageHandle &handle) const { - const uint64_t id = handle.getId(); - const bool isSwapchainImage = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return 0; - } - - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - + auto &image = (*this) [handle]; return image.m_height; } uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const { - const uint64_t id = handle.getId(); - const bool isSwapchainImage = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainImage) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return 0; - } - - auto& image = isSwapchainImage ? m_swapchainImages[m_currentSwapchainInputImage] : m_images[id]; - + auto &image = (*this) [handle]; return image.m_depth; } - void ImageManager::destroyImageById(uint64_t id) - { - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return; - } - - auto& image = m_images[id]; - - const vk::Device& device = m_core->getContext().getDevice(); - - for (auto& view : image.m_viewPerMip) { - if (view) { - device.destroyImageView(view); - view = nullptr; - } - } - - if (image.m_memory) { - device.freeMemory(image.m_memory); - image.m_memory = nullptr; - } - - if (image.m_handle) { - device.destroyImage(image.m_handle); - image.m_handle = nullptr; - } + vk::Format ImageManager::getImageFormat(const ImageHandle &handle) const { + auto &image = (*this) [handle]; + return image.m_format; } - - vk::Format ImageManager::getImageFormat(const ImageHandle& handle) const { - - const uint64_t id = handle.getId(); - const bool isSwapchainFormat = handle.isSwapchainImage(); - - if (id >= m_images.size() && !isSwapchainFormat) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return vk::Format::eUndefined; + + bool ImageManager::isImageSupportingStorage(const ImageHandle &handle) const { + if (handle.isSwapchainImage()) { + return false; } - - return isSwapchainFormat ? m_swapchainImages[m_currentSwapchainInputImage].m_format : m_images[id].m_format; + + auto &image = (*this) [handle]; + return image.m_storage; } - - uint32_t ImageManager::getImageMipCount(const ImageHandle& handle) const { - const uint64_t id = handle.getId(); - const bool isSwapchainFormat = handle.isSwapchainImage(); - + + uint32_t ImageManager::getImageMipCount(const ImageHandle &handle) const { if (handle.isSwapchainImage()) { return 1; } - - if (id >= m_images.size()) { - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return 0; - } - - return m_images[id].m_viewPerMip.size(); + + auto &image = (*this) [handle]; + return image.m_viewPerMip.size(); } - + + uint32_t ImageManager::getImageArrayLayers(const ImageHandle &handle) const { + auto &image = (*this) [handle]; + return static_cast<uint32_t>(image.m_layers.size()); + } + void ImageManager::setCurrentSwapchainImageIndex(int index) { m_currentSwapchainInputImage = index; } - - void ImageManager::setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, - uint32_t width, uint32_t height, vk::Format format) { - + + void ImageManager::setSwapchainImages(const std::vector<vk::Image> &images, + const std::vector<vk::ImageView> &views, uint32_t width, + uint32_t height, vk::Format format) { + // destroy old views - for (auto image : m_swapchainImages) { - for (const auto& view : image.m_viewPerMip) { - m_core->getContext().getDevice().destroyImageView(view); + for (const auto &image : m_swapchainImages) { + for (const auto &view : image.m_viewPerMip) { + getCore().getContext().getDevice().destroyImageView(view); } } - + assert(images.size() == views.size()); m_swapchainImages.clear(); - for (int i = 0; i < images.size(); i++) { - m_swapchainImages.push_back(Image(images[i], nullptr, { views[i] }, width, height, 1, format, 1)); + for (size_t i = 0; i < images.size(); i++) { + m_swapchainImages.push_back({ + images [i], + nullptr, + { views [i] }, + {}, + width, + height, + 1, + format, + { vk::ImageLayout::eUndefined }, + false + }); } } - -} \ No newline at end of file + + void ImageManager::updateImageLayoutManual(const vkcv::ImageHandle &handle, + vk::ImageLayout layout) { + auto &image = (*this) [handle]; + for (auto& layer : image.m_layers) { + layer = layout; + } + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp index ecba7eb5959c1d78a0be41e0b3ac555bffd92d95..7d9c278d7c85fbfb5f8206ad52f99913e5b58d7e 100644 --- a/src/vkcv/ImageManager.hpp +++ b/src/vkcv/ImageManager.hpp @@ -5,124 +5,140 @@ * @brief class creating and managing images */ #include <vector> +#include <vk_mem_alloc.hpp> #include <vulkan/vulkan.hpp> -#include "vkcv/BufferManager.hpp" -#include "vkcv/Handles.hpp" +#include "BufferManager.hpp" +#include "HandleManager.hpp" +#include "vkcv/ImageConfig.hpp" namespace vkcv { - class ImageManager - { + /** + * @brief Determine whether an image format is valid + * for depth buffers. + * + * @param[in] format Image format + * @return True, if the format is usable for depth buffers, otherwise false. + */ + bool isDepthImageFormat(vk::Format format); + + struct ImageEntry { + vk::Image m_handle; + vma::Allocation m_allocation; + + std::vector<vk::ImageView> m_viewPerMip; + std::vector<vk::ImageView> m_arrayViewPerMip; + + uint32_t m_width; + uint32_t m_height; + uint32_t m_depth; + + vk::Format m_format; + std::vector<vk::ImageLayout> m_layers; + bool m_storage; + }; + + /** + * @brief Class to manage the creation, destruction, allocation + * and filling of images. + */ + class ImageManager : HandleManager<ImageEntry, ImageHandle> { friend class Core; - public: - struct Image - { - vk::Image m_handle; - vk::DeviceMemory m_memory; - std::vector<vk::ImageView> m_viewPerMip; - uint32_t m_width = 0; - uint32_t m_height = 0; - uint32_t m_depth = 0; - vk::Format m_format; - uint32_t m_layers = 1; - vk::ImageLayout m_layout = vk::ImageLayout::eUndefined; - private: - // struct is public so utility functions can access members, but only ImageManager can create Image - friend ImageManager; - Image( - vk::Image handle, - vk::DeviceMemory memory, - std::vector<vk::ImageView> views, - uint32_t width, - uint32_t height, - uint32_t depth, - vk::Format format, - uint32_t layers); - - Image(); - }; + private: - - Core* m_core; - BufferManager& m_bufferManager; - - std::vector<Image> m_images; - std::vector<Image> m_swapchainImages; + BufferManager* m_bufferManager; + + std::vector<ImageEntry> m_swapchainImages; int m_currentSwapchainInputImage; - - ImageManager(BufferManager& bufferManager) noexcept; - + + bool init(Core &core, BufferManager &bufferManager); + + [[nodiscard]] uint64_t getIdFrom(const ImageHandle &handle) const override; + + [[nodiscard]] ImageHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + /** * Destroys and deallocates image represented by a given * image handle id. * * @param id Image handle id */ - void destroyImageById(uint64_t id); + void destroyById(uint64_t id) override; + + [[nodiscard]] const BufferManager &getBufferManager() const; - void recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, const ImageHandle& handle); + [[nodiscard]] BufferManager &getBufferManager(); + + void recordImageMipGenerationToCmdBuffer(vk::CommandBuffer cmdBuffer, + const ImageHandle &handle); + + protected: + [[nodiscard]] virtual const ImageEntry & + operator[](const ImageHandle &handle) const override; + + [[nodiscard]] virtual ImageEntry &operator[](const ImageHandle &handle) override; public: - ~ImageManager() noexcept; - ImageManager(ImageManager&& other) = delete; - ImageManager(const ImageManager& other) = delete; - - ImageManager& operator=(ImageManager&& other) = delete; - ImageManager& operator=(const ImageManager& other) = delete; - - ImageHandle createImage( - uint32_t width, - uint32_t height, - uint32_t depth, - vk::Format format, - uint32_t mipCount, - bool supportStorage, - bool supportColorAttachment); - - ImageHandle createSwapchainImage(); - - [[nodiscard]] - vk::Image getVulkanImage(const ImageHandle& handle) const; - - [[nodiscard]] - vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle& handle) const; - - [[nodiscard]] - vk::ImageView getVulkanImageView(const ImageHandle& handle, const size_t mipLevel = 0) const; - - void switchImageLayoutImmediate(const ImageHandle& handle, vk::ImageLayout newLayout); - void recordImageLayoutTransition( - const ImageHandle& handle, - vk::ImageLayout newLayout, - vk::CommandBuffer cmdBuffer); - - void recordImageMemoryBarrier( - const ImageHandle& handle, - vk::CommandBuffer cmdBuffer); - - void fillImage(const ImageHandle& handle, void* data, size_t size); - void generateImageMipChainImmediate(const ImageHandle& handle); - void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle& cmdStream, const ImageHandle& handle); - - [[nodiscard]] - uint32_t getImageWidth(const ImageHandle& handle) const; - - [[nodiscard]] - uint32_t getImageHeight(const ImageHandle& handle) const; - - [[nodiscard]] - uint32_t getImageDepth(const ImageHandle& handle) const; - - [[nodiscard]] - vk::Format getImageFormat(const ImageHandle& handle) const; - - [[nodiscard]] - uint32_t getImageMipCount(const ImageHandle& handle) const; + ImageManager() noexcept; + + ~ImageManager() noexcept override; + + [[nodiscard]] ImageHandle createImage(vk::Format format, + uint32_t mipCount, + const ImageConfig& config); + + [[nodiscard]] vk::Image getVulkanImage(const ImageHandle &handle) const; + + [[nodiscard]] vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle &handle) const; + + [[nodiscard]] vk::ImageView getVulkanImageView(const ImageHandle &handle, + size_t mipLevel = 0, + bool arrayView = false) const; + + void switchImageLayoutImmediate(const ImageHandle &handle, vk::ImageLayout newLayout); + + void recordImageLayoutTransition(const ImageHandle &handle, uint32_t mipLevelCount, + uint32_t mipLevelOffset, vk::ImageLayout newLayout, + vk::CommandBuffer cmdBuffer); + + void recordImageMemoryBarrier(const ImageHandle &handle, vk::CommandBuffer cmdBuffer); + + void fillImage(const ImageHandle &handle, + const void* data, + size_t size, + uint32_t firstLayer, + uint32_t layerCount); + + void recordImageMipChainGenerationToCmdStream(const vkcv::CommandStreamHandle &cmdStream, + const ImageHandle &handle); + + void recordMSAAResolve(vk::CommandBuffer cmdBuffer, const ImageHandle &src, + const ImageHandle &dst); + + [[nodiscard]] uint32_t getImageWidth(const ImageHandle &handle) const; + + [[nodiscard]] uint32_t getImageHeight(const ImageHandle &handle) const; + + [[nodiscard]] uint32_t getImageDepth(const ImageHandle &handle) const; + + [[nodiscard]] vk::Format getImageFormat(const ImageHandle &handle) const; + + [[nodiscard]] bool isImageSupportingStorage(const ImageHandle &handle) const; + + [[nodiscard]] uint32_t getImageMipCount(const ImageHandle &handle) const; + + [[nodiscard]] uint32_t getImageArrayLayers(const ImageHandle &handle) const; void setCurrentSwapchainImageIndex(int index); - void setSwapchainImages(const std::vector<vk::Image>& images, std::vector<vk::ImageView> views, - uint32_t width, uint32_t height, vk::Format format); + void setSwapchainImages(const std::vector<vk::Image> &images, + const std::vector<vk::ImageView> &views, uint32_t width, + uint32_t height, vk::Format format); + + // if manual vulkan work, e.g. ImGui integration, changes an image layout this function must + // be used to update the internal image state + void updateImageLayoutManual(const vkcv::ImageHandle &handle, vk::ImageLayout layout); }; -} \ No newline at end of file +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/Multisampling.cpp b/src/vkcv/Multisampling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..168ba0cf198491fb19cc29797d35d624f194b15d --- /dev/null +++ b/src/vkcv/Multisampling.cpp @@ -0,0 +1,38 @@ +#include <vkcv/Logger.hpp> +#include <vkcv/Multisampling.hpp> + +namespace vkcv { + + vk::SampleCountFlagBits msaaToSampleCountFlagBits(Multisampling msaa) { + switch (msaa) { + case Multisampling::None: + return vk::SampleCountFlagBits::e1; + case Multisampling::MSAA2X: + return vk::SampleCountFlagBits::e2; + case Multisampling::MSAA4X: + return vk::SampleCountFlagBits::e4; + case Multisampling::MSAA8X: + return vk::SampleCountFlagBits::e8; + default: + vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting"); + return vk::SampleCountFlagBits::e1; + } + } + + uint32_t msaaToSampleCount(Multisampling msaa) { + switch (msaa) { + case Multisampling::None: + return 1; + case Multisampling::MSAA2X: + return 2; + case Multisampling::MSAA4X: + return 4; + case Multisampling::MSAA8X: + return 8; + default: + vkcv_log(vkcv::LogLevel::ERROR, "Unknown Multisampling enum setting"); + return 1; + } + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/Pass.cpp b/src/vkcv/Pass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..147d715110b6a20bf1035ddd4f05270916a03d3f --- /dev/null +++ b/src/vkcv/Pass.cpp @@ -0,0 +1,38 @@ + +#include "vkcv/Pass.hpp" + +namespace vkcv { + + PassHandle passFormats(Core &core, const std::vector<vk::Format> &formats, bool clear, + Multisampling multisampling) { + AttachmentDescriptions attachments; + + for (const auto format : formats) { + attachments.emplace_back(format, + clear ? AttachmentOperation::CLEAR : AttachmentOperation::LOAD, + AttachmentOperation::STORE); + } + + const PassConfig config(attachments, multisampling); + return core.createPass(config); + } + + PassHandle passFormat(Core &core, vk::Format format, bool clear, Multisampling multisampling) { + return passFormats(core, { format }, clear, multisampling); + } + + PassHandle passSwapchain(Core &core, const SwapchainHandle &swapchain, + const std::vector<vk::Format> &formats, bool clear, + Multisampling multisampling) { + std::vector<vk::Format> swapchainFormats (formats); + + for (auto &format : swapchainFormats) { + if (vk::Format::eUndefined == format) { + format = core.getSwapchainFormat(swapchain); + } + } + + return passFormats(core, swapchainFormats, clear, multisampling); + } + +} // namespace vkcv diff --git a/src/vkcv/PassConfig.cpp b/src/vkcv/PassConfig.cpp index 602f1d3e2a8100ebd9bbb83772312d3d659abe86..72310f209484a355afc461d72022ad9aa1cf4cbd 100644 --- a/src/vkcv/PassConfig.cpp +++ b/src/vkcv/PassConfig.cpp @@ -1,19 +1,62 @@ + #include "vkcv/PassConfig.hpp" +#include "vkcv/Image.hpp" + +namespace vkcv { + + AttachmentDescription::AttachmentDescription(vk::Format format, AttachmentOperation load, + AttachmentOperation store) : + m_format(format), + m_load_op(load), m_store_op(store), m_clear_value() { + if (isDepthFormat(format)) { + setClearValue(vk::ClearValue(vk::ClearDepthStencilValue(1.0f, 0))); + } else { + setClearValue(vk::ClearValue( + vk::ClearColorValue(std::array<float, 4> { 0.0f, 0.0f, 0.0f, 0.0f }))); + } + } + + AttachmentDescription::AttachmentDescription(vk::Format format, AttachmentOperation load, + AttachmentOperation store, + const vk::ClearValue &clear) : + m_format(format), + m_load_op(load), m_store_op(store), m_clear_value(clear) {} + + vk::Format AttachmentDescription::getFormat() const { + return m_format; + } + + AttachmentOperation AttachmentDescription::getLoadOperation() const { + return m_load_op; + } + + AttachmentOperation AttachmentDescription::getStoreOperation() const { + return m_store_op; + } + + void AttachmentDescription::setClearValue(const vk::ClearValue &clear) { + m_clear_value = clear; + } + + const vk::ClearValue &AttachmentDescription::getClearValue() const { + return m_clear_value; + } + + PassConfig::PassConfig() : m_attachments(), m_multisampling(Multisampling::None) {} + + PassConfig::PassConfig(const AttachmentDescriptions &attachments, Multisampling multisampling) : + m_attachments(attachments), m_multisampling(multisampling) {} + + const AttachmentDescriptions &PassConfig::getAttachments() const { + return m_attachments; + } + + void PassConfig::setMultisampling(Multisampling multisampling) { + m_multisampling = multisampling; + } + + Multisampling PassConfig::getMultisampling() const { + return m_multisampling; + } -#include <utility> - -namespace vkcv -{ - AttachmentDescription::AttachmentDescription( - AttachmentOperation store_op, - AttachmentOperation load_op, - vk::Format format) noexcept : - store_operation{store_op}, - load_operation{load_op}, - format(format) - {}; - - PassConfig::PassConfig(std::vector<AttachmentDescription> attachments) noexcept : - attachments{std::move(attachments)} - {} -} \ No newline at end of file +} // namespace vkcv diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp index c34b0d3631c48561f42eb7f21ba5578156910f51..9b160bcf8c62eb5e3fe829a49d2e43d3f0eef1cb 100644 --- a/src/vkcv/PassManager.cpp +++ b/src/vkcv/PassManager.cpp @@ -1,175 +1,145 @@ #include "PassManager.hpp" +#include "vkcv/Core.hpp" #include "vkcv/Image.hpp" -namespace vkcv -{ - static vk::ImageLayout getVkLayoutFromAttachLayout(AttachmentLayout layout) - { - switch(layout) - { - case AttachmentLayout::GENERAL: - return vk::ImageLayout::eGeneral; - case AttachmentLayout::COLOR_ATTACHMENT: - return vk::ImageLayout::eColorAttachmentOptimal; - case AttachmentLayout::SHADER_READ_ONLY: - return vk::ImageLayout::eShaderReadOnlyOptimal; - case AttachmentLayout::DEPTH_STENCIL_ATTACHMENT: - return vk::ImageLayout::eDepthStencilAttachmentOptimal; - case AttachmentLayout::DEPTH_STENCIL_READ_ONLY: - return vk::ImageLayout::eDepthStencilReadOnlyOptimal; - case AttachmentLayout::PRESENTATION: - return vk::ImageLayout::ePresentSrcKHR; - default: - return vk::ImageLayout::eUndefined; - } - } - - static vk::AttachmentStoreOp getVkStoreOpFromAttachOp(AttachmentOperation op) - { - switch(op) - { - case AttachmentOperation::STORE: - return vk::AttachmentStoreOp::eStore; - default: - return vk::AttachmentStoreOp::eDontCare; - } - } - - static vk::AttachmentLoadOp getVKLoadOpFromAttachOp(AttachmentOperation op) - { - switch(op) - { - case AttachmentOperation::LOAD: - return vk::AttachmentLoadOp::eLoad; - case AttachmentOperation::CLEAR: - return vk::AttachmentLoadOp::eClear; - default: - return vk::AttachmentLoadOp::eDontCare; - } - } - - PassManager::PassManager(vk::Device device) noexcept : - m_Device{device}, - m_Passes{} - {} - - PassManager::~PassManager() noexcept - { - for (uint64_t id = 0; id < m_Passes.size(); id++) { - destroyPassById(id); - } - } - - PassHandle PassManager::createPass(const PassConfig &config) - { - // description of all {color, input, depth/stencil} attachments of the render pass - std::vector<vk::AttachmentDescription> attachmentDescriptions{}; - - // individual references to color attachments (of a subpass) - std::vector<vk::AttachmentReference> colorAttachmentReferences{}; - // individual reference to depth attachment (of a subpass) - vk::AttachmentReference depthAttachmentReference{}; - vk::AttachmentReference *pDepthAttachment = nullptr; //stays nullptr if no depth attachment used - - for (uint32_t i = 0; i < config.attachments.size(); i++) - { - // TODO: Renderpass struct should hold proper format information - vk::Format format = config.attachments[i].format; - vk::ImageLayout layout; - - if (isDepthFormat(config.attachments[i].format)) - { - layout = vk::ImageLayout::eDepthStencilAttachmentOptimal; - depthAttachmentReference.attachment = i; - depthAttachmentReference.layout = layout; - pDepthAttachment = &depthAttachmentReference; - } - else - { - layout = vk::ImageLayout::eColorAttachmentOptimal; - vk::AttachmentReference attachmentRef(i, layout); - colorAttachmentReferences.push_back(attachmentRef); - } - - vk::AttachmentDescription attachmentDesc( - {}, - format, - vk::SampleCountFlagBits::e1, - getVKLoadOpFromAttachOp(config.attachments[i].load_operation), - getVkStoreOpFromAttachOp(config.attachments[i].store_operation), - vk::AttachmentLoadOp::eDontCare, - vk::AttachmentStoreOp::eDontCare, - layout, - layout); - - attachmentDescriptions.push_back(attachmentDesc); - } - - const vk::SubpassDescription subpassDescription( - {}, - vk::PipelineBindPoint::eGraphics, - 0, - {}, - static_cast<uint32_t>(colorAttachmentReferences.size()), - colorAttachmentReferences.data(), - {}, - pDepthAttachment, - 0, - {}); - - const vk::RenderPassCreateInfo passInfo( - {}, - static_cast<uint32_t>(attachmentDescriptions.size()), - attachmentDescriptions.data(), - 1, - &subpassDescription, - 0, - {}); - - vk::RenderPass renderPass = m_Device.createRenderPass(passInfo); - - const uint64_t id = m_Passes.size(); - m_Passes.push_back({ renderPass, config }); - return PassHandle(id, [&](uint64_t id) { destroyPassById(id); }); - } - - vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const - { - const uint64_t id = handle.getId(); - - if (id >= m_Passes.size()) { - return nullptr; - } - - auto& pass = m_Passes[id]; - - return pass.m_Handle; - } - - const PassConfig& PassManager::getPassConfig(const PassHandle &handle) const { - const uint64_t id = handle.getId(); - - if (id >= m_Passes.size()) { - static PassConfig emptyConfig = PassConfig({}); - return emptyConfig; +namespace vkcv { + + static vk::AttachmentStoreOp getVkStoreOpFromAttachOp(AttachmentOperation op) { + switch (op) { + case AttachmentOperation::STORE: + return vk::AttachmentStoreOp::eStore; + default: + return vk::AttachmentStoreOp::eDontCare; } - - auto& pass = m_Passes[id]; - - return pass.m_Config; - } - - void PassManager::destroyPassById(uint64_t id) { - if (id >= m_Passes.size()) { - return; - } - - auto& pass = m_Passes[id]; - + } + + static vk::AttachmentLoadOp getVKLoadOpFromAttachOp(AttachmentOperation op) { + switch (op) { + case AttachmentOperation::LOAD: + return vk::AttachmentLoadOp::eLoad; + case AttachmentOperation::CLEAR: + return vk::AttachmentLoadOp::eClear; + default: + return vk::AttachmentLoadOp::eDontCare; + } + } + + uint64_t PassManager::getIdFrom(const PassHandle &handle) const { + return handle.getId(); + } + + PassHandle PassManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return PassHandle(id, destroy); + } + + void PassManager::destroyById(uint64_t id) { + auto &pass = getById(id); + if (pass.m_Handle) { - m_Device.destroy(pass.m_Handle); + getCore().getContext().getDevice().destroy(pass.m_Handle); pass.m_Handle = nullptr; } - } - -} + } + + PassManager::PassManager() noexcept : HandleManager<PassEntry, PassHandle>() {} + + PassManager::~PassManager() noexcept { + clear(); + } + + PassHandle PassManager::createPass(const PassConfig &config) { + // description of all {color, input, depth/stencil} attachments of the render pass + std::vector<vk::AttachmentDescription> attachmentDescriptions {}; + + // individual references to color attachments (of a subpass) + std::vector<vk::AttachmentReference> colorAttachmentReferences {}; + // individual reference to depth attachment (of a subpass) + vk::AttachmentReference depthStencilAttachmentRef; + + // stays nullptr if no depth attachment used + vk::AttachmentReference* pDepthStencil = nullptr; + + const auto &featureManager = getCore().getContext().getFeatureManager(); + + const bool separateDepthStencil = + (featureManager.checkFeatures<vk::PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR>( + vk::StructureType::ePhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR, + [](const vk::PhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR &features) { + return features.separateDepthStencilLayouts; + })); + + const auto &attachments = config.getAttachments(); + + std::vector<vk::ImageLayout> layouts; + layouts.reserve(attachments.size()); + + for (uint32_t i = 0; i < attachments.size(); i++) { + vk::Format format = attachments [i].getFormat(); + vk::ImageLayout layout; + + bool depthFormat = isDepthFormat(attachments [i].getFormat()); + bool stencilFormat = isStencilFormat(attachments [i].getFormat()); + + if ((separateDepthStencil) && (depthFormat) && (!stencilFormat)) { + layout = vk::ImageLayout::eDepthAttachmentOptimal; + } else if ((separateDepthStencil) && (!depthFormat) && (stencilFormat)) { + layout = vk::ImageLayout::eStencilAttachmentOptimal; + } else if ((depthFormat) || (stencilFormat)) { + layout = vk::ImageLayout::eDepthStencilAttachmentOptimal; + } else { + layout = vk::ImageLayout::eColorAttachmentOptimal; + } + + if ((depthFormat) || (stencilFormat)) { + depthStencilAttachmentRef = vk::AttachmentReference(i, layout); + pDepthStencil = &depthStencilAttachmentRef; + } else { + vk::AttachmentReference attachmentRef(i, layout); + colorAttachmentReferences.push_back(attachmentRef); + } + + vk::AttachmentDescription attachmentDesc( + {}, format, msaaToSampleCountFlagBits(config.getMultisampling()), + getVKLoadOpFromAttachOp(attachments [i].getLoadOperation()), + getVkStoreOpFromAttachOp(attachments [i].getStoreOperation()), + vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, layout, layout); + + if (stencilFormat) { + attachmentDesc.setStencilLoadOp(attachmentDesc.loadOp); + attachmentDesc.setStencilStoreOp(attachmentDesc.storeOp); + } + + attachmentDescriptions.push_back(attachmentDesc); + layouts.push_back(layout); + } + + const vk::SubpassDescription subpassDescription( + {}, vk::PipelineBindPoint::eGraphics, 0, {}, + static_cast<uint32_t>(colorAttachmentReferences.size()), + colorAttachmentReferences.data(), {}, pDepthStencil, 0, {}); + + const vk::RenderPassCreateInfo passInfo( + {}, static_cast<uint32_t>(attachmentDescriptions.size()), attachmentDescriptions.data(), + 1, &subpassDescription, 0, {}); + + vk::RenderPass renderPass = getCore().getContext().getDevice().createRenderPass(passInfo); + + return add({ renderPass, config, layouts }); + } + + vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const { + auto &pass = (*this) [handle]; + return pass.m_Handle; + } + + const PassConfig &PassManager::getPassConfig(const PassHandle &handle) const { + auto &pass = (*this) [handle]; + return pass.m_Config; + } + + const std::vector<vk::ImageLayout> &PassManager::getLayouts(const PassHandle &handle) const { + auto &pass = (*this) [handle]; + return pass.m_Layouts; + } + +} // namespace vkcv diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp index 661a8b277ecb446c4bbaeeb63560ffde28c31d99..fefbffaedc2b98da88b7ce625dbfc6f3ae448b48 100644 --- a/src/vkcv/PassManager.hpp +++ b/src/vkcv/PassManager.hpp @@ -1,43 +1,52 @@ #pragma once -#include <vulkan/vulkan.hpp> #include <vector> -#include "vkcv/Handles.hpp" +#include <vulkan/vulkan.hpp> + +#include "HandleManager.hpp" #include "vkcv/PassConfig.hpp" -namespace vkcv -{ - class PassManager - { - private: - struct Pass { - vk::RenderPass m_Handle; - PassConfig m_Config; - }; - - vk::Device m_Device; - std::vector<Pass> m_Passes; - - void destroyPassById(uint64_t id); - - public: - PassManager() = delete; // no default ctor - explicit PassManager(vk::Device device) noexcept; // ctor - ~PassManager() noexcept; // dtor - - PassManager(const PassManager &other) = delete; // copy-ctor - PassManager(PassManager &&other) = delete; // move-ctor; - - PassManager & operator=(const PassManager &other) = delete; // copy-assign op - PassManager & operator=(PassManager &&other) = delete; // move-assign op - - PassHandle createPass(const PassConfig &config); - - [[nodiscard]] - vk::RenderPass getVkPass(const PassHandle &handle) const; - - [[nodiscard]] - const PassConfig& getPassConfig(const PassHandle &handle) const; - - }; -} +namespace vkcv { + + struct PassEntry { + vk::RenderPass m_Handle; + PassConfig m_Config; + std::vector<vk::ImageLayout> m_Layouts; + }; + + /** + * @brief Class to manage the creation and destruction of passes. + */ + class PassManager : public HandleManager<PassEntry, PassHandle> { + friend class Core; + + private: + [[nodiscard]] uint64_t getIdFrom(const PassHandle &handle) const override; + + [[nodiscard]] PassHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + /** + * Destroys and deallocates pass represented by a given + * pass handle id. + * + * @param id Pass handle id + */ + void destroyById(uint64_t id) override; + + public: + PassManager() noexcept; + + ~PassManager() noexcept override; // dtor + + [[nodiscard]] PassHandle createPass(const PassConfig &config); + + [[nodiscard]] vk::RenderPass getVkPass(const PassHandle &handle) const; + + [[nodiscard]] const PassConfig &getPassConfig(const PassHandle &handle) const; + + [[nodiscard]] const std::vector<vk::ImageLayout> & + getLayouts(const PassHandle &handle) const; + }; + +} // namespace vkcv diff --git a/src/vkcv/PipelineConfig.cpp b/src/vkcv/PipelineConfig.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6fb1ceb9ead48369e8f944725a139bb3b7428a1 --- /dev/null +++ b/src/vkcv/PipelineConfig.cpp @@ -0,0 +1,38 @@ + +#include "vkcv/PipelineConfig.hpp" + +namespace vkcv { + + PipelineConfig::PipelineConfig() : m_ShaderProgram(), m_DescriptorSetLayouts() {} + + PipelineConfig::PipelineConfig(const ShaderProgram &program, + const std::vector<DescriptorSetLayoutHandle> &layouts) : + m_ShaderProgram(program), + m_DescriptorSetLayouts(layouts) {} + + void PipelineConfig::setShaderProgram(const ShaderProgram &program) { + m_ShaderProgram = program; + } + + const ShaderProgram &PipelineConfig::getShaderProgram() const { + return m_ShaderProgram; + } + + void PipelineConfig::addDescriptorSetLayout(const DescriptorSetLayoutHandle &layout) { + m_DescriptorSetLayouts.push_back(layout); + } + + void + PipelineConfig::addDescriptorSetLayouts(const std::vector<DescriptorSetLayoutHandle> &layouts) { + m_DescriptorSetLayouts.reserve(m_DescriptorSetLayouts.size() + layouts.size()); + + for (const auto &layout : layouts) { + m_DescriptorSetLayouts.push_back(layout); + } + } + + const std::vector<DescriptorSetLayoutHandle> &PipelineConfig::getDescriptorSetLayouts() const { + return m_DescriptorSetLayouts; + } + +} // namespace vkcv diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp deleted file mode 100644 index df36442efc2992bf16b6e82245ef9753dad95e5d..0000000000000000000000000000000000000000 --- a/src/vkcv/PipelineManager.cpp +++ /dev/null @@ -1,433 +0,0 @@ -#include "PipelineManager.hpp" -#include "vkcv/Image.hpp" -#include "vkcv/Logger.hpp" - -namespace vkcv -{ - - PipelineManager::PipelineManager(vk::Device device) noexcept : - m_Device{device}, - m_Pipelines{} - {} - - PipelineManager::~PipelineManager() noexcept - { - for (uint64_t id = 0; id < m_Pipelines.size(); id++) { - destroyPipelineById(id); - } - } - - // currently assuming default 32 bit formats, no lower precision or normalized variants supported - vk::Format vertexFormatToVulkanFormat(const VertexAttachmentFormat format) { - switch (format) { - case VertexAttachmentFormat::FLOAT: - return vk::Format::eR32Sfloat; - case VertexAttachmentFormat::FLOAT2: - return vk::Format::eR32G32Sfloat; - case VertexAttachmentFormat::FLOAT3: - return vk::Format::eR32G32B32Sfloat; - case VertexAttachmentFormat::FLOAT4: - return vk::Format::eR32G32B32A32Sfloat; - case VertexAttachmentFormat::INT: - return vk::Format::eR32Sint; - case VertexAttachmentFormat::INT2: - return vk::Format::eR32G32Sint; - case VertexAttachmentFormat::INT3: - return vk::Format::eR32G32B32Sint; - case VertexAttachmentFormat::INT4: - return vk::Format::eR32G32B32A32Sint; - default: - vkcv_log(LogLevel::WARNING, "Unknown vertex format"); - return vk::Format::eUndefined; - } - } - - vk::PrimitiveTopology primitiveTopologyToVulkanPrimitiveTopology(const PrimitiveTopology topology) { - switch (topology) { - case(PrimitiveTopology::PointList): return vk::PrimitiveTopology::ePointList; - case(PrimitiveTopology::LineList): return vk::PrimitiveTopology::eLineList; - case(PrimitiveTopology::TriangleList): return vk::PrimitiveTopology::eTriangleList; - default: std::cout << "Error: Unknown primitive topology type" << std::endl; return vk::PrimitiveTopology::eTriangleList; - } - } - - PipelineHandle PipelineManager::createPipeline(const PipelineConfig &config, PassManager& passManager) - { - const vk::RenderPass &pass = passManager.getVkPass(config.m_PassHandle); - - const bool existsVertexShader = config.m_ShaderProgram.existsShader(ShaderStage::VERTEX); - const bool existsFragmentShader = config.m_ShaderProgram.existsShader(ShaderStage::FRAGMENT); - if (!(existsVertexShader && existsFragmentShader)) - { - vkcv_log(LogLevel::ERROR, "Requires vertex and fragment shader code"); - return PipelineHandle(); - } - - // vertex shader stage - std::vector<char> vertexCode = config.m_ShaderProgram.getShader(ShaderStage::VERTEX).shaderCode; - vk::ShaderModuleCreateInfo vertexModuleInfo({}, vertexCode.size(), reinterpret_cast<uint32_t*>(vertexCode.data())); - vk::ShaderModule vertexModule{}; - if (m_Device.createShaderModule(&vertexModuleInfo, nullptr, &vertexModule) != vk::Result::eSuccess) - return PipelineHandle(); - - vk::PipelineShaderStageCreateInfo pipelineVertexShaderStageInfo( - {}, - vk::ShaderStageFlagBits::eVertex, - vertexModule, - "main", - nullptr - ); - - // fragment shader stage - std::vector<char> fragCode = config.m_ShaderProgram.getShader(ShaderStage::FRAGMENT).shaderCode; - vk::ShaderModuleCreateInfo fragmentModuleInfo({}, fragCode.size(), reinterpret_cast<uint32_t*>(fragCode.data())); - vk::ShaderModule fragmentModule{}; - if (m_Device.createShaderModule(&fragmentModuleInfo, nullptr, &fragmentModule) != vk::Result::eSuccess) - { - m_Device.destroy(vertexModule); - return PipelineHandle(); - } - - vk::PipelineShaderStageCreateInfo pipelineFragmentShaderStageInfo( - {}, - vk::ShaderStageFlagBits::eFragment, - fragmentModule, - "main", - nullptr - ); - - // vertex input state - - // Fill up VertexInputBindingDescription and VertexInputAttributeDescription Containers - std::vector<vk::VertexInputAttributeDescription> vertexAttributeDescriptions; - std::vector<vk::VertexInputBindingDescription> vertexBindingDescriptions; - - const VertexLayout &layout = config.m_VertexLayout; - - // iterate over the layout's specified, mutually exclusive buffer bindings that make up a vertex buffer - for (const auto &vertexBinding : layout.vertexBindings) - { - vertexBindingDescriptions.emplace_back(vertexBinding.bindingLocation, - vertexBinding.stride, - vk::VertexInputRate::eVertex); - - // iterate over the bindings' specified, mutually exclusive vertex input attachments that make up a vertex - for(const auto &vertexAttachment: vertexBinding.vertexAttachments) - { - vertexAttributeDescriptions.emplace_back(vertexAttachment.inputLocation, - vertexBinding.bindingLocation, - vertexFormatToVulkanFormat(vertexAttachment.format), - vertexAttachment.offset % vertexBinding.stride); - - } - } - - // Handover Containers to PipelineVertexInputStateCreateIngo Struct - vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo( - {}, - vertexBindingDescriptions.size(), - vertexBindingDescriptions.data(), - vertexAttributeDescriptions.size(), - vertexAttributeDescriptions.data() - ); - - // input assembly state - vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo( - {}, - primitiveTopologyToVulkanPrimitiveTopology(config.m_PrimitiveTopology), - false - ); - - // viewport state - vk::Viewport viewport(0.f, 0.f, static_cast<float>(config.m_Width), static_cast<float>(config.m_Height), 0.f, 1.f); - vk::Rect2D scissor({ 0,0 }, { config.m_Width, config.m_Height }); - vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo({}, 1, &viewport, 1, &scissor); - - // rasterization state - vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo( - {}, - false, - false, - vk::PolygonMode::eFill, - vk::CullModeFlagBits::eNone, - vk::FrontFace::eCounterClockwise, - false, - 0.f, - 0.f, - 0.f, - 1.f - ); - vk::PipelineRasterizationConservativeStateCreateInfoEXT conservativeRasterization; - if (config.m_UseConservativeRasterization) { - conservativeRasterization = vk::PipelineRasterizationConservativeStateCreateInfoEXT( - {}, - vk::ConservativeRasterizationModeEXT::eOverestimate, - 0.f); - pipelineRasterizationStateCreateInfo.pNext = &conservativeRasterization; - } - - // multisample state - vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo( - {}, - vk::SampleCountFlagBits::e1, - false, - 0.f, - nullptr, - false, - false - ); - - // color blend state - vk::ColorComponentFlags colorWriteMask(VK_COLOR_COMPONENT_R_BIT | - VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | - VK_COLOR_COMPONENT_A_BIT); - vk::PipelineColorBlendAttachmentState colorBlendAttachmentState( - false, - vk::BlendFactor::eOne, - vk::BlendFactor::eOne, - vk::BlendOp::eAdd, - vk::BlendFactor::eOne, - vk::BlendFactor::eOne, - vk::BlendOp::eAdd, - colorWriteMask - ); - vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo( - {}, - false, - vk::LogicOp::eClear, - 1, //TODO: hardcoded to one - &colorBlendAttachmentState, - { 1.f,1.f,1.f,1.f } - ); - - const size_t matrixPushConstantSize = config.m_ShaderProgram.getPushConstantSize(); - const vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eAll, 0, matrixPushConstantSize); - - // pipeline layout - vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo( - {}, - (config.m_DescriptorLayouts), - (pushConstantRange)); - - vk::PipelineLayout vkPipelineLayout{}; - if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) - { - m_Device.destroy(vertexModule); - m_Device.destroy(fragmentModule); - return PipelineHandle(); - } - - const vk::PipelineDepthStencilStateCreateInfo depthStencilCreateInfo( - vk::PipelineDepthStencilStateCreateFlags(), - true, - true, - vk::CompareOp::eLessOrEqual, - false, - false, - {}, - {}, - 0.0f, - 1.0f - ); - - const vk::PipelineDepthStencilStateCreateInfo* p_depthStencilCreateInfo = nullptr; - - const PassConfig& passConfig = passManager.getPassConfig(config.m_PassHandle); - - for (const auto& attachment : passConfig.attachments) { - if (isDepthFormat(attachment.format)) { - p_depthStencilCreateInfo = &depthStencilCreateInfo; - break; - } - } - - std::vector<vk::DynamicState> dynamicStates = {}; - if(config.m_UseDynamicViewport) - { - dynamicStates.push_back(vk::DynamicState::eViewport); - dynamicStates.push_back(vk::DynamicState::eScissor); - } - - vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo({}, - static_cast<uint32_t>(dynamicStates.size()), - dynamicStates.data()); - - // graphics pipeline create - std::vector<vk::PipelineShaderStageCreateInfo> shaderStages = { pipelineVertexShaderStageInfo, pipelineFragmentShaderStageInfo }; - - const char *geometryShaderName = "main"; // outside of if to make sure it stays in scope - vk::ShaderModule geometryModule; - if (config.m_ShaderProgram.existsShader(ShaderStage::GEOMETRY)) { - const vkcv::Shader geometryShader = config.m_ShaderProgram.getShader(ShaderStage::GEOMETRY); - const auto& geometryCode = geometryShader.shaderCode; - const vk::ShaderModuleCreateInfo geometryModuleInfo({}, geometryCode.size(), reinterpret_cast<const uint32_t*>(geometryCode.data())); - if (m_Device.createShaderModule(&geometryModuleInfo, nullptr, &geometryModule) != vk::Result::eSuccess) { - return PipelineHandle(); - } - vk::PipelineShaderStageCreateInfo geometryStage({}, vk::ShaderStageFlagBits::eGeometry, geometryModule, geometryShaderName); - shaderStages.push_back(geometryStage); - } - - const vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo( - {}, - static_cast<uint32_t>(shaderStages.size()), - shaderStages.data(), - &pipelineVertexInputStateCreateInfo, - &pipelineInputAssemblyStateCreateInfo, - nullptr, - &pipelineViewportStateCreateInfo, - &pipelineRasterizationStateCreateInfo, - &pipelineMultisampleStateCreateInfo, - p_depthStencilCreateInfo, - &pipelineColorBlendStateCreateInfo, - &dynamicStateCreateInfo, - vkPipelineLayout, - pass, - 0, - {}, - 0 - ); - - vk::Pipeline vkPipeline{}; - if (m_Device.createGraphicsPipelines(nullptr, 1, &graphicsPipelineCreateInfo, nullptr, &vkPipeline) != vk::Result::eSuccess) - { - m_Device.destroy(vertexModule); - m_Device.destroy(fragmentModule); - if (geometryModule) { - m_Device.destroy(geometryModule); - } - m_Device.destroy(); - return PipelineHandle(); - } - - m_Device.destroy(vertexModule); - m_Device.destroy(fragmentModule); - if (geometryModule) { - m_Device.destroy(geometryModule); - } - - const uint64_t id = m_Pipelines.size(); - m_Pipelines.push_back({ vkPipeline, vkPipelineLayout, config }); - return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); }); - } - - vk::Pipeline PipelineManager::getVkPipeline(const PipelineHandle &handle) const - { - const uint64_t id = handle.getId(); - - if (id >= m_Pipelines.size()) { - return nullptr; - } - - auto& pipeline = m_Pipelines[id]; - - return pipeline.m_handle; - } - - vk::PipelineLayout PipelineManager::getVkPipelineLayout(const PipelineHandle &handle) const - { - const uint64_t id = handle.getId(); - - if (id >= m_Pipelines.size()) { - return nullptr; - } - - auto& pipeline = m_Pipelines[id]; - - return pipeline.m_layout; - } - - void PipelineManager::destroyPipelineById(uint64_t id) { - if (id >= m_Pipelines.size()) { - return; - } - - auto& pipeline = m_Pipelines[id]; - - if (pipeline.m_handle) { - m_Device.destroy(pipeline.m_handle); - pipeline.m_handle = nullptr; - } - - if (pipeline.m_layout) { - m_Device.destroy(pipeline.m_layout); - pipeline.m_layout = nullptr; - } - } - - const PipelineConfig& PipelineManager::getPipelineConfig(const PipelineHandle &handle) const - { - const uint64_t id = handle.getId(); - - if (id >= m_Pipelines.size()) { - static PipelineConfig dummyConfig; - vkcv_log(LogLevel::ERROR, "Invalid handle"); - return dummyConfig; - } - - return m_Pipelines[id].m_config; - } - - PipelineHandle PipelineManager::createComputePipeline( - const ShaderProgram &shaderProgram, - const std::vector<vk::DescriptorSetLayout> &descriptorSetLayouts) { - - // Temporally handing over the Shader Program instead of a pipeline config - vk::ShaderModule computeModule{}; - if (createShaderModule(computeModule, shaderProgram, ShaderStage::COMPUTE) != vk::Result::eSuccess) - return PipelineHandle(); - - vk::PipelineShaderStageCreateInfo pipelineComputeShaderStageInfo( - {}, - vk::ShaderStageFlagBits::eCompute, - computeModule, - "main", - nullptr - ); - - vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo({}, descriptorSetLayouts); - - const size_t pushConstantSize = shaderProgram.getPushConstantSize(); - vk::PushConstantRange pushConstantRange(vk::ShaderStageFlagBits::eCompute, 0, pushConstantSize); - if (pushConstantSize > 0) { - pipelineLayoutCreateInfo.setPushConstantRangeCount(1); - pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRange); - } - - vk::PipelineLayout vkPipelineLayout{}; - if (m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &vkPipelineLayout) != vk::Result::eSuccess) - { - m_Device.destroy(computeModule); - return PipelineHandle(); - } - - vk::ComputePipelineCreateInfo computePipelineCreateInfo{}; - computePipelineCreateInfo.stage = pipelineComputeShaderStageInfo; - computePipelineCreateInfo.layout = vkPipelineLayout; - - vk::Pipeline vkPipeline; - if (m_Device.createComputePipelines(nullptr, 1, &computePipelineCreateInfo, nullptr, &vkPipeline)!= vk::Result::eSuccess) - { - m_Device.destroy(computeModule); - return PipelineHandle(); - } - - m_Device.destroy(computeModule); - - const uint64_t id = m_Pipelines.size(); - m_Pipelines.push_back({ vkPipeline, vkPipelineLayout, PipelineConfig() }); - - return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); }); - } - - // There is an issue for refactoring the Pipeline Manager. - // While including Compute Pipeline Creation, some private helper functions where introduced: - - vk::Result PipelineManager::createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, const ShaderStage stage) - { - std::vector<char> code = shaderProgram.getShader(stage).shaderCode; - vk::ShaderModuleCreateInfo moduleInfo({}, code.size(), reinterpret_cast<uint32_t*>(code.data())); - return m_Device.createShaderModule(&moduleInfo, nullptr, &module); - } -} \ No newline at end of file diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp deleted file mode 100644 index b153eb4632b844e84b92953fe8abf6666a13e0c9..0000000000000000000000000000000000000000 --- a/src/vkcv/PipelineManager.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include <vulkan/vulkan.hpp> -#include <vector> -#include "vkcv/Handles.hpp" -#include "vkcv/PipelineConfig.hpp" -#include "PassManager.hpp" - -namespace vkcv -{ - class PipelineManager - { - private: - struct Pipeline { - vk::Pipeline m_handle; - vk::PipelineLayout m_layout; - PipelineConfig m_config; - }; - - vk::Device m_Device; - std::vector<Pipeline> m_Pipelines; - - void destroyPipelineById(uint64_t id); - - vk::Result createShaderModule(vk::ShaderModule &module, const ShaderProgram &shaderProgram, ShaderStage stage); - - public: - PipelineManager() = delete; // no default ctor - explicit PipelineManager(vk::Device device) noexcept; // ctor - ~PipelineManager() noexcept; // dtor - - PipelineManager(const PipelineManager &other) = delete; // copy-ctor - PipelineManager(PipelineManager &&other) = delete; // move-ctor; - - PipelineManager & operator=(const PipelineManager &other) = delete; // copy-assign op - PipelineManager & operator=(PipelineManager &&other) = delete; // move-assign op - - PipelineHandle createPipeline(const PipelineConfig &config, PassManager& passManager); - - PipelineHandle createComputePipeline( - const ShaderProgram& shaderProgram, - const std::vector<vk::DescriptorSetLayout>& descriptorSetLayouts); - - [[nodiscard]] - vk::Pipeline getVkPipeline(const PipelineHandle &handle) const; - - [[nodiscard]] - vk::PipelineLayout getVkPipelineLayout(const PipelineHandle &handle) const; - - [[nodiscard]] - const PipelineConfig &getPipelineConfig(const PipelineHandle &handle) const; - }; -} diff --git a/src/vkcv/PushConstants.cpp b/src/vkcv/PushConstants.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df49f0a5ee08fc793e047f8755347fe47a2a3180 --- /dev/null +++ b/src/vkcv/PushConstants.cpp @@ -0,0 +1,38 @@ +#include <vkcv/PushConstants.hpp> + +namespace vkcv { + + PushConstants::PushConstants(size_t sizePerDrawcall) : m_typeGuard(sizePerDrawcall), m_data() {} + + PushConstants::PushConstants(const TypeGuard &guard) : m_typeGuard(guard), m_data() {} + + size_t PushConstants::getSizePerDrawcall() const { + return m_typeGuard.typeSize(); + } + + size_t PushConstants::getFullSize() const { + return m_data.size(); + } + + size_t PushConstants::getDrawcallCount() const { + return getFullSize() / getSizePerDrawcall(); + } + + void PushConstants::clear() { + m_data.clear(); + } + + const void* PushConstants::getDrawcallData(size_t index) const { + const size_t offset = (index * getSizePerDrawcall()); + return reinterpret_cast<const void*>(m_data.data() + offset); + } + + const void* PushConstants::getData() const { + if (m_data.empty()) { + return nullptr; + } else { + return m_data.data(); + } + } + +} // namespace vkcv diff --git a/src/vkcv/QueueManager.cpp b/src/vkcv/QueueManager.cpp index df6c74cccf6c4652adc6a4c78802f282ea6ae293..d292383ab58ff1e34343382828e7a5f43b6b6a33 100644 --- a/src/vkcv/QueueManager.cpp +++ b/src/vkcv/QueueManager.cpp @@ -1,218 +1,88 @@ +#include <iostream> #include <limits> #include <unordered_set> -#include <iostream> -#include "vkcv/QueueManager.hpp" #include "vkcv/Logger.hpp" +#include "vkcv/QueueManager.hpp" namespace vkcv { - /** - * Given the @p physicalDevice and the @p queuePriorities, the @p queueCreateInfos are computed. First, the requested - * queues are sorted by priority depending on the availability of queues in the queue families of the given - * @p physicalDevice. Then check, if all requested queues are creatable. If so, the @p queueCreateInfos will be computed. - * Furthermore, lists of index pairs (queueFamilyIndex, queueIndex) for later referencing of the separate queues will - * be computed. - * @param[in] physicalDevice The physical device - * @param[in] queuePriorities The queue priorities used for the computation of @p queueCreateInfos - * @param[in] queueFlags The queue flags requesting the queues - * @param[in,out] queueCreateInfos The queue create info structures to be created - * @param[in,out] queuePairsGraphics The list of index pairs (queueFamilyIndex, queueIndex) of queues of type - * vk::QueueFlagBits::eGraphics - * @param[in,out] queuePairsCompute The list of index pairs (queueFamilyIndex, queueIndex) of queues of type - * vk::QueueFlagBits::eCompute - * @param[in,out] queuePairsTransfer The list of index pairs (queueFamilyIndex, queueIndex) of queues of type - * vk::QueueFlagBits::eTransfer - * @throws std::runtime_error If the requested queues from @p queueFlags are not creatable due to insufficient availability. - */ - void QueueManager::queueCreateInfosQueueHandles(vk::PhysicalDevice &physicalDevice, - std::vector<float> &queuePriorities, - std::vector<vk::QueueFlagBits> &queueFlags, - std::vector<vk::DeviceQueueCreateInfo> &queueCreateInfos, - std::vector<std::pair<int, int>> &queuePairsGraphics, - std::vector<std::pair<int, int>> &queuePairsCompute, - std::vector<std::pair<int, int>> &queuePairsTransfer) - { - queueCreateInfos = {}; - queuePairsGraphics = {}; - queuePairsCompute = {}; - queuePairsTransfer = {}; - std::vector<vk::QueueFamilyProperties> qFamilyProperties = physicalDevice.getQueueFamilyProperties(); - - //check priorities of flags -> the lower prioCount the higher the priority - std::vector<int> prios; - for(auto flag: queueFlags) { - int prioCount = 0; - for (int i = 0; i < qFamilyProperties.size(); i++) { - prioCount += (static_cast<uint32_t>(flag & qFamilyProperties[i].queueFlags) != 0) * qFamilyProperties[i].queueCount; - } - prios.push_back(prioCount); - } - //resort flags with heighest priority before allocating the queues - std::vector<vk::QueueFlagBits> newFlags; - for(int i = 0; i < prios.size(); i++) { - auto minElem = std::min_element(prios.begin(), prios.end()); - int index = minElem - prios.begin(); - newFlags.push_back(queueFlags[index]); - prios[index] = std::numeric_limits<int>::max(); - } - - // create requested queues and check if more requested queues are supported - // herefore: create vector that updates available queues in each queue family - // structure: [qFamily_0, ..., qFamily_n] where - // - qFamily_i = [GraphicsCount, ComputeCount, TransferCount], 0 <= i <= n - std::vector<std::vector<int>> queueFamilyStatus, initialQueueFamilyStatus; - - for (auto qFamily : qFamilyProperties) { - int graphicsCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eGraphics) != 0) * qFamily.queueCount; - int computeCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eCompute) != 0) * qFamily.queueCount; - int transferCount = int(static_cast<uint32_t>(qFamily.queueFlags & vk::QueueFlagBits::eTransfer) != 0) * qFamily.queueCount; - queueFamilyStatus.push_back({graphicsCount, computeCount, transferCount}); - } - - initialQueueFamilyStatus = queueFamilyStatus; - // check if every queue with the specified queue flag can be created - // this automatically checks for queue flag support! - for (auto qFlag : newFlags) { - bool found; - switch (qFlag) { - case vk::QueueFlagBits::eGraphics: - found = false; - for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { - if (queueFamilyStatus[i][0] > 0) { - queuePairsGraphics.push_back(std::pair(i, initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0])); - queueFamilyStatus[i][0]--; - queueFamilyStatus[i][1]--; - queueFamilyStatus[i][2]--; - found = true; - } - } - if (!found) { - for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { - if (initialQueueFamilyStatus[i][0] > 0) { - queuePairsGraphics.push_back(std::pair(i, 0)); - found = true; - } - } - - vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str()); - } - break; - case vk::QueueFlagBits::eCompute: - found = false; - for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { - if (queueFamilyStatus[i][1] > 0) { - queuePairsCompute.push_back(std::pair(i, initialQueueFamilyStatus[i][1] - queueFamilyStatus[i][1])); - queueFamilyStatus[i][0]--; - queueFamilyStatus[i][1]--; - queueFamilyStatus[i][2]--; - found = true; - } - } - if (!found) { - for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { - if (initialQueueFamilyStatus[i][1] > 0) { - queuePairsCompute.push_back(std::pair(i, 0)); - found = true; - } - } - - vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str()); - } - break; - case vk::QueueFlagBits::eTransfer: - found = false; - for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { - if (queueFamilyStatus[i][2] > 0) { - queuePairsTransfer.push_back(std::pair(i, initialQueueFamilyStatus[i][2] - queueFamilyStatus[i][2])); - queueFamilyStatus[i][0]--; - queueFamilyStatus[i][1]--; - queueFamilyStatus[i][2]--; - found = true; - } - } - if (!found) { - for (int i = 0; i < queueFamilyStatus.size() && !found; i++) { - if (initialQueueFamilyStatus[i][2] > 0) { - queuePairsTransfer.push_back(std::pair(i, 0)); - found = true; - } - } - - vkcv_log(LogLevel::WARNING, "Not enough %s queues", vk::to_string(qFlag).c_str()); - } - break; - default: - throw std::runtime_error("Invalid input for queue flag bits. Valid inputs are 'vk::QueueFlagBits::eGraphics', 'vk::QueueFlagBits::eCompute' and 'vk::QueueFlagBits::eTransfer'."); - } - } - - // create all requested queues - for (int i = 0; i < qFamilyProperties.size(); i++) { - uint32_t create = std::abs(initialQueueFamilyStatus[i][0] - queueFamilyStatus[i][0]); - if (create > 0) { - vk::DeviceQueueCreateInfo qCreateInfo( - vk::DeviceQueueCreateFlags(), - i, - create, - queuePriorities.data() - ); - queueCreateInfos.push_back(qCreateInfo); - } - } - } - - /** - * Computes the queue handles from @p queuePairs - * @param device The device - * @param queuePairs The queuePairs that were created separately for each queue type (e.g., vk::QueueFlagBits::eGraphics) - * @return An array of queue handles based on the @p queuePairs - */ - std::vector<Queue> getQueues(const vk::Device& device, const std::vector<std::pair<int, int>>& queuePairs) { - std::vector<Queue> queues; - - for (auto q : queuePairs) { - const int queueFamilyIndex = q.first; // the queueIndex of the queue family - const int queueIndex = q.second; // the queueIndex within a queue family - - queues.push_back({ queueFamilyIndex, queueIndex, device.getQueue(queueFamilyIndex, queueIndex) }); - } - - return queues; - } - - - QueueManager QueueManager::create(vk::Device device, - std::vector<std::pair<int, int>> &queuePairsGraphics, - std::vector<std::pair<int, int>> &queuePairsCompute, - std::vector<std::pair<int, int>> &queuePairsTransfer) { - - std::vector<Queue> graphicsQueues = getQueues(device, queuePairsGraphics); - std::vector<Queue> computeQueues = getQueues(device, queuePairsCompute ); - std::vector<Queue> transferQueues = getQueues(device, queuePairsTransfer); - - return QueueManager( std::move(graphicsQueues), std::move(computeQueues), std::move(transferQueues), 0); + /** + * Computes the queue handles from @p queuePairs + * @param device The device + * @param queuePairs The queuePairs that were created separately for each queue type (e.g., + * vk::QueueFlagBits::eGraphics) + * @return An array of queue handles based on the @p queuePairs + */ + std::vector<Queue> getQueues(const vk::Device &device, + const std::vector<std::pair<int, int>> &queuePairs) { + std::vector<Queue> queues; + + for (auto q : queuePairs) { + const int queueFamilyIndex = q.first; // the queueIndex of the queue family + const int queueIndex = q.second; // the queueIndex within a queue family + + queues.push_back( + { queueFamilyIndex, queueIndex, device.getQueue(queueFamilyIndex, queueIndex) }); + } + + return queues; } - QueueManager::QueueManager(std::vector<Queue>&& graphicsQueues, std::vector<Queue>&& computeQueues, std::vector<Queue>&& transferQueues, size_t presentIndex) - : m_graphicsQueues(graphicsQueues), m_computeQueues(computeQueues), m_transferQueues(transferQueues), m_presentIndex(presentIndex) - {} + QueueManager QueueManager::create(vk::Device device, + const std::vector<std::pair<int, int>> &queuePairsGraphics, + const std::vector<std::pair<int, int>> &queuePairsCompute, + const std::vector<std::pair<int, int>> &queuePairsTransfer) { - const Queue &QueueManager::getPresentQueue() const { - return m_graphicsQueues[m_presentIndex]; - } + std::vector<Queue> graphicsQueues = getQueues(device, queuePairsGraphics); + std::vector<Queue> computeQueues = getQueues(device, queuePairsCompute); + std::vector<Queue> transferQueues = getQueues(device, queuePairsTransfer); - const std::vector<Queue> &QueueManager::getGraphicsQueues() const { - return m_graphicsQueues; - } + return QueueManager(std::move(graphicsQueues), std::move(computeQueues), + std::move(transferQueues), 0); + } - const std::vector<Queue> &QueueManager::getComputeQueues() const { - return m_computeQueues; - } + uint32_t QueueManager::checkSurfaceSupport(const vk::PhysicalDevice &physicalDevice, + const vk::SurfaceKHR &surface) { + std::vector<vk::QueueFamilyProperties> qFamilyProperties = + physicalDevice.getQueueFamilyProperties(); + + for (uint32_t i = 0; i < qFamilyProperties.size(); i++) { + vk::Bool32 presentSupport; + + if ((vk::Result::eSuccess + == physicalDevice.getSurfaceSupportKHR(i, surface, &presentSupport)) + && (presentSupport == VK_TRUE)) { + return i; + } + } + + vkcv_log(LogLevel::WARNING, "No supported present queue"); + return 0; + } - const std::vector<Queue> &QueueManager::getTransferQueues() const { - return m_transferQueues; - } + QueueManager::QueueManager(std::vector<Queue> &&graphicsQueues, + std::vector<Queue> &&computeQueues, + std::vector<Queue> &&transferQueues, size_t presentIndex) : + m_graphicsQueues(graphicsQueues), + m_computeQueues(computeQueues), m_transferQueues(transferQueues), + m_presentIndex(presentIndex) {} + + const Queue &QueueManager::getPresentQueue() const { + return m_graphicsQueues [m_presentIndex]; + } + + const std::vector<Queue> &QueueManager::getGraphicsQueues() const { + return m_graphicsQueues; + } + + const std::vector<Queue> &QueueManager::getComputeQueues() const { + return m_computeQueues; + } + + const std::vector<Queue> &QueueManager::getTransferQueues() const { + return m_transferQueues; + } -} \ No newline at end of file +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/Sampler.cpp b/src/vkcv/Sampler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f07e096ed5cd65c578eca0dee6c6e6693b5a260 --- /dev/null +++ b/src/vkcv/Sampler.cpp @@ -0,0 +1,21 @@ + +#include "vkcv/Sampler.hpp" + +namespace vkcv { + + SamplerHandle samplerLinear(Core &core, bool clampToEdge) { + return core.createSampler(vkcv::SamplerFilterType::LINEAR, vkcv::SamplerFilterType::LINEAR, + vkcv::SamplerMipmapMode::LINEAR, + clampToEdge ? vkcv::SamplerAddressMode::CLAMP_TO_EDGE : + vkcv::SamplerAddressMode::REPEAT); + } + + SamplerHandle samplerNearest(Core &core, bool clampToEdge) { + return core.createSampler(vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerFilterType::NEAREST, + vkcv::SamplerMipmapMode::NEAREST, + clampToEdge ? vkcv::SamplerAddressMode::CLAMP_TO_EDGE : + vkcv::SamplerAddressMode::REPEAT); + } + +} // namespace vkcv diff --git a/src/vkcv/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp index a6ebb95b5e237dcd06ed8041b3f16489f7339d6a..6907c6b1c0f2a58822b01180304ead671d995eeb 100644 --- a/src/vkcv/SamplerManager.cpp +++ b/src/vkcv/SamplerManager.cpp @@ -3,123 +3,130 @@ #include "vkcv/Core.hpp" namespace vkcv { - - SamplerManager::SamplerManager(const vk::Device& device) noexcept : - m_device(device), m_samplers() - {} - - SamplerManager::~SamplerManager() { - for (uint64_t id = 0; id < m_samplers.size(); id++) { - destroySamplerById(id); + + uint64_t SamplerManager::getIdFrom(const SamplerHandle &handle) const { + return handle.getId(); + } + + SamplerHandle SamplerManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return SamplerHandle(id, destroy); + } + + void SamplerManager::destroyById(uint64_t id) { + auto &sampler = getById(id); + + if (sampler) { + getCore().getContext().getDevice().destroySampler(sampler); + sampler = nullptr; } } - + + SamplerManager::SamplerManager() noexcept : HandleManager<vk::Sampler, SamplerHandle>() {} + + SamplerManager::~SamplerManager() noexcept { + clear(); + } + SamplerHandle SamplerManager::createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, SamplerMipmapMode mipmapMode, - SamplerAddressMode addressMode) { + SamplerAddressMode addressMode, float mipLodBias, + SamplerBorderColor borderColor) { vk::Filter vkMagFilter; vk::Filter vkMinFilter; vk::SamplerMipmapMode vkMipmapMode; vk::SamplerAddressMode vkAddressMode; - + vk::BorderColor vkBorderColor; + switch (magFilter) { - case SamplerFilterType::NEAREST: - vkMagFilter = vk::Filter::eNearest; - break; - case SamplerFilterType::LINEAR: - vkMagFilter = vk::Filter::eLinear; - break; - default: - return SamplerHandle(); + case SamplerFilterType::NEAREST: + vkMagFilter = vk::Filter::eNearest; + break; + case SamplerFilterType::LINEAR: + vkMagFilter = vk::Filter::eLinear; + break; + default: + return SamplerHandle(); } - + switch (minFilter) { - case SamplerFilterType::NEAREST: - vkMinFilter = vk::Filter::eNearest; - break; - case SamplerFilterType::LINEAR: - vkMinFilter = vk::Filter::eLinear; - break; - default: - return SamplerHandle(); + case SamplerFilterType::NEAREST: + vkMinFilter = vk::Filter::eNearest; + break; + case SamplerFilterType::LINEAR: + vkMinFilter = vk::Filter::eLinear; + break; + default: + return SamplerHandle(); } - + switch (mipmapMode) { - case SamplerMipmapMode::NEAREST: - vkMipmapMode = vk::SamplerMipmapMode::eNearest; - break; - case SamplerMipmapMode::LINEAR: - vkMipmapMode = vk::SamplerMipmapMode::eLinear; - break; - default: - return SamplerHandle(); + case SamplerMipmapMode::NEAREST: + vkMipmapMode = vk::SamplerMipmapMode::eNearest; + break; + case SamplerMipmapMode::LINEAR: + vkMipmapMode = vk::SamplerMipmapMode::eLinear; + break; + default: + return SamplerHandle(); } - + switch (addressMode) { - case SamplerAddressMode::REPEAT: - vkAddressMode = vk::SamplerAddressMode::eRepeat; - break; - case SamplerAddressMode::MIRRORED_REPEAT: - vkAddressMode = vk::SamplerAddressMode::eMirroredRepeat; - break; - case SamplerAddressMode::CLAMP_TO_EDGE: - vkAddressMode = vk::SamplerAddressMode::eClampToEdge; - break; - case SamplerAddressMode::MIRROR_CLAMP_TO_EDGE: - vkAddressMode = vk::SamplerAddressMode::eMirrorClampToEdge; - break; - default: - return SamplerHandle(); + case SamplerAddressMode::REPEAT: + vkAddressMode = vk::SamplerAddressMode::eRepeat; + break; + case SamplerAddressMode::MIRRORED_REPEAT: + vkAddressMode = vk::SamplerAddressMode::eMirroredRepeat; + break; + case SamplerAddressMode::CLAMP_TO_EDGE: + vkAddressMode = vk::SamplerAddressMode::eClampToEdge; + break; + case SamplerAddressMode::MIRROR_CLAMP_TO_EDGE: + vkAddressMode = vk::SamplerAddressMode::eMirrorClampToEdge; + break; + case SamplerAddressMode::CLAMP_TO_BORDER: + vkAddressMode = vk::SamplerAddressMode::eClampToBorder; + break; + default: + return SamplerHandle(); } - - const vk::SamplerCreateInfo samplerCreateInfo ( - vk::SamplerCreateFlags(), - vkMagFilter, - vkMinFilter, - vkMipmapMode, - vkAddressMode, - vkAddressMode, - vkAddressMode, - 0.0f, - false, - 16.0f, - false, - vk::CompareOp::eAlways, - 0.0f, - 16.0f, - vk::BorderColor::eIntOpaqueBlack, - false - ); - - const vk::Sampler sampler = m_device.createSampler(samplerCreateInfo); - - const uint64_t id = m_samplers.size(); - m_samplers.push_back(sampler); - return SamplerHandle(id, [&](uint64_t id) { destroySamplerById(id); }); - } - - vk::Sampler SamplerManager::getVulkanSampler(const SamplerHandle &handle) const { - const uint64_t id = handle.getId(); - - if (id >= m_samplers.size()) { - return nullptr; + + switch (borderColor) { + case SamplerBorderColor::INT_ZERO_OPAQUE: + vkBorderColor = vk::BorderColor::eIntOpaqueBlack; + break; + case SamplerBorderColor::INT_ZERO_TRANSPARENT: + vkBorderColor = vk::BorderColor::eIntTransparentBlack; + break; + case SamplerBorderColor::FLOAT_ZERO_OPAQUE: + vkBorderColor = vk::BorderColor::eFloatOpaqueBlack; + break; + case SamplerBorderColor::FLOAT_ZERO_TRANSPARENT: + vkBorderColor = vk::BorderColor::eFloatTransparentBlack; + break; + case SamplerBorderColor::INT_ONE_OPAQUE: + vkBorderColor = vk::BorderColor::eIntOpaqueWhite; + break; + case SamplerBorderColor::FLOAT_ONE_OPAQUE: + vkBorderColor = vk::BorderColor::eFloatOpaqueWhite; + break; + default: + return SamplerHandle(); } - - return m_samplers[id]; + + const vk::SamplerCreateInfo samplerCreateInfo( + vk::SamplerCreateFlags(), vkMagFilter, vkMinFilter, vkMipmapMode, vkAddressMode, + vkAddressMode, vkAddressMode, mipLodBias, false, 16.0f, false, vk::CompareOp::eAlways, + -1000.0f, 1000.0f, vkBorderColor, false); + + const vk::Sampler sampler = + getCore().getContext().getDevice().createSampler(samplerCreateInfo); + + return add(sampler); } - - void SamplerManager::destroySamplerById(uint64_t id) { - if (id >= m_samplers.size()) { - return; - } - - auto& sampler = m_samplers[id]; - - if (sampler) { - m_device.destroySampler(sampler); - sampler = nullptr; - } + + vk::Sampler SamplerManager::getVulkanSampler(const SamplerHandle &handle) const { + return (*this) [handle]; } -} +} // namespace vkcv diff --git a/src/vkcv/SamplerManager.hpp b/src/vkcv/SamplerManager.hpp index 511176d4f87633a8691ca730ecc383e2588d8cf0..5ac033037f401ca97a658f2bec2d818110d2aa6a 100644 --- a/src/vkcv/SamplerManager.hpp +++ b/src/vkcv/SamplerManager.hpp @@ -3,40 +3,36 @@ #include <vector> #include <vulkan/vulkan.hpp> -#include "vkcv/Handles.hpp" +#include "HandleManager.hpp" + #include "vkcv/Sampler.hpp" namespace vkcv { - - class Core; - - class SamplerManager { + + /** + * @brief Class to manage the creation and destruction of samplers. + */ + class SamplerManager : public HandleManager<vk::Sampler, SamplerHandle> { friend class Core; + private: - vk::Device m_device; - std::vector<vk::Sampler> m_samplers; - - explicit SamplerManager(const vk::Device& device) noexcept; - - void destroySamplerById(uint64_t id); - + [[nodiscard]] uint64_t getIdFrom(const SamplerHandle &handle) const override; + + [[nodiscard]] SamplerHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + void destroyById(uint64_t id) override; + public: - ~SamplerManager(); - - SamplerManager(const SamplerManager& other) = delete; - SamplerManager(SamplerManager&& other) = delete; - - SamplerManager& operator=(const SamplerManager& other) = delete; - SamplerManager& operator=(SamplerManager&& other) = delete; - - SamplerHandle createSampler(SamplerFilterType magFilter, - SamplerFilterType minFilter, - SamplerMipmapMode mipmapMode, - SamplerAddressMode addressMode); - - [[nodiscard]] - vk::Sampler getVulkanSampler(const SamplerHandle& handle) const; - + SamplerManager() noexcept; + + ~SamplerManager() noexcept override; + + SamplerHandle createSampler(SamplerFilterType magFilter, SamplerFilterType minFilter, + SamplerMipmapMode mipmapMode, SamplerAddressMode addressMode, + float mipLodBias, SamplerBorderColor borderColor); + + [[nodiscard]] vk::Sampler getVulkanSampler(const SamplerHandle &handle) const; }; - -} + +} // namespace vkcv diff --git a/src/vkcv/ShaderProgram.cpp b/src/vkcv/ShaderProgram.cpp index 971797d9a42d071a1730ebf31a0b554f92fa361f..508f4de536fc193f9b213c87e7de7e3fe0679959 100644 --- a/src/vkcv/ShaderProgram.cpp +++ b/src/vkcv/ShaderProgram.cpp @@ -1,5 +1,5 @@ /** - * @authors Simeon Hermann, Leonie Franken + * @authors Simeon Hermann, Leonie Franken, Tobias Frisch * @file src/vkcv/ShaderProgram.cpp * @brief ShaderProgram class to handle and prepare the shader stages for a graphics pipeline */ @@ -8,217 +8,351 @@ #include "vkcv/Logger.hpp" namespace vkcv { - /** - * Reads the file of a given shader code. - * Only used within the class. - * @param[in] relative path to the shader code - * @return vector of chars as a buffer for the code - */ - std::vector<char> readShaderCode(const std::filesystem::path &shaderPath) { - std::ifstream file (shaderPath.string(), std::ios::ate | std::ios::binary); - + /** + * Reads the file of a given shader code. + * Only used within the class. + * @param[in] relative path to the shader code + * @return vector of chars as a buffer for the code + */ + std::vector<uint32_t> readShaderCode(const std::filesystem::path &shaderPath) { + std::ifstream file(shaderPath.string(), std::ios::ate | std::ios::binary); + if (!file.is_open()) { - vkcv_log(LogLevel::ERROR, "The file could not be opened"); - return std::vector<char>{}; + vkcv_log(LogLevel::ERROR, "The file could not be opened: %s", shaderPath.c_str()); + return std::vector<uint32_t>(); } - + size_t fileSize = (size_t)file.tellg(); - std::vector<char> buffer(fileSize); - + + if (fileSize % sizeof(uint32_t) != 0) { + vkcv_log(LogLevel::ERROR, "The file is not a valid shader: %s", shaderPath.c_str()); + return std::vector<uint32_t>(); + } + + std::vector<uint32_t> buffer(fileSize / sizeof(uint32_t)); + file.seekg(0); - file.read(buffer.data(), fileSize); + file.read(reinterpret_cast<char*>(buffer.data()), fileSize); file.close(); - - return buffer; + + return buffer; } - VertexAttachmentFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, uint32_t vecsize){ - switch (basetype) { - case spirv_cross::SPIRType::Int: - switch (vecsize) { - case 1: - return VertexAttachmentFormat::INT; - case 2: - return VertexAttachmentFormat::INT2; - case 3: - return VertexAttachmentFormat::INT3; - case 4: - return VertexAttachmentFormat::INT4; - default: - break; - } - break; - case spirv_cross::SPIRType::Float: - switch (vecsize) { - case 1: - return VertexAttachmentFormat::FLOAT; - case 2: - return VertexAttachmentFormat::FLOAT2; - case 3: - return VertexAttachmentFormat::FLOAT3; - case 4: - return VertexAttachmentFormat::FLOAT4; - default: - break; - } - break; - default: - break; - } - + VertexAttachmentFormat convertFormat(spirv_cross::SPIRType::BaseType basetype, + uint32_t vecsize) { + switch (basetype) { + case spirv_cross::SPIRType::Int: + switch (vecsize) { + case 1: + return VertexAttachmentFormat::INT; + case 2: + return VertexAttachmentFormat::INT2; + case 3: + return VertexAttachmentFormat::INT3; + case 4: + return VertexAttachmentFormat::INT4; + default: + break; + } + break; + case spirv_cross::SPIRType::Float: + switch (vecsize) { + case 1: + return VertexAttachmentFormat::FLOAT; + case 2: + return VertexAttachmentFormat::FLOAT2; + case 3: + return VertexAttachmentFormat::FLOAT3; + case 4: + return VertexAttachmentFormat::FLOAT4; + default: + break; + } + break; + default: + break; + } + vkcv_log(LogLevel::WARNING, "Unknown vertex format"); - return VertexAttachmentFormat::FLOAT; + return VertexAttachmentFormat::FLOAT; } ShaderProgram::ShaderProgram() noexcept : - m_Shaders{}, - m_VertexAttachments{}, - m_DescriptorSets{} - {} - - bool ShaderProgram::addShader(ShaderStage shaderStage, const std::filesystem::path &shaderPath) - { - if(m_Shaders.find(shaderStage) != m_Shaders.end()) { + m_Shaders {}, m_VertexAttachments {}, m_DescriptorSets {} {} + + bool ShaderProgram::addShader(ShaderStage stage, const std::filesystem::path &path) { + if (m_Shaders.find(stage) != m_Shaders.end()) { vkcv_log(LogLevel::WARNING, "Overwriting existing shader stage"); } - const std::vector<char> shaderCode = readShaderCode(shaderPath); - - if (shaderCode.empty()) { + const std::vector<uint32_t> shaderCode = readShaderCode(path); + + if (shaderCode.empty()) { return false; } else { - Shader shader{shaderCode, shaderStage}; - m_Shaders.insert(std::make_pair(shaderStage, shader)); - reflectShader(shaderStage); - return true; - } + m_Shaders.insert(std::make_pair(stage, shaderCode)); + reflectShader(stage); + return true; + } } - const Shader &ShaderProgram::getShader(ShaderStage shaderStage) const - { - return m_Shaders.at(shaderStage); + const std::vector<uint32_t> &ShaderProgram::getShaderBinary(ShaderStage stage) const { + return m_Shaders.at(stage); } - bool ShaderProgram::existsShader(ShaderStage shaderStage) const - { - if(m_Shaders.find(shaderStage) == m_Shaders.end()) - return false; - else - return true; - } - - void ShaderProgram::reflectShader(ShaderStage shaderStage) - { - auto shaderCodeChar = m_Shaders.at(shaderStage).shaderCode; - std::vector<uint32_t> shaderCode; + bool ShaderProgram::existsShader(ShaderStage stage) const { + if (m_Shaders.find(stage) == m_Shaders.end()) + return false; + else + return true; + } - for (uint32_t i = 0; i < shaderCodeChar.size()/4; i++) - shaderCode.push_back(((uint32_t*) shaderCodeChar.data())[i]); + void ShaderProgram::reflectShader(ShaderStage shaderStage) { + auto shaderCode = m_Shaders.at(shaderStage); - spirv_cross::Compiler comp(move(shaderCode)); - spirv_cross::ShaderResources resources = comp.get_shader_resources(); + spirv_cross::Compiler comp(shaderCode); + spirv_cross::ShaderResources resources = comp.get_shader_resources(); - //reflect vertex input - if (shaderStage == ShaderStage::VERTEX) - { + // reflect vertex input + if (shaderStage == ShaderStage::VERTEX) { // spirv-cross API (hopefully) returns the stage_inputs in order - for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) - { - // spirv-cross specific objects - auto& stage_input = resources.stage_inputs[i]; - const spirv_cross::SPIRType& base_type = comp.get_type(stage_input.base_type_id); + for (uint32_t i = 0; i < resources.stage_inputs.size(); i++) { + // spirv-cross specific objects + auto &stage_input = resources.stage_inputs [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(stage_input.base_type_id); // vertex input location - const uint32_t attachment_loc = comp.get_decoration(stage_input.id, spv::DecorationLocation); - // vertex input name - const std::string attachment_name = stage_input.name; + const uint32_t attachment_loc = + comp.get_decoration(stage_input.id, spv::DecorationLocation); + // vertex input name + const std::string attachment_name = stage_input.name; // vertex input format (implies its size) - const VertexAttachmentFormat attachment_format = convertFormat(base_type.basetype, base_type.vecsize); + const VertexAttachmentFormat attachment_format = + convertFormat(base_type.basetype, base_type.vecsize); - m_VertexAttachments.emplace_back(attachment_loc, attachment_name, attachment_format); - } + m_VertexAttachments.push_back( + { attachment_loc, attachment_name, attachment_format, 0 }); + } } - //reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage image) - std::vector<std::pair<uint32_t, DescriptorBinding>> bindings; - int32_t maxSetID = -1; - for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++) { - auto& u = resources.uniform_buffers[i]; - const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); - std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), - DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::UNIFORM_BUFFER, base_type.vecsize, shaderStage)); - bindings.push_back(descriptor); - if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) - maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); - } - - for (uint32_t i = 0; i < resources.storage_buffers.size(); i++) { - auto& u = resources.storage_buffers[i]; - const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); - std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), - DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::STORAGE_BUFFER, base_type.vecsize, shaderStage)); - bindings.push_back(descriptor); - if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) - maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); - } - - for (uint32_t i = 0; i < resources.separate_samplers.size(); i++) { - auto& u = resources.separate_samplers[i]; - const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); - std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), - DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::SAMPLER, base_type.vecsize, shaderStage)); - bindings.push_back(descriptor); - if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) - maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); - } - - for (uint32_t i = 0; i < resources.separate_images.size(); i++) { - auto& u = resources.separate_images[i]; - const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); - std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), - DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_SAMPLED, base_type.vecsize, shaderStage)); - bindings.push_back(descriptor); - if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) - maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); - - } - - for (uint32_t i = 0; i < resources.storage_images.size(); i++) { - auto& u = resources.storage_images[i]; - const spirv_cross::SPIRType& base_type = comp.get_type(u.base_type_id); - std::pair descriptor(comp.get_decoration(u.id, spv::DecorationDescriptorSet), - DescriptorBinding(comp.get_decoration(u.id, spv::DecorationBinding), DescriptorType::IMAGE_STORAGE, base_type.vecsize, shaderStage)); - bindings.push_back(descriptor); - if ((int32_t)comp.get_decoration(u.id, spv::DecorationDescriptorSet) > maxSetID) - maxSetID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); - } - if (maxSetID != -1) { - if((int32_t)m_DescriptorSets.size() <= maxSetID) m_DescriptorSets.resize(maxSetID + 1); - for (const auto &binding : bindings) { - m_DescriptorSets[binding.first].push_back(binding.second); - } - } - - //reflect push constants + // reflect descriptor sets (uniform buffer, storage buffer, sampler, sampled image, storage + // image) + std::vector<std::pair<uint32_t, DescriptorBinding>> bindings; + + for (uint32_t i = 0; i < resources.uniform_buffers.size(); i++) { + auto &u = resources.uniform_buffers [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + const spirv_cross::SPIRType &type = comp.get_type(u.type_id); + + uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding); + + uint32_t descriptorCount = base_type.vecsize; + bool variableCount = false; + // query whether reflected resources are qualified as one-dimensional array + if (type.array_size_literal [0]) { + descriptorCount = type.array [0]; + if (type.array [0] == 0) + variableCount = true; + } + + DescriptorBinding binding { + bindingID, DescriptorType::UNIFORM_BUFFER, descriptorCount, shaderStage, + variableCount, + variableCount // partialBinding == variableCount + }; + + auto insertionResult = + m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding)); + if (!insertionResult.second) { + insertionResult.first->second.shaderStages |= shaderStage; + + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } + } + + for (uint32_t i = 0; i < resources.storage_buffers.size(); i++) { + auto &u = resources.storage_buffers [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + const spirv_cross::SPIRType &type = comp.get_type(u.type_id); + + uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding); + + uint32_t descriptorCount = base_type.vecsize; + bool variableCount = false; + // query whether reflected resources are qualified as one-dimensional array + if (type.array_size_literal [0]) { + descriptorCount = type.array [0]; + if (type.array [0] == 0) + variableCount = true; + } + + DescriptorBinding binding { + bindingID, DescriptorType::STORAGE_BUFFER, descriptorCount, shaderStage, + variableCount, + variableCount // partialBinding == variableCount + }; + + auto insertionResult = + m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding)); + if (!insertionResult.second) { + insertionResult.first->second.shaderStages |= shaderStage; + + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } + } + + for (uint32_t i = 0; i < resources.separate_samplers.size(); i++) { + auto &u = resources.separate_samplers [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + const spirv_cross::SPIRType &type = comp.get_type(u.type_id); + + uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding); + + uint32_t descriptorCount = base_type.vecsize; + bool variableCount = false; + // query whether reflected resources are qualified as one-dimensional array + if (type.array_size_literal [0]) { + descriptorCount = type.array [0]; + if (type.array [0] == 0) + variableCount = true; + } + + DescriptorBinding binding { + bindingID, DescriptorType::SAMPLER, descriptorCount, shaderStage, variableCount, + variableCount // partialBinding == variableCount + }; + + auto insertionResult = + m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding)); + if (!insertionResult.second) { + insertionResult.first->second.shaderStages |= shaderStage; + + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } + } + + for (uint32_t i = 0; i < resources.separate_images.size(); i++) { + auto &u = resources.separate_images [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + const spirv_cross::SPIRType &type = comp.get_type(u.type_id); + + uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding); + + uint32_t descriptorCount = base_type.vecsize; + bool variableCount = false; + // query whether reflected resources are qualified as one-dimensional array + if (type.array_size_literal [0]) { + descriptorCount = type.array [0]; + if (type.array [0] == 0) + variableCount = true; + } + + DescriptorBinding binding { + bindingID, DescriptorType::IMAGE_SAMPLED, descriptorCount, shaderStage, + variableCount, + variableCount // partialBinding == variableCount + }; + + auto insertionResult = + m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding)); + if (!insertionResult.second) { + insertionResult.first->second.shaderStages |= shaderStage; + + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } + } + + for (uint32_t i = 0; i < resources.storage_images.size(); i++) { + auto &u = resources.storage_images [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + const spirv_cross::SPIRType &type = comp.get_type(u.type_id); + + uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding); + + uint32_t descriptorCount = base_type.vecsize; + bool variableCount = false; + // query whether reflected resources are qualified as one-dimensional array + if (type.array_size_literal [0]) { + descriptorCount = type.array [0]; + if (type.array [0] == 0) + variableCount = true; + } + + DescriptorBinding binding { + bindingID, DescriptorType::IMAGE_STORAGE, descriptorCount, shaderStage, + variableCount, + variableCount // partialBinding == variableCount + }; + + auto insertionResult = + m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding)); + if (!insertionResult.second) { + insertionResult.first->second.shaderStages |= shaderStage; + + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } + } + + // Used to reflect acceleration structure bindings for RTX. + for (uint32_t i = 0; i < resources.acceleration_structures.size(); i++) { + auto &u = resources.acceleration_structures [i]; + const spirv_cross::SPIRType &base_type = comp.get_type(u.base_type_id); + + uint32_t setID = comp.get_decoration(u.id, spv::DecorationDescriptorSet); + uint32_t bindingID = comp.get_decoration(u.id, spv::DecorationBinding); + auto binding = DescriptorBinding { bindingID, + DescriptorType::ACCELERATION_STRUCTURE_KHR, + base_type.vecsize, + shaderStage, + false, + false }; + + auto insertionResult = + m_DescriptorSets [setID].insert(std::make_pair(bindingID, binding)); + if (!insertionResult.second) { + insertionResult.first->second.shaderStages |= shaderStage; + + vkcv_log(LogLevel::WARNING, + "Attempting to overwrite already existing binding %u at set ID %u.", + bindingID, setID); + } + } + + // reflect push constants for (const auto &pushConstantBuffer : resources.push_constant_buffers) { for (const auto &range : comp.get_active_buffer_ranges(pushConstantBuffer.id)) { const size_t size = range.range + range.offset; - m_pushConstantSize = std::max(m_pushConstantSize, size); + m_pushConstantsSize = std::max(m_pushConstantsSize, size); } } - } + } - const std::vector<VertexAttachment> &ShaderProgram::getVertexAttachments() const - { - return m_VertexAttachments; + const VertexAttachments &ShaderProgram::getVertexAttachments() const { + return m_VertexAttachments; } - const std::vector<std::vector<DescriptorBinding>>& ShaderProgram::getReflectedDescriptors() const { - return m_DescriptorSets; - } + const std::unordered_map<uint32_t, DescriptorBindings> & + ShaderProgram::getReflectedDescriptors() const { + return m_DescriptorSets; + } - size_t ShaderProgram::getPushConstantSize() const - { - return m_pushConstantSize; + size_t ShaderProgram::getPushConstantsSize() const { + return m_pushConstantsSize; } -} +} // namespace vkcv + diff --git a/src/vkcv/Swapchain.cpp b/src/vkcv/Swapchain.cpp deleted file mode 100644 index 2c5b3530c396bc3532aa94cb59a120e3555291bf..0000000000000000000000000000000000000000 --- a/src/vkcv/Swapchain.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include <vkcv/Swapchain.hpp> -#include <utility> - -#include <GLFW/glfw3.h> - -namespace vkcv -{ - /** - * creates surface and checks availability - * @param window current window for the surface - * @param instance Vulkan-Instance - * @param physicalDevice Vulkan-PhysicalDevice - * @return created surface - */ - vk::SurfaceKHR createSurface(GLFWwindow* window, const vk::Instance& instance, const vk::PhysicalDevice& physicalDevice) { - //create surface - VkSurfaceKHR surface; - if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &surface) != VK_SUCCESS) { - throw std::runtime_error("failed to create a window surface!"); - } - vk::Bool32 surfaceSupport = false; - if (physicalDevice.getSurfaceSupportKHR(0, vk::SurfaceKHR(surface), &surfaceSupport) != vk::Result::eSuccess && surfaceSupport != true) { - throw std::runtime_error("surface is not supported by the device!"); - } - - return vk::SurfaceKHR(surface); - } - - Swapchain::Swapchain(const Surface &surface, - vk::SwapchainKHR swapchain, - vk::Format format, - vk::ColorSpaceKHR colorSpace, - vk::PresentModeKHR presentMode, - uint32_t imageCount, - vk::Extent2D extent) noexcept : - m_Surface(surface), - m_Swapchain(swapchain), - m_Format(format), - m_ColorSpace(colorSpace), - m_PresentMode(presentMode), - m_ImageCount(imageCount), - m_Extent(extent), - m_RecreationRequired(false) - {} - - Swapchain::Swapchain(const Swapchain &other) : - m_Surface(other.m_Surface), - m_Swapchain(other.m_Swapchain), - m_Format(other.m_Format), - m_ColorSpace(other.m_ColorSpace), - m_PresentMode(other.m_PresentMode), - m_ImageCount(other.m_ImageCount), - m_Extent(other.m_Extent), - m_RecreationRequired(other.m_RecreationRequired.load()) - {} - - const vk::SwapchainKHR& Swapchain::getSwapchain() const { - return m_Swapchain; - } - - vk::SurfaceKHR Swapchain::getSurface() const { - return m_Surface.handle; - } - - vk::Format Swapchain::getFormat() const{ - return m_Format; - } - - /** - * chooses Extent and clapms values to the available - * @param physicalDevice Vulkan-PhysicalDevice - * @param surface of the swapchain - * @param window of the current application - * @return chosen Extent for the surface - */ - vk::Extent2D chooseExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, const Window &window){ - vk::SurfaceCapabilitiesKHR surfaceCapabilities; - if(physicalDevice.getSurfaceCapabilitiesKHR(surface,&surfaceCapabilities) != vk::Result::eSuccess){ - throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface."); - } - - VkExtent2D extent2D = { - static_cast<uint32_t>(window.getWidth()), - static_cast<uint32_t>(window.getHeight()) - }; - - extent2D.width = std::max(surfaceCapabilities.minImageExtent.width, std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width)); - extent2D.height = std::max(surfaceCapabilities.minImageExtent.height, std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height)); - - return extent2D; - } - - /** - * chooses Surface Format for the current surface - * @param physicalDevice Vulkan-PhysicalDevice - * @param surface of the swapchain - * @return available Format - */ - vk::SurfaceFormatKHR chooseSurfaceFormat(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { - uint32_t formatCount; - physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, nullptr); - std::vector<vk::SurfaceFormatKHR> availableFormats(formatCount); - if (physicalDevice.getSurfaceFormatsKHR(surface, &formatCount, &availableFormats[0]) != vk::Result::eSuccess) { - throw std::runtime_error("Failed to get surface formats"); - } - - for (const auto& availableFormat : availableFormats) { - if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { - return availableFormat; - } - } - return availableFormats[0]; - } - - /** - * returns vk::PresentModeKHR::eMailbox if available or vk::PresentModeKHR::eFifo otherwise - * @param physicalDevice Vulkan-PhysicalDevice - * @param surface of the swapchain - * @return available PresentationMode - */ - vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { - uint32_t modeCount; - physicalDevice.getSurfacePresentModesKHR( surface, &modeCount, nullptr ); - std::vector<vk::PresentModeKHR> availablePresentModes(modeCount); - if (physicalDevice.getSurfacePresentModesKHR(surface, &modeCount, &availablePresentModes[0]) != vk::Result::eSuccess) { - throw std::runtime_error("Failed to get presentation modes"); - } - - for (const auto& availablePresentMode : availablePresentModes) { - if (availablePresentMode == vk::PresentModeKHR::eMailbox) { - return availablePresentMode; - } - } - // The FIFO present mode is guaranteed by the spec to be supported - return vk::PresentModeKHR::eFifo; - } - - /** - * returns the minImageCount +1 for at least doublebuffering, if it's greater than maxImageCount return maxImageCount - * @param physicalDevice Vulkan-PhysicalDevice - * @param surface of the swapchain - * @return available ImageCount - */ - uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { - vk::SurfaceCapabilitiesKHR surfaceCapabilities; - if(physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) != vk::Result::eSuccess){ - throw std::runtime_error("cannot get surface capabilities. There is an issue with the surface."); - } - - uint32_t imageCount = surfaceCapabilities.minImageCount + 1; // minImageCount should always be at least 2; set to 3 for triple buffering - // check if requested image count is supported - if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount) { - imageCount = surfaceCapabilities.maxImageCount; - } - - return imageCount; - } - /** - * creates and returns a swapchain with default specs - * @param window of the current application - * @param context that keeps instance, physicalDevice and a device. - * @return swapchain - */ - Swapchain Swapchain::create(const Window &window, const Context &context) { - const vk::Instance& instance = context.getInstance(); - const vk::PhysicalDevice& physicalDevice = context.getPhysicalDevice(); - const vk::Device& device = context.getDevice(); - - Surface surface; - surface.handle = createSurface(window.getWindow(), instance, physicalDevice); - surface.formats = physicalDevice.getSurfaceFormatsKHR(surface.handle); - surface.capabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface.handle); - surface.presentModes = physicalDevice.getSurfacePresentModesKHR(surface.handle); - - vk::Extent2D chosenExtent = chooseExtent(physicalDevice, surface.handle, window); - vk::SurfaceFormatKHR chosenSurfaceFormat = chooseSurfaceFormat(physicalDevice, surface.handle); - vk::PresentModeKHR chosenPresentMode = choosePresentMode(physicalDevice, surface.handle); - uint32_t chosenImageCount = chooseImageCount(physicalDevice, surface.handle); - - vk::SwapchainCreateInfoKHR swapchainCreateInfo( - vk::SwapchainCreateFlagsKHR(), //flags - surface.handle, // surface - chosenImageCount, // minImageCount TODO: how many do we need for our application?? "must be less than or equal to the value returned in maxImageCount" -> 3 for Triple Buffering, else 2 for Double Buffering (should be the standard) - chosenSurfaceFormat.format, // imageFormat - chosenSurfaceFormat.colorSpace, // imageColorSpace - chosenExtent, // imageExtent - 1, // imageArrayLayers TODO: should we only allow non-stereoscopic applications? yes -> 1, no -> ? "must be greater than 0, less or equal to maxImageArrayLayers" - vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, // imageUsage TODO: what attachments? only color? depth? - vk::SharingMode::eExclusive, // imageSharingMode TODO: which sharing mode? "VK_SHARING_MODE_EXCLUSIV access exclusive to a single queue family, better performance", "VK_SHARING_MODE_CONCURRENT access from multiple queues" - 0, // queueFamilyIndexCount, the number of queue families having access to the image(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT - nullptr, // pQueueFamilyIndices, the pointer to an array of queue family indices having access to the images(s) of the swapchain when imageSharingMode is VK_SHARING_MODE_CONCURRENT - vk::SurfaceTransformFlagBitsKHR::eIdentity, // preTransform, transformations applied onto the image before display - vk::CompositeAlphaFlagBitsKHR::eOpaque, // compositeAlpha, TODO: how to handle transparent pixels? do we need transparency? If no -> opaque - chosenPresentMode, // presentMode - true, // clipped - nullptr // oldSwapchain - ); - - vk::SwapchainKHR swapchain = device.createSwapchainKHR(swapchainCreateInfo); - - return Swapchain(surface, - swapchain, - chosenSurfaceFormat.format, - chosenSurfaceFormat.colorSpace, - chosenPresentMode, - chosenImageCount, - chosenExtent); - } - - bool Swapchain::shouldUpdateSwapchain() const { - return m_RecreationRequired; - } - - void Swapchain::updateSwapchain(const Context &context, const Window &window) { - if (!m_RecreationRequired.exchange(false)) - return; - - vk::SwapchainKHR oldSwapchain = m_Swapchain; - vk::Extent2D extent2D = chooseExtent(context.getPhysicalDevice(), m_Surface.handle, window); - - vk::SwapchainCreateInfoKHR swapchainCreateInfo( - vk::SwapchainCreateFlagsKHR(), - m_Surface.handle, - m_ImageCount, - m_Format, - m_ColorSpace, - extent2D, - 1, - vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, - vk::SharingMode::eExclusive, - 0, - nullptr, - vk::SurfaceTransformFlagBitsKHR::eIdentity, - vk::CompositeAlphaFlagBitsKHR::eOpaque, - m_PresentMode, - true, - oldSwapchain - ); - - m_Swapchain = context.getDevice().createSwapchainKHR(swapchainCreateInfo); - context.getDevice().destroySwapchainKHR(oldSwapchain); - - m_Extent = extent2D; - } - - void Swapchain::signalSwapchainRecreation() { - m_RecreationRequired = true; - } - - const vk::Extent2D& Swapchain::getExtent() const { - return m_Extent; - } - - Swapchain::~Swapchain() { - // needs to be destroyed by creator - } - - uint32_t Swapchain::getImageCount() const { - return m_ImageCount; - } -} diff --git a/src/vkcv/SwapchainManager.cpp b/src/vkcv/SwapchainManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..27efa5031853552ab9a2926d37ba3cb64cdbdc6c --- /dev/null +++ b/src/vkcv/SwapchainManager.cpp @@ -0,0 +1,328 @@ +#include "SwapchainManager.hpp" + +#include <GLFW/glfw3.h> + +#include "vkcv/Core.hpp" + +namespace vkcv { + + uint64_t SwapchainManager::getIdFrom(const SwapchainHandle &handle) const { + return handle.getId(); + } + + SwapchainHandle SwapchainManager::createById(uint64_t id, + const HandleDestroyFunction &destroy) { + return SwapchainHandle(id, destroy); + } + + void SwapchainManager::destroyById(uint64_t id) { + auto &swapchain = getById(id); + + if (swapchain.m_Swapchain) { + getCore().getContext().getDevice().destroySwapchainKHR(swapchain.m_Swapchain); + swapchain.m_Swapchain = nullptr; + } + + if (swapchain.m_Surface) { + getCore().getContext().getInstance().destroySurfaceKHR(swapchain.m_Surface); + swapchain.m_Surface = nullptr; + } + } + + SwapchainManager::SwapchainManager() noexcept : + HandleManager<SwapchainEntry, SwapchainHandle>() {} + + SwapchainManager::~SwapchainManager() noexcept { + clear(); + } + + /** + * @brief Creates vulkan surface and checks availability. + * + * @param[in,out] window Current window for the surface + * @param[in,out] instance Vulkan-Instance + * @param[in,out] physicalDevice Vulkan-PhysicalDevice + * @param[out] surface Vulkan-Surface + * @return Created vulkan surface + */ + static bool createVulkanSurface(GLFWwindow* window, const vk::Instance &instance, + const vk::PhysicalDevice &physicalDevice, + vk::SurfaceKHR &surface) { + VkSurfaceKHR api_surface; + + if (glfwCreateWindowSurface(VkInstance(instance), window, nullptr, &api_surface) + != VK_SUCCESS) { + vkcv_log(LogLevel::ERROR, "Failed to create a window surface"); + return false; + } + + vk::Bool32 surfaceSupport = false; + surface = vk::SurfaceKHR(api_surface); + + if ((physicalDevice.getSurfaceSupportKHR(0, surface, &surfaceSupport) + != vk::Result::eSuccess) + || (!surfaceSupport)) { + vkcv_log(LogLevel::ERROR, "Surface is not supported by the device"); + instance.destroy(surface); + surface = nullptr; + return false; + } + + return true; + } + + /** + * @brief Chooses an Extent and clamps values to the available capabilities. + * + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface Vulkan-Surface of the swapchain + * @param window Window of the current application + * @return Chosen Extent for the surface + */ + static vk::Extent2D chooseExtent(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface, + const Window &window) { + int fb_width, fb_height; + window.getFramebufferSize(fb_width, fb_height); + + VkExtent2D extent2D = { static_cast<uint32_t>(fb_width), static_cast<uint32_t>(fb_height) }; + + vk::SurfaceCapabilitiesKHR surfaceCapabilities; + if (physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities) + != vk::Result::eSuccess) { + vkcv_log(LogLevel::WARNING, "The capabilities of the surface can not be retrieved"); + + extent2D.width = std::max(MIN_SURFACE_SIZE, extent2D.width); + extent2D.height = std::max(MIN_SURFACE_SIZE, extent2D.height); + } else { + extent2D.width = + std::max(surfaceCapabilities.minImageExtent.width, + std::min(surfaceCapabilities.maxImageExtent.width, extent2D.width)); + extent2D.height = + std::max(surfaceCapabilities.minImageExtent.height, + std::min(surfaceCapabilities.maxImageExtent.height, extent2D.height)); + } + + return extent2D; + } + + /** + * @brief Chooses Surface Format for the current surface + * + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface Vulkan-Surface of the swapchain + * @return Available Format + */ + static vk::SurfaceFormatKHR chooseSurfaceFormat(vk::PhysicalDevice physicalDevice, + vk::SurfaceKHR surface) { + std::vector<vk::SurfaceFormatKHR> availableFormats = + physicalDevice.getSurfaceFormatsKHR(surface); + + for (const auto &availableFormat : availableFormats) { + if (availableFormat.format == vk::Format::eB8G8R8A8Unorm + && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { + return availableFormat; + } + } + + return availableFormats [0]; + } + + /** + * @brief Returns vk::PresentModeKHR::eMailbox if available or + * vk::PresentModeKHR::eFifo otherwise + * + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface Vulkan-Surface of the swapchain + * @return Available PresentationMode + */ + static vk::PresentModeKHR choosePresentMode(vk::PhysicalDevice physicalDevice, + vk::SurfaceKHR surface) { + std::vector<vk::PresentModeKHR> availablePresentModes = + physicalDevice.getSurfacePresentModesKHR(surface); + + for (const auto &availablePresentMode : availablePresentModes) { + if (availablePresentMode == vk::PresentModeKHR::eMailbox) { + return availablePresentMode; + } + } + // The FIFO present mode is guaranteed by the spec to be supported + return vk::PresentModeKHR::eFifo; + } + + /** + * @brief Returns the minImageCount +1 for at least double buffering, + * if it's greater than maxImageCount return maxImageCount + * + * @param physicalDevice Vulkan-PhysicalDevice + * @param surface Vulkan-Surface of the swapchain + * @return Available image count + */ + static uint32_t chooseImageCount(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface) { + vk::SurfaceCapabilitiesKHR surfaceCapabilities = + physicalDevice.getSurfaceCapabilitiesKHR(surface); + + // minImageCount should always be at least 2; set to 3 for triple buffering + uint32_t imageCount = surfaceCapabilities.minImageCount + 1; + + // check if requested image count is supported + if (surfaceCapabilities.maxImageCount > 0 + && imageCount > surfaceCapabilities.maxImageCount) { + imageCount = surfaceCapabilities.maxImageCount; + } + + return imageCount; + } + + static bool createVulkanSwapchain(const Context &context, const Window &window, + SwapchainEntry &entry) { + const vk::PhysicalDevice &physicalDevice = context.getPhysicalDevice(); + const vk::Device &device = context.getDevice(); + + entry.m_Extent = chooseExtent(physicalDevice, entry.m_Surface, window); + + if ((entry.m_Extent.width < MIN_SURFACE_SIZE) + || (entry.m_Extent.height < MIN_SURFACE_SIZE)) { + return false; + } + + vk::SurfaceFormatKHR chosenSurfaceFormat = + chooseSurfaceFormat(physicalDevice, entry.m_Surface); + vk::PresentModeKHR chosenPresentMode = choosePresentMode(physicalDevice, entry.m_Surface); + uint32_t chosenImageCount = chooseImageCount(physicalDevice, entry.m_Surface); + + entry.m_Format = chosenSurfaceFormat.format; + entry.m_ColorSpace = chosenSurfaceFormat.colorSpace; + + vk::SwapchainCreateInfoKHR swapchainCreateInfo( + vk::SwapchainCreateFlagsKHR(), entry.m_Surface, chosenImageCount, entry.m_Format, + entry.m_ColorSpace, entry.m_Extent, 1, + vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eStorage, + vk::SharingMode::eExclusive, 0, nullptr, vk::SurfaceTransformFlagBitsKHR::eIdentity, + vk::CompositeAlphaFlagBitsKHR::eOpaque, chosenPresentMode, true, entry.m_Swapchain); + + entry.m_Swapchain = device.createSwapchainKHR(swapchainCreateInfo); + return true; + } + + SwapchainHandle SwapchainManager::createSwapchain(Window &window) { + const vk::Instance &instance = getCore().getContext().getInstance(); + const vk::PhysicalDevice &physicalDevice = getCore().getContext().getPhysicalDevice(); + + vk::SurfaceKHR surfaceHandle; + if (!createVulkanSurface(window.getWindow(), instance, physicalDevice, surfaceHandle)) { + return {}; + } + + uint32_t presentQueueIndex = + QueueManager::checkSurfaceSupport(physicalDevice, surfaceHandle); + + const vk::Extent2D extent = chooseExtent(physicalDevice, surfaceHandle, window); + const vk::SurfaceFormatKHR format = chooseSurfaceFormat(physicalDevice, surfaceHandle); + + SwapchainEntry entry { nullptr, false, + + surfaceHandle, presentQueueIndex, + extent, format.format, + format.colorSpace }; + + if (!createVulkanSwapchain(getCore().getContext(), window, entry)) { + instance.destroySurfaceKHR(surfaceHandle); + return {}; + } + + window.m_swapchainHandle = add(entry); + return window.m_swapchainHandle; + } + + SwapchainEntry &SwapchainManager::getSwapchain(const SwapchainHandle &handle) { + return (*this) [handle]; + } + + bool SwapchainManager::shouldUpdateSwapchain(const SwapchainHandle &handle) const { + return (*this) [handle].m_RecreationRequired; + } + + void SwapchainManager::updateSwapchain(const SwapchainHandle &handle, const Window &window) { + auto &swapchain = (*this) [handle]; + + if (!swapchain.m_RecreationRequired) { + return; + } else { + swapchain.m_RecreationRequired = false; + } + + vk::SwapchainKHR oldSwapchain = swapchain.m_Swapchain; + + if (createVulkanSwapchain(getCore().getContext(), window, swapchain)) { + if (oldSwapchain) { + getCore().getContext().getDevice().destroySwapchainKHR(oldSwapchain); + } + } else { + signalRecreation(handle); + } + } + + void SwapchainManager::signalRecreation(const SwapchainHandle &handle) { + (*this) [handle].m_RecreationRequired = true; + } + + vk::Format SwapchainManager::getFormat(const SwapchainHandle &handle) const { + return (*this) [handle].m_Format; + } + + uint32_t SwapchainManager::getImageCount(const SwapchainHandle &handle) const { + auto &swapchain = (*this) [handle]; + + uint32_t imageCount; + if (vk::Result::eSuccess + != getCore().getContext().getDevice().getSwapchainImagesKHR(swapchain.m_Swapchain, + &imageCount, nullptr)) { + return 0; + } else { + return imageCount; + } + } + + const vk::Extent2D &SwapchainManager::getExtent(const SwapchainHandle &handle) const { + return (*this) [handle].m_Extent; + } + + uint32_t SwapchainManager::getPresentQueueIndex(const SwapchainHandle &handle) const { + return (*this) [handle].m_PresentQueueIndex; + } + + vk::ColorSpaceKHR SwapchainManager::getSurfaceColorSpace(const SwapchainHandle &handle) const { + return (*this) [handle].m_ColorSpace; + } + + std::vector<vk::Image> + SwapchainManager::getSwapchainImages(const SwapchainHandle &handle) const { + return getCore().getContext().getDevice().getSwapchainImagesKHR( + (*this) [handle].m_Swapchain); + } + + std::vector<vk::ImageView> + SwapchainManager::createSwapchainImageViews(SwapchainHandle &handle) { + std::vector<vk::Image> images = getSwapchainImages(handle); + auto &swapchain = (*this) [handle]; + + std::vector<vk::ImageView> imageViews; + imageViews.reserve(images.size()); + // here can be swizzled with vk::ComponentSwizzle if needed + vk::ComponentMapping componentMapping(vk::ComponentSwizzle::eR, vk::ComponentSwizzle::eG, + vk::ComponentSwizzle::eB, vk::ComponentSwizzle::eA); + + vk::ImageSubresourceRange subResourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1); + + for (auto image : images) { + vk::ImageViewCreateInfo imageViewCreateInfo(vk::ImageViewCreateFlags(), image, + vk::ImageViewType::e2D, swapchain.m_Format, + componentMapping, subResourceRange); + + imageViews.push_back( + getCore().getContext().getDevice().createImageView(imageViewCreateInfo)); + } + + return imageViews; + } +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/SwapchainManager.hpp b/src/vkcv/SwapchainManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..27262ec65da2b841597347c965fa462fae6a0d4c --- /dev/null +++ b/src/vkcv/SwapchainManager.hpp @@ -0,0 +1,159 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/SwapchainManager.hpp + * @brief Class to manage the swapchains and their surfaces. + */ + +#include <atomic> +#include <vector> +#include <vulkan/vulkan.hpp> + +#include "vkcv/Window.hpp" + +#include "HandleManager.hpp" + +namespace vkcv { + + const uint32_t MIN_SURFACE_SIZE = 2; + + /** + * @brief Structure to handle swapchains. + */ + struct SwapchainEntry { + vk::SwapchainKHR m_Swapchain; + bool m_RecreationRequired; + + vk::SurfaceKHR m_Surface; + uint32_t m_PresentQueueIndex; + vk::Extent2D m_Extent; + vk::Format m_Format; + vk::ColorSpaceKHR m_ColorSpace; + }; + + /** + * @brief Class to manage the creation, destruction and + * allocation of swapchains. + */ + class SwapchainManager : public HandleManager<SwapchainEntry, SwapchainHandle> { + friend class Core; + + private: + [[nodiscard]] uint64_t getIdFrom(const SwapchainHandle &handle) const override; + + [[nodiscard]] SwapchainHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + /** + * @brief Destroys a specific swapchain by a given id + * + * @param[in] id ID of the swapchain to be destroyed + */ + void destroyById(uint64_t id) override; + + public: + SwapchainManager() noexcept; + + /** + * destroys every swapchain + */ + ~SwapchainManager() noexcept override; + + /** + * creates a swapchain and returns the handle + * @param window of the to creatable window + * @return the swapchainHandle of the created swapchain + */ + SwapchainHandle createSwapchain(Window &window); + + /** + * @param handle of the swapchain to get + * @return the reference of the swapchain + */ + [[nodiscard]] SwapchainEntry &getSwapchain(const SwapchainHandle &handle); + + /** + * @brief Checks whether the swapchain needs to be recreated. + * + * @param[in] handle Swapchain handle + * @return True, if the swapchain should be updated, + * otherwise false. + */ + bool shouldUpdateSwapchain(const SwapchainHandle &handle) const; + + /** + * @brief Updates and recreates the swapchain. + * + * @param[in] handle Swapchain handle + * @param[in] window that the new swapchain gets bound to + */ + void updateSwapchain(const SwapchainHandle &handle, const Window &window); + + /** + * @brief Signals the swapchain to be recreated. + * + * @param[in] handle Swapchain handle + */ + void signalRecreation(const SwapchainHandle &handle); + + /** + * @brief Returns the image format for the current surface + * of the swapchain. + * + * @param[in] handle Swapchain handle + * @return Swapchain image format + */ + [[nodiscard]] vk::Format getFormat(const SwapchainHandle &handle) const; + + /** + * @brief Returns the amount of images for the swapchain. + * + * @param[in] handle Swapchain handle + * @return Number of images + */ + uint32_t getImageCount(const SwapchainHandle &handle) const; + + /** + * @brief Returns the extent from the current surface of + * the swapchain. + * + * @param[in] handle Swapchain handle + * @return Extent of the swapchains surface + */ + [[nodiscard]] const vk::Extent2D &getExtent(const SwapchainHandle &handle) const; + + /** + * @brief Returns the present queue index to be used with + * the swapchain and its current surface. + * + * @param[in] handle Swapchain handle + * @return Present queue family index + */ + [[nodiscard]] uint32_t getPresentQueueIndex(const SwapchainHandle &handle) const; + + /** + * @brief Returns the color space of the surface from + * a swapchain. + * + * @param[in] handle Swapchain handle + * @return Color space + */ + [[nodiscard]] vk::ColorSpaceKHR getSurfaceColorSpace(const SwapchainHandle &handle) const; + + /** + * gets the swapchain images + * @param handle of the swapchain + * @return a vector of the swapchain images + */ + [[nodiscard]] std::vector<vk::Image> + getSwapchainImages(const SwapchainHandle &handle) const; + + /** + * creates the swapchain imageViews for the swapchain + * @param handle of the swapchain which ImageViews should be created + * @return a ov ImageViews of the swapchain + */ + [[nodiscard]] std::vector<vk::ImageView> createSwapchainImageViews(SwapchainHandle &handle); + }; + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/SyncResources.cpp b/src/vkcv/SyncResources.cpp deleted file mode 100644 index 9c27fe32452e0ae648565020d92891764ececb3f..0000000000000000000000000000000000000000 --- a/src/vkcv/SyncResources.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "vkcv/SyncResources.hpp" - -namespace vkcv { - SyncResources createSyncResources(const vk::Device& device) { - SyncResources resources; - - const vk::SemaphoreCreateFlags semaphoreFlags = vk::SemaphoreCreateFlagBits(); - const vk::SemaphoreCreateInfo semaphoreInfo(semaphoreFlags); - resources.renderFinished = device.createSemaphore(semaphoreInfo, nullptr, {}); - resources.swapchainImageAcquired = device.createSemaphore(semaphoreInfo); - - resources.presentFinished = createFence(device); - - return resources; - } - - void destroySyncResources(const vk::Device& device, const SyncResources& resources) { - device.destroySemaphore(resources.renderFinished); - device.destroySemaphore(resources.swapchainImageAcquired); - device.destroyFence(resources.presentFinished); - } - - vk::Fence createFence(const vk::Device& device) { - const vk::FenceCreateFlags fenceFlags = vk::FenceCreateFlagBits(); - vk::FenceCreateInfo fenceInfo(fenceFlags); - return device.createFence(fenceInfo, nullptr, {}); - } - - void waitForFence(const vk::Device& device, const vk::Fence fence) { - const auto result = device.waitForFences(fence, true, UINT64_MAX); - assert(result == vk::Result::eSuccess); - } -} \ No newline at end of file diff --git a/src/vkcv/TypeGuard.cpp b/src/vkcv/TypeGuard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..742071bb7b8d055ff1e3037a825052b0f24ae84a --- /dev/null +++ b/src/vkcv/TypeGuard.cpp @@ -0,0 +1,84 @@ +#include <vkcv/TypeGuard.hpp> + +#include <string.h> +#include <vkcv/Logger.hpp> + +namespace vkcv { + +#ifndef NDEBUG + bool TypeGuard::checkType(const char* name, size_t hash, size_t size) const { + if (!checkTypeSize(size)) { + return false; + } + + if ((!m_typeName) || (!name)) { + return true; + } + + if (m_typeHash != hash) { + vkcv_log(LogLevel::WARNING, + "Hash (%lu) does not match the specified hash of the type guard (%lu)", hash, + m_typeHash); + + return false; + } + + if (strcmp(m_typeName, name) != 0) { + vkcv_log(LogLevel::WARNING, + "Name (%s) does not match the specified name of the type guard (%s)", name, + m_typeName); + + return false; + } else { + return true; + } + } +#endif + + bool TypeGuard::checkTypeSize(size_t size) const { + if (m_typeSize != size) { + vkcv_log(LogLevel::WARNING, + "Size (%lu) does not match the specified size of the type guard (%lu)", size, + m_typeSize); + + return false; + } else { + return true; + } + } + + TypeGuard::TypeGuard(size_t size) : +#ifndef NDEBUG + m_typeName(nullptr), m_typeHash(0), +#endif + m_typeSize(size) { + } + + TypeGuard::TypeGuard(const std::type_info &info, size_t size) : +#ifndef NDEBUG + m_typeName(info.name()), m_typeHash(info.hash_code()), +#endif + m_typeSize(size) { + } + + bool TypeGuard::operator==(const TypeGuard &other) const { +#ifndef NDEBUG + return checkType(other.m_typeName, other.m_typeHash, other.m_typeSize); +#else + return checkTypeSize(other.m_typeSize); +#endif + } + + bool TypeGuard::operator!=(const TypeGuard &other) const { +#ifndef NDEBUG + return !checkType(other.m_typeName, other.m_typeHash, other.m_typeSize); +#else + return !checkTypeSize(other.m_typeSize); +#endif + } + + size_t TypeGuard::typeSize() const { + return m_typeSize; + } + +} // namespace vkcv diff --git a/src/vkcv/VertexData.cpp b/src/vkcv/VertexData.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1fcfb3692fcc6051e237e759ce29c38e4c688f4 --- /dev/null +++ b/src/vkcv/VertexData.cpp @@ -0,0 +1,41 @@ + +#include "vkcv/VertexData.hpp" + +namespace vkcv { + + VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer, size_t offset) { + VertexBufferBinding binding; + binding.buffer = buffer; + binding.offset = offset; + return binding; + } + + VertexData::VertexData(const std::vector<VertexBufferBinding> &bindings) : + m_bindings(bindings), m_indices(), m_indexBitCount(IndexBitCount::Bit16), m_count(0) {} + + const std::vector<VertexBufferBinding> &VertexData::getVertexBufferBindings() const { + return m_bindings; + } + + void VertexData::setIndexBuffer(const BufferHandle &indices, IndexBitCount indexBitCount) { + m_indices = indices; + m_indexBitCount = indexBitCount; + } + + const BufferHandle &VertexData::getIndexBuffer() const { + return m_indices; + } + + IndexBitCount VertexData::getIndexBitCount() const { + return m_indexBitCount; + } + + void VertexData::setCount(size_t count) { + m_count = count; + } + + size_t VertexData::getCount() const { + return m_count; + } + +} // namespace vkcv diff --git a/src/vkcv/VertexLayout.cpp b/src/vkcv/VertexLayout.cpp index fa079a3264ae47b32461bda26485adb97b0be280..27347019a711a252c7e0b7c91232410e0b49fc1f 100644 --- a/src/vkcv/VertexLayout.cpp +++ b/src/vkcv/VertexLayout.cpp @@ -6,57 +6,59 @@ #include "vkcv/Logger.hpp" namespace vkcv { - uint32_t getFormatSize(VertexAttachmentFormat format) { - switch (format) { - case VertexAttachmentFormat::FLOAT: - return 4; - case VertexAttachmentFormat::FLOAT2: - return 8; - case VertexAttachmentFormat::FLOAT3: - return 12; - case VertexAttachmentFormat::FLOAT4: - return 16; - case VertexAttachmentFormat::INT: - return 4; - case VertexAttachmentFormat::INT2: - return 8; - case VertexAttachmentFormat::INT3: - return 12; - case VertexAttachmentFormat::INT4: - return 16; - default: - vkcv_log(LogLevel::WARNING, "No format given"); - return 0; - } - } - - VertexAttachment::VertexAttachment(uint32_t inputLocation, const std::string &name, VertexAttachmentFormat format) noexcept: - inputLocation{inputLocation}, - name{name}, - format{format}, - offset{0} - {} - - - VertexBinding::VertexBinding(uint32_t bindingLocation, const std::vector<VertexAttachment> &attachments) noexcept : - bindingLocation{bindingLocation}, - stride{0}, - vertexAttachments{attachments} - { - uint32_t offset = 0; - for (auto &attachment : vertexAttachments) - { - offset += getFormatSize(attachment.format); - attachment.offset = offset; - } - stride = offset; - } - - VertexLayout::VertexLayout() noexcept : - vertexBindings{} - {} - - VertexLayout::VertexLayout(const std::vector<VertexBinding> &bindings) noexcept : - vertexBindings{bindings} - {} -} \ No newline at end of file + + uint32_t getFormatSize(VertexAttachmentFormat format) { + switch (format) { + case VertexAttachmentFormat::FLOAT: + return 4; + case VertexAttachmentFormat::FLOAT2: + return 8; + case VertexAttachmentFormat::FLOAT3: + return 12; + case VertexAttachmentFormat::FLOAT4: + return 16; + case VertexAttachmentFormat::INT: + return 4; + case VertexAttachmentFormat::INT2: + return 8; + case VertexAttachmentFormat::INT3: + return 12; + case VertexAttachmentFormat::INT4: + return 16; + default: + vkcv_log(LogLevel::WARNING, "No format given"); + return 0; + } + } + + VertexBinding createVertexBinding(uint32_t bindingLocation, + const VertexAttachments &attachments) { + VertexBinding binding { bindingLocation, 0, attachments }; + uint32_t offset = 0; + + for (auto &attachment : binding.vertexAttachments) { + attachment.offset = offset; + offset += getFormatSize(attachment.format); + } + + binding.stride = offset; + return binding; + } + + VertexBindings createVertexBindings(const VertexAttachments &attachments) { + VertexBindings bindings; + bindings.reserve(attachments.size()); + + for (const auto& attachment : attachments) { + bindings.push_back(createVertexBinding(attachment.inputLocation, { attachment })); + } + + return bindings; + } + + VertexLayout createVertexLayout(const VertexBindings &bindings) { + VertexLayout layout { bindings }; + return layout; + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/Window.cpp b/src/vkcv/Window.cpp index 03a58a23b994209c7a0ee195732dc98543f0eddc..f989f1b84014aaca0a703e57d27151faa960a8bd 100644 --- a/src/vkcv/Window.cpp +++ b/src/vkcv/Window.cpp @@ -1,76 +1,153 @@ -/** - * @authors Sebastian Gaida - * @file src/vkcv/Window.cpp - * @brief Window class to handle a basic rendering surface and input - */ #include <GLFW/glfw3.h> +#include <thread> +#include <vector> + #include "vkcv/Window.hpp" namespace vkcv { + void Window_onMouseButtonEvent(GLFWwindow* callbackWindow, int button, int action, int mods) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_mouseButton(button, action, mods); + } + } + + void Window_onMouseMoveEvent(GLFWwindow* callbackWindow, double x, double y) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_mouseMove(x, y); + } + } + + void Window_onMouseScrollEvent(GLFWwindow* callbackWindow, double xoffset, double yoffset) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_mouseScroll(xoffset, yoffset); + } + } + + void Window_onResize(GLFWwindow* callbackWindow, int width, int height) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_resize(width, height); + } + } + + void Window_onKeyEvent(GLFWwindow* callbackWindow, int key, int scancode, int action, + int mods) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_key(key, scancode, action, mods); + } + } + + void Window_onCharEvent(GLFWwindow* callbackWindow, unsigned int c) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(callbackWindow)); + + if (window != nullptr) { + window->e_char(c); + } + } + static std::vector<GLFWwindow*> s_Windows; - Window::Window(GLFWwindow *window) : - m_window(window), - e_mouseButton(true), - e_mouseMove(true), - e_mouseScroll(true), - e_resize(true), - e_key(true), - e_char(true), - e_gamepad(true) - { - glfwSetWindowUserPointer(m_window, this); - - // combine Callbacks with Events - glfwSetMouseButtonCallback(m_window, Window::onMouseButtonEvent); - glfwSetCursorPosCallback(m_window, Window::onMouseMoveEvent); - glfwSetWindowSizeCallback(m_window, Window::onResize); - glfwSetKeyCallback(m_window, Window::onKeyEvent); - glfwSetScrollCallback(m_window, Window::onMouseScrollEvent); - glfwSetCharCallback(m_window, Window::onCharEvent); - } - - Window::~Window() { - Window::e_mouseButton.unlock(); - Window::e_mouseMove.unlock(); - Window::e_mouseScroll.unlock(); - Window::e_resize.unlock(); - Window::e_key.unlock(); - Window::e_char.unlock(); - Window::e_gamepad.unlock(); - - s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window)); - glfwDestroyWindow(m_window); - - if(s_Windows.empty()) { - glfwTerminate(); - } - } - - Window Window::create( const char *windowTitle, int width, int height, bool resizable) { - if(s_Windows.empty()) { + void Window_onGamepadEvent(int gamepadIndex) { + Window::getFocusedWindow().e_gamepad(gamepadIndex); + } + + static GLFWwindow* createGLFWWindow(const char* windowTitle, int width, int height, + bool resizable) { + if (s_Windows.empty()) { glfwInit(); } - + width = std::max(width, 1); height = std::max(height, 1); - + + glfwDefaultWindowHints(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE); - GLFWwindow *window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); - - s_Windows.push_back(window); - - return Window(window); - } - - void Window::pollEvents() { - - for (auto glfwWindow : s_Windows) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); - + + GLFWwindow* window = glfwCreateWindow(width, height, windowTitle, nullptr, nullptr); + + if (window) { + s_Windows.push_back(window); + } + + return window; + } + + static void bindGLFWWindow(GLFWwindow* windowHandle, Window* window) { + if (!windowHandle) { + return; + } + + glfwSetWindowUserPointer(windowHandle, window); + + // combine Callbacks with Events + glfwSetMouseButtonCallback(windowHandle, Window_onMouseButtonEvent); + glfwSetCursorPosCallback(windowHandle, Window_onMouseMoveEvent); + glfwSetWindowSizeCallback(windowHandle, Window_onResize); + glfwSetKeyCallback(windowHandle, Window_onKeyEvent); + glfwSetScrollCallback(windowHandle, Window_onMouseScrollEvent); + glfwSetCharCallback(windowHandle, Window_onCharEvent); + } + + Window::Window() : + m_title(), m_resizable(false), m_shouldClose(false), m_window(nullptr), e_mouseButton(true), + e_mouseMove(true), e_mouseScroll(true), e_resize(true), e_key(true), e_char(true), + e_gamepad(true) {} + + Window::Window(const std::string &title, int width, int height, bool resizable) : + m_title(title), m_resizable(resizable), m_shouldClose(false), + m_window(createGLFWWindow(title.c_str(), width, height, resizable)), e_mouseButton(true), + e_mouseMove(true), e_mouseScroll(true), e_resize(true), e_key(true), e_char(true), + e_gamepad(true) { + bindGLFWWindow(m_window, this); + } + + Window::~Window() { + Window::e_mouseButton.unlock(); + Window::e_mouseMove.unlock(); + Window::e_mouseScroll.unlock(); + Window::e_resize.unlock(); + Window::e_key.unlock(); + Window::e_char.unlock(); + Window::e_gamepad.unlock(); + Window::e_resize.remove(m_resizeHandle); + if (m_window) { + s_Windows.erase(std::find(s_Windows.begin(), s_Windows.end(), m_window)); + glfwDestroyWindow(m_window); + } + + if (s_Windows.empty()) { + glfwTerminate(); + } + } + + bool Window::hasOpenWindow() { + for (auto glfwWindow : s_Windows) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(glfwWindow)); + + if (window->isOpen()) { + return true; + } + } + + return false; + } + + void Window::pollEvents() { + for (auto glfwWindow : s_Windows) { + auto window = static_cast<Window*>(glfwGetWindowUserPointer(glfwWindow)); + window->e_mouseButton.unlock(); window->e_mouseMove.unlock(); window->e_mouseScroll.unlock(); @@ -78,19 +155,20 @@ namespace vkcv { window->e_key.unlock(); window->e_char.unlock(); window->e_gamepad.unlock(); - } + } + + glfwPollEvents(); - glfwPollEvents(); - - for (int gamepadIndex = GLFW_JOYSTICK_1; gamepadIndex <= GLFW_JOYSTICK_LAST; gamepadIndex++) { - if (glfwJoystickPresent(gamepadIndex)) { - onGamepadEvent(gamepadIndex); + for (int gamepadIndex = GLFW_JOYSTICK_1; gamepadIndex <= GLFW_JOYSTICK_LAST; + gamepadIndex++) { + if (glfwJoystickPresent(gamepadIndex)) { + Window_onGamepadEvent(gamepadIndex); } } - + for (auto glfwWindow : s_Windows) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(glfwWindow)); - + auto window = static_cast<Window*>(glfwGetWindowUserPointer(glfwWindow)); + window->e_mouseButton.lock(); window->e_mouseMove.lock(); window->e_mouseScroll.lock(); @@ -98,87 +176,99 @@ namespace vkcv { window->e_key.lock(); window->e_char.lock(); window->e_gamepad.lock(); + + window->m_shouldClose |= glfwWindowShouldClose(glfwWindow); + } + } + + const std::vector<std::string> &Window::getExtensions() { + static std::vector<std::string> extensions; + + if (extensions.empty()) { + if (s_Windows.empty()) { + glfwInit(); + } + + uint32_t glfwExtensionCount = 0; + const char** glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + for (uint32_t i = 0; i < glfwExtensionCount; i++) { + extensions.emplace_back(glfwExtensions [i]); + } + + if (s_Windows.empty()) { + glfwTerminate(); + } + } + + return extensions; + } + + bool Window::isOpen() const { + if (!m_window) { + return false; } - } - void Window::onMouseButtonEvent(GLFWwindow *callbackWindow, int button, int action, int mods) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + return !m_shouldClose; + } - if (window != nullptr) { - window->e_mouseButton(button, action, mods); - } - } + const std::string &Window::getTitle() const { + return m_title; + } - void Window::onMouseMoveEvent(GLFWwindow *callbackWindow, double x, double y) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + int Window::getWidth() const { + int width = 0; - if (window != nullptr) { - window->e_mouseMove(x, y); - } - } + if (m_window) { + glfwGetWindowSize(m_window, &width, nullptr); + } - void Window::onMouseScrollEvent(GLFWwindow *callbackWindow, double xoffset, double yoffset) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + return std::max(width, 1); + } - if (window != nullptr) { - window->e_mouseScroll(xoffset, yoffset); - } - } + int Window::getHeight() const { + int height = 0; - void Window::onResize(GLFWwindow *callbackWindow, int width, int height) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + if (m_window) { + glfwGetWindowSize(m_window, nullptr, &height); + } - if (window != nullptr) { - window->e_resize(width, height); - } - } + return std::max(height, 1); + } - void Window::onKeyEvent(GLFWwindow *callbackWindow, int key, int scancode, int action, int mods) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); + bool Window::isResizable() const { + return m_resizable; + } - if (window != nullptr) { - window->e_key(key, scancode, action, mods); - } - } - - void Window::onCharEvent(GLFWwindow *callbackWindow, unsigned int c) { - auto window = static_cast<Window *>(glfwGetWindowUserPointer(callbackWindow)); - - if (window != nullptr) { - window->e_char(c); + GLFWwindow* Window::getWindow() const { + return m_window; + } + + void Window::getFramebufferSize(int &width, int &height) const { + if (m_window) { + glfwGetFramebufferSize(m_window, &width, &height); + } else { + width = 0; + height = 0; } - } - - void Window::onGamepadEvent(int gamepadIndex) { - int activeWindowIndex = std::find_if(s_Windows.begin(), - s_Windows.end(), - [](GLFWwindow* window){return glfwGetWindowAttrib(window, GLFW_FOCUSED);}) - - s_Windows.begin(); - activeWindowIndex *= (activeWindowIndex < s_Windows.size()); // fixes index getting out of bounds (e.g. if there is no focused window) - auto window = static_cast<Window *>(glfwGetWindowUserPointer(s_Windows[activeWindowIndex])); - - if (window != nullptr) { - window->e_gamepad(gamepadIndex); - } - } - - bool Window::isWindowOpen() const { - return !glfwWindowShouldClose(m_window); - } - - int Window::getWidth() const { - int width; - glfwGetWindowSize(m_window, &width, nullptr); - return width; - } - - int Window::getHeight() const { - int height; - glfwGetWindowSize(m_window, nullptr, &height); - return height; - } - - GLFWwindow *Window::getWindow() const { - return m_window; - } -} + } + + Window &Window::getFocusedWindow() { + static Window defaultWindow; + + auto activeWindowIterator = + std::find_if(s_Windows.begin(), s_Windows.end(), [](GLFWwindow* window) { + return glfwGetWindowAttrib(window, GLFW_FOCUSED); + }); + + if (activeWindowIterator == s_Windows.end()) { + return defaultWindow; + } + Window &window = *static_cast<Window*>(glfwGetWindowUserPointer(*activeWindowIterator)); + return window; + } + + SwapchainHandle Window::getSwapchain() const { + return m_swapchainHandle; + } +} // namespace vkcv diff --git a/src/vkcv/WindowManager.cpp b/src/vkcv/WindowManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..058ee21933ee00df49bd158ded4e8a38979f67c3 --- /dev/null +++ b/src/vkcv/WindowManager.cpp @@ -0,0 +1,66 @@ +#include "WindowManager.hpp" + +namespace vkcv { + + uint64_t WindowManager::getIdFrom(const WindowHandle &handle) const { + return handle.getId(); + } + + WindowHandle WindowManager::createById(uint64_t id, const HandleDestroyFunction &destroy) { + return WindowHandle(id, destroy); + } + + void WindowManager::destroyById(uint64_t id) { + auto &window = getById(id); + + if (window) { + delete window; + window = nullptr; + } + } + + WindowManager::WindowManager() noexcept : HandleManager<Window*, WindowHandle>() {} + + WindowManager::~WindowManager() noexcept { + clear(); + } + + WindowHandle WindowManager::createWindow(SwapchainManager &swapchainManager, + const std::string &applicationName, + uint32_t windowWidth, uint32_t windowHeight, + bool resizeable) { + auto window = new Window(applicationName, static_cast<int>(windowWidth), + static_cast<int>(windowHeight), resizeable); + + SwapchainHandle swapchainHandle = swapchainManager.createSwapchain(*window); + + if (resizeable) { + const event_handle<int, int> &resizeHandle = + window->e_resize.add([&, handle = swapchainHandle](int width, int height) { + // copy handle because it would run out of scope and be invalid + swapchainManager.signalRecreation(handle); + }); + + window->m_resizeHandle = resizeHandle; + } + + return add(window); + } + + Window &WindowManager::getWindow(const WindowHandle &handle) const { + return *(*this) [handle]; + } + + std::vector<WindowHandle> WindowManager::getWindowHandles() const { + std::vector<WindowHandle> handles; + + for (size_t id = 0; id < getCount(); id++) { + if (getById(id)->isOpen()) { + handles.push_back(WindowHandle(id)); + } + } + + return handles; + } + +} // namespace vkcv \ No newline at end of file diff --git a/src/vkcv/WindowManager.hpp b/src/vkcv/WindowManager.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71b107c71baca467b035cc326abc7e2d488ad586 --- /dev/null +++ b/src/vkcv/WindowManager.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include <GLFW/glfw3.h> +#include <memory> +#include <string> +#include <vector> + +#include "vkcv/Window.hpp" + +#include "HandleManager.hpp" +#include "SwapchainManager.hpp" + +namespace vkcv { + + /** + * @brief Class to manage the windows of applications. + */ + class WindowManager : public HandleManager<Window*, WindowHandle> { + friend class Core; + + private: + [[nodiscard]] uint64_t getIdFrom(const WindowHandle &handle) const override; + + [[nodiscard]] WindowHandle createById(uint64_t id, + const HandleDestroyFunction &destroy) override; + + /** + * Destroys a specific window by a given id. + * + * @param[in] id ID of the window to be destroyed + */ + void destroyById(uint64_t id) override; + + public: + WindowManager() noexcept; + + /** + * destroys every window + */ + ~WindowManager() noexcept override; + + /** + * creates a window and returns it's handle + * @param swapchainManager for swapchain creation + * @param applicationName name of the window + * @param windowWidth + * @param windowHeight + * @param resizeable if the window is resizable + * @return window handle + */ + WindowHandle createWindow(SwapchainManager &swapchainManager, + const std::string &applicationName, uint32_t windowWidth, + uint32_t windowHeight, bool resizeable); + + /** + * @param handle of the window to get + * @return the reference of the window + */ + [[nodiscard]] Window &getWindow(const WindowHandle &handle) const; + + /** + * Returns a list of window handles for current active + * and open windows. + * + * @return List of window handles + */ + [[nodiscard]] std::vector<WindowHandle> getWindowHandles() const; + }; + +} // namespace vkcv \ No newline at end of file